From b54a60fdddc2c314dd9ab6fb3c68d0b7a59dd48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 11 Jul 2021 00:25:01 +0200 Subject: [PATCH 01/49] Trade: initial redesign of the SceneData class. No backwards compatibility in place yet, will get added back in the followup commits, together with proper documentation. --- doc/changelog.dox | 3 + doc/vulkan-mapping.dox | 6 +- src/Magnum/Trade/CMakeLists.txt | 2 +- src/Magnum/Trade/SceneData.cpp | 1125 ++++++- src/Magnum/Trade/SceneData.h | 1892 ++++++++++- .../Trade/Test/AbstractImporterTest.cpp | 5 +- src/Magnum/Trade/Test/CMakeLists.txt | 3 +- src/Magnum/Trade/Test/SceneDataTest.cpp | 2999 ++++++++++++++++- src/Magnum/Trade/Trade.h | 5 + 9 files changed, 5975 insertions(+), 65 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 00c587c04e..db333ca6ba 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -219,6 +219,9 @@ See also: @ref Trade::PhongMaterialData::normalTextureScale() and @ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features added for PBR materials recognizable also in classic Phong workflows. +- A completely redesigned @ref Trade::SceneData class that stores data of + the whole scene in a data-oriented way, allowing for storing custom fields + as well. See [mosra/magnum#525](https://github.com/mosra/magnum/pull/525). - New @ref Trade::SkinData class and @ref Trade::AbstractImporter::skin2D() / @ref Trade::AbstractImporter::skin3D() family of APIs for skin import, as well as support in @ref Trade::AnySceneImporter "AnySceneImporter" diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 617d805c0a..4e0b52354b 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -429,7 +429,7 @@ Vulkan structure | Matching API @type_vk{ClearColorValue} | convertible from/to @ref Magnum::Vector3 "Vector3", @ref Magnum::Color3 "Color3", @ref Magnum::Vector4 "Vector4", @ref Magnum::Color4 "Color4", @ref Magnum::Vector4i "Vector4i", @ref Magnum::Vector4ui "Vector4ui" using @ref Magnum/Vk/Integration.h; only exposed through @ref RenderPassBeginInfo::clearColor() and @ref CommandBuffer::clearColorImage() overloads @type_vk{ClearDepthStencilValue} | only exposed through @ref RenderPassBeginInfo::clearDepthStencil(), @ref CommandBuffer::clearDepthStencilImage() and friends @type_vk{ClearValue} | only exposed through @ref RenderPassBeginInfo::clearColor() and @relativeref{RenderPassBeginInfo,clearDepthStencil()} overloads -@type_vk{ClearRect} | convertible from/to @ref Range3Di using @ref Magnum/Vk/Integration.h +@type_vk{ClearRect} | convertible from/to @relativeref{Magnum,Range3Di} using @ref Magnum/Vk/Integration.h @type_vk{CommandBufferAllocateInfo} | not exposed, internal to @ref CommandPool::allocate() @type_vk{CommandBufferBeginInfo} | @ref CommandBufferBeginInfo @type_vk{CommandBufferInheritanceInfo} | | @@ -692,7 +692,7 @@ Vulkan structure | Matching API @type_vk{RayTracingPipelineCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{RayTracingPipelineInterfaceCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{RayTracingShaderGroupCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{Rect2D} | convertible from/to @ref Range2Di using @ref Magnum/Vk/Integration.h +@type_vk{Rect2D} | convertible from/to @relativeref{Magnum,Range2Di} using @ref Magnum/Vk/Integration.h @type_vk{RenderPassBeginInfo} | @ref RenderPassBeginInfo @type_vk{RenderPassAttachmentBeginInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{RenderPassCreateInfo}, \n @type_vk{RenderPassCreateInfo2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref RenderPassCreateInfo @@ -755,7 +755,7 @@ Vulkan structure | Matching API @type_vk{VertexInputBindingDescription} | @ref MeshLayout @type_vk{VertexInputBindingDivisorDescriptionEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref MeshLayout @type_vk{VertexInputAttributeDescription} | @ref MeshLayout -@type_vk{Viewport} | convertible from/to @ref Range3D using @ref Magnum/Vk/Integration.h +@type_vk{Viewport} | convertible from/to @relativeref{Magnum,Range3D} using @ref Magnum/Vk/Integration.h @subsection vulkan-mapping-structures-w W diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index 9aa7a27c66..ec032592ea 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -30,7 +30,6 @@ set(MagnumTrade_SRCS Data.cpp MeshObjectData2D.cpp MeshObjectData3D.cpp - SceneData.cpp TextureData.cpp) set(MagnumTrade_GracefulAssert_SRCS @@ -50,6 +49,7 @@ set(MagnumTrade_GracefulAssert_SRCS PbrMetallicRoughnessMaterialData.cpp PbrSpecularGlossinessMaterialData.cpp PhongMaterialData.cpp + SceneData.cpp SkinData.cpp) set(MagnumTrade_HEADERS diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 44c6bf29e9..4e92ebe257 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -25,20 +25,1127 @@ #include "SceneData.h" +#include + +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Math/DualComplex.h" +#include "Magnum/Math/DualQuaternion.h" +#include "Magnum/Math/PackingBatch.h" +#include "Magnum/Trade/Implementation/arrayUtilities.h" + namespace Magnum { namespace Trade { -SceneData::SceneData(std::vector children2D, std::vector children3D, const void* const importerState): _children2D{std::move(children2D)}, _children3D{std::move(children3D)}, _importerState{importerState} {} +Debug& operator<<(Debug& debug, const SceneObjectType value) { + debug << "Trade::SceneObjectType" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case SceneObjectType::value: return debug << "::" #value; + _c(UnsignedByte) + _c(UnsignedInt) + _c(UnsignedShort) + _c(UnsignedLong) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +UnsignedInt sceneObjectTypeSize(const SceneObjectType type) { + switch(type) { + case SceneObjectType::UnsignedByte: return 1; + case SceneObjectType::UnsignedShort: return 2; + case SceneObjectType::UnsignedInt: return 4; + case SceneObjectType::UnsignedLong: return 8; + } + + CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {}); +} + +Debug& operator<<(Debug& debug, const SceneField value) { + debug << "Trade::SceneField" << Debug::nospace; + + if(UnsignedInt(value) >= UnsignedInt(SceneField::Custom)) + return debug << "::Custom(" << Debug::nospace << (UnsignedInt(value) - UnsignedInt(SceneField::Custom)) << Debug::nospace << ")"; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case SceneField::value: return debug << "::" #value; + _c(Parent) + _c(Transformation) + _c(Translation) + _c(Rotation) + _c(Scaling) + _c(Mesh) + _c(MeshMaterial) + _c(Light) + _c(Camera) + _c(Skin) + #undef _c + /* LCOV_EXCL_STOP */ + + /* To silence compiler warning about unhandled values */ + case SceneField::Custom: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const SceneFieldType value) { + debug << "Trade::SceneFieldType" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case SceneFieldType::value: return debug << "::" #value; + _c(Float) + _c(Half) + _c(Double) + _c(UnsignedByte) + _c(Byte) + _c(UnsignedShort) + _c(Short) + _c(UnsignedInt) + _c(Int) + _c(UnsignedLong) + _c(Long) + _c(Vector2) + _c(Vector2h) + _c(Vector2d) + _c(Vector2ub) + _c(Vector2b) + _c(Vector2us) + _c(Vector2s) + _c(Vector2ui) + _c(Vector2i) + _c(Vector3) + _c(Vector3h) + _c(Vector3d) + _c(Vector3ub) + _c(Vector3b) + _c(Vector3us) + _c(Vector3s) + _c(Vector3ui) + _c(Vector3i) + _c(Vector4) + _c(Vector4h) + _c(Vector4d) + _c(Vector4ub) + _c(Vector4b) + _c(Vector4us) + _c(Vector4s) + _c(Vector4ui) + _c(Vector4i) + _c(Matrix2x2) + _c(Matrix2x2h) + _c(Matrix2x2d) + _c(Matrix2x3) + _c(Matrix2x3h) + _c(Matrix2x3d) + _c(Matrix2x4) + _c(Matrix2x4h) + _c(Matrix2x4d) + _c(Matrix3x2) + _c(Matrix3x2h) + _c(Matrix3x2d) + _c(Matrix3x3) + _c(Matrix3x3h) + _c(Matrix3x3d) + _c(Matrix3x4) + _c(Matrix3x4h) + _c(Matrix3x4d) + _c(Matrix4x2) + _c(Matrix4x2h) + _c(Matrix4x2d) + _c(Matrix4x3) + _c(Matrix4x3h) + _c(Matrix4x3d) + _c(Matrix4x4) + _c(Matrix4x4h) + _c(Matrix4x4d) + _c(Range1D) + _c(Range1Dh) + _c(Range1Dd) + _c(Range1Di) + _c(Range2D) + _c(Range2Dh) + _c(Range2Dd) + _c(Range2Di) + _c(Range3D) + _c(Range3Dh) + _c(Range3Dd) + _c(Range3Di) + _c(Complex) + _c(Complexd) + _c(DualComplex) + _c(DualComplexd) + _c(Quaternion) + _c(Quaterniond) + _c(DualQuaternion) + _c(DualQuaterniond) + _c(Deg) + _c(Degh) + _c(Degd) + _c(Rad) + _c(Radh) + _c(Radd) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; +} + +UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { + switch(type) { + case SceneFieldType::UnsignedByte: + case SceneFieldType::Byte: + return 1; + case SceneFieldType::UnsignedShort: + case SceneFieldType::Short: + case SceneFieldType::Half: + case SceneFieldType::Vector2ub: + case SceneFieldType::Vector2b: + case SceneFieldType::Degh: + case SceneFieldType::Radh: + return 2; + case SceneFieldType::Vector3ub: + case SceneFieldType::Vector3b: + return 3; + case SceneFieldType::UnsignedInt: + case SceneFieldType::Int: + case SceneFieldType::Float: + case SceneFieldType::Vector2us: + case SceneFieldType::Vector2s: + case SceneFieldType::Vector2h: + case SceneFieldType::Vector4ub: + case SceneFieldType::Vector4b: + case SceneFieldType::Range1Dh: + case SceneFieldType::Deg: + case SceneFieldType::Rad: + return 4; + case SceneFieldType::Vector3us: + case SceneFieldType::Vector3s: + case SceneFieldType::Vector3h: + return 6; + case SceneFieldType::UnsignedLong: + case SceneFieldType::Long: + case SceneFieldType::Double: + case SceneFieldType::Vector2: + case SceneFieldType::Vector2ui: + case SceneFieldType::Vector2i: + case SceneFieldType::Vector4us: + case SceneFieldType::Vector4s: + case SceneFieldType::Vector4h: + case SceneFieldType::Matrix2x2h: + case SceneFieldType::Range1D: + case SceneFieldType::Range1Di: + case SceneFieldType::Range2Dh: + case SceneFieldType::Complex: + case SceneFieldType::Degd: + case SceneFieldType::Radd: + return 8; + case SceneFieldType::Vector3: + case SceneFieldType::Vector3ui: + case SceneFieldType::Vector3i: + case SceneFieldType::Matrix2x3h: + case SceneFieldType::Matrix3x2h: + case SceneFieldType::Range3Dh: + return 12; + case SceneFieldType::Vector2d: + case SceneFieldType::Vector4: + case SceneFieldType::Vector4ui: + case SceneFieldType::Vector4i: + case SceneFieldType::Matrix2x2: + case SceneFieldType::Matrix2x4h: + case SceneFieldType::Matrix4x2h: + case SceneFieldType::Range1Dd: + case SceneFieldType::Range2D: + case SceneFieldType::Range2Di: + case SceneFieldType::Complexd: + case SceneFieldType::DualComplex: + case SceneFieldType::Quaternion: + return 16; + case SceneFieldType::Matrix3x3h: + return 18; + case SceneFieldType::Vector3d: + case SceneFieldType::Matrix2x3: + case SceneFieldType::Matrix3x4h: + case SceneFieldType::Matrix3x2: + case SceneFieldType::Matrix4x3h: + case SceneFieldType::Range3D: + case SceneFieldType::Range3Di: + return 24; + case SceneFieldType::Vector4d: + case SceneFieldType::Matrix2x2d: + case SceneFieldType::Matrix2x4: + case SceneFieldType::Matrix4x2: + case SceneFieldType::Matrix4x4h: + case SceneFieldType::Range2Dd: + case SceneFieldType::DualComplexd: + case SceneFieldType::Quaterniond: + case SceneFieldType::DualQuaternion: + return 32; + case SceneFieldType::Matrix3x3: + return 36; + case SceneFieldType::Matrix2x3d: + case SceneFieldType::Matrix3x4: + case SceneFieldType::Matrix3x2d: + case SceneFieldType::Matrix4x3: + case SceneFieldType::Range3Dd: + return 48; + case SceneFieldType::Matrix2x4d: + case SceneFieldType::Matrix4x2d: + case SceneFieldType::Matrix4x4: + case SceneFieldType::DualQuaterniond: + return 64; + case SceneFieldType::Matrix3x3d: + return 72; + case SceneFieldType::Matrix3x4d: + case SceneFieldType::Matrix4x3d: + return 96; + case SceneFieldType::Matrix4x4d: + return 128; + } + + CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): invalid type" << type, {}); +} + +SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{objectData.data(), ~std::size_t{}}, objectData.size()[0], objectData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} { + /* Yes, this calls into a constexpr function defined in the header -- + because I feel that makes more sense than duplicating the full assert + logic */ + #ifndef CORRADE_NO_ASSERT + if(fieldArraySize) CORRADE_ASSERT(fieldData.empty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType)*fieldArraySize, + "Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType << "and field array size" << fieldArraySize, ); + else CORRADE_ASSERT(fieldData.empty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType), + "Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType, ); + #endif + + if(objectData.size()[1] == 8) _objectType = SceneObjectType::UnsignedLong; + else if(objectData.size()[1] == 4) _objectType = SceneObjectType::UnsignedInt; + else if(objectData.size()[1] == 2) _objectType = SceneObjectType::UnsignedShort; + else if(objectData.size()[1] == 1) _objectType = SceneObjectType::UnsignedByte; + else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got" << objectData.size()[1], ); + + CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", ); + CORRADE_ASSERT(objectData.isContiguous<1>(), "Trade::SceneFieldData: second object view dimension is not contiguous", ); +} + +Containers::Array sceneFieldDataNonOwningArray(const Containers::ArrayView view) { + /* Ugly, eh? */ + return Containers::Array{const_cast(view.data()), view.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}; +} + +SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{objectType}, _objectCount{objectCount}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} { + /* Check that object type is large enough */ + CORRADE_ASSERT( + (objectType == SceneObjectType::UnsignedByte && objectCount <= 0xffull) || + (objectType == SceneObjectType::UnsignedShort && objectCount <= 0xffffull) || + (objectType == SceneObjectType::UnsignedInt && objectCount <= 0xffffffffull) || + objectType == SceneObjectType::UnsignedLong, + "Trade::SceneData:" << objectType << "is too small for" << objectCount << "objects", ); + + #ifndef CORRADE_NO_ASSERT + /* Check various assumptions about field data */ + Math::BoolVector<11> fieldsPresent; /** @todo some constant for this */ + const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType); + UnsignedInt translationField = ~UnsignedInt{}; + UnsignedInt rotationField = ~UnsignedInt{}; + UnsignedInt scalingField = ~UnsignedInt{}; + UnsignedInt meshField = ~UnsignedInt{}; + UnsignedInt meshMaterialField = ~UnsignedInt{}; + for(std::size_t i = 0; i != _fields.size(); ++i) { + const SceneFieldData& field = _fields[i]; + + /* The object type has to be the same among all fields. Technically it + wouldn't need to be, but if there's 60k objects then using a 8bit + type for certain fields would mean only the first 256 objects can be + referenced, which makes no practical sense, and to improve the + situation there would need to be some additional per-field object + offset and ... it's simpler to just require the object type to be + large enough to reference all objects (checked outside of the loop + above) and that it's the same for all fields. This also makes it + more convenient for the user. */ + CORRADE_ASSERT(field._objectType == _objectType, + "Trade::SceneData: inconsistent object type, got" << field._objectType << "for field" << i << "but expected" << _objectType, ); + + /* We could check that object indices are in bounds, but that's rather + expensive. OTOH it's fine if field size is larger than object count, + as a single object can have multiple instances of the same field + attached, so checking that would be wrong. */ + + /* Check that there are only unique fields. To avoid a O(n^2) operation + always (or allocating a sorted field map), builtin fields are + checked against a map and only custom fields are checked in an + O(n^2) way with the assumption there isn't many of them (and that + they'll gradually become builtin). */ + if(!isSceneFieldCustom(_fields[i]._name)) { + CORRADE_INTERNAL_ASSERT(UnsignedInt(_fields[i]._name) < fieldsPresent.Size); + CORRADE_ASSERT(!fieldsPresent[UnsignedInt(_fields[i]._name)], + "Trade::SceneData: duplicate field" << _fields[i]._name, ); + fieldsPresent.set(UnsignedInt(_fields[i]._name), true); + } else for(std::size_t j = 0; j != i; ++j) { + CORRADE_ASSERT(_fields[j]._name != _fields[i]._name, + "Trade::SceneData: duplicate field" << _fields[i]._name, ); + } + + /* Check that both the object and field view fits into the provided + data array. If the field is empty, we don't check anything -- + accessing the memory would be invalid anyway and enforcing this + would lead to unnecessary friction with optional fields. */ + if(field._size) { + const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)* + (field._fieldArraySize ? field._fieldArraySize : 1); + if(field._isOffsetOnly) { + const std::size_t objectSize = field._objectData.offset + (field._size - 1)*field._objectStride + objectTypeSize; + const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize; + CORRADE_ASSERT(objectSize <= _data.size(), + "Trade::SceneData: offset-only object data of field" << i << "span" << objectSize << "bytes but passed data array has only" << _data.size(), ); + CORRADE_ASSERT(fieldSize <= _data.size(), + "Trade::SceneData: offset-only field data of field" << i << "span" << fieldSize << "bytes but passed data array has only" << _data.size(), ); + } else { + const void* const objectBegin = field._objectData.pointer; + const void* const fieldBegin = field._fieldData.pointer; + const void* const objectEnd = static_cast(field._objectData.pointer) + (field._size - 1)*field._objectStride + objectTypeSize; + const void* const fieldEnd = static_cast(field._fieldData.pointer) + (field._size - 1)*field._fieldStride + fieldTypeSize; + CORRADE_ASSERT(objectBegin >= _data.begin() && objectEnd <= _data.end(), + "Trade::SceneData: object data [" << Debug::nospace << objectBegin << Debug::nospace << ":" << Debug::nospace << objectEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); + CORRADE_ASSERT(fieldBegin >= _data.begin() && fieldEnd <= _data.end(), + "Trade::SceneData: field data [" << Debug::nospace << fieldBegin << Debug::nospace << ":" << Debug::nospace << fieldEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); + } + } + + /* Remember TRS and mesh/material fields to check their object mapping + consistency outside of the loop below */ + if(_fields[i]._name == SceneField::Translation) { + translationField = i; + } else if(_fields[i]._name == SceneField::Rotation) { + rotationField = i; + } else if(_fields[i]._name == SceneField::Scaling) { + scalingField = i; + } else if(_fields[i]._name == SceneField::Mesh) { + meshField = i; + } else if(_fields[i]._name == SceneField::MeshMaterial) { + meshMaterialField = i; + } + } + + /* Check that certain fields share the same object mapping. Printing as if + all would be pointers (and not offset-only), it's not worth the extra + effort just for an assert message. Also, compared to above, where + "begin" was always zero, here we're always comparing four values, so the + message for offset-only wouldn't be simpler either. */ + const auto checkFieldObjectDataMatch = [](const SceneFieldData& a, const SceneFieldData& b) { + const std::size_t objectTypeSize = sceneObjectTypeSize(a._objectType); + const void* const aBegin = a._objectData.pointer; + const void* const bBegin = b._objectData.pointer; + const void* const aEnd = static_cast(a._objectData.pointer) + a._size*objectTypeSize; + const void* const bEnd = static_cast(b._objectData.pointer) + b._size*objectTypeSize; + CORRADE_ASSERT(aBegin == bBegin && aEnd == bEnd, + "Trade::SceneData:" << b._name << "object data [" << Debug::nospace << bBegin << Debug::nospace << ":" << Debug::nospace << bEnd << Debug::nospace << "] is different from" << a._name << "object data [" << Debug::nospace << aBegin << Debug::nospace << ":" << Debug::nospace << aEnd << Debug::nospace << "]", ); + }; + + /* All present TRS fields should share the same object mapping */ + if(translationField != ~UnsignedInt{}) { + if(rotationField != ~UnsignedInt{}) + checkFieldObjectDataMatch(_fields[translationField], _fields[rotationField]); + if(scalingField != ~UnsignedInt{}) + checkFieldObjectDataMatch(_fields[translationField], _fields[scalingField]); + } + if(rotationField != ~UnsignedInt{} && scalingField != ~UnsignedInt{}) + checkFieldObjectDataMatch(_fields[rotationField], _fields[scalingField]); + + /* Mesh and materials also */ + if(meshField != ~UnsignedInt{} && meshMaterialField != ~UnsignedInt{}) + checkFieldObjectDataMatch(_fields[meshField], _fields[meshMaterialField]); + #endif +} + +SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} + +SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView data, Containers::Array&& fields, const void* const importerState) noexcept: SceneData{objectType, objectCount, Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(fields), importerState} { + CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), + "Trade::SceneData: can't construct with non-owned data but" << dataFlags, ); + _dataFlags = dataFlags; +} + +SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, dataFlags, data, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} + +SceneData::SceneData(SceneData&&) noexcept = default; + +SceneData::~SceneData() = default; + +SceneData& SceneData::operator=(SceneData&&) noexcept = default; + +Containers::ArrayView SceneData::mutableData() & { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::SceneData::mutableData(): data not mutable", {}); + return _data; +} + +Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field) const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + {field._isOffsetOnly ? _data.data() + field._objectData.offset : + field._objectData.pointer, ~std::size_t{}}, + field._size, field._objectStride}; +} + +Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal(const SceneFieldData& field) const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + {field._isOffsetOnly ? _data.data() + field._fieldData.offset : + field._fieldData.pointer, ~std::size_t{}}, + field._size, field._fieldStride}; +} + +SceneFieldData SceneData::fieldData(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldData(): index" << id << "out of range for" << _fields.size() << "fields", SceneFieldData{}); + const SceneFieldData& field = _fields[id]; + return SceneFieldData{field._name, field._objectType, fieldDataObjectViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize}; +} + +SceneField SceneData::fieldName(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldName(): index" << id << "out of range for" << _fields.size() << "fields", {}); + return _fields[id]._name; +} + +SceneFieldType SceneData::fieldType(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldType(): index" << id << "out of range for" << _fields.size() << "fields", {}); + return _fields[id]._fieldType; +} + +std::size_t SceneData::fieldSize(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldSize(): index" << id << "out of range for" << _fields.size() << "fields", {}); + return _fields[id]._size; +} + +UnsignedShort SceneData::fieldArraySize(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldArraySize(): index" << id << "out of range for" << _fields.size() << "fields", {}); + return _fields[id]._fieldArraySize; +} + +UnsignedInt SceneData::fieldFor(const SceneField name) const { + for(std::size_t i = 0; i != _fields.size(); ++i) + if(_fields[i]._name == name) return i; + return ~UnsignedInt{}; +} + +bool SceneData::hasField(const SceneField name) const { + return fieldFor(name) != ~UnsignedInt{}; +} + +UnsignedInt SceneData::fieldId(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldId(): field" << name << "not found", {}); + return fieldId; +} + +SceneFieldType SceneData::fieldType(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {}); + return _fields[fieldId]._fieldType; +} + +std::size_t SceneData::fieldSize(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldSize(): field" << name << "not found", {}); + return _fields[fieldId]._size; +} + +UnsignedShort SceneData::fieldArraySize(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {}); + return _fields[fieldId]._fieldArraySize; +} + +Containers::StridedArrayView2D SceneData::objects(const UnsignedInt fieldId) const { + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::objects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + const SceneFieldData& field = _fields[fieldId]; + /* Build a 2D view using information about object type size */ + return Containers::arrayCast<2, const char>( + fieldDataObjectViewInternal(field), + sceneObjectTypeSize(field._objectType)); +} + +Containers::StridedArrayView2D SceneData::mutableObjects(const UnsignedInt fieldId) { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::SceneData::mutableObjects(): data not mutable", {}); + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::mutableObjects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + const SceneFieldData& field = _fields[fieldId]; + /* Build a 2D view using information about attribute type size */ + const auto out = Containers::arrayCast<2, const char>( + fieldDataObjectViewInternal(field), + sceneObjectTypeSize(field._objectType)); + /** @todo some arrayConstCast? UGH */ + return Containers::StridedArrayView2D{ + /* The view size is there only for a size assert, we're pretty sure the + view is valid */ + {static_cast(const_cast(out.data())), ~std::size_t{}}, + out.size(), out.stride()}; +} + +Containers::StridedArrayView2D SceneData::objects(const SceneField fieldName) const { + const UnsignedInt fieldId = fieldFor(fieldName); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {}); + return objects(fieldId); +} + +Containers::StridedArrayView2D SceneData::mutableObjects(const SceneField fieldName) { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::SceneData::mutableObjects(): data not mutable", {}); + const UnsignedInt fieldId = fieldFor(fieldName); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {}); + return mutableObjects(fieldId); +} + +Containers::StridedArrayView2D SceneData::field(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {}); + const SceneFieldData& field = _fields[id]; + /* Build a 2D view using information about object type size */ + return Containers::arrayCast<2, const char>( + fieldDataFieldViewInternal(field), + sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1)); +} + +Containers::StridedArrayView2D SceneData::mutableField(const UnsignedInt id) { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::SceneData::mutableField(): data not mutable", {}); + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::mutableField(): index" << id << "out of range for" << _fields.size() << "fields", {}); + const SceneFieldData& field = _fields[id]; + /* Build a 2D view using information about attribute type size */ + const auto out = Containers::arrayCast<2, const char>( + fieldDataFieldViewInternal(field), + sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1)); + /** @todo some arrayConstCast? UGH */ + return Containers::StridedArrayView2D{ + /* The view size is there only for a size assert, we're pretty sure the + view is valid */ + {static_cast(const_cast(out.data())), ~std::size_t{}}, + out.size(), out.stride()}; +} + +Containers::StridedArrayView2D SceneData::field(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::field(): field" << name << "not found", {}); + return field(fieldId); +} -SceneData::SceneData(SceneData&&) - #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 - noexcept +Containers::StridedArrayView2D SceneData::mutableField(const SceneField name) { + CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, + "Trade::SceneData::mutableField(): data not mutable", {}); + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::mutableField(): field" << name << "not found", {}); + return mutableField(fieldId); +} + +void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", ); + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(destination.size() == field._size, + "Trade::SceneData::objectsInto(): expected a view with" << field._size << "elements but got" << destination.size(), ); + const Containers::StridedArrayView1D objectData = fieldDataObjectViewInternal(field); + const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination); + + if(field._objectType == SceneObjectType::UnsignedInt) + Utility::copy(Containers::arrayCast(objectData), destination); + else if(field._objectType == SceneObjectType::UnsignedShort) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(objectData, 1), destination1ui); + else if(field._objectType == SceneObjectType::UnsignedByte) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(objectData, 1), destination1ui); + else if(field._objectType == SceneObjectType::UnsignedLong) { + CORRADE_ASSERT(_objectCount <= 0xffffffffull, "Trade::SceneData::objectsInto(): indices for up to" << _objectCount << "objects can't fit into a 32-bit type, access them directly via objects() instead", ); + Math::castInto(Containers::arrayCast<2, const UnsignedLong>(objectData, 1), destination1ui); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Containers::Array SceneData::objectsAsArray(const UnsignedInt fieldId) const { + CORRADE_ASSERT(fieldId < _fields.size(), + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; + objectsInto(fieldId, out); + return out; +} + +void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::objectsInto(): field" << name << "not found", ); + objectsInto(fieldId, destination); +} + +Containers::Array SceneData::objectsAsArray(const SceneField name) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::objectsInto(): field" << name << "not found", {}); + return objectsAsArray(fieldId); +} + +void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(SceneField::Parent); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::parentsInto(): field not found", ); + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(destination.size() == field._size, + "Trade::SceneData::parentsInto(): expected a view with" << field._size << "elements but got" << destination.size(), ); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const auto destination1i = Containers::arrayCast<2, Int>(destination); + + if(field._fieldType == SceneFieldType::Int) + Utility::copy(Containers::arrayCast(fieldData), destination); + else if(field._fieldType == SceneFieldType::Short) + Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1i); + else if(field._fieldType == SceneFieldType::Byte) + Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1i); + else if(field._fieldType == SceneFieldType::Long) { + CORRADE_ASSERT(field._size <= 0xffffffffull, "Trade::SceneData::parentsInto(): parent indices for up to" << field._size << "objects can't fit into a 32-bit type, access them directly via field() instead", ); + Math::castInto(Containers::arrayCast<2, const Long>(fieldData, 1), destination1i); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Containers::Array SceneData::parentsAsArray() const { + const UnsignedInt fieldId = fieldFor(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)}; + parentsInto(out); + return out; +} + +namespace { + +template void convertTransformation(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination{sourceT[i].toMatrix()}; +} + +/** @todo these (or the float variants at least) should eventually be replaced + with optimized batched APIs (applyTranslationsInto() updating just the + last matrix column etc.) */ + +template void applyTranslation(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination::translation(Math::Vector{sourceT[i]})*destination[i]; +} + +template void applyRotation(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination{Math::Matrix{ sourceT[i].toMatrix()}}*destination[i]; +} + +template void applyScaling(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination::scaling(Math::Vector{sourceT[i]})*destination[i]; +} + +} + +std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { + UnsignedInt fieldToCheckForSize = ~UnsignedInt{}; + transformationFieldId = ~UnsignedInt{}; + translationFieldId = ~UnsignedInt{}; + rotationFieldId = ~UnsignedInt{}; + scalingFieldId = ~UnsignedInt{}; + for(std::size_t i = 0; i != _fields.size(); ++i) { + /* If we find a transformation field, we don't need to look any + further */ + if(_fields[i]._name == SceneField::Transformation) { + fieldToCheckForSize = transformationFieldId = i; + break; + } else if(_fields[i]._name == SceneField::Translation) { + fieldToCheckForSize = translationFieldId = i; + } else if(_fields[i]._name == SceneField::Rotation) { + fieldToCheckForSize = rotationFieldId = i; + } else if(_fields[i]._name == SceneField::Scaling) { + fieldToCheckForSize = scalingFieldId = i; + } + } + + /* Assuming the caller fires an appropriate assertion */ + return fieldToCheckForSize == ~UnsignedInt{} ? + ~std::size_t{} : _fields[fieldToCheckForSize]._size; +} + +void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const { + /** @todo apply scalings as well if dual complex? */ + + /* Prefer the transformation field, if present */ + if(transformationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[transformationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const auto destination1f = Containers::arrayCast<2, Float>(destination); + + if(field._fieldType == SceneFieldType::Matrix3x3) { + Utility::copy(Containers::arrayCast(fieldData), destination); + } else if(field._fieldType == SceneFieldType::Matrix3x3d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 9), destination1f); + } else if(field._fieldType == SceneFieldType::DualComplex) { + convertTransformation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::DualComplexd) { + convertTransformation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix4x4 || + field._fieldType == SceneFieldType::Matrix4x4d || + field._fieldType == SceneFieldType::DualQuaternion || + field._fieldType == SceneFieldType::DualQuaterniond) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D transformation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + + /* If not, combine from TRS components */ + } else if(translationFieldId != ~UnsignedInt{} || rotationFieldId != ~UnsignedInt{} || scalingFieldId != ~UnsignedInt{}) { + /* First fill the destination with identity matrices */ + const Matrix3 identity[1]{Matrix3{Math::IdentityInit}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(destination.size()), destination); + + /* Apply scaling first, if present */ + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[scalingFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Vector2) { + applyScaling(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector2d) { + applyScaling(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector3 || + field._fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D scaling type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Apply rotation second, if present */ + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[rotationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Complex) { + applyRotation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Complexd) { + applyRotation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Quaternion || + field._fieldType == SceneFieldType::Quaterniond) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D rotation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Apply translation last, if present */ + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[translationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Vector2) { + applyTranslation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector2d) { + applyTranslation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector3 || + field._fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D translation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Checked in the caller */ + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +void SceneData::transformations2DInto(const Containers::StridedArrayView1D& destination) const { + UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; + #ifndef CORRADE_NO_ASSERT + const std::size_t expectedSize = #endif - = default; + findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + "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, destination); +} + +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{}, + /* 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, out); + return out; +} -SceneData& SceneData::operator=(SceneData&&) - #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 - noexcept +void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const { + /** @todo apply scalings as well if dual quat? */ + + /* Prefer the transformation field, if present */ + if(transformationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[transformationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const auto destination1f = Containers::arrayCast<2, Float>(destination); + + if(field._fieldType == SceneFieldType::Matrix4x4) { + Utility::copy(Containers::arrayCast(fieldData), destination); + } else if(field._fieldType == SceneFieldType::Matrix4x4d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 16), destination1f); + } else if(field._fieldType == SceneFieldType::DualQuaternion) { + convertTransformation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::DualQuaterniond) { + convertTransformation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix3x3 || + field._fieldType == SceneFieldType::Matrix3x3d || + field._fieldType == SceneFieldType::DualComplex || + field._fieldType == SceneFieldType::DualComplexd) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D transformation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + + /* If not, combine from TRS components */ + } else if(translationFieldId != ~UnsignedInt{} || rotationFieldId != ~UnsignedInt{} || scalingFieldId != ~UnsignedInt{}) { + /* First fill the destination with identity matrices */ + const Matrix4 identity[1]{Matrix4{Math::IdentityInit}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(destination.size()), destination); + + /* Apply scaling first, if present */ + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[scalingFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Vector3) { + applyScaling(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector3d) { + applyScaling(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector2 || + field._fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D scaling type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Apply rotation second, if present */ + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[rotationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Quaternion) { + applyRotation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Quaterniond) { + applyRotation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Complex || + field._fieldType == SceneFieldType::Complexd) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D rotation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Apply translation last, if present */ + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldData& field = _fields[translationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + + if(field._fieldType == SceneFieldType::Vector3) { + applyTranslation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector3d) { + applyTranslation(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Vector2 || + field._fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D translation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Checked in the caller */ + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +void SceneData::transformations3DInto(const Containers::StridedArrayView1D& destination) const { + UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; + #ifndef CORRADE_NO_ASSERT + const std::size_t expectedSize = #endif - = default; + findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + "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, destination); +} + +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{}, + /* 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, out); + return out; +} + +void SceneData::indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + const char* const prefix, + #endif + const SceneField name, const Containers::StridedArrayView1D& destination) const +{ + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + prefix << "field not found", ); + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(destination.size() == field._size, + prefix << "expected a view with" << field._size << "elements but got" << destination.size(), ); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination); + + if(field._fieldType == SceneFieldType::UnsignedInt) + Utility::copy(Containers::arrayCast(fieldData), destination); + else if(field._fieldType == SceneFieldType::UnsignedShort) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(fieldData, 1), destination1ui); + else if(field._fieldType == SceneFieldType::UnsignedByte) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(fieldData, 1), destination1ui); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Containers::Array SceneData::indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + const char* const prefix, + #endif + const SceneField name) const +{ + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, prefix << "field not found", {}); + Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + prefix, + #endif + name, out); + return out; +} + +void SceneData::meshesInto(const Containers::StridedArrayView1D& destination) const { + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + "Trade::SceneData::meshesInto():", + #endif + SceneField::Mesh, destination); +} + +Containers::Array SceneData::meshesAsArray() const { + return indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::meshesInto():", + #endif + SceneField::Mesh); +} + +void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + "Trade::SceneData::meshMaterialsInto():", + #endif + SceneField::MeshMaterial, destination); +} + +Containers::Array SceneData::meshMaterialsAsArray() const { + return indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::meshMaterialsInto():", + #endif + SceneField::MeshMaterial); +} + +void SceneData::lightsInto(const Containers::StridedArrayView1D& destination) const { + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + "Trade::SceneData::lightsInto():", + #endif + SceneField::Light, destination); +} + +Containers::Array SceneData::lightsAsArray() const { + return indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::lightsInto():", + #endif + SceneField::Light); +} + +void SceneData::camerasInto(const Containers::StridedArrayView1D& destination) const { + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + "Trade::SceneData::camerasInto():", + #endif + SceneField::Camera, destination); +} + +Containers::Array SceneData::camerasAsArray() const { + return indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::camerasInto():", + #endif + SceneField::Camera); +} + +void SceneData::skinsInto(const Containers::StridedArrayView1D& destination) const { + indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + "Trade::SceneData::skinsInto():", + #endif + SceneField::Skin, destination); +} + +Containers::Array SceneData::skinsAsArray() const { + return indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::skinsInto():", + #endif + SceneField::Skin); +} + +Containers::Array SceneData::releaseFieldData() { + Containers::Array out = std::move(_fields); + _fields = {}; + return out; +} + +Containers::Array SceneData::releaseData() { + _fields = {}; + Containers::Array out = std::move(_data); + _data = {}; + return out; +} }} diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 8a10cfd8fc..2eb7e2943b 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -26,62 +26,1508 @@ */ /** @file - * @brief Class @ref Magnum::Trade::SceneData + * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneObjectType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneObjectTypeSize(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() */ -#include +#include +#include -#include "Magnum/Types.h" +#include "Magnum/Trade/Data.h" +#include "Magnum/Trade/Trade.h" #include "Magnum/Trade/visibility.h" namespace Magnum { namespace Trade { +/** +@brief Scene object type +@m_since_latest + +Type used for mapping fields to corresponding objects. Unlike +@ref SceneFieldType that is different for different fields, the object type is +the same for all fields, and is guaranteed to be large enough to fit all +@ref SceneData::objectCount() objects. +@see @ref SceneData::objectType(), @ref sceneObjectTypeSize() +*/ +enum class SceneObjectType: UnsignedByte { + /* Zero used for an invalid value */ + + UnsignedByte = 1, /**< @relativeref{Magnum,UnsignedByte} */ + UnsignedShort, /**< @relativeref{Magnum,UnsignedShort} */ + UnsignedInt, /**< @relativeref{Magnum,UnsignedInt} */ + + /** + * @relativeref{Magnum,UnsignedLong}. Meant to be used only in rare cases + * for *really huge* scenes. If this type is used and + * @ref SceneData::objectCount() is larger than the max representable + * 32-bit value, the indices can't be retrieved using + * @ref SceneData::objectsAsArray(), but only using appropriately typed + * @ref SceneData::objects(). + */ + UnsignedLong +}; + +/** +@debugoperatorenum{SceneObjectType} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneObjectType value); + +/** +@brief Size of given scene object type +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeSize(SceneObjectType type); + +/** +@brief Scene field name +@m_since_latest + +See @ref SceneData for more information. +@see @ref SceneFieldData, @ref SceneFieldType +*/ +enum class SceneField: UnsignedInt { + /* Zero used for an invalid value */ + + /** + * Parent index. Type is usually @ref SceneFieldType::Int, but can be also + * any of @relativeref{SceneFieldType,Byte}, + * @relativeref{SceneFieldType,Short} or, rarely, a + * @relativeref{SceneFieldType,Long}. A value of @cpp -1 @ce means there's + * no parent. An object should have only one parent, altough this isn't + * enforced in any way, and which of the duplicate fields gets used is not + * defined. + * + * Note that the index points to the parent array itself and isn't the + * actual object index --- the object mapping is stored in the + * corresponding @ref SceneData::objects() array. + * @see @ref SceneData::parentsAsArray() + */ + Parent = 1, + + /** + * Transformation. Type is usually @ref SceneFieldType::Matrix3x3 for 2D + * and @ref SceneFieldType::Matrix4x4 for 3D, but can be also any of + * @relativeref{SceneFieldType,Matrix3x3d}, + * @relativeref{SceneFieldType,DualComplex} or + * @relativeref{SceneFieldType,DualComplexd} for 2D and + * @relativeref{SceneFieldType,Matrix4x4d}, + * @relativeref{SceneFieldType,DualQuaternion} or + * @relativeref{SceneFieldType,DualQuaterniond} for 3D. An object should + * have only one transformation, altough this isn't enforced in any way, + * and which of the duplicate fields gets used is not defined. + * + * The transformation can be also represented by separate + * @ref SceneField::Translation, @ref SceneField::Rotation and + * @ref SceneField::Scaling fields. If both @ref SceneField::Transformation + * and TRS fields are specified, it's expected that all objects that have + * TRS fields have a combined transformation field as well, and + * @ref SceneData::transformations2DAsArray() / + * @ref SceneData::transformations3DAsArray() then takes into account only + * the combined transformation field. TRS fields can however be specified + * only for a subset of transformed objects, useful for example when only + * certain objects have these properties animated. + * @see @ref SceneData::transformations2DAsArray(), + * @ref SceneData::transformations3DAsArray() + */ + Transformation, + + /** + * Translation. Type is usually @ref SceneFieldType::Vector2 for 2D and + * @ref SceneFieldType::Vector3 for 3D, but can be also any of + * @relativeref{SceneFieldType,Vector2d} for 2D and + * @relativeref{SceneFieldType,Vector3d} for 3D. An object should + * have only one translation, altough this isn't enforced in any way, + * and which of the duplicate fields gets used is not defined. + * + * The translation field usually is (but doesn't have to be) complemented + * by a @ref SceneField::Rotation and @ref SceneField::Scaling, which, if + * present, are expected to all share the same object mapping view. The TRS + * components can either completely replace @ref SceneField::Transformation + * or be provided just for a subset of it --- see its documentation for + * details. + * @see @ref SceneData::transformations2DAsArray(), + * @ref SceneData::transformations3DAsArray() + */ + Translation, + + /** + * Rotation. Type is usually @ref SceneFieldType::Complex for 2D and + * @ref SceneFieldType::Quaternion for 3D, but can be also any of + * @relativeref{SceneFieldType,Complexd} for 2D and + * @relativeref{SceneFieldType,Quaterniond} for 3D. An object should have + * only one rotation, altough this isn't enforced in any way, and which of + * the duplicate fields gets used is not defined. + * + * The rotation field usually is (but doesn't have to be) complemented by a + * @ref SceneField::Translation and @ref SceneField::Scaling, which, if + * present, are expected to all share the same object mapping view. The TRS + * components can either completely replace @ref SceneField::Transformation + * or be provided just for a subset of it --- see its documentation for + * details. + * @see @ref SceneData::transformations2DAsArray(), + * @ref SceneData::transformations3DAsArray() + */ + Rotation, + + /** + * Scaling. Type is usually @ref SceneFieldType::Vector2 for 2D and + * @ref SceneFieldType::Vector3 for 3D, but can be also any of + * @relativeref{SceneFieldType,Vector2d} for 2D and + * @relativeref{SceneFieldType,Vector3d} for 3D. An object should + * have only one scaling, altough this isn't enforced in any way, and which + * of the duplicate fields gets used is not defined. + * + * The scaling field usually is (but doesn't have to be) complemented by a + * @ref SceneField::Translation and @ref SceneField::Rotation, which, if + * present, are expected to all share the same object mapping view. The TRS + * components can either completely replace @ref SceneField::Transformation + * or be provided just for a subset of it --- see its documentation for + * details. + * @see @ref SceneData::transformations2DAsArray(), + * @ref SceneData::transformations3DAsArray() + */ + Scaling, + + /** + * ID of a mesh associated with this object, corresponding to the ID passed + * to @ref AbstractImporter::mesh(). Type is usually + * @ref SceneFieldType::UnsignedInt, but can be also any of + * @relativeref{SceneFieldType,UnsignedByte} or + * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple + * meshes associated. + * + * Usually complemented with a @ref SceneField::MeshMaterial, although not + * 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() + */ + Mesh, + + /** + * ID of a material for a @ref SceneField::Mesh, corresponding to the ID + * passed to @ref AbstractImporter::material(). Type is usually + * @ref SceneFieldType::UnsignedInt, but can be also any of + * @relativeref{SceneFieldType,UnsignedByte} or + * @relativeref{SceneFieldType,UnsignedShort}. Expected to share the + * object mapping view with @ref SceneField::Mesh. + * @see @ref SceneData::meshMaterialsAsArray() + */ + MeshMaterial, + + /** + * ID of a light associated with this object, corresponding to the ID + * passed to @ref AbstractImporter::light(). Type is usually + * @ref SceneFieldType::UnsignedInt, but can be also any of + * @relativeref{SceneFieldType,UnsignedByte} or + * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple + * lights associated. + * @see @ref SceneData::lightsAsArray() + */ + Light, + + /** + * ID of a camera associated with this object, corresponding to the ID + * passed to @ref AbstractImporter::camera(). Type is usually + * @ref SceneFieldType::UnsignedInt, but can be also any of + * @relativeref{SceneFieldType,UnsignedByte} or + * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple + * cameras associated. + * @see @ref SceneData::camerasAsArray() + */ + Camera, + + /** + * ID of a skin associated with this object, corresponding to the ID + * passed to @ref AbstractImporter::skin(). Type is usually + * @ref SceneFieldType::UnsignedInt, but can be also any of + * @relativeref{SceneFieldType,UnsignedByte} or + * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple + * skins associated. + * @see @ref SceneData::skinsAsArray() + */ + Skin, + + /** + * This and all higher values are for importer-specific fields. Can be + * of any type. See documentation of a particular importer for details. + * + * While it's unlikely to have billions of custom fields, the enum + * intentionally reserves a full 31-bit range to avoid the need to remap + * field identifiers coming from 3rd party ECS frameworks, for example. + * @see @ref isSceneFieldCustom(), @ref sceneFieldCustom(SceneField), + * @ref sceneFieldCustom(UnsignedInt) + */ + Custom = 0x80000000u +}; + +/** +@debugoperatorenum{SceneField} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneField value); + +/** +@brief Whether a scene field is custom +@m_since_latest + +Returns @cpp true @ce if @p name has a value larger or equal to +@ref SceneField::Custom, @cpp false @ce otherwise. +@see @ref sceneFieldCustom(UnsignedInt), @ref sceneFieldCustom(SceneField) +*/ +constexpr bool isSceneFieldCustom(SceneField name) { + return UnsignedInt(name) >= UnsignedInt(SceneField::Custom); +} + +/** +@brief Create a custom scene field +@m_since_latest + +Returns a custom scene field with index @p id. The index is expected to be less +than the value of @ref SceneField::Custom. Use @ref sceneFieldCustom(SceneField) +to get the index back. +*/ +/* Constexpr so it's usable for creating compile-time SceneFieldData + instances */ +constexpr SceneField sceneFieldCustom(UnsignedInt id) { + return CORRADE_CONSTEXPR_ASSERT(id < UnsignedInt(SceneField::Custom), + "Trade::sceneFieldCustom(): index" << id << "too large"), + SceneField(UnsignedInt(SceneField::Custom) + id); +} + +/** +@brief Get index of a custom scene field +@m_since_latest + +Inverse to @ref sceneFieldCustom(UnsignedInt). Expects that the field is +custom. +@see @ref isSceneFieldCustom() +*/ +constexpr UnsignedInt sceneFieldCustom(SceneField name) { + return CORRADE_CONSTEXPR_ASSERT(isSceneFieldCustom(name), + "Trade::sceneFieldCustom():" << name << "is not custom"), + UnsignedInt(name) - UnsignedInt(SceneField::Custom); +} + +/** +@brief Scene field type +@m_since_latest + +A type in which a @ref SceneField is stored. See @ref SceneData for more +information. +@see @ref SceneFieldData, @ref sceneFieldTypeSize() +*/ +enum class SceneFieldType: UnsignedShort { + /* Zero used for an invalid value */ + + /* 1 reserved for Bool (Bit?), which needs [Strided]BitArray[View] first */ + + Float = 2, /**< @relativeref{Magnum,Float} */ + Half, /**< @relativeref{Magnum,Half} */ + Double, /**< @relativeref{Magnum,Double} */ + UnsignedByte, /**< @relativeref{Magnum,UnsignedByte} */ + Byte, /**< @relativeref{Magnum,Byte} */ + UnsignedShort, /**< @relativeref{Magnum,UnsignedShort} */ + Short, /**< @relativeref{Magnum,Short} */ + UnsignedInt, /**< @relativeref{Magnum,UnsignedInt} */ + Int, /**< @relativeref{Magnum,Int} */ + UnsignedLong, /**< @relativeref{Magnum,UnsignedLong} */ + Long, /**< @relativeref{Magnum,Long} */ + + Vector2, /**< @relativeref{Magnum,Vector2} */ + Vector2h, /**< @relativeref{Magnum,Vector2h} */ + Vector2d, /**< @relativeref{Magnum,Vector2d} */ + Vector2ub, /**< @relativeref{Magnum,Vector2ub} */ + Vector2b, /**< @relativeref{Magnum,Vector2b} */ + Vector2us, /**< @relativeref{Magnum,Vector2us} */ + Vector2s, /**< @relativeref{Magnum,Vector2s} */ + Vector2ui, /**< @relativeref{Magnum,Vector2ui} */ + Vector2i, /**< @relativeref{Magnum,Vector2i} */ + + Vector3, /**< @relativeref{Magnum,Vector3} */ + Vector3h, /**< @relativeref{Magnum,Vector3h} */ + Vector3d, /**< @relativeref{Magnum,Vector3d} */ + Vector3ub, /**< @relativeref{Magnum,Vector3ub} */ + Vector3b, /**< @relativeref{Magnum,Vector3b} */ + Vector3us, /**< @relativeref{Magnum,Vector3us} */ + Vector3s, /**< @relativeref{Magnum,Vector3s} */ + Vector3ui, /**< @relativeref{Magnum,Vector3ui} */ + Vector3i, /**< @relativeref{Magnum,Vector3i} */ + + Vector4, /**< @relativeref{Magnum,Vector4} */ + Vector4h, /**< @relativeref{Magnum,Vector4h} */ + Vector4d, /**< @relativeref{Magnum,Vector4d} */ + Vector4ub, /**< @relativeref{Magnum,Vector4ub} */ + Vector4b, /**< @relativeref{Magnum,Vector4b} */ + Vector4us, /**< @relativeref{Magnum,Vector4us} */ + Vector4s, /**< @relativeref{Magnum,Vector4s} */ + Vector4ui, /**< @relativeref{Magnum,Vector4ui} */ + Vector4i, /**< @relativeref{Magnum,Vector4i} */ + + Matrix2x2, /**< @relativeref{Magnum,Matrix2x2} */ + Matrix2x2h, /**< @relativeref{Magnum,Matrix2x2h} */ + Matrix2x2d, /**< @relativeref{Magnum,Matrix2x2d} */ + + Matrix2x3, /**< @relativeref{Magnum,Matrix2x3} */ + Matrix2x3h, /**< @relativeref{Magnum,Matrix2x3h} */ + Matrix2x3d, /**< @relativeref{Magnum,Matrix2x3d} */ + + Matrix2x4, /**< @relativeref{Magnum,Matrix2x4} */ + Matrix2x4h, /**< @relativeref{Magnum,Matrix2x4h} */ + Matrix2x4d, /**< @relativeref{Magnum,Matrix2x4d} */ + + Matrix3x2, /**< @relativeref{Magnum,Matrix3x2} */ + Matrix3x2h, /**< @relativeref{Magnum,Matrix3x2h} */ + Matrix3x2d, /**< @relativeref{Magnum,Matrix3x2d} */ + + Matrix3x3, /**< @relativeref{Magnum,Matrix3x3} */ + Matrix3x3h, /**< @relativeref{Magnum,Matrix3x3h} */ + Matrix3x3d, /**< @relativeref{Magnum,Matrix3x3d} */ + + Matrix3x4, /**< @relativeref{Magnum,Matrix3x4} */ + Matrix3x4h, /**< @relativeref{Magnum,Matrix3x4h} */ + Matrix3x4d, /**< @relativeref{Magnum,Matrix3x4d} */ + + Matrix4x2, /**< @relativeref{Magnum,Matrix4x2} */ + Matrix4x2h, /**< @relativeref{Magnum,Matrix4x2h} */ + Matrix4x2d, /**< @relativeref{Magnum,Matrix4x2d} */ + + Matrix4x3, /**< @relativeref{Magnum,Matrix4x3} */ + Matrix4x3h, /**< @relativeref{Magnum,Matrix4x3h} */ + Matrix4x3d, /**< @relativeref{Magnum,Matrix4x3d} */ + + Matrix4x4, /**< @relativeref{Magnum,Matrix4x4} */ + Matrix4x4h, /**< @relativeref{Magnum,Matrix4x4h} */ + Matrix4x4d, /**< @relativeref{Magnum,Matrix4x4d} */ + + Range1D, /**< @relativeref{Magnum,Range1D} */ + Range1Dh, /**< @relativeref{Magnum,Range1Dh} */ + Range1Dd, /**< @relativeref{Magnum,Range1Dd} */ + Range1Di, /**< @relativeref{Magnum,Range1Di} */ + + Range2D, /**< @relativeref{Magnum,Range2D} */ + Range2Dh, /**< @relativeref{Magnum,Range2Dh} */ + Range2Dd, /**< @relativeref{Magnum,Range2Dd} */ + Range2Di, /**< @relativeref{Magnum,Range2Di} */ + + Range3D, /**< @relativeref{Magnum,Range3D} */ + Range3Dh, /**< @relativeref{Magnum,Range3Dh} */ + Range3Dd, /**< @relativeref{Magnum,Range3Dd} */ + Range3Di, /**< @relativeref{Magnum,Range3Di} */ + + Complex, /**< @relativeref{Magnum,Complex} */ + Complexd, /**< @relativeref{Magnum,Complexd} */ + DualComplex, /**< @relativeref{Magnum,DualComplex} */ + DualComplexd, /**< @relativeref{Magnum,DualComplexd} */ + + Quaternion, /**< @relativeref{Magnum,Quaternion} */ + Quaterniond, /**< @relativeref{Magnum,Quaterniond} */ + DualQuaternion, /**< @relativeref{Magnum,DualQuaternion} */ + DualQuaterniond,/**< @relativeref{Magnum,DualQuaterniond} */ + + Deg, /**< @relativeref{Magnum,Deg} */ + Degh, /**< @relativeref{Magnum,Degh} */ + Degd, /**< @relativeref{Magnum,Degh} */ + Rad, /**< @relativeref{Magnum,Rad} */ + Radh, /**< @relativeref{Magnum,Radh} */ + Radd /**< @relativeref{Magnum,Radd} */ +}; + +/** +@debugoperatorenum{SceneFieldType} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldType value); + +/** +@brief Size of given scene field type +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type); + +/** +@brief Scene field data +@m_since_latest + +Convenience type for populating @ref SceneData, see its documentation for an +introduction. +*/ +class MAGNUM_TRADE_EXPORT SceneFieldData { + public: + /** + * @brief Default constructor + * + * Leaves contents at unspecified values. Provided as a convenience for + * initialization of the field array for @ref SceneData, expected to be + * replaced with concrete values later. + */ + constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _isOffsetOnly{}, _objectType{}, _objectStride{}, _objectData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} + + /** + * @brief Type-erased constructor + * @param name Field name + * @param objectType Object type + * @param objectData Object data + * @param fieldType Field type + * @param fieldData Field data + * @param fieldArraySize Field array size. Use @cpp 0 @ce for + * non-array fields. + * + * Expects that @p objectData and @p fieldData have the same size, + * @p fieldType corresponds to @p name and @p fieldArraySize is zero + * for builtin fields. + */ + constexpr explicit SceneFieldData(SceneField name, SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + + /** + * @brief Constructor + * @param name Field name + * @param objectData Object data + * @param fieldType Field type + * @param fieldData Field data + * @param fieldArraySize Field array size. Use @cpp 0 @ce for + * non-array fields. + * + * Expects that @p objectData and @p fieldData have the same size in + * the first dimension, that the second dimension of @p objectData is + * contiguous and its size is either 1, 2, 4 or 8, corresponding to one + * of the @ref SceneObjectType values, that the second dimension of + * @p fieldData is contiguous and its size matches @p fieldType and + * @p fieldArraySize and that @p fieldType corresponds to @p name and + * @p fieldArraySize is zero for builtin attributes. + */ + explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& objectData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + + /** + * @brief Constructor + * @param name Field name + * @param objectData Object data + * @param fieldData Field data + * + * Detects @ref SceneObjectType based on @p T and @ref SceneFieldType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort). + * For all types known by Magnum, the detected @ref SceneFieldType is + * of the same name as the type (so e.g. @relativeref{Magnum,Vector3ui} + * gets recognized as @ref SceneFieldType::Vector3ui). + */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView1D& fieldData) noexcept; + + /** @overload */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, objectData, Containers::stridedArrayView(fieldData)} {} + + /** @overload */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), fieldData} {} + + /** @overload */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), Containers::stridedArrayView(fieldData)} {} + + /** + * @brief Construct an array field + * @param name Field name + * @param objectData Object data + * @param fieldData Field data + * + * Detects @ref SceneObjectType based on @p T and @ref SceneFieldType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * with the @p fieldData second dimension size passed to + * @p fieldArraySize. Expects that the second dimension of @p fieldData + * is contiguous. At the moment only custom fields can be arrays, which + * means this function can't be used with a builtin @p name. See + * @ref SceneFieldData(SceneField, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) + * for details about @ref SceneObjectType and @ref SceneFieldType + * detection. + */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView2D& fieldData) noexcept; + + /** @overload */ + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), fieldData} {} + + /** + * @brief Construct an offset-only field + * @param name Field name + * @param size Number of entries + * @param objectType Object type + * @param objectOffset Object data offset + * @param objectStride Object data stride + * @param fieldType Field type + * @param fieldOffset Field data offset + * @param fieldStride Field data stride + * @param fieldArraySize Field array size. Use @cpp 0 @ce for + * non-array fields. + * + * Instances created this way refer to offsets in unspecified + * external scene data instead of containing the data views directly. + * Useful when the location of the scene data array is not known at + * field construction time. Expects that @p fieldType corresponds to + * @p name and @p fieldArraySize is zero for builtin attributes. + * + * Note that due to the @cpp constexpr @ce nature of this constructor, + * no @p objectType checks against @p objectStride or + * @p fieldType / @p fieldArraySize checks against @p fieldStride can + * be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * constructor if you want additional safeguards. + * @see @ref isOffsetOnly(), @ref fieldArraySize(), + * @ref objectData(Containers::ArrayView) const, + * @ref fieldData(Containers::ArrayView) const + */ + explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneObjectType objectType, std::size_t objectOffset, std::ptrdiff_t objectStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0) noexcept; + + /** + * @brief If the field is offset-only + * + * Returns @cpp true @ce if the field doesn't contain the data views + * directly, but instead refers to unspecified external data. + * @see @ref objectData(Containers::ArrayView) const, + * @ref fieldData(Containers::ArrayView) const, + * @ref SceneFieldData(SceneField, std::size_t, SceneObjectType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort) + */ + constexpr bool isOffsetOnly() const { return _isOffsetOnly; } + + /** @brief Field name */ + constexpr SceneField name() const { return _name; } + + /** @brief Number of entries */ + constexpr UnsignedLong size() const { return _size; } + + /** @brief Object type */ + constexpr SceneObjectType objectType() const { return _objectType; } + + /** + * @brief Type-erased object data + * + * Expects that the field is not offset-only, in that case use the + * @ref objectData(Containers::ArrayView) const overload + * instead. + * @see @ref isOffsetOnly() + */ + constexpr Containers::StridedArrayView1D objectData() const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + {_objectData.pointer, ~std::size_t{}}, _size, + (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::objectData(): the field is offset-only, supply a data array"), _objectStride)}; + } + + /** + * @brief Type-erased object data for an offset-only attribute + * + * If the field is not offset-only, the @p data parameter is ignored. + * @see @ref isOffsetOnly(), @ref objectData() const + */ + Containers::StridedArrayView1D objectData(Containers::ArrayView data) const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + data, _isOffsetOnly ? reinterpret_cast(data.data()) + _objectData.offset : _objectData.pointer, _size, _objectStride}; + } + + /** @brief Field type */ + constexpr SceneFieldType fieldType() const { return _fieldType; } + + /** @brief Field array size */ + constexpr UnsignedShort fieldArraySize() const { return _fieldArraySize; } + + /** + * @brief Type-erased field data + * + * Expects that the field is not offset-only, in that case use the + * @ref fieldData(Containers::ArrayView) const overload + * instead. + * @see @ref isOffsetOnly() + */ + constexpr Containers::StridedArrayView1D fieldData() const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + {_fieldData.pointer, ~std::size_t{}}, _size, + (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array"), _fieldStride)}; + } + + /** + * @brief Type-erased field data for an offset-only attribute + * + * If the field is not offset-only, the @p data parameter is ignored. + * @see @ref isOffsetOnly(), @ref fieldData() const + */ + Containers::StridedArrayView1D fieldData(Containers::ArrayView data) const { + return Containers::StridedArrayView1D{ + /* We're *sure* the view is correct, so faking the view size */ + /** @todo better ideas for the StridedArrayView API? */ + data, _isOffsetOnly ? reinterpret_cast(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride}; + } + + private: + friend SceneData; + + union Data { + /* FFS C++ why this doesn't JUST WORK goddamit?! It's already past + the End Of Times AND YET this piece of complex shit can't do the + obvious! */ + constexpr Data(const void* pointer = nullptr): pointer{pointer} {} + constexpr Data(std::size_t offset): offset{offset} {} + + const void* pointer; + std::size_t offset; + }; + + UnsignedLong _size; + SceneField _name; + bool _isOffsetOnly; + SceneObjectType _objectType; + Short _objectStride; + Data _objectData; + + SceneFieldType _fieldType; + Short _fieldStride; + UnsignedShort _fieldArraySize; + /* 2 bytes free */ + Data _fieldData; +}; + +/** @relatesalso SceneFieldData +@brief Create a non-owning array of @ref SceneFieldData items +@m_since_latest + +Useful when you have the field definitions statically defined (for example when +the data themselves are already defined at compile time) and don't want to +allocate just to pass those to @ref SceneData. +*/ +Containers::Array MAGNUM_TRADE_EXPORT sceneFieldDataNonOwningArray(Containers::ArrayView view); + /** @brief Scene data +Contains scene hierarchy, object transformations and association of mesh, +material, camera, light and other resources with particular objects. + @see @ref AbstractImporter::scene() */ class MAGNUM_TRADE_EXPORT SceneData { public: /** - * @brief Constructor - * @param children2D Two-dimensional child objects - * @param children3D Three-dimensional child objects - * @param importerState Importer-specific state + * @brief Construct scene data + * @param objectType Object type + * @param objectCount Total count of all objects in the scene + * @param data Data for all fields and objects + * @param fields Description of all scene field data + * @param importerState Importer-specific state + * @m_since_latest + * + * The @p objectType is expected to be large enough to index all + * @p objectCount objects. The @p fields are expected to reference + * (sparse) sub-ranges of @p data, each having an unique + * @ref SceneField and @ref SceneObjectType equal to @p objectType. + * Particular fields can have additional restrictions, see + * documentation of @ref SceneField values for more information. + * + * The @ref dataFlags() are implicitly set to a combination of + * @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned data + * use the @ref SceneData(SceneObjectType, UnsignedLong, DataFlags, Containers::ArrayView, Containers::Array&&, const void*) + * constructor or its variants instead. */ - explicit SceneData(std::vector children2D, std::vector children3D, const void* importerState = nullptr); + explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + + /** + * @overload + * @m_since_latest + */ + /* Not noexcept because allocation happens inside */ + explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, Containers::Array&& data, std::initializer_list fields, const void* importerState = nullptr); + + /** + * @brief Construct non-owned scene data + * @param objectType Object type + * @param objectCount Total count of all objects in the scene + * @param dataFlags Data flags + * @param data View on data for all fields and objects + * @param fields Description of all scene field data + * @param importerState Importer-specific state + * @m_since_latest + * + * Compared to @ref SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + * creates an instance that doesn't own the passed data. The + * @p dataFlags parameter can contain @ref DataFlag::Mutable to + * indicate the external data can be modified, and is expected to *not* + * have @ref DataFlag::Owned set. + */ + explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + + /** + * @overload + * @m_since_latest + */ + /* Not noexcept because allocation happens inside */ + explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, DataFlags dataFlags, Containers::ArrayView data, std::initializer_list fields, const void* importerState = nullptr); /** @brief Copying is not allowed */ SceneData(const SceneData&) = delete; /** @brief Move constructor */ - SceneData(SceneData&&) - /* GCC 4.9.0 (the one from Android NDK) thinks this does not match - the implicit signature so it can't be defaulted. Works on 4.8, - 5.0 and everywhere else, so I don't bother. */ - #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 - noexcept - #endif - ; + SceneData(SceneData&&) noexcept; + + ~SceneData(); /** @brief Copying is not allowed */ SceneData& operator=(const SceneData&) = delete; /** @brief Move assignment */ - SceneData& operator=(SceneData&&) - /* GCC 4.9.0 (the one from Android NDK) thinks this does not match - the implicit signature so it can't be defaulted. Works on 4.8, - 5.0 and everywhere else, so I don't bother. */ - #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 - noexcept - #endif - ; + SceneData& operator=(SceneData&&) noexcept; + + /** + * @brief Data flags + * @m_since_latest + * + * @see @ref releaseData(), @ref mutableData(), @ref mutableField(), + * @ref mutableObjects() + */ + DataFlags dataFlags() const { return _dataFlags; } + + /** + * @brief Raw data + * @m_since_latest + * + * Returns @cpp nullptr @ce if the scene has no data. + */ + Containers::ArrayView data() const & { return _data; } + + /** + * @brief Taking a view to a r-value instance is not allowed + * @m_since_latest + */ + Containers::ArrayView data() const && = delete; + + /** + * @brief Mutable raw data + * @m_since_latest + * + * Like @ref data(), but returns a non-const view. Expects that the + * scene is mutable. + * @see @ref dataFlags() + */ + Containers::ArrayView mutableData() &; + + /** + * @brief Taking a view to a r-value instance is not allowed + * @m_since_latest + */ + Containers::ArrayView mutableData() && = delete; + + /** + * @brief Type used for object mapping + * @m_since_latest + * + * Type returned from @ref objects() and @ref mutableObjects(). It's + * the same for all fields and is guaranteed to be large enough to fit + * all @ref objectCount() objects. + */ + SceneObjectType objectType() const { return _objectType; } + + /** + * @brief Total object count + * @m_since_latest + * + * Total number of objects contained in the scene. + * @see @ref fieldCount(), @ref fieldSize() + */ + UnsignedLong objectCount() const { return _objectCount; } + + /** + * @brief Field count + * @m_since_latest + * + * Count of different fields contained in the scene, or @cpp 0 @ce for + * a scene with no fields. Each @ref SceneField can be present only + * once, however an object can have a certain field associated with it + * multiple times with different values (for example an object having + * multiple meshes). See also @ref objectCount() which returns count of + * actual objects. + */ + UnsignedInt fieldCount() const { return _fields.size(); } + + /** + * @brief Raw field metadata + * @m_since_latest + * + * Returns the raw data that are used as a base for all `field*()` + * accessors, or @cpp nullptr @ce if the scene has no fields. In most + * cases you don't want to access those directly, but rather use the + * @ref objects(), @ref field(), @ref fieldName(), @ref fieldType(), + * @ref fieldSize() and @ref fieldArraySize() accessors. Compared to + * those and to @ref fieldData(UnsignedInt) const, the + * @ref SceneFieldData instances returned by this function may have + * different data pointers, and some of them might be offset-only --- + * use this function only if you *really* know what are you doing. + * @see @ref SceneFieldData::isOffsetOnly() + */ + Containers::ArrayView fieldData() const & { return _fields; } + + /** + * @brief Taking a view to a r-value instance is not allowed + * @m_since_latest + */ + Containers::ArrayView fieldData() const && = delete; + + /** + * @brief Raw field data + * @m_since_latest + * + * Returns the raw data that are used as a base for all `field*()` + * accessors. In most cases you don't want to access those directly, + * but rather use the @ref objects(), @ref field(), @ref fieldName(), + * @ref fieldType(), @ref fieldSize() and @ref fieldArraySize() + * accessors. This is also the reason why there's no overload taking a + * @ref SceneField, unlike the other accessors. + * + * Unlike with @ref fieldData() and @ref releaseFieldData(), returned + * instances are guaranteed to always have an absolute data pointer + * (i.e., @ref SceneFieldData::isOffsetOnly() always returning + * @cpp false @ce). The @p id is expected to be smaller than + * @ref fieldCount(). + */ + SceneFieldData fieldData(UnsignedInt id) const; + + /** + * @brief Field name + * @m_since_latest + * + * The @p id is expected to be smaller than @ref fieldCount(). + * @see @ref fieldType(), @ref isSceneFieldCustom(), + * @ref AbstractImporter::sceneFieldForName(), + * @ref AbstractImporter::sceneFieldName() + */ + SceneField fieldName(UnsignedInt id) const; + + /** + * @brief Field type + * @m_since_latest + * + * The @p id is expected to be smaller than @ref fieldCount(). You can + * also use @ref fieldType(SceneField) const to directly get a type of + * given named field. + * @see @ref fieldName(), @ref objectType() + */ + SceneFieldType fieldType(UnsignedInt id) const; + + /** + * @brief Size of given field + * @m_since_latest + * + * Size of the view returned by @ref field() / @ref mutableField() and + * also @ref objects() / @ref mutableObjects() for given @p id. Since + * an object can have multiple entries of the same field (for example + * multiple meshes associated with an object), the size doesn't + * necessarily match the number of objects having given field. + * + * The @p id is expected to be smaller than @ref fieldCount(). You can + * also use @ref fieldSize(SceneField) const to directly get a size of + * given named field. + */ + std::size_t fieldSize(UnsignedInt id) const; + + /** + * @brief Field array size + * @m_since_latest + * + * In case given field is an array (the euqivalent of e.g. + * @cpp int[30] @ce), returns array size, otherwise returns @cpp 0 @ce. + * At the moment only custom fields can be arrays, no builtin + * @ref SceneField is an array attribute. Note that this is different + * from the count of entries for given field, which is exposed through + * @ref fieldSize(). See @ref Trade-SceneData-populating-custom for an + * example. + * + * The @p id is expected to be smaller than @ref fieldCount(). You can + * also use @ref fieldArraySize(SceneField) const to directly get a + * type of given named field. + */ + UnsignedShort fieldArraySize(UnsignedInt id) const; + + /** + * @brief Whether the scene has given field + * @m_since_latest + */ + bool hasField(SceneField name) const; + + /** + * @brief Absolute ID of a named field + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref hasField(), @ref fieldName(UnsignedInt) const + */ + UnsignedInt fieldId(SceneField name) const; + + /** + * @brief Type of a named field + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref hasField(), @ref fieldType(UnsignedInt) const + */ + SceneFieldType fieldType(SceneField name) const; + + /** + * @brief Number of entries for given named field + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref hasField(), @ref fieldSize(UnsignedInt) const + */ + std::size_t fieldSize(SceneField name) const; + + /** + * @brief Array size of a named field + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref hasField(), @ref fieldArraySize(UnsignedInt) const + */ + UnsignedShort fieldArraySize(SceneField name) const; + + /** + * @brief Object mapping data for given field + * @m_since_latest + * + * The @p fieldId is expected to be smaller than @ref fieldCount(). The + * second dimension represents the actual data type (its size is equal + * to @ref SceneObjectType size) and is guaranteed to be contiguous. + * Use the templated overload below to get the objects in a concrete + * type. + * @see @ref mutableObjects(UnsignedInt), + * @ref Corrade::Containers::StridedArrayView::isContiguous(), + * @ref sceneObjectTypeSize() + */ + Containers::StridedArrayView2D objects(UnsignedInt fieldId) const; + + /** + * @brief Mutable object mapping data for given field + * @m_since_latest + * + * Like @ref objects(UnsignedInt) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + Containers::StridedArrayView2D mutableObjects(UnsignedInt fieldId); + + /** + * @brief Object mapping for given field + * @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. + * @see @ref mutableObjects(UnsignedInt) + */ + template Containers::StridedArrayView1D objects(UnsignedInt fieldId) const; + + /** + * @brief Mutable object mapping for given field + * @m_since_latest + * + * Like @ref objects(UnsignedInt) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + template Containers::StridedArrayView1D mutableObjects(UnsignedInt fieldId); + + /** + * @brief Object mapping data for given named field + * @m_since_latest + * + * The @p fieldName is expected to exist. The second dimension + * represents the actual data type (its size is equal to + * @ref SceneObjectType size) and is guaranteed to be contiguous. Use + * the templated overload below to get the objects in a concrete type. + * @see @ref hasField(), @ref objects(UnsignedInt) const, + * @ref mutableObjects(SceneField), + * @ref Corrade::Containers::StridedArrayView::isContiguous() + */ + Containers::StridedArrayView2D objects(SceneField fieldName) const; + + /** + * @brief Mutable object mapping data for given named field + * @m_since_latest + * + * Like @ref objects(SceneField) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + Containers::StridedArrayView2D mutableObjects(SceneField fieldName); + + /** + * @brief Object mapping for given named field + * @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. + * @see @ref hasField(), @ref objects(UnsignedInt) const, + * @ref mutableObjects(UnsignedInt) + */ + template Containers::StridedArrayView1D objects(SceneField fieldName) const; + + /** + * @brief Mutable object mapping for given named field + * @m_since_latest + * + * Like @ref objects(SceneField) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + template Containers::StridedArrayView1D mutableObjects(SceneField fieldName); + + /** + * @brief Data for given field + * @m_since_latest + * + * The @p id is expected to be smaller than @ref fieldCount(). The + * second dimension represents the actual data type (its size is equal + * to @ref SceneFieldType size, possibly multiplied by array size) and + * is guaranteed to be contiguous. Use the templated overload below to + * get the field in a concrete type. + * @see @ref Corrade::Containers::StridedArrayView::isContiguous(), + * @ref sceneFieldTypeSize(), @ref mutableField(UnsignedInt) + */ + Containers::StridedArrayView2D field(UnsignedInt id) const; + + /** + * @brief Mutable data for given field + * @m_since_latest + * + * Like @ref field(UnsignedInt) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + Containers::StridedArrayView2D mutableField(UnsignedInt id); + + /** + * @brief Data for given field in a concrete type + * @m_since_latest + * + * The @p id is expected to be smaller than @ref fieldCount() and @p T + * 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 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. + * @see @ref field(SceneField) const, @ref mutableField(UnsignedInt), + * @ref fieldArraySize() + */ + template::value>::type> Containers::StridedArrayView1D field(UnsignedInt id) const; + + /** + * @brief Data for given array field in a concrete type + * @m_since_latest + * + * Same as above, except that it works with array fields instead --- + * you're expected to select this overload by passing @cpp T[] @ce + * instead of @cpp T @ce. The second dimension is guaranteed to be + * contiguous and have the same size as reported by + * @ref fieldArraySize(UnsignedInt) const for given field. + */ + template::value>::type> Containers::StridedArrayView2D::type> field(UnsignedInt id) const; + + /** + * @brief Mutable data for given field in a concrete type + * @m_since_latest + * + * Like @ref field(UnsignedInt) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + template::value>::type> Containers::StridedArrayView1D mutableField(UnsignedInt id); + + /** + * @brief Mutable data for given array field in a concrete type + * @m_since_latest + * + * Same as above, except that it works with array fields instead --- + * you're expected to select this overload by passing @cpp T[] @ce + * instead of @cpp T @ce. The second dimension is guaranteed to be + * contiguous and have the same size as reported by + * @ref fieldArraySize(UnsignedInt) const for given field. + */ + template::value>::type> Containers::StridedArrayView2D::type> mutableField(UnsignedInt id); + + /** + * @brief Data for given named field + * @m_since_latest + * + * The @p name is expected to exist. The second dimension represents + * the actual data type (its size is equal to @ref SceneFieldType size, + * possibly multiplied by array size) and is guaranteed to be + * contiguous. Use the templated overload below to get the field in a + * concrete type. + * @see @ref hasField(), @ref field(UnsignedInt) const, + * @ref mutableField(SceneField), + * @ref Corrade::Containers::StridedArrayView::isContiguous() + */ + Containers::StridedArrayView2D field(SceneField name) const; + + /** + * @brief Mutable data for given named field + * @m_since_latest + * + * Like @ref field(SceneField) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + Containers::StridedArrayView2D mutableField(SceneField name); + + /** + * @brief Data for given named field in a concrete type + * @m_since_latest + * + * 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(), @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. + * @see @ref field(UnsignedInt) const, @ref mutableField(SceneField) + */ + template::value>::type> Containers::StridedArrayView1D field(SceneField name) const; + + /** + * @brief Data for given named array field in a concrete type + * @m_since_latest + * + * Same as above, except that it works with array fields instead --- + * you're expected to select this overload by passing @cpp T[] @ce + * instead of @cpp T @ce. The second dimension is guaranteed to be + * contiguous and have the same size as reported by + * @ref fieldArraySize(SceneField) const for given field. + */ + template::value>::type> Containers::StridedArrayView2D::type> field(SceneField name) const; + + /** + * @brief Mutable data for given named field + * @m_since_latest + * + * Like @ref field(SceneField) const, but returns a mutable view. + * Expects that the scene is mutable. + * @see @ref dataFlags() + */ + template::value>::type> Containers::StridedArrayView1D mutableField(SceneField name); + + /** + * @brief Mutable data for given named array field in a concrete type + * @m_since_latest + * + * Same as above, except that it works with array fields instead --- + * you're expected to select this overload by passing @cpp T[] @ce + * instead of @cpp T @ce. The second dimension is guaranteed to be + * contiguous and have the same size as reported by + * @ref fieldArraySize(SceneField) const for given field. + */ + template::value>::type> Containers::StridedArrayView2D::type> mutableField(SceneField name); + + /** + * @brief Object mapping for given field as 32-bit integers + * @m_since_latest + * + * Convenience alternative to the templated + * @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(). + * @attention In the rare case when @ref objectType() is + * @ref SceneObjectType::UnsignedLong and @ref objectCount() is + * larger than the max representable 32-bit value, this function + * can't be used, only an appropriately typed + * @ref objects(UnsignedInt) const. + * @see @ref objectsInto(UnsignedInt, const Containers::StridedArrayView1D&) const + */ + Containers::Array objectsAsArray(UnsignedInt fieldId) const; + + /** + * @brief Object mapping for given field as 32-bit integers into a pre-allocated view + * @m_since_latest + * + * 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. + * @see @ref fieldSize(UnsignedInt) const + */ + void objectsInto(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; + + /** + * @brief Object mapping for given named field as 32-bit integers + * @m_since_latest + * + * Convenience alternative to the templated + * @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. + * @attention In the rare case when @ref objectType() is + * @ref SceneObjectType::UnsignedLong and @ref objectCount() is + * larger than the max representable 32-bit value, this function + * can't be used, only an appropriately typed + * @ref objects(SceneField) const. + * @see @ref objectsInto(SceneField, const Containers::StridedArrayView1D&) const, + * @ref hasField() + */ + Containers::Array objectsAsArray(SceneField fieldName) const; + + /** + * @brief Object mapping for given named field as 32-bit integers into a pre-allocated view + * @m_since_latest + * + * 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. + * @see @ref fieldSize(SceneField) const + */ + void objectsInto(SceneField fieldName, const Containers::StridedArrayView1D& destination) const; + + /** + * @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 + * array. The field is expected to exist. + * @attention In the rare case when @ref fieldType(SceneField) const is + * @ref SceneFieldType::Long and @ref fieldSize(SceneField) const + * is larger than the max representable 32-bit value, this + * function can't be used, only an appropriately typed + * @ref field(SceneField) const. + * @see @ref parentsInto(), @ref hasField() + */ + Containers::Array parentsAsArray() const; - /** @brief Two-dimensional child objects */ - const std::vector& children2D() const { return _children2D; } + /** + * @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. + * @see @ref fieldSize(SceneField) const + */ + void parentsInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief 2D transformations as 3x3 float matrices + * @m_since_latest + * + * Convenience alternative to @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 + * expected to exist and they are expected to have a type corresponding + * to 2D, otherwise you're supposed to use + * @ref transformations3DAsArray(). + * @see @ref transformations2DInto(), @ref hasField(), + * @ref fieldType(SceneField) const + */ + Containers::Array transformations2DAsArray() const; - /** @brief Three-dimensional child objects */ - const std::vector& children3D() const { return _children3D; } + /** + * @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. + * @see @ref fieldSize(SceneField) const + */ + void transformations2DInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief 3D transformations as 4x4 float matrices + * @m_since_latest + * + * Convenience alternative to @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 + * expected to exist and they are expected to have a type corresponding + * to 3D, otherwise you're supposed to use + * @ref transformations2DAsArray(). + * @see @ref transformations3DInto(), @ref hasField(), + * @ref fieldType(SceneField) 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. + * @see @ref fieldSize(SceneField) const + */ + void transformations3DInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief Mesh 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() + */ + Containers::Array meshesAsArray() const; + + /** + * @brief Mesh 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. + * @see @ref fieldSize(SceneField) const + */ + void meshesInto(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 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. + * @see @ref lightsInto(), @ref hasField() + */ + 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. + * @see @ref fieldSize(SceneField) const + */ + void lightsInto(const Containers::StridedArrayView1D& destination) 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 + * array. The field is expected to exist. + * @see @ref camerasInto(), @ref hasField() + */ + 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. + * @see @ref fieldSize(SceneField) const + */ + void camerasInto(const Containers::StridedArrayView1D& destination) 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. + * @see @ref skinsInto(), @ref hasField() + */ + 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. + * @see @ref fieldSize(SceneField) const + */ + void skinsInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief Release field data storage + * @m_since_latest + * + * Releases the ownership of the field data array and resets internal + * field-related state to default. The scene then behaves like if it + * has no fields (but it can still have non-empty data). Note that the + * returned array has a custom no-op deleter when the data are not + * owned by the scene, and while the returned array type is mutable, + * the actual memory might be not. Additionally, the returned + * @ref SceneFieldData instances may have different data pointers and + * sizes than what's returned by the @ref field() and + * @ref fieldData(UnsignedInt) const accessors as some of them might be + * offset-only --- use this function only if you *really* know what are + * you doing. + * @see @ref fieldData(), @ref SceneFieldData::isOffsetOnly() + */ + Containers::Array releaseFieldData(); + + /** + * @brief Release data storage + * @m_since_latest + * + * Releases the ownership of the data array and resets internal + * field-related state to default. The scene then behaves like it has + * no fields and no data. If you want to release field data as well, + * first call @ref releaseFieldData() and then this function. + * + * Note that the returned array has a custom no-op deleter when the + * data are not owned by the scene, and while the returned array type + * is mutable, the actual memory might be not. + * @see @ref data(), @ref dataFlags() + */ + Containers::Array releaseData(); /** * @brief Importer-specific state @@ -91,11 +1537,397 @@ class MAGNUM_TRADE_EXPORT SceneData { const void* importerState() const { return _importerState; } private: - std::vector _children2D, - _children3D; + /* Internal helper that doesn't assert, unlike fieldId() */ + UnsignedInt fieldFor(SceneField name) const; + + /* Like objects() / field(), but returning just a 1D view */ + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataObjectViewInternal(const SceneFieldData& field) const; + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataFieldViewInternal(const SceneFieldData& field) const; + + #ifndef CORRADE_NO_ASSERT + template bool checkFieldTypeCompatibility(const SceneFieldData& attribute, const char* prefix) const; + #endif + + MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; + MAGNUM_TRADE_LOCAL void transformations2DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; + MAGNUM_TRADE_LOCAL void transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; + MAGNUM_TRADE_LOCAL void indexFieldIntoInternal( + #ifndef CORRADE_NO_ASSERT + const char* const prefix, + #endif + const SceneField name, const Containers::StridedArrayView1D& destination) const; + MAGNUM_TRADE_LOCAL Containers::Array indexFieldAsArrayInternal( + #ifndef CORRADE_NO_ASSERT + const char* const prefix, + #endif + const SceneField name) const; + + DataFlags _dataFlags; + SceneObjectType _objectType; + /* 2/6 bytes free */ + UnsignedLong _objectCount; const void* _importerState; + Containers::Array _fields; + Containers::Array _data; }; +namespace Implementation { + /* Making this a struct because there can't be partial specializations for + a function (which we may need for pointers, matrix/vector subclasses + etc) */ + template struct SceneFieldTypeFor { + static_assert(sizeof(T) == 0, "unsupported field type"); + }; + #ifndef DOXYGEN_GENERATING_OUTPUT + #define _c(type_) template<> struct SceneFieldTypeFor { \ + constexpr static SceneFieldType type() { \ + return SceneFieldType::type_; \ + } \ + }; + /* Bool needs a special type */ + _c(Float) + _c(Half) + _c(Double) + _c(UnsignedByte) + _c(Byte) + _c(UnsignedShort) + _c(Short) + _c(UnsignedInt) + _c(Int) + _c(UnsignedLong) + _c(Long) + _c(Vector2) + _c(Vector2h) + _c(Vector2d) + _c(Vector2ub) + _c(Vector2b) + _c(Vector2us) + _c(Vector2s) + _c(Vector2ui) + _c(Vector2i) + _c(Vector3) + _c(Vector3h) + _c(Vector3d) + _c(Vector3ub) + _c(Vector3b) + _c(Vector3us) + _c(Vector3s) + _c(Vector3ui) + _c(Vector3i) + _c(Vector4) + _c(Vector4h) + _c(Vector4d) + _c(Vector4ub) + _c(Vector4b) + _c(Vector4us) + _c(Vector4s) + _c(Vector4ui) + _c(Vector4i) + _c(Matrix2x2) + _c(Matrix2x2h) + _c(Matrix2x2d) + _c(Matrix2x3) + _c(Matrix2x3h) + _c(Matrix2x3d) + _c(Matrix2x4) + _c(Matrix2x4h) + _c(Matrix2x4d) + _c(Matrix3x2) + _c(Matrix3x2h) + _c(Matrix3x2d) + _c(Matrix3x3) + _c(Matrix3x3h) + _c(Matrix3x3d) + _c(Matrix3x4) + _c(Matrix3x4h) + _c(Matrix3x4d) + _c(Matrix4x2) + _c(Matrix4x2h) + _c(Matrix4x2d) + _c(Matrix4x3) + _c(Matrix4x3h) + _c(Matrix4x3d) + _c(Matrix4x4) + _c(Matrix4x4h) + _c(Matrix4x4d) + _c(Range1D) + _c(Range1Dh) + _c(Range1Dd) + _c(Range1Di) + _c(Range2D) + _c(Range2Dh) + _c(Range2Dd) + _c(Range2Di) + _c(Range3D) + _c(Range3Dh) + _c(Range3Dd) + _c(Range3Di) + _c(Complex) + _c(Complexd) + _c(DualComplex) + _c(DualComplexd) + _c(Quaternion) + _c(Quaterniond) + _c(DualQuaternion) + _c(DualQuaterniond) + _c(Deg) + _c(Degh) + _c(Degd) + _c(Rad) + _c(Radh) + _c(Radd) + #undef _c + #endif + /** @todo this doesn't handle RectangleMatrix and Vector at the moment, do we need those? */ + template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + + template constexpr SceneObjectType sceneObjectTypeFor() { + static_assert(sizeof(T) == 0, "unsupported object type"); + return {}; + } + #ifndef DOXYGEN_GENERATING_OUTPUT + #define _c(type) \ + template<> constexpr SceneObjectType sceneObjectTypeFor() { return SceneObjectType::type; } + _c(UnsignedByte) + _c(UnsignedShort) + _c(UnsignedInt) + _c(UnsignedLong) + #undef _c + #endif + + constexpr bool isSceneFieldTypeCompatibleWithField(SceneField name, SceneFieldType type) { + return + /* Named fields are restricted so we can decode them */ + (name == SceneField::Parent && + (type == SceneFieldType::Byte || + type == SceneFieldType::Short || + type == SceneFieldType::Int || + type == SceneFieldType::Long)) || + (name == SceneField::Transformation && + (type == SceneFieldType::Matrix3x3 || + type == SceneFieldType::Matrix3x3d || + type == SceneFieldType::Matrix4x4 || + type == SceneFieldType::Matrix4x4d || + type == SceneFieldType::DualComplex || + type == SceneFieldType::DualComplexd || + type == SceneFieldType::DualQuaternion || + type == SceneFieldType::DualQuaterniond)) || + ((name == SceneField::Translation || + name == SceneField::Scaling) && + (type == SceneFieldType::Vector2 || + type == SceneFieldType::Vector2d || + type == SceneFieldType::Vector3 || + type == SceneFieldType::Vector3d)) || + (name == SceneField::Rotation && + (type == SceneFieldType::Complex || + type == SceneFieldType::Complexd || + type == SceneFieldType::Quaternion || + type == SceneFieldType::Quaterniond)) || + ((name == SceneField::Mesh || + name == SceneField::MeshMaterial || + name == SceneField::Light || + name == SceneField::Camera || + name == SceneField::Skin) && + (type == SceneFieldType::UnsignedByte || + type == SceneFieldType::UnsignedShort || + type == SceneFieldType::UnsignedInt)) || + /* Custom fields can be anything */ + isSceneFieldCustom(name); + } + + constexpr bool isSceneFieldArrayAllowed(SceneField name) { + return isSceneFieldCustom(name); + } +} + +constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize) noexcept: + _size{(CORRADE_CONSTEXPR_ASSERT(objectData.size() == fieldData.size(), + "Trade::SceneFieldData: expected object and field view to have the same size but got" << objectData.size() << "and" << fieldData.size()), objectData.size())}, + _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), + "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, + _isOffsetOnly{false}, + _objectType{objectType}, + _objectStride{(CORRADE_CONSTEXPR_ASSERT(objectData.stride() >= -32768 && objectData.stride() <= 32767, + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got" << objectData.stride()), Short(objectData.stride()))}, + _objectData{objectData.data()}, + _fieldType{fieldType}, + _fieldStride{(CORRADE_CONSTEXPR_ASSERT(fieldData.stride() >= -32768 && fieldData.stride() <= 32767, + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got" << fieldData.stride()), Short(fieldData.stride()))}, + _fieldArraySize{(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name), + "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, + _fieldData{fieldData.data()} {} + +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Implementation::sceneObjectTypeFor::type>(), objectData, Implementation::SceneFieldTypeFor::type>::type(), fieldData, 0} {} + +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{ + name, + Implementation::sceneObjectTypeFor::type>(), + objectData, + Implementation::SceneFieldTypeFor::type>::type(), + Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, + /* Not using isContiguous<1>() as that's not constexpr */ + (CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])) +} {} + +constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneObjectType objectType, const std::size_t objectOffset, const std::ptrdiff_t objectStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize) noexcept: + _size{size}, + _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), + "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, + _isOffsetOnly{true}, + _objectType{objectType}, + _objectStride{(CORRADE_CONSTEXPR_ASSERT(objectStride >= -32768 && objectStride <= 32767, + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got" << objectStride), Short(objectStride))}, + _objectData{objectOffset}, + _fieldType{fieldType}, + _fieldStride{(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767, + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got" << fieldStride), Short(fieldStride))}, + _fieldArraySize{(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name), + "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, + _fieldData{fieldOffset} {} + +template Containers::StridedArrayView1D SceneData::objects(const UnsignedInt fieldId) const { + Containers::StridedArrayView2D data = objects(fieldId); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, + "Trade::SceneData::objects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + return Containers::arrayCast<1, const T>(data); +} + +template Containers::StridedArrayView1D SceneData::mutableObjects(const UnsignedInt fieldId) { + Containers::StridedArrayView2D data = mutableObjects(fieldId); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, + "Trade::SceneData::mutableObjects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + return Containers::arrayCast<1, T>(data); +} + +template Containers::StridedArrayView1D SceneData::objects(const SceneField fieldName) const { + Containers::StridedArrayView2D data = objects(fieldName); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, + "Trade::SceneData::objects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + return Containers::arrayCast<1, const T>(data); +} + +template Containers::StridedArrayView1D SceneData::mutableObjects(const SceneField fieldName) { + Containers::StridedArrayView2D data = mutableObjects(fieldName); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, + "Trade::SceneData::mutableObjects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + return Containers::arrayCast<1, T>(data); +} + +#ifndef CORRADE_NO_ASSERT +template bool SceneData::checkFieldTypeCompatibility(const SceneFieldData& field, const char* const prefix) const { + CORRADE_ASSERT(Implementation::SceneFieldTypeFor::type>::type() == field._fieldType, + prefix << field._name << "is" << field._fieldType << "but requested a type equivalent to" << Implementation::SceneFieldTypeFor::type>::type(), false); + if(field._fieldArraySize) CORRADE_ASSERT(std::is_array::value, + prefix << field._name << "is an array field, use T[] to access it", false); + else CORRADE_ASSERT(!std::is_array::value, + prefix << field._name << "is not an array field, can't use T[] to access it", false); + return true; +} +#endif + +template Containers::StridedArrayView1D SceneData::field(const UnsignedInt id) const { + Containers::StridedArrayView2D data = field(id); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[id], "Trade::SceneData::field():")) return {}; + #endif + return Containers::arrayCast<1, const T>(data); +} + +template Containers::StridedArrayView2D::type> SceneData::field(const UnsignedInt id) const { + Containers::StridedArrayView2D data = field(id); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[id], "Trade::SceneData::field():")) return {}; + #endif + return Containers::arrayCast<2, const typename std::remove_extent::type>(data); +} + +template Containers::StridedArrayView1D SceneData::mutableField(const UnsignedInt id) { + Containers::StridedArrayView2D data = mutableField(id); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[id], "Trade::SceneData::mutableField():")) return {}; + #endif + return Containers::arrayCast<1, T>(data); +} + +template Containers::StridedArrayView2D::type> SceneData::mutableField(const UnsignedInt id) { + Containers::StridedArrayView2D data = mutableField(id); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[id], "Trade::SceneData::mutableField():")) return {}; + #endif + return Containers::arrayCast<2, typename std::remove_extent::type>(data); +} + +template Containers::StridedArrayView1D SceneData::field(const SceneField name) const { + Containers::StridedArrayView2D data = field(name); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; + #endif + return Containers::arrayCast<1, const T>(data); +} + +template Containers::StridedArrayView2D::type> SceneData::field(const SceneField name) const { + Containers::StridedArrayView2D data = field(name); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; + #endif + return Containers::arrayCast<2, const typename std::remove_extent::type>(data); +} + +template Containers::StridedArrayView1D SceneData::mutableField(const SceneField name) { + Containers::StridedArrayView2D data = mutableField(name); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; + #endif + return Containers::arrayCast<1, T>(data); +} + +template Containers::StridedArrayView2D::type> SceneData::mutableField(const SceneField name) { + Containers::StridedArrayView2D data = mutableField(name); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif + #ifndef CORRADE_NO_ASSERT + if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; + #endif + return Containers::arrayCast<2, typename std::remove_extent::type>(data); +} + }} #endif diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 671aec909d..6f4f43f28e 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -1644,8 +1644,9 @@ void AbstractImporterTest::scene() { return {}; } Containers::Optional doScene(UnsignedInt id) override { - if(id == 7) return SceneData{{}, {}, &state}; - return SceneData{{}, {}}; + if(id == 7) + return SceneData{SceneObjectType::UnsignedByte, 0, nullptr, {}, &state}; + return SceneData{SceneObjectType::UnsignedByte, 0, nullptr, {}}; } } importer; diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index f90dba8b22..75103d4619 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -60,7 +60,7 @@ corrade_add_test(TradePbrClearCoatMaterialDataTest PbrClearCoatMaterialDataTest. corrade_add_test(TradePbrMetallicRoughnessMate___Test PbrMetallicRoughnessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePbrSpecularGlossinessMat___Test PbrSpecularGlossinessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePhongMaterialDataTest PhongMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) -corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTrade) +corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeSkinDataTest SkinDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) @@ -68,6 +68,7 @@ set_property(TARGET TradeAnimationDataTest TradeMaterialDataTest TradeMeshDataTest + TradeSceneDataTest APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT") set_target_properties( diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 26e81c9b7b..a96702135b 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -23,9 +23,18 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include +#include +#include #include "Magnum/Magnum.h" +#include "Magnum/Math/Half.h" +#include "Magnum/Math/DualComplex.h" +#include "Magnum/Math/DualQuaternion.h" +#include "Magnum/Math/Range.h" #include "Magnum/Trade/SceneData.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -33,24 +42,1526 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct SceneDataTest: TestSuite::Tester { explicit SceneDataTest(); + void objectTypeSize(); + void objectTypeSizeInvalid(); + void debugObjectType(); + + void customFieldName(); + void customFieldNameTooLarge(); + void customFieldNameNotCustom(); + void debugFieldName(); + + void fieldTypeSize(); + void fieldTypeSizeInvalid(); + void debugFieldType(); + + void constructField(); + void constructFieldDefault(); + void constructFieldCustom(); + void constructField2D(); + void constructFieldTypeErased(); + void constructFieldNonOwningArray(); + void constructFieldOffsetOnly(); + void constructFieldArray(); + void constructFieldArray2D(); + void constructFieldArrayTypeErased(); + void constructFieldArrayOffsetOnly(); + + void constructFieldWrongType(); + void constructFieldInconsistentViewSize(); + void constructFieldTooLargeObjectStride(); + void constructFieldTooLargeFieldStride(); + void constructFieldWrongDataAccess(); + void constructField2DWrongSize(); + void constructField2DNonContiguous(); + void constructFieldArrayNonContiguous(); + void constructFieldArrayNotAllowed(); + void constructFieldArray2DWrongSize(); + void constructFieldArray2DNonContiguous(); + void construct(); + void constructZeroFields(); + void constructZeroObjects(); + + void constructNotOwned(); + + void constructDuplicateField(); + void constructDuplicateCustomField(); + void constructInconsistentObjectType(); + void constructObjectDataNotContained(); + void constructFieldDataNotContained(); + void constructObjectTypeTooSmall(); + void constructNotOwnedFlagOwned(); + void constructMismatchedTRSViews(); + void constructMismatchedMeshMaterialView(); + void constructCopy(); void constructMove(); + + template void objectsAsArrayByIndex(); + template void objectsAsArrayByName(); + void objectsAsArrayLongType(); + void objectsIntoArrayInvalidSize(); + template void parentsAsArray(); + #ifndef CORRADE_TARGET_32BIT + void parentsAsArrayLongType(); + #endif + void parentsIntoArrayInvalidSize(); + template void transformations2DAsArray(); + template void transformations2DAsArrayTRS(); + template void transformations2DAsArrayBut3DType(); + template void transformations2DAsArrayBut3DTypeTRS(); + void transformations2DIntoArrayInvalidSize(); + template void transformations3DAsArray(); + template void transformations3DAsArrayTRS(); + template void transformations3DAsArrayBut2DType(); + template void transformations3DAsArrayBut2DTypeTRS(); + void transformations3DIntoArrayInvalidSize(); + template void meshesAsArray(); + void meshesIntoArrayInvalidSize(); + template void meshMaterialsAsArray(); + void meshMaterialsIntoArrayInvalidSize(); + template void lightsAsArray(); + void lightsIntoArrayInvalidSize(); + template void camerasAsArray(); + void camerasIntoArrayInvalidSize(); + template void skinsAsArray(); + void skinsIntoArrayInvalidSize(); + + void mutableAccessNotAllowed(); + + void objectsNotFound(); + void objectsWrongType(); + + void fieldNotFound(); + void fieldWrongType(); + void fieldWrongArrayAccess(); + + void releaseFieldData(); + void releaseData(); +}; + +const struct { + const char* name; + DataFlags dataFlags; +} NotOwnedData[]{ + {"", {}}, + {"mutable", DataFlag::Mutable} }; SceneDataTest::SceneDataTest() { - addTests({&SceneDataTest::construct, + addTests({&SceneDataTest::objectTypeSize, + &SceneDataTest::objectTypeSizeInvalid, + &SceneDataTest::debugObjectType, + + &SceneDataTest::customFieldName, + &SceneDataTest::customFieldNameTooLarge, + &SceneDataTest::customFieldNameNotCustom, + &SceneDataTest::debugFieldName, + + &SceneDataTest::fieldTypeSize, + &SceneDataTest::fieldTypeSizeInvalid, + &SceneDataTest::debugFieldType, + + &SceneDataTest::constructField, + &SceneDataTest::constructFieldDefault, + &SceneDataTest::constructFieldCustom, + &SceneDataTest::constructField2D, + &SceneDataTest::constructFieldTypeErased, + &SceneDataTest::constructFieldNonOwningArray, + &SceneDataTest::constructFieldOffsetOnly, + &SceneDataTest::constructFieldArray, + &SceneDataTest::constructFieldArray2D, + &SceneDataTest::constructFieldArrayTypeErased, + &SceneDataTest::constructFieldArrayOffsetOnly, + + &SceneDataTest::constructFieldWrongType, + &SceneDataTest::constructFieldInconsistentViewSize, + &SceneDataTest::constructFieldTooLargeObjectStride, + &SceneDataTest::constructFieldTooLargeFieldStride, + &SceneDataTest::constructFieldWrongDataAccess, + &SceneDataTest::constructField2DWrongSize, + &SceneDataTest::constructField2DNonContiguous, + &SceneDataTest::constructFieldArrayNonContiguous, + &SceneDataTest::constructFieldArrayNotAllowed, + &SceneDataTest::constructFieldArray2DWrongSize, + &SceneDataTest::constructFieldArray2DNonContiguous, + + &SceneDataTest::construct, + &SceneDataTest::constructZeroFields, + &SceneDataTest::constructZeroObjects}); + + addInstancedTests({&SceneDataTest::constructNotOwned}, + Containers::arraySize(NotOwnedData)); + + addTests({&SceneDataTest::constructDuplicateField, + &SceneDataTest::constructDuplicateCustomField, + &SceneDataTest::constructInconsistentObjectType, + &SceneDataTest::constructObjectDataNotContained, + &SceneDataTest::constructFieldDataNotContained, + &SceneDataTest::constructObjectTypeTooSmall, + &SceneDataTest::constructNotOwnedFlagOwned, + &SceneDataTest::constructMismatchedTRSViews, + &SceneDataTest::constructMismatchedMeshMaterialView, + &SceneDataTest::constructCopy, - &SceneDataTest::constructMove}); + &SceneDataTest::constructMove, + + &SceneDataTest::objectsAsArrayByIndex, + &SceneDataTest::objectsAsArrayByIndex, + &SceneDataTest::objectsAsArrayByIndex, + &SceneDataTest::objectsAsArrayByIndex, + &SceneDataTest::objectsAsArrayByName, + &SceneDataTest::objectsAsArrayByName, + &SceneDataTest::objectsAsArrayByName, + &SceneDataTest::objectsAsArrayByName, + &SceneDataTest::objectsAsArrayLongType, + &SceneDataTest::objectsIntoArrayInvalidSize, + &SceneDataTest::parentsAsArray, + &SceneDataTest::parentsAsArray, + &SceneDataTest::parentsAsArray, + &SceneDataTest::parentsAsArray, + #ifndef CORRADE_TARGET_32BIT + &SceneDataTest::parentsAsArrayLongType, + #endif + &SceneDataTest::parentsIntoArrayInvalidSize, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArrayTRS, + &SceneDataTest::transformations2DAsArrayTRS, + &SceneDataTest::transformations2DAsArrayBut3DType, + &SceneDataTest::transformations2DAsArrayBut3DType, + &SceneDataTest::transformations2DAsArrayBut3DType, + &SceneDataTest::transformations2DAsArrayBut3DType, + &SceneDataTest::transformations2DAsArrayBut3DTypeTRS, + &SceneDataTest::transformations2DAsArrayBut3DTypeTRS, + &SceneDataTest::transformations2DIntoArrayInvalidSize, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArrayTRS, + &SceneDataTest::transformations3DAsArrayTRS, + &SceneDataTest::transformations3DAsArrayBut2DType, + &SceneDataTest::transformations3DAsArrayBut2DType, + &SceneDataTest::transformations3DAsArrayBut2DType, + &SceneDataTest::transformations3DAsArrayBut2DType, + &SceneDataTest::transformations3DAsArrayBut2DTypeTRS, + &SceneDataTest::transformations3DAsArrayBut2DTypeTRS, + &SceneDataTest::transformations3DIntoArrayInvalidSize, + &SceneDataTest::meshesAsArray, + &SceneDataTest::meshesAsArray, + &SceneDataTest::meshesAsArray, + &SceneDataTest::meshesIntoArrayInvalidSize, + &SceneDataTest::meshMaterialsAsArray, + &SceneDataTest::meshMaterialsAsArray, + &SceneDataTest::meshMaterialsAsArray, + &SceneDataTest::meshMaterialsIntoArrayInvalidSize, + &SceneDataTest::lightsAsArray, + &SceneDataTest::lightsAsArray, + &SceneDataTest::lightsAsArray, + &SceneDataTest::lightsIntoArrayInvalidSize, + &SceneDataTest::camerasAsArray, + &SceneDataTest::camerasAsArray, + &SceneDataTest::camerasAsArray, + &SceneDataTest::camerasIntoArrayInvalidSize, + &SceneDataTest::skinsAsArray, + &SceneDataTest::skinsAsArray, + &SceneDataTest::skinsAsArray, + &SceneDataTest::skinsIntoArrayInvalidSize, + + &SceneDataTest::mutableAccessNotAllowed, + + &SceneDataTest::objectsNotFound, + &SceneDataTest::objectsWrongType, + + &SceneDataTest::fieldNotFound, + &SceneDataTest::fieldWrongType, + &SceneDataTest::fieldWrongArrayAccess, + + &SceneDataTest::releaseFieldData, + &SceneDataTest::releaseData}); +} + +using namespace Math::Literals; + +void SceneDataTest::objectTypeSize() { + CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedByte), 1); + CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedShort), 2); + CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedInt), 4); + CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedLong), 8); +} + +void SceneDataTest::objectTypeSizeInvalid() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + sceneObjectTypeSize(SceneObjectType{}); + sceneObjectTypeSize(SceneObjectType(0x73)); + + CORRADE_COMPARE(out.str(), + "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x0)\n" + "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x73)\n"); +} + +void SceneDataTest::debugObjectType() { + std::ostringstream out; + Debug{&out} << SceneObjectType::UnsignedLong << SceneObjectType(0x73); + CORRADE_COMPARE(out.str(), "Trade::SceneObjectType::UnsignedLong Trade::SceneObjectType(0x73)\n"); +} + +void SceneDataTest::customFieldName() { + CORRADE_VERIFY(!isSceneFieldCustom(SceneField::Rotation)); + CORRADE_VERIFY(!isSceneFieldCustom(SceneField(0x0fffffffu))); + CORRADE_VERIFY(isSceneFieldCustom(SceneField::Custom)); + CORRADE_VERIFY(isSceneFieldCustom(SceneField(0x80000000u))); + + CORRADE_COMPARE(UnsignedInt(sceneFieldCustom(0)), 0x80000000u); + CORRADE_COMPARE(UnsignedInt(sceneFieldCustom(0xabcd)), 0x8000abcdu); + CORRADE_COMPARE(UnsignedInt(sceneFieldCustom(0x7fffffff)), 0xffffffffu); + + CORRADE_COMPARE(sceneFieldCustom(SceneField::Custom), 0); + CORRADE_COMPARE(sceneFieldCustom(SceneField(0x8000abcdu)), 0xabcd); + CORRADE_COMPARE(sceneFieldCustom(SceneField(0xffffffffu)), 0x7fffffffu); + + constexpr bool is = isSceneFieldCustom(SceneField(0x8000abcdu)); + CORRADE_VERIFY(is); + constexpr SceneField a = sceneFieldCustom(0xabcd); + CORRADE_COMPARE(UnsignedInt(a), 0x8000abcdu); + constexpr UnsignedInt b = sceneFieldCustom(a); + CORRADE_COMPARE(b, 0xabcd); +} + +void SceneDataTest::customFieldNameTooLarge() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + sceneFieldCustom(1u << 31); + CORRADE_COMPARE(out.str(), "Trade::sceneFieldCustom(): index 2147483648 too large\n"); +} + +void SceneDataTest::customFieldNameNotCustom() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + sceneFieldCustom(SceneField::Transformation); + CORRADE_COMPARE(out.str(), "Trade::sceneFieldCustom(): Trade::SceneField::Transformation is not custom\n"); +} + +void SceneDataTest::debugFieldName() { + std::ostringstream out; + Debug{&out} << SceneField::Transformation << sceneFieldCustom(73) << SceneField(0xdeadda7); + CORRADE_COMPARE(out.str(), "Trade::SceneField::Transformation Trade::SceneField::Custom(73) Trade::SceneField(0xdeadda7)\n"); +} + +void SceneDataTest::fieldTypeSize() { + /* Test at least one of every size */ + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Byte), sizeof(Byte)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Degh), sizeof(Degh)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Vector3ub), sizeof(Vector3ub)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Range1Dh), sizeof(Range1Dh)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Vector3s), sizeof(Vector3s)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Long), sizeof(Long)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x2h), sizeof(Matrix3x2h)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix4x2h), sizeof(Matrix4x2h)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x3h), sizeof(Matrix3x3h)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Range3Di), sizeof(Range3Di)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::DualQuaternion), sizeof(DualQuaternion)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x3), sizeof(Matrix3x3)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x2d), sizeof(Matrix3x2d)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::DualQuaterniond), sizeof(DualQuaterniond)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x3d), sizeof(Matrix3x3d)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x4d), sizeof(Matrix3x4d)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix4x4d), sizeof(Matrix4x4d)); +} + +void SceneDataTest::fieldTypeSizeInvalid() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + sceneFieldTypeSize(SceneFieldType{}); + sceneFieldTypeSize(SceneFieldType(0xdead)); + + CORRADE_COMPARE(out.str(), + "Trade::sceneFieldTypeSize(): invalid type Trade::SceneFieldType(0x0)\n" + "Trade::sceneFieldTypeSize(): invalid type Trade::SceneFieldType(0xdead)\n"); +} + +void SceneDataTest::debugFieldType() { + std::ostringstream out; + Debug{&out} << SceneFieldType::Matrix3x4h << SceneFieldType(0xdead); + CORRADE_COMPARE(out.str(), "Trade::SceneFieldType::Matrix3x4h Trade::SceneFieldType(0xdead)\n"); +} + +constexpr Complexd Rotations2D[3] { + Complexd{Constantsd::sqrtHalf(), Constantsd::sqrtHalf()}, /* 45° */ + Complexd{1.0, 0.0}, /* 0° */ + Complexd{0.0, 1.0}, /* 90° */ +}; +const UnsignedShort RotationObjects2D[3] { + 17, + 35, + 98 +}; + +void SceneDataTest::constructField() { + const UnsignedShort rotationObjectData[3]{}; + const Complexd rotationFieldData[3]; + + SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; + CORRADE_VERIFY(!rotations.isOffsetOnly()); + CORRADE_COMPARE(rotations.name(), SceneField::Rotation); + CORRADE_COMPARE(rotations.size(), 3); + CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(rotations.objectData().size(), 3); + CORRADE_COMPARE(rotations.objectData().stride(), sizeof(UnsignedShort)); + CORRADE_VERIFY(rotations.objectData().data() == rotationObjectData); + CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); + CORRADE_COMPARE(rotations.fieldArraySize(), 0); + CORRADE_COMPARE(rotations.fieldData().size(), 3); + CORRADE_COMPARE(rotations.fieldData().stride(), sizeof(Complexd)); + CORRADE_VERIFY(rotations.fieldData().data() == rotationFieldData); + + /* This is allowed too for simplicity, the parameter has to be large enough + tho */ + char someArray[3*sizeof(Complexd)]; + CORRADE_COMPARE(rotations.fieldData(someArray).size(), 3); + CORRADE_COMPARE(rotations.fieldData(someArray).stride(), sizeof(Complexd)); + CORRADE_VERIFY(rotations.fieldData(someArray).data() == rotationFieldData); + CORRADE_COMPARE(rotations.objectData(someArray).size(), 3); + CORRADE_COMPARE(rotations.objectData(someArray).stride(), sizeof(UnsignedShort)); + CORRADE_VERIFY(rotations.objectData(someArray).data() == rotationObjectData); + + constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationObjects2D), Containers::arrayView(Rotations2D)}; + constexpr bool isOffsetOnly = crotations.isOffsetOnly(); + constexpr SceneField name = crotations.name(); + constexpr SceneObjectType objectType = crotations.objectType(); + constexpr Containers::StridedArrayView1D objectData = crotations.objectData(); + constexpr SceneFieldType fieldType = crotations.fieldType(); + constexpr UnsignedShort fieldArraySize = crotations.fieldArraySize(); + constexpr Containers::StridedArrayView1D fieldData = crotations.fieldData(); + CORRADE_VERIFY(!isOffsetOnly); + CORRADE_COMPARE(name, SceneField::Rotation); + CORRADE_COMPARE(objectType, SceneObjectType::UnsignedShort); + CORRADE_COMPARE(objectData.size(), 3); + CORRADE_COMPARE(objectData.stride(), sizeof(UnsignedShort)); + CORRADE_COMPARE(objectData.data(), RotationObjects2D); + CORRADE_COMPARE(fieldType, SceneFieldType::Complexd); + CORRADE_COMPARE(fieldArraySize, 0); + CORRADE_COMPARE(fieldData.size(), 3); + CORRADE_COMPARE(fieldData.stride(), sizeof(Complexd)); + CORRADE_COMPARE(fieldData.data(), Rotations2D); +} + +void SceneDataTest::constructFieldDefault() { + SceneFieldData data; + CORRADE_COMPARE(data.name(), SceneField{}); + CORRADE_COMPARE(data.fieldType(), SceneFieldType{}); + CORRADE_COMPARE(data.objectType(), SceneObjectType{}); + + constexpr SceneFieldData cdata; + CORRADE_COMPARE(cdata.name(), SceneField{}); + CORRADE_COMPARE(cdata.fieldType(), SceneFieldType{}); + CORRADE_COMPARE(cdata.objectType(), SceneObjectType{}); +} + +void SceneDataTest::constructFieldCustom() { + /* Verifying it doesn't hit any assertion about disallowed type for given + attribute */ + + const UnsignedByte rangeObjectData[3]{}; + const Range2Dh rangeFieldData[3]; + SceneFieldData ranges{sceneFieldCustom(13), Containers::arrayView(rangeObjectData), Containers::arrayView(rangeFieldData)}; + CORRADE_COMPARE(ranges.name(), sceneFieldCustom(13)); + CORRADE_COMPARE(ranges.objectType(), SceneObjectType::UnsignedByte); + CORRADE_VERIFY(ranges.objectData().data() == rangeObjectData); + CORRADE_COMPARE(ranges.fieldType(), SceneFieldType::Range2Dh); + CORRADE_VERIFY(ranges.fieldData().data() == rangeFieldData); +} + +void SceneDataTest::constructField2D() { + char rotationObjectData[6*sizeof(UnsignedShort)]; + char rotationFieldData[6*sizeof(Complexd)]; + auto rotationObjectView = Containers::StridedArrayView2D{rotationObjectData, {6, sizeof(UnsignedShort)}}.every(2); + auto rotationFieldView = Containers::StridedArrayView2D{rotationFieldData, {6, sizeof(Complexd)}}.every(2); + + SceneFieldData rotations{SceneField::Rotation, rotationObjectView, SceneFieldType::Complexd, rotationFieldView}; + CORRADE_VERIFY(!rotations.isOffsetOnly()); + CORRADE_COMPARE(rotations.name(), SceneField::Rotation); + CORRADE_COMPARE(rotations.size(), 3); + CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(rotations.objectData().size(), 3); + CORRADE_COMPARE(rotations.objectData().stride(), 2*sizeof(UnsignedShort)); + CORRADE_COMPARE(rotations.objectData().data(), rotationObjectView.data()); + CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); + CORRADE_COMPARE(rotations.fieldArraySize(), 0); + CORRADE_COMPARE(rotations.fieldData().size(), 3); + CORRADE_COMPARE(rotations.fieldData().stride(), 2*sizeof(Complexd)); + CORRADE_COMPARE(rotations.fieldData().data(), rotationFieldView.data()); +} + +void SceneDataTest::constructFieldTypeErased() { + const UnsignedLong scalingObjectData[3]{}; + const Vector3 scalingFieldData[3]; + SceneFieldData scalings{SceneField::Scaling, SceneObjectType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingObjectData)), SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingFieldData))}; + CORRADE_VERIFY(!scalings.isOffsetOnly()); + CORRADE_COMPARE(scalings.name(), SceneField::Scaling); + CORRADE_COMPARE(scalings.size(), 3); + CORRADE_COMPARE(scalings.objectType(), SceneObjectType::UnsignedLong); + CORRADE_COMPARE(scalings.objectData().size(), 3); + CORRADE_COMPARE(scalings.objectData().stride(), sizeof(UnsignedLong)); + CORRADE_COMPARE(scalings.objectData().data(), scalingObjectData); + CORRADE_COMPARE(scalings.fieldType(), SceneFieldType::Vector3); + CORRADE_COMPARE(scalings.fieldArraySize(), 0); + CORRADE_COMPARE(scalings.fieldData().size(), 3); + CORRADE_COMPARE(scalings.fieldData().stride(), sizeof(Vector3)); + CORRADE_COMPARE(scalings.fieldData().data(), scalingFieldData); +} + +void SceneDataTest::constructFieldNonOwningArray() { + const SceneFieldData data[3]; + Containers::Array array = sceneFieldDataNonOwningArray(data); + CORRADE_COMPARE(array.size(), 3); + CORRADE_COMPARE(static_cast(array.data()), data); +} + +void SceneDataTest::constructFieldOffsetOnly() { + struct Data { + Byte parent; + UnsignedShort object; + Vector2 translation; + } data[] { + {0, 2, {2.0f, 3.0f}}, + {0, 15, {67.0f, -1.1f}} + }; + + SceneFieldData a{SceneField::Translation, 2, SceneObjectType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data)}; + CORRADE_VERIFY(a.isOffsetOnly()); + CORRADE_COMPARE(a.name(), SceneField::Translation); + CORRADE_COMPARE(a.size(), 2); + CORRADE_COMPARE(a.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(a.objectData(data).size(), 2); + CORRADE_COMPARE(a.objectData(data).stride(), sizeof(Data)); + CORRADE_COMPARE_AS(Containers::arrayCast(a.objectData(data)), + Containers::arrayView({2, 15}), + TestSuite::Compare::Container); + CORRADE_COMPARE(a.fieldType(), SceneFieldType::Vector2); + CORRADE_COMPARE(a.fieldArraySize(), 0); + CORRADE_COMPARE(a.fieldData(data).size(), 2); + CORRADE_COMPARE(a.fieldData(data).stride(), sizeof(Data)); + CORRADE_COMPARE_AS(Containers::arrayCast(a.fieldData(data)), + Containers::arrayView({{2.0f, 3.0f}, {67.0f, -1.1f}}), + TestSuite::Compare::Container); +} + +constexpr UnsignedByte ArrayOffsetObjectData[3]{}; +constexpr Int ArrayOffsetFieldData[3*4]{}; + +void SceneDataTest::constructFieldArray() { + UnsignedByte offsetObjectData[3]; + Int offsetFieldData[3*4]; + SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetObjectData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.size(), 3); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.fieldArraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetFieldData); + + constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetObjectData), Containers::StridedArrayView2D{ArrayOffsetFieldData, {3, 4}}}; + CORRADE_VERIFY(!cdata.isOffsetOnly()); + CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(cdata.size(), 3); + CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(cdata.objectData().size(), 3); + CORRADE_COMPARE(cdata.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(cdata.objectData().data() == ArrayOffsetObjectData); + CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(cdata.fieldArraySize(), 4); + CORRADE_COMPARE(cdata.fieldData().size(), 3); + CORRADE_COMPARE(cdata.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(cdata.fieldData().data() == ArrayOffsetFieldData); +} + +void SceneDataTest::constructFieldArray2D() { + char offsetObjectData[3*sizeof(UnsignedByte)]; + char offsetFieldData[3*4*sizeof(Int)]; + SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 4*sizeof(Int)}}, 4}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.size(), 3); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.fieldArraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetFieldData); +} + +void SceneDataTest::constructFieldArrayTypeErased() { + Int offsetData[3*4]; + Containers::StridedArrayView1D offset{offsetData, 3, 4*sizeof(Int)}; + UnsignedByte offsetObjectData[3]; + SceneFieldData data{sceneFieldCustom(34), SceneObjectType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetObjectData)), SceneFieldType::Int, Containers::arrayCast(offset), 4}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.size(), 3); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.fieldArraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetData); +} + +void SceneDataTest::constructFieldArrayOffsetOnly() { + struct Data { + Byte parent; + UnsignedByte object; + Int offset[4]; + }; + + SceneFieldData data{sceneFieldCustom(34), 3, SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; + CORRADE_VERIFY(data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.size(), 3); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.fieldArraySize(), 4); + + Data actual[3]; + CORRADE_COMPARE(data.fieldData(actual).size(), 3); + CORRADE_COMPARE(data.fieldData(actual).stride(), sizeof(Data)); + CORRADE_VERIFY(data.fieldData(actual).data() == &actual[0].offset); + CORRADE_COMPARE(data.objectData(actual).size(), 3); + CORRADE_COMPARE(data.objectData(actual).stride(), sizeof(Data)); + CORRADE_VERIFY(data.objectData(actual).data() == &actual[0].object); + + constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; + CORRADE_VERIFY(cdata.isOffsetOnly()); + CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(cdata.size(), 3); + CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(cdata.fieldArraySize(), 4); +} + +void SceneDataTest::constructFieldInconsistentViewSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const UnsignedShort rotationObjectData[3]{}; + const Complexd rotationFieldData[2]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: expected object and field view to have the same size but got 3 and 2\n"); +} + +void SceneDataTest::constructFieldWrongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const UnsignedShort rotationObjectData[3]{}; + const Quaternion rotationFieldData[3]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Transformation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; + SceneFieldData{SceneField::Transformation, 3, SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: Trade::SceneFieldType::Quaternion is not a valid type for Trade::SceneField::Transformation\n" + "Trade::SceneFieldData: Trade::SceneFieldType::Quaternion is not a valid type for Trade::SceneField::Transformation\n"); +} + +void SceneDataTest::constructFieldTooLargeObjectStride() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + UnsignedInt enough[2]; + char toomuch[2*(32768 + sizeof(UnsignedInt))]; + + /* These should be fine */ + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}, SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 32767, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 65536, -32768, SceneFieldType::UnsignedInt, 0, 4}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}, SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 32768, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 65538, -32769, SceneFieldType::UnsignedInt, 0, 4}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got -32769\n" + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got -32769\n"); +} + +void SceneDataTest::constructFieldTooLargeFieldStride() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + UnsignedInt enough[2]; + char toomuch[2*(32768 + sizeof(UnsignedInt))]; + + /* These should be fine */ + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32767}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65536, -32768}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32768}; + SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65538, -32769}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got -32769\n" + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got -32769\n"); +} + +void SceneDataTest::constructFieldWrongDataAccess() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const UnsignedShort rotationObjectData[3]{}; + const Quaternion rotationFieldData[3]; + SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; + SceneFieldData b{SceneField::Rotation, 3, SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; + CORRADE_VERIFY(!a.isOffsetOnly()); + CORRADE_VERIFY(b.isOffsetOnly()); + + a.objectData(rotationObjectData); /* This is fine, no asserts */ + a.fieldData(rotationFieldData); + + std::ostringstream out; + Error redirectError{&out}; + b.objectData(); + b.fieldData(); + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData::objectData(): the field is offset-only, supply a data array\n" + "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array\n"); +} + +void SceneDataTest::constructField2DWrongSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char rotationFieldData[5*sizeof(Complex)]; + char rotationObjectData[5*sizeof(UnsignedInt)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, + Containers::StridedArrayView2D{rotationObjectData, {4, 5}}.every(2), + SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2)}; + SceneFieldData{SceneField::Translation, + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2), + SceneFieldType::Vector3, + Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2)}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got 5\n" + "Trade::SceneFieldData: second field view dimension size 8 doesn't match Trade::SceneFieldType::Vector3\n"); +} + +void SceneDataTest::constructField2DNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char rotationObjectData[8*sizeof(UnsignedInt)]; + char rotationFieldData[8*sizeof(Complex)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, + Containers::StridedArrayView2D{rotationObjectData, {4, 2*sizeof(UnsignedInt)}}.every({1, 2}), + SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}}; + SceneFieldData{SceneField::Rotation, + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}, + SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationFieldData, {4, 2*sizeof(Complex)}}.every({1, 2})}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second object view dimension is not contiguous\n" + "Trade::SceneFieldData: second field view dimension is not contiguous\n"); +} + +void SceneDataTest::constructFieldArrayNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + UnsignedByte offsetObjectData[3]; + Int offsetFieldData[3*4]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetObjectData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}.every({1, 2})}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: second field view dimension is not contiguous\n"); +} + +void SceneDataTest::constructFieldArrayNotAllowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + UnsignedShort rotationObjectData[3]{}; + Quaternion rotationFieldData[3]; + Containers::ArrayView rotationObjects = rotationObjectData; + Containers::ArrayView rotationFields = rotationFieldData; + Containers::StridedArrayView2D rotationFields2D{rotationFieldData, {3, 3}, {0, sizeof(Quaternion)}}; + auto rotationFields2DChar = Containers::arrayCast<2, const char>(rotationFields2D); + auto rotationObjectsChar = Containers::arrayCast<2, const char>(rotationObjects); + + /* This is all fine */ + SceneFieldData{SceneField::Rotation, + SceneObjectType::UnsignedShort, rotationObjects, + SceneFieldType::Quaternion, rotationFields, 0}; + SceneFieldData{SceneField::Rotation, 3, + SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneFieldType::Quaternion, 0, sizeof(Quaternion), 0}; + SceneFieldData{sceneFieldCustom(37), + rotationObjects, + rotationFields2D}; + SceneFieldData{sceneFieldCustom(37), + rotationObjectsChar, + SceneFieldType::Quaternion, rotationFields2DChar, 3}; + SceneFieldData{sceneFieldCustom(37), 3, + SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneFieldType::Quaternion, 0, sizeof(Quaternion), 3}; + + /* This is not */ + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, + SceneObjectType::UnsignedShort, rotationObjects, + SceneFieldType::Quaternion, rotationFields, 3}; + SceneFieldData{SceneField::Rotation, 3, + SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneFieldType::Quaternion, 0, sizeof(Quaternion), 3}; + SceneFieldData{SceneField::Rotation, + rotationObjects, + rotationFields2D}; + SceneFieldData{SceneField::Rotation, + rotationObjectsChar, + SceneFieldType::Quaternion, rotationFields2DChar, 3}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n" + "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n" + "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n" + "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n"); +} + +void SceneDataTest::constructFieldArray2DWrongSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char rotationObjectData[4*sizeof(UnsignedInt)]; + char rotationFieldData[4*sizeof(Complex)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{sceneFieldCustom(37), + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2), + SceneFieldType::Int, + Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2), 3}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second field view dimension size 8 doesn't match Trade::SceneFieldType::Int and field array size 3\n"); +} + +void SceneDataTest::constructFieldArray2DNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char offsetObjectData[18*sizeof(UnsignedInt)]; + char offsetFieldData[18*sizeof(Int)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{sceneFieldCustom(37), + Containers::StridedArrayView2D{offsetObjectData, {3, 2*sizeof(UnsignedInt)}}.every({1, 2}), + SceneFieldType::Int, + Containers::StridedArrayView2D{offsetFieldData, {3, 3*sizeof(Int)}}, 3}; + SceneFieldData{sceneFieldCustom(37), + Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedInt)}}, + SceneFieldType::Int, + Containers::StridedArrayView2D{offsetFieldData, {3, 6*sizeof(Int)}}.every({1, 2}), 3}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second object view dimension is not contiguous\n" + "Trade::SceneFieldData: second field view dimension is not contiguous\n"); } void SceneDataTest::construct() { - const int a{}; - const SceneData data{{0, 1, 4}, {2, 5}, &a}; + struct TransformParent { + UnsignedShort object; + Matrix4 transformation; + Int parent; + }; + + Containers::StridedArrayView1D transformsParentFieldObjectData; + Containers::StridedArrayView1D meshFieldData; + Containers::StridedArrayView1D radiusFieldData; + Containers::StridedArrayView1D materialMeshRadiusObjectData; + Containers::ArrayTuple data{ + {NoInit, 5, transformsParentFieldObjectData}, + {NoInit, 2, meshFieldData}, + {NoInit, 2, radiusFieldData}, + {NoInit, 2, materialMeshRadiusObjectData}, + }; + transformsParentFieldObjectData[0].object = 4; + transformsParentFieldObjectData[0].transformation = Matrix4::translation(Vector3::xAxis(5.0f)); + transformsParentFieldObjectData[0].parent = -1; + + transformsParentFieldObjectData[1].object = 2; + transformsParentFieldObjectData[1].transformation = Matrix4::translation(Vector3::yAxis(5.0f)); + transformsParentFieldObjectData[1].parent = 0; + + transformsParentFieldObjectData[2].object = 3; + transformsParentFieldObjectData[2].transformation = Matrix4::translation(Vector3::zAxis(5.0f)); + transformsParentFieldObjectData[2].parent = 2; + + transformsParentFieldObjectData[3].object = 0; + transformsParentFieldObjectData[3].transformation = Matrix4::translation(Vector3::yScale(5.0f)); + transformsParentFieldObjectData[3].parent = 1; + + transformsParentFieldObjectData[4].object = 1; + transformsParentFieldObjectData[4].transformation = Matrix4::translation(Vector3::zScale(5.0f)); + transformsParentFieldObjectData[4].parent = -1; + + meshFieldData[0] = 5; + radiusFieldData[0] = {37.5f, 1.5f}; + materialMeshRadiusObjectData[0] = 2; + + meshFieldData[1] = 7; + radiusFieldData[1] = {22.5f, 0.5f}; + materialMeshRadiusObjectData[1] = 6; + + int importerState; + SceneFieldData transformations{SceneField::Transformation, + transformsParentFieldObjectData.slice(&TransformParent::object), + transformsParentFieldObjectData.slice(&TransformParent::transformation)}; + /* Offset-only */ + SceneFieldData parents{SceneField::Parent, 5, + SceneObjectType::UnsignedShort, offsetof(TransformParent, object), sizeof(TransformParent), + SceneFieldType::Int, offsetof(TransformParent, parent), sizeof(TransformParent)}; + SceneFieldData meshes{SceneField::Mesh, + materialMeshRadiusObjectData, + meshFieldData}; + /* Custom & array */ + SceneFieldData radiuses{sceneFieldCustom(37), + materialMeshRadiusObjectData, + Containers::arrayCast<2, Float>(radiusFieldData)}; + SceneData scene{SceneObjectType::UnsignedShort, 8, std::move(data), { + transformations, parents, meshes, radiuses + }, &importerState}; + + /* Basics */ + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_VERIFY(!scene.fieldData().empty()); + CORRADE_COMPARE(static_cast(scene.data()), transformsParentFieldObjectData.data()); + CORRADE_COMPARE(static_cast(scene.mutableData()), transformsParentFieldObjectData.data()); + CORRADE_COMPARE(scene.objectCount(), 8); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.fieldCount(), 4); + CORRADE_COMPARE(scene.importerState(), &importerState); + + /* Field property access by ID */ + CORRADE_COMPARE(scene.fieldName(0), SceneField::Transformation); + CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); + CORRADE_COMPARE(scene.fieldName(2), SceneField::Mesh); + CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(37)); + CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Matrix4x4); + CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Int); + CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldType(3), SceneFieldType::Float); + CORRADE_COMPARE(scene.fieldSize(0), 5); + CORRADE_COMPARE(scene.fieldSize(1), 5); + CORRADE_COMPARE(scene.fieldSize(2), 2); + CORRADE_COMPARE(scene.fieldSize(3), 2); + CORRADE_COMPARE(scene.fieldArraySize(0), 0); + CORRADE_COMPARE(scene.fieldArraySize(1), 0); + CORRADE_COMPARE(scene.fieldArraySize(2), 0); + CORRADE_COMPARE(scene.fieldArraySize(3), 2); + + /* Raw field data access by ID */ + CORRADE_COMPARE(scene.fieldData(2).name(), SceneField::Mesh); + CORRADE_COMPARE(scene.fieldData(2).size(), 2); + CORRADE_COMPARE(scene.fieldData(2).objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).objectData())[1], 6); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).fieldData())[1], 7); + CORRADE_VERIFY(!scene.fieldData(2).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(2).fieldType(), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldData(2).fieldArraySize(), 0); + /* Offset-only */ + CORRADE_COMPARE(scene.fieldData(1).name(), SceneField::Parent); + CORRADE_COMPARE(scene.fieldData(1).size(), 5); + CORRADE_COMPARE(scene.fieldData(1).objectType(), SceneObjectType::UnsignedShort); + CORRADE_VERIFY(!scene.fieldData(1).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(1).fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(scene.fieldData(1).fieldArraySize(), 0); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).objectData())[4], 1); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).fieldData())[4], -1); + /* Array */ + CORRADE_COMPARE(scene.fieldData(3).name(), sceneFieldCustom(37)); + CORRADE_COMPARE(scene.fieldData(3).size(), 2); + CORRADE_COMPARE(scene.fieldData(3).objectType(), SceneObjectType::UnsignedShort); + CORRADE_VERIFY(!scene.fieldData(3).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(3).fieldType(), SceneFieldType::Float); + CORRADE_COMPARE(scene.fieldData(3).fieldArraySize(), 2); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).objectData())[0], 2); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).fieldData())[0], (Vector2{37.5f, 1.5f})); + + /* Typeless object access by ID with a cast later */ + CORRADE_COMPARE(scene.objects(0).size()[0], 5); + CORRADE_COMPARE(scene.objects(1).size()[0], 5); + CORRADE_COMPARE(scene.objects(2).size()[0], 2); + CORRADE_COMPARE(scene.objects(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableObjects(0).size()[0], 5); + CORRADE_COMPARE(scene.mutableObjects(1).size()[0], 5); + CORRADE_COMPARE(scene.mutableObjects(2).size()[0], 2); + CORRADE_COMPARE(scene.mutableObjects(3).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(0))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(1))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(3))[0]), 2); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(0))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(1))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(3))[0]), 2); + + /* Typeless field access by ID with a cast later */ + CORRADE_COMPARE(scene.field(0).size()[0], 5); + CORRADE_COMPARE(scene.field(1).size()[0], 5); + CORRADE_COMPARE(scene.field(2).size()[0], 2); + CORRADE_COMPARE(scene.field(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(0).size()[0], 5); + CORRADE_COMPARE(scene.mutableField(1).size()[0], 5); + CORRADE_COMPARE(scene.mutableField(2).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(3).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const Matrix4>(scene.field(0))[2]), Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE((Containers::arrayCast<1, const Int>(scene.field(1))[4]), -1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedByte>(scene.field(2))[1]), 7); + CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>(scene.field(3))[0]), (Vector2{37.5f, 1.5f})); + CORRADE_COMPARE((Containers::arrayCast<1, Matrix4>(scene.mutableField(0))[2]), Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE((Containers::arrayCast<1, Int>(scene.mutableField(1))[4]), -1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedByte>(scene.mutableField(2))[1]), 7); + CORRADE_COMPARE((Containers::arrayCast<1, Vector2>(scene.mutableField(3))[0]), (Vector2{37.5f, 1.5f})); + + /* Typed object access by ID */ + CORRADE_COMPARE(scene.objects(0).size(), 5); + CORRADE_COMPARE(scene.objects(1).size(), 5); + CORRADE_COMPARE(scene.objects(2).size(), 2); + CORRADE_COMPARE(scene.objects(3).size(), 2); + CORRADE_COMPARE(scene.mutableObjects(0).size(), 5); + CORRADE_COMPARE(scene.mutableObjects(1).size(), 5); + CORRADE_COMPARE(scene.mutableObjects(2).size(), 2); + CORRADE_COMPARE(scene.mutableObjects(3).size(), 2); + CORRADE_COMPARE(scene.objects(0)[2], 3); + CORRADE_COMPARE(scene.objects(1)[4], 1); + CORRADE_COMPARE(scene.objects(2)[1], 6); + CORRADE_COMPARE(scene.objects(3)[0], 2); + CORRADE_COMPARE(scene.mutableObjects(0)[2], 3); + CORRADE_COMPARE(scene.mutableObjects(1)[4], 1); + CORRADE_COMPARE(scene.mutableObjects(2)[1], 6); + CORRADE_COMPARE(scene.mutableObjects(3)[0], 2); + + /* Typed field access by ID */ + CORRADE_COMPARE(scene.field(0).size(), 5); + CORRADE_COMPARE(scene.field(1).size(), 5); + CORRADE_COMPARE(scene.field(2).size(), 2); + CORRADE_COMPARE(scene.field(3).size()[0], 2); + CORRADE_COMPARE(scene.field(3).size()[1], 2); + CORRADE_COMPARE(scene.mutableField(0).size(), 5); + CORRADE_COMPARE(scene.mutableField(1).size(), 5); + CORRADE_COMPARE(scene.mutableField(2).size(), 2); + CORRADE_COMPARE(scene.mutableField(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(3).size()[1], 2); + CORRADE_COMPARE(scene.field(0)[2], Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE(scene.field(1)[4], -1); + CORRADE_COMPARE(scene.field(2)[1], 7); + CORRADE_COMPARE(scene.field(3)[0][0], 37.5f); + CORRADE_COMPARE(scene.field(3)[0][1], 1.5f); + CORRADE_COMPARE(scene.mutableField(0)[2], Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE(scene.mutableField(1)[4], -1); + CORRADE_COMPARE(scene.mutableField(2)[1], 7); + CORRADE_COMPARE(scene.mutableField(3)[0][0], 37.5f); + CORRADE_COMPARE(scene.mutableField(3)[0][1], 1.5f); + + /* Field property access by name */ + CORRADE_COMPARE(scene.fieldId(SceneField::Transformation), 0); + CORRADE_COMPARE(scene.fieldId(SceneField::Parent), 1); + CORRADE_COMPARE(scene.fieldId(SceneField::Mesh), 2); + CORRADE_COMPARE(scene.fieldId(sceneFieldCustom(37)), 3); + CORRADE_VERIFY(scene.hasField(SceneField::Transformation)); + CORRADE_VERIFY(scene.hasField(SceneField::Parent)); + CORRADE_VERIFY(scene.hasField(SceneField::Mesh)); + CORRADE_VERIFY(scene.hasField(sceneFieldCustom(37))); + CORRADE_VERIFY(!scene.hasField(SceneField::Skin)); + CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4); + CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int); + CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldType(sceneFieldCustom(37)), SceneFieldType::Float); + CORRADE_COMPARE(scene.fieldSize(SceneField::Transformation), 5); + CORRADE_COMPARE(scene.fieldSize(SceneField::Parent), 5); + CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 2); + CORRADE_COMPARE(scene.fieldSize(sceneFieldCustom(37)), 2); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Transformation), 0); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Parent), 0); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); + CORRADE_COMPARE(scene.fieldArraySize(sceneFieldCustom(37)), 2); + + /* Typeless object access by name with a cast later */ + CORRADE_COMPARE(scene.objects(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.objects(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.objects(2).size()[0], 2); + CORRADE_COMPARE(scene.objects(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.mutableObjects(2).size()[0], 2); + CORRADE_COMPARE(scene.mutableObjects(3).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(SceneField::Transformation))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(SceneField::Parent))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(3))[0]), 2); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(SceneField::Transformation))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(SceneField::Parent))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(3))[0]), 2); + + /* Typeless field access by name with a cast later */ + CORRADE_COMPARE(scene.field(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.field(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.field(SceneField::Mesh).size()[0], 2); + CORRADE_COMPARE(scene.field(sceneFieldCustom(37)).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.mutableField(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.mutableField(SceneField::Mesh).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(sceneFieldCustom(37)).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const Matrix4>(scene.field(SceneField::Transformation))[2]), Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE((Containers::arrayCast<1, const Int>(scene.field(SceneField::Parent))[4]), -1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedByte>(scene.field(SceneField::Mesh))[1]), 7); + CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>(scene.field(sceneFieldCustom(37)))[0]), (Vector2{37.5f, 1.5f})); + CORRADE_COMPARE((Containers::arrayCast<1, Matrix4>(scene.mutableField(SceneField::Transformation))[2]), Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE((Containers::arrayCast<1, Int>(scene.mutableField(SceneField::Parent))[4]), -1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedByte>(scene.mutableField(SceneField::Mesh))[1]), 7); + CORRADE_COMPARE((Containers::arrayCast<1, Vector2>(scene.mutableField(sceneFieldCustom(37)))[0]), (Vector2{37.5f, 1.5f})); + + /* Typed object access by name */ + CORRADE_COMPARE(scene.objects(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.objects(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.objects(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.objects(sceneFieldCustom(37)).size(), 2); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.mutableObjects(sceneFieldCustom(37)).size(), 2); + CORRADE_COMPARE(scene.objects(SceneField::Transformation)[2], 3); + CORRADE_COMPARE(scene.objects(SceneField::Parent)[4], 1); + CORRADE_COMPARE(scene.objects(SceneField::Mesh)[1], 6); + CORRADE_COMPARE(scene.objects(sceneFieldCustom(37))[0], 2); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation)[2], 3); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent)[4], 1); + CORRADE_COMPARE(scene.mutableObjects(SceneField::Mesh)[1], 6); + CORRADE_COMPARE(scene.mutableObjects(sceneFieldCustom(37))[0], 2); + + /* Typed field access by name */ + CORRADE_COMPARE(scene.field(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.field(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.field(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.field(sceneFieldCustom(37)).size()[0], 2); + CORRADE_COMPARE(scene.field(sceneFieldCustom(37)).size()[1], 2); + CORRADE_COMPARE(scene.mutableField(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.mutableField(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.mutableField(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.mutableField(sceneFieldCustom(37)).size()[0], 2); + CORRADE_COMPARE(scene.mutableField(sceneFieldCustom(37)).size()[1], 2); + CORRADE_COMPARE(scene.field(SceneField::Transformation)[2], Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE(scene.field(SceneField::Parent)[4], -1); + CORRADE_COMPARE(scene.field(SceneField::Mesh)[1], 7); + CORRADE_COMPARE(scene.field(sceneFieldCustom(37))[0][0], 37.5f); + CORRADE_COMPARE(scene.field(sceneFieldCustom(37))[0][1], 1.5f); + CORRADE_COMPARE(scene.mutableField(SceneField::Transformation)[2], Matrix4::translation(Vector3::zAxis(5.0f))); + CORRADE_COMPARE(scene.mutableField(SceneField::Parent)[4], -1); + CORRADE_COMPARE(scene.mutableField(SceneField::Mesh)[1], 7); + CORRADE_COMPARE(scene.mutableField(sceneFieldCustom(37))[0][0], 37.5f); + CORRADE_COMPARE(scene.mutableField(sceneFieldCustom(37))[0][1], 1.5f); +} + +void SceneDataTest::constructZeroFields() { + int importerState; + SceneData scene{SceneObjectType::UnsignedShort, 37563, nullptr, {}, &importerState}; + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_VERIFY(scene.fieldData().empty()); + CORRADE_COMPARE(static_cast(scene.data()), nullptr); + CORRADE_COMPARE(static_cast(scene.mutableData()), nullptr); + CORRADE_COMPARE(scene.importerState(), &importerState); + CORRADE_COMPARE(scene.objectCount(), 37563); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.fieldCount(), 0); +} - CORRADE_COMPARE(data.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(data.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(data.importerState(), &a); +void SceneDataTest::constructZeroObjects() { + int importerState; + SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials}, &importerState}; + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_VERIFY(!scene.fieldData().empty()); + CORRADE_COMPARE(static_cast(scene.data()), nullptr); + CORRADE_COMPARE(static_cast(scene.mutableData()), nullptr); + CORRADE_COMPARE(scene.importerState(), &importerState); + CORRADE_COMPARE(scene.objectCount(), 0); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + CORRADE_COMPARE(scene.fieldCount(), 2); + + /* Field property access by name */ + CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedShort); + CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::UnsignedInt); + CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 0); + CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 0); + CORRADE_COMPARE(scene.objects(SceneField::Mesh).data(), nullptr); + CORRADE_COMPARE(scene.objects(SceneField::MeshMaterial).data(), nullptr); +} + +void SceneDataTest::constructNotOwned() { + auto&& instanceData = NotOwnedData[testCaseInstanceId()]; + setTestCaseDescription(instanceData.name); + + struct Data { + UnsignedShort object; + UnsignedByte mesh; + } data[]{ + {0, 2}, + {1, 1}, + {2, 0} + }; + + int importerState; + SceneFieldData mesh{SceneField::Mesh, + Containers::stridedArrayView(data).slice(&Data::object), + Containers::stridedArrayView(data).slice(&Data::mesh)}; + SceneData scene{SceneObjectType::UnsignedShort, 7, instanceData.dataFlags, Containers::arrayView(data), {mesh}, &importerState}; + + CORRADE_COMPARE(scene.dataFlags(), instanceData.dataFlags); + CORRADE_COMPARE(static_cast(scene.data()), +data); + if(instanceData.dataFlags & DataFlag::Mutable) + CORRADE_COMPARE(static_cast(scene.mutableData()), +data); + CORRADE_COMPARE(scene.objectCount(), 7); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.fieldCount(), 1); + CORRADE_COMPARE(scene.importerState(), &importerState); + + CORRADE_COMPARE(scene.objects(0).size(), 3); + CORRADE_COMPARE(scene.objects(0)[2], 2); + if(instanceData.dataFlags & DataFlag::Mutable) + CORRADE_COMPARE(scene.mutableObjects(0)[2], 2); + + CORRADE_COMPARE(scene.field(0).size(), 3); + CORRADE_COMPARE(scene.field(0)[2], 0); + if(instanceData.dataFlags & DataFlag::Mutable) + CORRADE_COMPARE(scene.mutableField(0)[2], 0); +} + +void SceneDataTest::constructDuplicateField() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + /* Builtin fields are checked using a bitfield, as they have monotonic + numbering */ + SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData meshesAgain{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials, meshesAgain}}; + CORRADE_COMPARE(out.str(), "Trade::SceneData: duplicate field Trade::SceneField::Mesh\n"); +} + +void SceneDataTest::constructDuplicateCustomField() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + /* These are checked in an O(n^2) way, separately from builtin fields. + Can't use a bitfield since the field index can be anything. */ + SceneFieldData customA{sceneFieldCustom(37), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData customB{sceneFieldCustom(1038576154), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData customAAgain{sceneFieldCustom(37), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {customA, customB, customAAgain}}; + CORRADE_COMPARE(out.str(), "Trade::SceneData: duplicate field Trade::SceneField::Custom(37)\n"); +} + +void SceneDataTest::constructInconsistentObjectType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedShort, nullptr, SceneFieldType::UnsignedInt, nullptr}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials}}; + CORRADE_COMPARE(out.str(), "Trade::SceneData: inconsistent object type, got Trade::SceneObjectType::UnsignedShort for field 1 but expected Trade::SceneObjectType::UnsignedInt\n"); +} + +void SceneDataTest::constructObjectDataNotContained() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::Array data{reinterpret_cast(0xbadda9), 10, [](char*, std::size_t){}}; + Containers::ArrayView dataIn{reinterpret_cast(0xbadda9), 5}; + Containers::ArrayView dataSlightlyOut{reinterpret_cast(0xbaddaa), 5}; + Containers::ArrayView dataOut{reinterpret_cast(0xdead), 5}; + + std::ostringstream out; + Error redirectError{&out}; + /* First a "slightly off" view that exceeds the original by one byte */ + SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneFieldData{SceneField::Mesh, dataSlightlyOut, dataIn} + }}; + /* Second a view that's in a completely different location */ + SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Mesh, dataOut, dataIn} + }}; + /* Verify the owning constructor does the checks as well */ + SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { + SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Mesh, dataOut, dataIn} + }}; + /* And if we have no data at all, it doesn't try to dereference them but + still checks properly */ + SceneData{SceneObjectType::UnsignedShort, 5, nullptr, { + SceneFieldData{SceneField::Mesh, dataOut, dataIn} + }}; + /* Finally, offset-only fields with a different message */ + SceneData{SceneObjectType::UnsignedByte, 6, Containers::Array{24}, { + SceneFieldData{SceneField::Mesh, 6, SceneObjectType::UnsignedByte, 4, 4, SceneFieldType::UnsignedByte, 0, 4} + }}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: object data [0xbaddaa:0xbaddb4] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: object data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: object data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: object data [0xdead:0xdeb7] of field 0 are not contained in passed data array [0x0:0x0]\n" + + "Trade::SceneData: offset-only object data of field 0 span 25 bytes but passed data array has only 24\n"); +} + +void SceneDataTest::constructFieldDataNotContained() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + /* Mostly the same as constructObjectDataNotContained() with object and + field views swapped, and added checks for array fields */ + + Containers::Array data{reinterpret_cast(0xbadda9), 10, [](char*, std::size_t){}}; + Containers::ArrayView dataIn{reinterpret_cast(0xbadda9), 5}; + Containers::ArrayView dataSlightlyOut{reinterpret_cast(0xbaddaa), 5}; + Containers::ArrayView dataOut{reinterpret_cast(0xdead), 5}; + + std::ostringstream out; + Error redirectError{&out}; + /* First a "slightly off" view that exceeds the original by one byte */ + SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneFieldData{SceneField::Mesh, dataIn, dataSlightlyOut} + }}; + /* Second a view that's in a completely different location */ + SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Mesh, dataIn, dataOut} + }}; + /* Verify array size is taken into account as well. If not, the data would + span only 7 bytes out of 10 (instead of 12), which is fine. */ + SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneFieldData{sceneFieldCustom(37), dataIn.prefix(2), Containers::StridedArrayView2D{Containers::ArrayView{reinterpret_cast(0xbadda9), 12}, {2, 6}}} + }}; + /* Verify the owning constructor does the checks as well */ + SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { + SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Mesh, dataIn, dataOut} + }}; + /* Not checking for nullptr data, since that got checked for object view + already and there's no way to trigger it for fields */ + /* Finally, offset-only fields with a different message */ + SceneData{SceneObjectType::UnsignedShort, 6, Containers::Array{24}, { + SceneFieldData{SceneField::Mesh, 6, SceneObjectType::UnsignedShort, 0, 4, SceneFieldType::UnsignedByte, 4, 4} + }}; + /* This again spans 21 bytes if array size isn't taken into account, and 25 + if it is */ + SceneData{SceneObjectType::UnsignedShort, 5, Containers::Array{24}, { + SceneFieldData{sceneFieldCustom(37), 5, SceneObjectType::UnsignedShort, 0, 5, SceneFieldType::UnsignedByte, 0, 5, 5} + }}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: field data [0xbaddaa:0xbaddb4] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: field data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: field data [0xbadda9:0xbaddb5] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: field data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + + "Trade::SceneData: offset-only field data of field 0 span 25 bytes but passed data array has only 24\n" + "Trade::SceneData: offset-only field data of field 0 span 25 bytes but passed data array has only 24\n"); +} + +void SceneDataTest::constructObjectTypeTooSmall() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + /* This is fine */ + SceneData{SceneObjectType::UnsignedByte, 0xff, nullptr, {}}; + SceneData{SceneObjectType::UnsignedShort, 0xffff, nullptr, {}}; + SceneData{SceneObjectType::UnsignedInt, 0xffffffffu, nullptr, {}}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedByte, 0x100, nullptr, {}}; + SceneData{SceneObjectType::UnsignedShort, 0x10000, nullptr, {}}; + SceneData{SceneObjectType::UnsignedInt, 0x100000000ull, nullptr, {}}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: Trade::SceneObjectType::UnsignedByte is too small for 256 objects\n" + "Trade::SceneData: Trade::SceneObjectType::UnsignedShort is too small for 65536 objects\n" + "Trade::SceneData: Trade::SceneObjectType::UnsignedInt is too small for 4294967296 objects\n"); +} + +void SceneDataTest::constructNotOwnedFlagOwned() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const char data[32]{}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedByte, 5, DataFlag::Owned, Containers::arrayView(data), {}}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: can't construct with non-owned data but Trade::DataFlag::Owned\n"); +} + +void SceneDataTest::constructMismatchedTRSViews() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::ArrayView data{reinterpret_cast(0xcafe0000), + /* Three entries, each having a 2D TRS and 3 object IDs */ + 3*(24 + 12)}; + Containers::ArrayView translationObjectData{ + reinterpret_cast(data.data()), 3}; + Containers::ArrayView translationFieldData{ + reinterpret_cast(data.data() + 0x0c), 3}; + Containers::ArrayView rotationObjectData{ + reinterpret_cast(data.data() + 0x24), 3}; + Containers::ArrayView rotationFieldData{ + reinterpret_cast(data.data() + 0x30), 3}; + Containers::ArrayView scalingObjectData{ + reinterpret_cast(data.data() + 0x48), 3}; + Containers::ArrayView scalingFieldData{ + reinterpret_cast(data.data() + 0x54), 3}; + + SceneFieldData translations{SceneField::Translation, translationObjectData, translationFieldData}; + SceneFieldData rotationsDifferent{SceneField::Rotation, rotationObjectData, rotationFieldData}; + SceneFieldData scalingsDifferent{SceneField::Scaling, scalingObjectData, scalingFieldData}; + SceneFieldData rotationsSameButLess{SceneField::Rotation, translationObjectData.except(1), rotationFieldData.except(1)}; + SceneFieldData scalingsSameButLess{SceneField::Scaling, translationObjectData.except(2), scalingFieldData.except(2)}; + + /* Test that all pairs get checked */ + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, rotationsDifferent}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, scalingsDifferent}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {rotationsDifferent, scalingsDifferent}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, rotationsSameButLess}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, scalingsSameButLess}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {rotationsSameButLess, scalingsSameButLess}}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: Trade::SceneField::Rotation object data [0xcafe0024:0xcafe0030] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Rotation object data [0xcafe0024:0xcafe0030]\n" + "Trade::SceneData: Trade::SceneField::Rotation object data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Rotation object data [0xcafe0000:0xcafe0008]\n"); +} + +void SceneDataTest::constructMismatchedMeshMaterialView() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::ArrayView data{reinterpret_cast(0xcafe0000), + /* Three entries, each having mesh/material ID and 2 object IDs */ + 3*(8 + 8)}; + Containers::ArrayView meshObjectData{ + reinterpret_cast(data.data()), 3}; + Containers::ArrayView meshFieldData{ + reinterpret_cast(data.data() + 0x0c), 3}; + Containers::ArrayView meshMaterialObjectData{ + reinterpret_cast(data.data() + 0x18), 3}; + Containers::ArrayView meshMaterialFieldData{ + reinterpret_cast(data.data() + 0x24), 3}; + + SceneFieldData meshes{SceneField::Mesh, meshObjectData, meshFieldData}; + SceneFieldData meshMaterialsDifferent{SceneField::MeshMaterial, meshMaterialObjectData, meshMaterialFieldData}; + SceneFieldData meshMaterialsSameButLess{SceneField::MeshMaterial, meshObjectData.except(1), meshMaterialFieldData.except(1)}; + + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsDifferent}}; + SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsSameButLess}}; + CORRADE_COMPARE(out.str(), + "Trade::SceneData: Trade::SceneField::MeshMaterial object data [0xcafe0018:0xcafe0024] is different from Trade::SceneField::Mesh object data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::MeshMaterial object data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Mesh object data [0xcafe0000:0xcafe000c]\n"); } void SceneDataTest::constructCopy() { @@ -59,27 +1570,1477 @@ void SceneDataTest::constructCopy() { } void SceneDataTest::constructMove() { - const int a{}; - SceneData data{{0, 1, 4}, {2, 5}, &a}; + struct Mesh { + UnsignedShort object; + UnsignedInt mesh; + }; - SceneData b{std::move(data)}; + Containers::Array data{NoInit, 3*sizeof(Mesh)}; + auto meshData = Containers::arrayCast(data); + meshData[0] = {0, 2}; + meshData[1] = {73, 1}; + meshData[2] = {122, 2}; - CORRADE_COMPARE(b.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(b.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(b.importerState(), &a); + int importerState; + SceneFieldData meshes{SceneField::Mesh, stridedArrayView(meshData).slice(&Mesh::object), stridedArrayView(meshData).slice(&Mesh::mesh)}; + SceneData a{SceneObjectType::UnsignedShort, 15, std::move(data), {meshes}, &importerState}; - const int c{}; - SceneData d{{1, 3}, {1, 4, 5}, &c}; - d = std::move(b); + SceneData b{std::move(a)}; + CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(b.objectCount(), 15); + CORRADE_COMPARE(b.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(b.fieldCount(), 1); + CORRADE_COMPARE(b.importerState(), &importerState); + CORRADE_COMPARE(static_cast(b.data()), meshData.data()); + CORRADE_COMPARE(b.fieldName(0), SceneField::Mesh); + CORRADE_COMPARE(b.fieldType(0), SceneFieldType::UnsignedInt); + CORRADE_COMPARE(b.fieldSize(0), 3); + CORRADE_COMPARE(b.fieldArraySize(0), 0); + CORRADE_COMPARE(b.objects(0)[2], 122); + CORRADE_COMPARE(b.field(0)[2], 2); - CORRADE_COMPARE(d.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(d.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(d.importerState(), &a); + SceneData c{SceneObjectType::UnsignedByte, 76, nullptr, {}}; + c = std::move(b); + CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(c.objectCount(), 15); + CORRADE_COMPARE(c.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(c.fieldCount(), 1); + CORRADE_COMPARE(c.importerState(), &importerState); + CORRADE_COMPARE(static_cast(c.data()), meshData.data()); + CORRADE_COMPARE(c.fieldName(0), SceneField::Mesh); + CORRADE_COMPARE(c.fieldType(0), SceneFieldType::UnsignedInt); + CORRADE_COMPARE(c.fieldSize(0), 3); + CORRADE_COMPARE(c.fieldArraySize(0), 0); + CORRADE_COMPARE(c.objects(0)[2], 122); + CORRADE_COMPARE(c.field(0)[2], 2); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } +template struct NameTraits; +#define _c(format) template<> struct NameTraits { \ + static const char* name() { return #format; } \ + }; +_c(UnsignedByte) +_c(Byte) +_c(UnsignedShort) +_c(Short) +_c(UnsignedInt) +_c(Int) +_c(UnsignedLong) +_c(Long) +_c(Float) +_c(Double) +_c(Vector2) +_c(Vector2d) +_c(Vector3) +_c(Vector3d) +_c(Matrix3) +_c(Matrix3x3) +_c(Matrix3d) +_c(Matrix3x3d) +_c(Matrix4) +_c(Matrix4x4) +_c(Matrix4d) +_c(Matrix4x4d) +_c(Complex) +_c(Complexd) +_c(Quaternion) +_c(Quaterniond) +_c(DualComplex) +_c(DualComplexd) +_c(DualQuaternion) +_c(DualQuaterniond) +#undef _c + +template void SceneDataTest::objectsAsArrayByIndex() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + T object; + UnsignedByte mesh; + } fields[]{ + {T(15), 0}, + {T(37), 1}, + {T(44), 15} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{Implementation::sceneObjectTypeFor(), 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + CORRADE_COMPARE_AS(scene.objectsAsArray(1), + Containers::arrayView({15, 37, 44}), + TestSuite::Compare::Container); +} + +template void SceneDataTest::objectsAsArrayByName() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + T object; + UnsignedByte mesh; + } fields[]{ + {T(15), 0}, + {T(37), 1}, + {T(44), 15} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{Implementation::sceneObjectTypeFor(), 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.objectsAsArray(SceneField::Mesh)), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.objectsInto(SceneField::Mesh, out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::objectsAsArrayLongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedLong object; + UnsignedByte mesh; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedLong, 0x100000000ull, {}, fields, { + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + /* AsArray calls into IntoArray, which then has the assert, so this tests + both */ + std::ostringstream out; + Error redirectError{&out}; + scene.objectsAsArray(0); + scene.objectsAsArray(SceneField::Mesh); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::objectsInto(): indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via objects() instead\n" + "Trade::SceneData::objectsInto(): indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via objects() instead\n"); +} + +void SceneDataTest::objectsIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedByte 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.objectsInto(0, destination); + scene.objectsInto(SceneField::Mesh, destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::parentsAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T parent; + } 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::Mesh, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::UnsignedInt, nullptr}, + SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} + }}; + CORRADE_COMPARE_AS(scene.parentsAsArray(), + Containers::arrayView({15, -1, 44}), + TestSuite::Compare::Container); +} + +#ifndef CORRADE_TARGET_32BIT +void SceneDataTest::parentsAsArrayLongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedLong object; + Long parent; + }; + + Containers::Array data{nullptr, 0x100000000ull*sizeof(Field), [](char*, std::size_t) {}}; + Containers::StridedArrayView1D view = Containers::arrayCast(data); + + SceneData scene{SceneObjectType::UnsignedLong, 0x100000000ull, std::move(data), { + SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} + }}; + + /* AsArray calls into IntoArray, which then has the assert, so this tests + both */ + std::ostringstream out; + Error redirectError{&out}; + scene.parentsAsArray(); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::parentsInto(): parent indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via field() instead\n"); +} +#endif + +void SceneDataTest::parentsIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + Int parent; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + Int destination[2]; + scene.parentsInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::parentsInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::transformations2DAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + typedef typename T::Type U; + + struct Transformation { + UnsignedInt object; + T transformation; + }; + + struct Component { + UnsignedInt object; + Vector2 translation; + Vector2 scaling; + }; + + Containers::StridedArrayView1D transformations; + Containers::StridedArrayView1D components; + Containers::Array data = Containers::ArrayTuple{ + {NoInit, 4, transformations}, + {NoInit, 2, components} + }; + transformations[0] = {1, T::translation({U(3.0), U(2.0)})}; + transformations[1] = {0, T::rotation(Math::Deg(35.0))}; + transformations[2] = {4, T::translation({U(1.5), U(2.5)})* + T::rotation(Math::Deg(-15.0))}; + transformations[3] = {5, T::rotation(Math::Deg(-15.0))* + T::translation({U(1.5), U(2.5)})}; + /* Object number 4 additionally has a scaling component (which isn't + representable with dual complex numbers). It currently doesn't get added + to the transformations returned from transformations2DInto() but that + may change in the future for dual complex numbers). The translation + component is then *assumed* to be equivalent to what's stored in the + Transformation field and so applied neither. Here it's different, and + that shouldn't affect anything. */ + components[0] = {4, {-1.5f, -2.5f}, {2.0f, 5.0f}}; + /* This is deliberately an error -- specifying a TRS for an object that + doesn't have a Transformation field. Since there's no fast way to check + for those and error/warn on those, they get just ignored. */ + components[1] = {2, {3.5f, -1.0f}, {1.0f, 1.5f}}; + + SceneData scene{SceneObjectType::UnsignedInt, 6, std::move(data), { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Transformation, + transformations.slice(&Transformation::object), + transformations.slice(&Transformation::transformation)}, + SceneFieldData{SceneField::Translation, + components.slice(&Component::object), + components.slice(&Component::translation)}, + SceneFieldData{SceneField::Scaling, + components.slice(&Component::object), + components.slice(&Component::scaling)}, + }}; + + Matrix3 expected[]{ + 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}) + }; + CORRADE_COMPARE_AS(arrayView(scene.transformations2DAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + Matrix3 out[4]; + scene.transformations2DInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +template void SceneDataTest::transformations2DAsArrayTRS() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedInt object; + Math::Vector2 translation; + Math::Complex rotation; + Math::Vector2 scaling; + } fields[]{ + {1, {T(3.0), T(2.0)}, + {}, + {T(1.0), T(1.0)}}, + {0, {}, + Math::Complex::rotation(Math::Deg{T(35.0)}), + {T(1.0), T(1.0)}}, + {2, {}, /* Identity transformation here */ + {}, + {T(1.0), T(1.0)}}, + {4, {}, + {}, + {T(2.0), T(1.0)}}, + {7, {T(1.5), T(2.5)}, + Math::Complex::rotation(Math::Deg{T(-15.0)}), + {T(-0.5), T(4.0)}}, + }; + + Containers::StridedArrayView1D view = fields; + + SceneFieldData translation{SceneField::Translation, + view.slice(&Field::object), + view.slice(&Field::translation)}; + SceneFieldData rotation{SceneField::Rotation, + view.slice(&Field::object), + view.slice(&Field::rotation)}; + SceneFieldData scaling{SceneField::Scaling, + view.slice(&Field::object), + view.slice(&Field::scaling)}; + + /* Just one of translation / rotation / scaling */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + rotation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + scaling + }}; + 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); + } + + /* Pairs */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + rotation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + scaling + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + rotation, + scaling + }}; + 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); + } + + /* All */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + rotation, + scaling + }}; + 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); + } +} + +template void SceneDataTest::transformations2DAsArrayBut3DType() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.transformations2DAsArray(); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformations2DInto(): field has a 3D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); +} + +template void SceneDataTest::transformations2DAsArrayBut3DTypeTRS() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData translation{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData rotation{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData scaling{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + translation.transformations2DAsArray(); + rotation.transformations2DAsArray(); + scaling.transformations2DAsArray(); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformations2DInto(): field has a 3D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::transformations2DInto(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformations2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); +} + +void SceneDataTest::transformations2DIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + Matrix3 transformation; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + Matrix3 destination[2]; + scene.transformations2DInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformations2DInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::transformations3DAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + typedef typename T::Type U; + + struct Transformation { + UnsignedInt object; + T transformation; + }; + + struct Component { + UnsignedInt object; + Vector3 translation; + Vector3 scaling; + }; + + Containers::StridedArrayView1D transformations; + Containers::StridedArrayView1D components; + Containers::Array data = Containers::ArrayTuple{ + {NoInit, 4, transformations}, + {NoInit, 2, components} + }; + transformations[0] = {1, T::translation({U(3.0), U(2.0), U(-0.5)})}; + transformations[1] = {0, T::rotation(Math::Deg(35.0), + Math::Vector3::yAxis())}; + transformations[2] = {4, T::translation({U(1.5), U(2.5), U(0.75)})* + T::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())}; + transformations[3] = {5, T::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())* + T::translation({U(1.5), U(2.5), U(0.75)})}; + /* Object number 4 additionally has a scaling component (which isn't + representable with dual quaternions). It currently doesn't get added + to the transformations returned from transformations2DInto() but that + may change in the future for dual quaternions). The translation + component is then *assumed* to be equivalent to what's stored in the + Transformation field and so applied neither. Here it's different, and + that shouldn't affect anything. */ + components[0] = {4, {-1.5f, -2.5f, 5.5f}, {2.0f, 5.0f, 3.0f}}; + /* This is deliberately an error -- specifying a TRS for an object that + doesn't have a Transformation field. Since there's no fast way to check + for those and error/warn on those, they get just ignored. */ + components[1] = {2, {3.5f, -1.0f, 2.2f}, {1.0f, 1.5f, 1.0f}}; + + SceneData scene{SceneObjectType::UnsignedInt, 6, std::move(data), { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Transformation, + transformations.slice(&Transformation::object), + transformations.slice(&Transformation::transformation)}, + SceneFieldData{SceneField::Translation, + components.slice(&Component::object), + components.slice(&Component::translation)}, + SceneFieldData{SceneField::Scaling, + components.slice(&Component::object), + components.slice(&Component::scaling)}, + }}; + + Matrix4 expected[]{ + 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}) + }; + CORRADE_COMPARE_AS(arrayView(scene.transformations3DAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + Matrix4 out[4]; + scene.transformations3DInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +template void SceneDataTest::transformations3DAsArrayTRS() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedInt object; + Math::Vector3 translation; + Math::Quaternion rotation; + Math::Vector3 scaling; + } fields[]{ + {1, {T(3.0), T(2.0), T(1.0)}, + {}, + {T(1.0), T(1.0), T(1.0)}}, + {0, {}, + Math::Quaternion::rotation(Math::Deg{T(35.0)}, Math::Vector3::yAxis()), + {T(1.0), T(1.0), T(1.0)}}, + {2, {}, /* Identity transformation here */ + {}, + {T(1.0), T(1.0), T(1.0)}}, + {4, {}, + {}, + {T(2.0), T(1.0), T(0.0)}}, + {7, {T(1.5), T(2.5), T(3.5)}, + Math::Quaternion::rotation(Math::Deg{T(-15.0)}, Math::Vector3::xAxis()), + {T(-0.5), T(4.0), T(-16.0)}}, + }; + + Containers::StridedArrayView1D view = fields; + + SceneFieldData translation{SceneField::Translation, + view.slice(&Field::object), + view.slice(&Field::translation)}; + SceneFieldData rotation{SceneField::Rotation, + view.slice(&Field::object), + view.slice(&Field::rotation)}; + SceneFieldData scaling{SceneField::Scaling, + view.slice(&Field::object), + view.slice(&Field::scaling)}; + + /* Just one of translation / rotation / scaling */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + rotation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + scaling + }}; + 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); + } + + /* Pairs */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + rotation + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + scaling + }}; + 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); + } { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + rotation, + scaling + }}; + 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); + } + + /* All */ + { + SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + translation, + rotation, + scaling + }}; + 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); + } +} + +template void SceneDataTest::transformations3DAsArrayBut2DType() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.transformations3DAsArray(); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformations3DInto(): field has a 2D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); +} + +template void SceneDataTest::transformations3DAsArrayBut2DTypeTRS() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData translation{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData rotation{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData scaling{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + translation.transformations3DAsArray(); + rotation.transformations3DAsArray(); + scaling.transformations3DAsArray(); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformations3DInto(): field has a 2D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::transformations3DInto(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformations3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); +} + +void SceneDataTest::transformations3DIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + Matrix4 transformation; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + Matrix4 destination[2]; + scene.transformations3DInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformations3DInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::meshesAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T mesh; + } fields[3]{ + {0, T(15)}, + {1, T(37)}, + {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::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.meshesAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.meshesInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::meshesIntoArrayInvalidSize() { + #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); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::meshesInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::meshMaterialsAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T meshMaterial; + } fields[]{ + {0, T(15)}, + {1, T(37)}, + {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)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.meshMaterialsAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.meshMaterialsInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::meshMaterialsIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt meshMaterial; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + UnsignedInt destination[2]; + scene.meshMaterialsInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::meshMaterialsInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::lightsAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T light; + } fields[]{ + {0, T(15)}, + {1, T(37)}, + {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::Light, view.slice(&Field::object), view.slice(&Field::light)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.lightsAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.lightsInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::lightsIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt light; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + UnsignedInt destination[2]; + scene.lightsInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::lightsInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::camerasAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T camera; + } fields[]{ + {0, T(15)}, + {1, T(37)}, + {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::Camera, view.slice(&Field::object), view.slice(&Field::camera)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.camerasAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.camerasInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::camerasIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt camera; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + UnsignedInt destination[2]; + scene.camerasInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::camerasInto(): expected a view with 3 elements but got 2\n"); +} + +template void SceneDataTest::skinsAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + UnsignedByte object; + T skin; + } fields[]{ + {0, T(15)}, + {1, T(37)}, + {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::Skin, view.slice(&Field::object), view.slice(&Field::skin)} + }}; + + UnsignedInt expected[]{15, 37, 44}; + CORRADE_COMPARE_AS(arrayView(scene.skinsAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + UnsignedInt out[3]; + scene.skinsInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + TestSuite::Compare::Container); +} + +void SceneDataTest::skinsIntoArrayInvalidSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt skin; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + UnsignedInt destination[2]; + scene.skinsInto(destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::skinsInto(): expected a view with 3 elements but got 2\n"); +} + +void SceneDataTest::mutableAccessNotAllowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const struct Field { + UnsignedInt object; + UnsignedShort foobar; + UnsignedShort mesh; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{sceneFieldCustom(35), + view.slice(&Field::object), + view.slice(&Field::foobar)}, + SceneFieldData{SceneField::Mesh, + view.slice(&Field::object), + view.slice(&Field::mesh)}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.mutableData(); + scene.mutableObjects(0); + scene.mutableObjects(0); + scene.mutableObjects(SceneField::Mesh); + scene.mutableObjects(SceneField::Mesh); + scene.mutableField(0); + scene.mutableField(0); + scene.mutableField(1); + scene.mutableField(SceneField::Mesh); + scene.mutableField(SceneField::Mesh); + scene.mutableField(sceneFieldCustom(35)); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::mutableData(): data not mutable\n" + "Trade::SceneData::mutableObjects(): data not mutable\n" + "Trade::SceneData::mutableObjects(): data not mutable\n" + "Trade::SceneData::mutableObjects(): data not mutable\n" + "Trade::SceneData::mutableObjects(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n" + "Trade::SceneData::mutableField(): data not mutable\n"); +} + +void SceneDataTest::objectsNotFound() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedShort foobar; + UnsignedShort mesh; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.objects(2); + scene.objects(2); + scene.mutableObjects(2); + scene.mutableObjects(2); + scene.objects(sceneFieldCustom(666)); + scene.objects(sceneFieldCustom(666)); + scene.mutableObjects(sceneFieldCustom(666)); + scene.mutableObjects(sceneFieldCustom(666)); + + scene.objectsAsArray(2); + scene.objectsAsArray(sceneFieldCustom(666)); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::objects(): index 2 out of range for 2 fields\n" + "Trade::SceneData::objects(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableObjects(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableObjects(): index 2 out of range for 2 fields\n" + "Trade::SceneData::objects(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::objects(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableObjects(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableObjects(): field Trade::SceneField::Custom(666) not found\n" + + "Trade::SceneData::objectsInto(): index 2 out of range for 2 fields\n" + "Trade::SceneData::objectsInto(): field Trade::SceneField::Custom(666) not found\n"); +} + +void SceneDataTest::objectsWrongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedShort object; + UnsignedShort foobar; + UnsignedInt mesh; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedShort, 5, DataFlag::Mutable, fields, { + SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.objects(1); + scene.mutableObjects(1); + scene.objects(SceneField::Mesh); + scene.mutableObjects(SceneField::Mesh); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::objects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" + "Trade::SceneData::mutableObjects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" + "Trade::SceneData::objects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" + "Trade::SceneData::mutableObjects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n"); +} + +void SceneDataTest::fieldNotFound() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt foo, bar; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneFieldData{sceneFieldCustom(34), view.slice(&Field::object), view.slice(&Field::foo)}, + SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::bar)}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.fieldData(2); + scene.fieldName(2); + scene.fieldType(2); + scene.fieldSize(2); + scene.fieldArraySize(2); + scene.field(2); + scene.field(2); + scene.field(2); + scene.mutableField(2); + scene.mutableField(2); + scene.mutableField(2); + + scene.fieldId(sceneFieldCustom(666)); + scene.fieldType(sceneFieldCustom(666)); + scene.fieldSize(sceneFieldCustom(666)); + scene.fieldArraySize(sceneFieldCustom(666)); + scene.field(sceneFieldCustom(666)); + scene.field(sceneFieldCustom(666)); + scene.field(sceneFieldCustom(666)); + scene.mutableField(sceneFieldCustom(666)); + scene.mutableField(sceneFieldCustom(666)); + scene.mutableField(sceneFieldCustom(666)); + + scene.parentsAsArray(); + scene.transformations2DAsArray(); + scene.transformations3DAsArray(); + /* Test both AsArray() and Into() for transformations as they only share a + common helper but have different top-level code paths. They however have + the same assertion messages to save binary size a bit. */ + scene.transformations2DInto(nullptr); + scene.transformations3DInto(nullptr); + scene.meshesAsArray(); + scene.meshMaterialsAsArray(); + scene.lightsAsArray(); + scene.camerasAsArray(); + scene.skinsAsArray(); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" + "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" + "Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n" + "Trade::SceneData::fieldSize(): index 2 out of range for 2 fields\n" + "Trade::SceneData::fieldArraySize(): index 2 out of range for 2 fields\n" + "Trade::SceneData::field(): index 2 out of range for 2 fields\n" + "Trade::SceneData::field(): index 2 out of range for 2 fields\n" + "Trade::SceneData::field(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" + + "Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::fieldType(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::fieldSize(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::fieldArraySize(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::field(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::field(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::field(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableField(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableField(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableField(): field Trade::SceneField::Custom(666) not found\n" + + "Trade::SceneData::parentsInto(): field not found\n" + /* Test both AsArray() and Into() for transformations as they only + share a common helper but have different top-level code paths. They + however have the same assertion messages to save binary size a + bit. */ + "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" + "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" + "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" + "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" + "Trade::SceneData::meshesInto(): field not found\n" + "Trade::SceneData::meshMaterialsInto(): field not found\n" + "Trade::SceneData::lightsInto(): field not found\n" + "Trade::SceneData::camerasInto(): field not found\n" + "Trade::SceneData::skinsInto(): field not found\n"); +} + +void SceneDataTest::fieldWrongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedShort foobar; + UnsignedShort mesh; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.field(1); + scene.field(1); + scene.mutableField(1); + scene.mutableField(1); + scene.field(SceneField::Mesh); + scene.field(SceneField::Mesh); + scene.mutableField(SceneField::Mesh); + scene.mutableField(SceneField::Mesh); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::field(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::field(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::field(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::field(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n"); +} + +void SceneDataTest::fieldWrongArrayAccess() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt mesh; + UnsignedInt foobar; + } fields[2]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), Containers::arrayCast<2, UnsignedInt>(view.slice(&Field::foobar))}, + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.field(0); + scene.field(1); + scene.mutableField(0); + scene.mutableField(1); + scene.field(SceneField::Mesh); + scene.field(sceneFieldCustom(35)); + scene.mutableField(SceneField::Mesh); + scene.mutableField(sceneFieldCustom(35)); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::field(): Trade::SceneField::Mesh is not an array field, can't use T[] to access it\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is not an array field, can't use T[] to access it\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n" + "Trade::SceneData::field(): Trade::SceneField::Mesh is not an array field, can't use T[] to access it\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is not an array field, can't use T[] to access it\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n"); +} + +void SceneDataTest::releaseFieldData() { + struct Field { + UnsignedByte object; + UnsignedInt mesh; + }; + + Containers::Array data{NoInit, 3*sizeof(Field)}; + Containers::StridedArrayView1D view = Containers::arrayCast(data); + + auto fields = Containers::array({ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }); + SceneFieldData* originalFields = fields; + + SceneData scene{SceneObjectType::UnsignedByte, 50, std::move(data), std::move(fields)}; + + Containers::Array released = scene.releaseFieldData(); + CORRADE_COMPARE(released.data(), originalFields); + CORRADE_COMPARE(released.size(), 2); + + /* Fields are all gone */ + CORRADE_COMPARE(static_cast(scene.fieldData()), nullptr); + CORRADE_COMPARE(scene.fieldCount(), 0); + + /* Data stays untouched, object count and type as well, as it con't result + in any dangling data access */ + CORRADE_COMPARE(scene.data(), view.data()); + CORRADE_COMPARE(scene.objectCount(), 50); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedByte); +} + +void SceneDataTest::releaseData() { + struct Field { + UnsignedByte object; + UnsignedByte mesh; + }; + + Containers::Array data{NoInit, 3*sizeof(Field)}; + Containers::StridedArrayView1D view = Containers::arrayCast(data); + + SceneData scene{SceneObjectType::UnsignedByte, 50, std::move(data), { + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + Containers::Array released = scene.releaseData(); + CORRADE_COMPARE(released.data(), view.data()); + CORRADE_COMPARE(released.size(), 3*sizeof(Field)); + + /* Both fields and data are all gone */ + CORRADE_COMPARE(static_cast(scene.fieldData()), nullptr); + CORRADE_COMPARE(scene.fieldCount(), 0); + CORRADE_COMPARE(static_cast(scene.data()), nullptr); + + /* Object count and type stays untouched, as it con't result in any + dangling data access */ + CORRADE_COMPARE(scene.objectCount(), 50); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedByte); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::SceneDataTest) diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index e0104d5a11..46f98370c3 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -99,6 +99,11 @@ class PbrMetallicRoughnessMaterialData; class PbrSpecularGlossinessMaterialData; class PhongMaterialData; class TextureData; + +enum class SceneObjectType: UnsignedByte; +enum class SceneField: UnsignedInt; +enum class SceneFieldType: UnsignedShort; +class SceneFieldData; class SceneData; template class SkinData; From d1dda87231eb56c82ad333adcfb5d49c191368b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 2 Sep 2021 18:26:28 +0200 Subject: [PATCH 02/49] Trade: add SceneField string mapping APIs to AbstractImporter. --- src/Magnum/Trade/AbstractImporter.cpp | 19 ++++++ src/Magnum/Trade/AbstractImporter.h | 49 +++++++++++++- src/Magnum/Trade/SceneData.h | 4 +- .../Trade/Test/AbstractImporterTest.cpp | 64 +++++++++++++++++++ 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 3d7d95f7e5..c470ac375c 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -306,6 +306,25 @@ Containers::Optional AbstractImporter::scene(const std::string& name) return scene(id); /* not doScene(), so we get the range checks also */ } +SceneField AbstractImporter::sceneFieldForName(const std::string& name) { + const SceneField out = doSceneFieldForName(name); + CORRADE_ASSERT(out == SceneField{} || isSceneFieldCustom(out), + "Trade::AbstractImporter::sceneFieldForName(): implementation-returned" << out << "is neither custom nor invalid", {}); + return out; +} + +SceneField AbstractImporter::doSceneFieldForName(const std::string&) { + return {}; +} + +std::string AbstractImporter::sceneFieldName(SceneField name) { + CORRADE_ASSERT(isSceneFieldCustom(name), + "Trade::AbstractImporter::sceneFieldName():" << name << "is not custom", {}); + return doSceneFieldName(sceneFieldCustom(name)); +} + +std::string AbstractImporter::doSceneFieldName(UnsignedInt) { return {}; } + UnsignedInt AbstractImporter::animationCount() const { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::animationCount(): no file opened", {}); return doAnimationCount(); diff --git a/src/Magnum/Trade/AbstractImporter.h b/src/Magnum/Trade/AbstractImporter.h index a1684f47a5..47c240250e 100644 --- a/src/Magnum/Trade/AbstractImporter.h +++ b/src/Magnum/Trade/AbstractImporter.h @@ -307,7 +307,9 @@ name doesn't exist. @ref object2DForName() / @ref object3DForName(), imported with @ref object2D(const std::string&) / @ref object3D(const std::string&) - Scene names using @ref sceneName() & @ref sceneForName(), imported with - @ref scene(const std::string&) + @ref scene(const std::string&). Scenes themselves can have custom fields, + for which the name mapping can be retrieved using @ref sceneFieldName() and + @ref sceneFieldForName(). - Skin names using @ref skin2DName() / @ref skin3DName() & @ref skin2DForName() / @ref skin3DForName(), imported with @ref skin2D(const std::string&) / @ref skin3D(const std::string&) @@ -763,6 +765,34 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ Containers::Optional scene(const std::string& name); + /** + * @brief Scene field for given name + * @m_since_latest + * + * If the name is not recognized, returns a zero (invalid) + * @ref SceneField, otherwise returns a custom scene field. Note that + * the value returned by this function may depend on whether a file is + * opened or not and also be different for different files --- see + * documentation of a particular importer for more information. + * @see @ref isSceneFieldCustom() + */ + SceneField sceneFieldForName(const std::string& name); + + /** + * @brief String name for given custom scene field + * @m_since_latest + * + * Given a custom @p name returned by @ref scene() in a @ref SceneData, + * returns a string identifier. If a string representation is not + * available or @p name is not recognized, returns an empty string. + * Expects that @p name is custom. Note that the value returned by + * this function may depend on whether a file is opened or not and also + * be different for different files --- see documentation of a + * particular importer for more information. + * @see @ref isSceneFieldCustom() + */ + std::string sceneFieldName(SceneField name); + /** * @brief Animation count * @@ -1759,6 +1789,23 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi /** @brief Implementation for @ref scene() */ virtual Containers::Optional doScene(UnsignedInt id); + /** + * @brief Implementation for @ref sceneFieldForName() + * @m_since_latest + * + * Default implementation returns an invalid (zero) value. + */ + virtual SceneField doSceneFieldForName(const std::string& name); + + /** + * @brief Implementation for @ref sceneFieldName() + * @m_since_latest + * + * Receives the custom ID extracted via @ref sceneFieldCustom(SceneField). + * Default implementation returns an empty string. + */ + virtual std::string doSceneFieldName(UnsignedInt name); + /** * @brief Implementation for @ref animationCount() * diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 2eb7e2943b..48569d955d 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -83,7 +83,9 @@ MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeSize(SceneObjectType type); @m_since_latest See @ref SceneData for more information. -@see @ref SceneFieldData, @ref SceneFieldType +@see @ref SceneFieldData, @ref SceneFieldType, + @ref AbstractImporter::sceneFieldForName(), + @ref AbstractImporter::sceneFieldName() */ enum class SceneField: UnsignedInt { /* Zero used for an invalid value */ diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 6f4f43f28e..05deeba0f9 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -115,6 +115,10 @@ struct AbstractImporterTest: TestSuite::Tester { void sceneNotImplemented(); void sceneOutOfRange(); + void sceneFieldName(); + void sceneFieldNameNotImplemented(); + void sceneFieldNameNotCustom(); + void animation(); void animationNameNotImplemented(); void animationForNameOutOfRange(); @@ -360,6 +364,10 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::sceneNotImplemented, &AbstractImporterTest::sceneOutOfRange, + &AbstractImporterTest::sceneFieldName, + &AbstractImporterTest::sceneFieldNameNotImplemented, + &AbstractImporterTest::sceneFieldNameNotCustom, + &AbstractImporterTest::animation, &AbstractImporterTest::animationForNameOutOfRange, &AbstractImporterTest::animationNameNotImplemented, @@ -1757,6 +1765,62 @@ void AbstractImporterTest::sceneOutOfRange() { CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::scene(): index 8 out of range for 8 entries\n"); } +void AbstractImporterTest::sceneFieldName() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + SceneField doSceneFieldForName(const std::string& name) override { + if(name == "OctreeCell") return sceneFieldCustom(100037); + return SceneField{}; + } + + std::string doSceneFieldName(UnsignedInt id) override { + if(id == 100037) return "OctreeCell"; + return ""; + } + } importer; + + CORRADE_COMPARE(importer.sceneFieldForName("OctreeCell"), sceneFieldCustom(100037)); + CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), "OctreeCell"); +} + +void AbstractImporterTest::sceneFieldNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + CORRADE_COMPARE(importer.sceneFieldForName(""), SceneField{}); + CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), ""); +} + +void AbstractImporterTest::sceneFieldNameNotCustom() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + SceneField doSceneFieldForName(const std::string&) override { + return SceneField::Translation; + } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + importer.sceneFieldForName("OctreeCell"); + importer.sceneFieldName(SceneField::Translation); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::sceneFieldForName(): implementation-returned Trade::SceneField::Translation is neither custom nor invalid\n" + "Trade::AbstractImporter::sceneFieldName(): Trade::SceneField::Translation is not custom\n"); +} + void AbstractImporterTest::animation() { struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } From a7f48519f67cc820d2da3bb9476195dfb24fbb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 3 Sep 2021 17:32:19 +0200 Subject: [PATCH 03/49] Trade: MSVC 2017, what are you doing. How is the data const? Why? --- src/Magnum/Trade/Test/SceneDataTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index a96702135b..a6bc53f20d 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -1290,7 +1290,7 @@ void SceneDataTest::constructNotOwned() { CORRADE_COMPARE(scene.dataFlags(), instanceData.dataFlags); CORRADE_COMPARE(static_cast(scene.data()), +data); if(instanceData.dataFlags & DataFlag::Mutable) - CORRADE_COMPARE(static_cast(scene.mutableData()), +data); + CORRADE_COMPARE(static_cast(scene.mutableData()), static_cast(data)); CORRADE_COMPARE(scene.objectCount(), 7); CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); CORRADE_COMPARE(scene.fieldCount(), 1); From 73b32def51243a05beec957f94d60ea934855adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 3 Sep 2021 18:40:45 +0200 Subject: [PATCH 04/49] Trade: hi, MSVC 2015, you ancient curse. --- src/Magnum/Trade/Test/SceneDataTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index a6bc53f20d..1a91c68464 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -448,6 +448,7 @@ void SceneDataTest::constructField() { CORRADE_COMPARE(rotations.objectData(someArray).stride(), sizeof(UnsignedShort)); CORRADE_VERIFY(rotations.objectData(someArray).data() == rotationObjectData); + #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Won't bother anymore */ constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationObjects2D), Containers::arrayView(Rotations2D)}; constexpr bool isOffsetOnly = crotations.isOffsetOnly(); constexpr SceneField name = crotations.name(); @@ -467,6 +468,7 @@ void SceneDataTest::constructField() { CORRADE_COMPARE(fieldData.size(), 3); CORRADE_COMPARE(fieldData.stride(), sizeof(Complexd)); CORRADE_COMPARE(fieldData.data(), Rotations2D); + #endif } void SceneDataTest::constructFieldDefault() { From 1505d431599ab03b2834ac6ae9c957f388df771e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 8 Sep 2021 16:57:58 +0200 Subject: [PATCH 05/49] Trade: it's probably good to have these switches guarded. --- src/Magnum/Trade/SceneData.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 4e92ebe257..13b90d0ce8 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -54,12 +54,19 @@ Debug& operator<<(Debug& debug, const SceneObjectType value) { } UnsignedInt sceneObjectTypeSize(const SceneObjectType type) { + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif switch(type) { case SceneObjectType::UnsignedByte: return 1; case SceneObjectType::UnsignedShort: return 2; case SceneObjectType::UnsignedInt: return 4; case SceneObjectType::UnsignedLong: return 8; } + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic pop + #endif CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {}); } @@ -198,6 +205,10 @@ Debug& operator<<(Debug& debug, const SceneFieldType value) { } UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif switch(type) { case SceneFieldType::UnsignedByte: case SceneFieldType::Byte: @@ -308,6 +319,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { case SceneFieldType::Matrix4x4d: return 128; } + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic pop + #endif CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): invalid type" << type, {}); } From 59d6709007a81dde666391f7ec2c8b66c7e6676e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 9 Sep 2021 13:56:58 +0200 Subject: [PATCH 06/49] Trade: avoid double lookup in SceneData::fooAsArray(). These need to query field size for allocating the output array which means looking up the field by name, but the same lookup was then done again in the fooInto() implementation. --- src/Magnum/Trade/SceneData.cpp | 24 +++++++++++++----------- src/Magnum/Trade/SceneData.h | 3 ++- src/Magnum/Trade/Test/SceneDataTest.cpp | 14 ++++++++++++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 13b90d0ce8..1ae8f15b2f 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -715,8 +715,7 @@ Containers::Array SceneData::objectsAsArray(const SceneField name) return objectsAsArray(fieldId); } -void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Parent); +void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", ); const SceneFieldData& field = _fields[fieldId]; @@ -737,6 +736,10 @@ void SceneData::parentsInto(const Containers::StridedArrayView1D& destinati } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { + parentsIntoInternal(fieldFor(SceneField::Parent), destination); +} + Containers::Array SceneData::parentsAsArray() const { const UnsignedInt fieldId = fieldFor(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, @@ -744,7 +747,7 @@ Containers::Array SceneData::parentsAsArray() const { strings in the binary */ "Trade::SceneData::parentsInto(): field not found", {}); Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - parentsInto(out); + parentsIntoInternal(fieldId, out); return out; } @@ -1022,9 +1025,8 @@ void SceneData::indexFieldIntoInternal( #ifndef CORRADE_NO_ASSERT const char* const prefix, #endif - const SceneField name, const Containers::StridedArrayView1D& destination) const + const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, prefix << "field not found", ); const SceneFieldData& field = _fields[fieldId]; @@ -1055,7 +1057,7 @@ Containers::Array SceneData::indexFieldAsArrayInternal( #ifndef CORRADE_NO_ASSERT prefix, #endif - name, out); + fieldId, out); return out; } @@ -1064,7 +1066,7 @@ void SceneData::meshesInto(const Containers::StridedArrayView1D& de #ifndef CORRADE_NO_ASSERT "Trade::SceneData::meshesInto():", #endif - SceneField::Mesh, destination); + fieldFor(SceneField::Mesh), destination); } Containers::Array SceneData::meshesAsArray() const { @@ -1082,7 +1084,7 @@ void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D SceneData::meshMaterialsAsArray() const { @@ -1100,7 +1102,7 @@ void SceneData::lightsInto(const Containers::StridedArrayView1D& de #ifndef CORRADE_NO_ASSERT "Trade::SceneData::lightsInto():", #endif - SceneField::Light, destination); + fieldFor(SceneField::Light), destination); } Containers::Array SceneData::lightsAsArray() const { @@ -1118,7 +1120,7 @@ void SceneData::camerasInto(const Containers::StridedArrayView1D& d #ifndef CORRADE_NO_ASSERT "Trade::SceneData::camerasInto():", #endif - SceneField::Camera, destination); + fieldFor(SceneField::Camera), destination); } Containers::Array SceneData::camerasAsArray() const { @@ -1136,7 +1138,7 @@ void SceneData::skinsInto(const Containers::StridedArrayView1D& des #ifndef CORRADE_NO_ASSERT "Trade::SceneData::skinsInto():", #endif - SceneField::Skin, destination); + fieldFor(SceneField::Skin), destination); } Containers::Array SceneData::skinsAsArray() const { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 48569d955d..04e4ca4e3b 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -1550,6 +1550,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template bool checkFieldTypeCompatibility(const SceneFieldData& attribute, const char* prefix) const; #endif + MAGNUM_TRADE_LOCAL void parentsIntoInternal(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; MAGNUM_TRADE_LOCAL void transformations2DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL void transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; @@ -1557,7 +1558,7 @@ class MAGNUM_TRADE_EXPORT SceneData { #ifndef CORRADE_NO_ASSERT const char* const prefix, #endif - const SceneField name, const Containers::StridedArrayView1D& destination) const; + const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL Containers::Array indexFieldAsArrayInternal( #ifndef CORRADE_NO_ASSERT const char* const prefix, diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 1a91c68464..1cb1e62549 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -1786,8 +1786,18 @@ template void SceneDataTest::parentsAsArray() { SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::UnsignedInt, nullptr}, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; - CORRADE_COMPARE_AS(scene.parentsAsArray(), - Containers::arrayView({15, -1, 44}), + + Int expected[]{15, -1, 44}; + CORRADE_COMPARE_AS(arrayView(scene.parentsAsArray()), + Containers::arrayView(expected), + TestSuite::Compare::Container); + + /* Test Into() as well as it only shares a common helper with AsArray() but + has different top-level code paths */ + Int out[3]; + scene.parentsInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), TestSuite::Compare::Container); } From 4e8fa9d847fb992a5ea03199e75518273f2d44a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Sep 2021 21:09:59 +0200 Subject: [PATCH 07/49] Trade: add SceneData::fooInto() overloads for subranges. Useful for cases when it's desirable to extract just some elements, for example just for a particular object ID. Or when allocating an arbitrarily large array isn't wanted for perf/memory reasons. --- src/Magnum/Trade/SceneData.cpp | 349 ++++++--- src/Magnum/Trade/SceneData.h | 155 +++- src/Magnum/Trade/Test/SceneDataTest.cpp | 904 ++++++++++++++++++++---- 3 files changed, 1130 insertions(+), 278 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 1ae8f15b2f..b757871940 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -499,22 +499,32 @@ Containers::ArrayView SceneData::mutableData() & { return _data; } -Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field) const { +Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const { + CORRADE_INTERNAL_ASSERT(offset + size <= field._size); return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ - /** @todo better ideas for the StridedArrayView API? */ - {field._isOffsetOnly ? _data.data() + field._objectData.offset : - field._objectData.pointer, ~std::size_t{}}, - field._size, field._objectStride}; + {static_cast(field._isOffsetOnly ? + _data.data() + field._objectData.offset : field._objectData.pointer) + + field._fieldStride*offset, ~std::size_t{}}, + size, field._objectStride}; } -Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal(const SceneFieldData& field) const { +Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field) const { + return fieldDataObjectViewInternal(field, 0, field._size); +} + +Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const { + CORRADE_INTERNAL_ASSERT(offset + size <= field._size); return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ - /** @todo better ideas for the StridedArrayView API? */ - {field._isOffsetOnly ? _data.data() + field._fieldData.offset : - field._fieldData.pointer, ~std::size_t{}}, - field._size, field._fieldStride}; + {static_cast(field._isOffsetOnly ? + _data.data() + field._fieldData.offset : field._fieldData.pointer) + + field._fieldStride*offset, ~std::size_t{}}, + size, field._fieldStride}; +} + +Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal(const SceneFieldData& field) const { + return fieldDataFieldViewInternal(field, 0, field._size); } SceneFieldData SceneData::fieldData(const UnsignedInt id) const { @@ -668,13 +678,12 @@ Containers::StridedArrayView2D SceneData::mutableField(const SceneField na return mutableField(fieldId); } -void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { - CORRADE_ASSERT(fieldId < _fields.size(), - "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", ); +void SceneData::objectsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + const SceneFieldData& field = _fields[fieldId]; - CORRADE_ASSERT(destination.size() == field._size, - "Trade::SceneData::objectsInto(): expected a view with" << field._size << "elements but got" << destination.size(), ); - const Containers::StridedArrayView1D objectData = fieldDataObjectViewInternal(field); + const Containers::StridedArrayView1D objectData = fieldDataObjectViewInternal(field, offset, destination.size()); const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination); if(field._objectType == SceneObjectType::UnsignedInt) @@ -689,13 +698,31 @@ void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::Strided } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", ); + CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, + "Trade::SceneData::objectsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); + objectsIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::objectsInto(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + CORRADE_ASSERT(offset <= _fields[fieldId]._size, + "Trade::SceneData::objectsInto(): 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); + objectsIntoInternal(fieldId, offset, destination.prefix(size)); + return size; +} + Containers::Array SceneData::objectsAsArray(const UnsignedInt fieldId) const { CORRADE_ASSERT(fieldId < _fields.size(), /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - objectsInto(fieldId, out); + objectsIntoInternal(fieldId, 0, out); return out; } @@ -706,6 +733,13 @@ void SceneData::objectsInto(const SceneField name, const Containers::StridedArra objectsInto(fieldId, destination); } +std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::objectsInto(): field" << name << "not found", {}); + return objectsInto(fieldId, offset, destination); +} + Containers::Array SceneData::objectsAsArray(const SceneField name) const { const UnsignedInt fieldId = fieldFor(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, @@ -715,13 +749,12 @@ Containers::Array SceneData::objectsAsArray(const SceneField name) return objectsAsArray(fieldId); } -void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::parentsInto(): field not found", ); +void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + const SceneFieldData& field = _fields[fieldId]; - CORRADE_ASSERT(destination.size() == field._size, - "Trade::SceneData::parentsInto(): expected a view with" << field._size << "elements but got" << destination.size(), ); - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); const auto destination1i = Containers::arrayCast<2, Int>(destination); if(field._fieldType == SceneFieldType::Int) @@ -737,7 +770,23 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const Containers: } void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { - parentsIntoInternal(fieldFor(SceneField::Parent), destination); + const UnsignedInt fieldId = fieldFor(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); +} + +std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(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)); + return size; } Containers::Array SceneData::parentsAsArray() const { @@ -747,7 +796,7 @@ Containers::Array SceneData::parentsAsArray() const { strings in the binary */ "Trade::SceneData::parentsInto(): field not found", {}); Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - parentsIntoInternal(fieldId, out); + parentsIntoInternal(fieldId, 0, out); return out; } @@ -813,13 +862,16 @@ std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, U ~std::size_t{} : _fields[fieldToCheckForSize]._size; } -void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const { +void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* *FieldId, offset and destination.size() is assumed to be in bounds (or + an invalid field ID), checked by the callers */ + /** @todo apply scalings as well if dual complex? */ /* Prefer the transformation field, if present */ if(transformationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[transformationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); const auto destination1f = Containers::arrayCast<2, Float>(destination); if(field._fieldType == SceneFieldType::Matrix3x3) { @@ -846,7 +898,7 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi /* Apply scaling first, if present */ if(scalingFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[scalingFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Vector2) { applyScaling(fieldData, destination); @@ -861,7 +913,7 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi /* Apply rotation second, if present */ if(rotationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[rotationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Complex) { applyRotation(fieldData, destination); @@ -876,7 +928,7 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi /* Apply translation last, if present */ if(translationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[translationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Vector2) { applyTranslation(fieldData, destination); @@ -902,7 +954,22 @@ void SceneData::transformations2DInto(const Containers::StridedArrayView1D& destination) 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{}, + "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)); + return size; } Containers::Array SceneData::transformations2DAsArray() const { @@ -913,17 +980,20 @@ Containers::Array SceneData::transformations2DAsArray() const { strings in the binary */ "Trade::SceneData::transformations2DInto(): no transformation-related field found", {}); Containers::Array out{NoInit, expectedSize}; - transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, out); + transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out); return out; } -void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const { +void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* *FieldId, offset and destination.size() is assumed to be in bounds (or + an invalid field ID), checked by the callers */ + /** @todo apply scalings as well if dual quat? */ /* Prefer the transformation field, if present */ if(transformationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[transformationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); const auto destination1f = Containers::arrayCast<2, Float>(destination); if(field._fieldType == SceneFieldType::Matrix4x4) { @@ -950,7 +1020,7 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi /* Apply scaling first, if present */ if(scalingFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[scalingFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Vector3) { applyScaling(fieldData, destination); @@ -965,7 +1035,7 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi /* Apply rotation second, if present */ if(rotationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[rotationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Quaternion) { applyRotation(fieldData, destination); @@ -980,7 +1050,7 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi /* Apply translation last, if present */ if(translationFieldId != ~UnsignedInt{}) { const SceneFieldData& field = _fields[translationFieldId]; - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); if(field._fieldType == SceneFieldType::Vector3) { applyTranslation(fieldData, destination); @@ -1006,7 +1076,22 @@ void SceneData::transformations3DInto(const Containers::StridedArrayView1D& destination) 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{}, + "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)); + return size; } Containers::Array SceneData::transformations3DAsArray() const { @@ -1017,22 +1102,16 @@ Containers::Array SceneData::transformations3DAsArray() const { strings in the binary */ "Trade::SceneData::transformations3DInto(): no transformation-related field found", {}); Containers::Array out{NoInit, expectedSize}; - transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, out); + transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out); return out; } -void SceneData::indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - const char* const prefix, - #endif - const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const -{ - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - prefix << "field not found", ); +void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + const SceneFieldData& field = _fields[fieldId]; - CORRADE_ASSERT(destination.size() == field._size, - prefix << "expected a view with" << field._size << "elements but got" << destination.size(), ); - const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field); + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination); if(field._fieldType == SceneFieldType::UnsignedInt) @@ -1044,111 +1123,155 @@ void SceneData::indexFieldIntoInternal( else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -Containers::Array SceneData::indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT - const char* const prefix, - #endif - const SceneField name) const -{ - const UnsignedInt fieldId = fieldFor(name); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, prefix << "field not found", {}); +Containers::Array SceneData::indexFieldAsArrayInternal(const UnsignedInt fieldId) const { Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - prefix, - #endif - fieldId, out); + indexFieldIntoInternal(fieldId, 0, out); return out; } void SceneData::meshesInto(const Containers::StridedArrayView1D& destination) const { - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - "Trade::SceneData::meshesInto():", - #endif - fieldFor(SceneField::Mesh), destination); + 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(), ); + indexFieldIntoInternal(fieldId, 0, destination); +} + +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); + indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + return size; } Containers::Array SceneData::meshesAsArray() const { - return indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT + 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():", - #endif - SceneField::Mesh); + "Trade::SceneData::meshesInto(): field not found", {}); + return indexFieldAsArrayInternal(fieldId); } void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - "Trade::SceneData::meshMaterialsInto():", - #endif - fieldFor(SceneField::MeshMaterial), destination); + 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); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::meshMaterialsInto(): field 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)); + return size; } Containers::Array SceneData::meshMaterialsAsArray() const { - return indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT + const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::meshMaterialsInto():", - #endif - SceneField::MeshMaterial); + "Trade::SceneData::meshMaterialsInto(): field not found", {}); + return indexFieldAsArrayInternal(fieldId); } void SceneData::lightsInto(const Containers::StridedArrayView1D& destination) const { - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - "Trade::SceneData::lightsInto():", - #endif - fieldFor(SceneField::Light), destination); + const UnsignedInt fieldId = fieldFor(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(), ); + indexFieldIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(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); + indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + return size; } Containers::Array SceneData::lightsAsArray() const { - return indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT + const UnsignedInt fieldId = fieldFor(SceneField::Light); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::lightsInto():", - #endif - SceneField::Light); + "Trade::SceneData::lightsInto(): field not found", {}); + return indexFieldAsArrayInternal(fieldId); } void SceneData::camerasInto(const Containers::StridedArrayView1D& destination) const { - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - "Trade::SceneData::camerasInto():", - #endif - fieldFor(SceneField::Camera), destination); + const UnsignedInt fieldId = fieldFor(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(), ); + indexFieldIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(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); + indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + return size; } Containers::Array SceneData::camerasAsArray() const { - return indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT + const UnsignedInt fieldId = fieldFor(SceneField::Camera); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::camerasInto():", - #endif - SceneField::Camera); + "Trade::SceneData::camerasInto(): field not found", {}); + return indexFieldAsArrayInternal(fieldId); } void SceneData::skinsInto(const Containers::StridedArrayView1D& destination) const { - indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - "Trade::SceneData::skinsInto():", - #endif - fieldFor(SceneField::Skin), destination); + const UnsignedInt fieldId = fieldFor(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(), ); + indexFieldIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(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); + indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + return size; } Containers::Array SceneData::skinsAsArray() const { - return indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT + const UnsignedInt fieldId = fieldFor(SceneField::Skin); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::skinsInto():", - #endif - SceneField::Skin); + "Trade::SceneData::skinsInto(): field not found", {}); + return indexFieldAsArrayInternal(fieldId); } Containers::Array SceneData::releaseFieldData() { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 04e4ca4e3b..84ac44e7a2 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -1264,6 +1264,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void objectsInto(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; + /** + * @brief A subrange of object mapping for given field as 32-bit integers into a pre-allocated view + * @m_since_latest + * + * Compared to @ref objectsInto(UnsignedInt, const Containers::StridedArrayView1D&) const + * extracts only a subrange of the object mapping 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(UnsignedInt) const + */ + std::size_t objectsInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Object mapping for given named field as 32-bit integers * @m_since_latest @@ -1293,6 +1306,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void objectsInto(SceneField fieldName, const Containers::StridedArrayView1D& destination) const; + /** + * @brief A subrange of object mapping for given named field as 32-bit integers into a pre-allocated view + * @m_since_latest + * + * Compared to @ref objectsInto(SceneField, const Containers::StridedArrayView1D&) const + * extracts only a subrange of the object mapping 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 objectsInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Parent indices as 32-bit integers * @m_since_latest @@ -1321,6 +1347,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void parentsInto(const Containers::StridedArrayView1D& destination) 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 + * 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 parentsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief 2D transformations as 3x3 float matrices * @m_since_latest @@ -1351,6 +1390,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void transformations2DInto(const Containers::StridedArrayView1D& destination) 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 + * 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 transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief 3D transformations as 4x4 float matrices * @m_since_latest @@ -1381,6 +1433,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void transformations3DInto(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 + * 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 transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Mesh IDs as 32-bit integers * @m_since_latest @@ -1404,6 +1469,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void meshesInto(const Containers::StridedArrayView1D& destination) const; + /** + * @brief A subrange of mesh IDs as 32-bit integers into a pre-allocated view + * @m_since_latest + * + * Compared to @ref meshesInto(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 @@ -1427,6 +1505,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ 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. + * @see @ref fieldSize(SceneField) const + */ + std::size_t meshMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Light IDs as 32-bit integers * @m_since_latest @@ -1450,6 +1541,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void lightsInto(const Containers::StridedArrayView1D& destination) 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 + * 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 lightsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Camera IDs as 32-bit integers * @m_since_latest @@ -1473,6 +1577,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void camerasInto(const Containers::StridedArrayView1D& destination) 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 + * 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 camerasInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Skin IDs as 32-bit integers * @m_since_latest @@ -1496,6 +1613,19 @@ class MAGNUM_TRADE_EXPORT SceneData { */ void skinsInto(const Containers::StridedArrayView1D& destination) 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 + * 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 skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Release field data storage * @m_since_latest @@ -1542,28 +1672,25 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Internal helper that doesn't assert, unlike fieldId() */ UnsignedInt fieldFor(SceneField name) const; - /* Like objects() / field(), but returning just a 1D view */ + /* Like objects() / field(), but returning just a 1D view, sliced from + offset to offset + size. The parameterless overloads are equal to + offset = 0 and size = field.size(). */ + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataObjectViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const; MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataObjectViewInternal(const SceneFieldData& field) const; + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataFieldViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const; MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataFieldViewInternal(const SceneFieldData& field) const; #ifndef CORRADE_NO_ASSERT template bool checkFieldTypeCompatibility(const SceneFieldData& attribute, const char* prefix) const; #endif - MAGNUM_TRADE_LOCAL void parentsIntoInternal(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; + 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) const; - MAGNUM_TRADE_LOCAL void transformations2DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; - MAGNUM_TRADE_LOCAL void transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, const Containers::StridedArrayView1D& destination) const; - MAGNUM_TRADE_LOCAL void indexFieldIntoInternal( - #ifndef CORRADE_NO_ASSERT - const char* const prefix, - #endif - const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; - MAGNUM_TRADE_LOCAL Containers::Array indexFieldAsArrayInternal( - #ifndef CORRADE_NO_ASSERT - const char* const prefix, - #endif - const SceneField name) 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 transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, 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 indexFieldAsArrayInternal(const UnsignedInt fieldId) const; DataFlags _dataFlags; SceneObjectType _objectType; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 1cb1e62549..c9630c62d4 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -101,32 +101,44 @@ struct SceneDataTest: TestSuite::Tester { template void objectsAsArrayByIndex(); template void objectsAsArrayByName(); void objectsAsArrayLongType(); - void objectsIntoArrayInvalidSize(); + void objectsIntoArrayByIndex(); + void objectsIntoArrayByName(); + void objectsIntoArrayInvalidSizeOrOffset(); template void parentsAsArray(); #ifndef CORRADE_TARGET_32BIT void parentsAsArrayLongType(); #endif - void parentsIntoArrayInvalidSize(); + void parentsIntoArray(); + void parentsIntoArrayInvalidSizeOrOffset(); template void transformations2DAsArray(); template void transformations2DAsArrayTRS(); template void transformations2DAsArrayBut3DType(); template void transformations2DAsArrayBut3DTypeTRS(); - void transformations2DIntoArrayInvalidSize(); + void transformations2DIntoArray(); + void transformations2DIntoArrayTRS(); + void transformations2DIntoArrayInvalidSizeOrOffset(); template void transformations3DAsArray(); template void transformations3DAsArrayTRS(); template void transformations3DAsArrayBut2DType(); template void transformations3DAsArrayBut2DTypeTRS(); - void transformations3DIntoArrayInvalidSize(); + void transformations3DIntoArray(); + void transformations3DIntoArrayTRS(); + void transformations3DIntoArrayInvalidSizeOrOffset(); template void meshesAsArray(); - void meshesIntoArrayInvalidSize(); + void meshesIntoArray(); + void meshesIntoArrayInvalidSizeOrOffset(); template void meshMaterialsAsArray(); - void meshMaterialsIntoArrayInvalidSize(); + void meshMaterialsIntoArray(); + void meshMaterialsIntoArrayInvalidSizeOrOffset(); template void lightsAsArray(); - void lightsIntoArrayInvalidSize(); + void lightsIntoArray(); + void lightsIntoArrayInvalidSizeOrOffset(); template void camerasAsArray(); - void camerasIntoArrayInvalidSize(); + void camerasIntoArray(); + void camerasIntoArrayInvalidSizeOrOffset(); template void skinsAsArray(); - void skinsIntoArrayInvalidSize(); + void skinsIntoArray(); + void skinsIntoArrayInvalidSizeOrOffset(); void mutableAccessNotAllowed(); @@ -149,6 +161,18 @@ const struct { {"mutable", DataFlag::Mutable} }; +const struct { + const char* name; + std::size_t offset; + std::size_t size; + std::size_t expectedSize; +} IntoArrayOffsetData[]{ + {"whole", 0, 3, 3}, + {"one element in the middle", 1, 1, 1}, + {"suffix to a larger array", 2, 10, 1}, + {"offset at the end", 3, 10, 0} +}; + SceneDataTest::SceneDataTest() { addTests({&SceneDataTest::objectTypeSize, &SceneDataTest::objectTypeSizeInvalid, @@ -215,8 +239,13 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::objectsAsArrayByName, &SceneDataTest::objectsAsArrayByName, &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayLongType, - &SceneDataTest::objectsIntoArrayInvalidSize, + &SceneDataTest::objectsAsArrayLongType}); + + addInstancedTests({&SceneDataTest::objectsIntoArrayByIndex, + &SceneDataTest::objectsIntoArrayByName}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::objectsIntoArrayInvalidSizeOrOffset, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, @@ -224,7 +253,12 @@ SceneDataTest::SceneDataTest() { #ifndef CORRADE_TARGET_32BIT &SceneDataTest::parentsAsArrayLongType, #endif - &SceneDataTest::parentsIntoArrayInvalidSize, + }); + + addInstancedTests({&SceneDataTest::parentsIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::parentsIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, @@ -236,8 +270,13 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DAsArrayBut3DType, &SceneDataTest::transformations2DAsArrayBut3DType, &SceneDataTest::transformations2DAsArrayBut3DTypeTRS, - &SceneDataTest::transformations2DAsArrayBut3DTypeTRS, - &SceneDataTest::transformations2DIntoArrayInvalidSize, + &SceneDataTest::transformations2DAsArrayBut3DTypeTRS}); + + addInstancedTests({&SceneDataTest::transformations2DIntoArray, + &SceneDataTest::transformations2DIntoArrayTRS}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, @@ -249,28 +288,53 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations3DAsArrayBut2DType, &SceneDataTest::transformations3DAsArrayBut2DType, &SceneDataTest::transformations3DAsArrayBut2DTypeTRS, - &SceneDataTest::transformations3DAsArrayBut2DTypeTRS, - &SceneDataTest::transformations3DIntoArrayInvalidSize, + &SceneDataTest::transformations3DAsArrayBut2DTypeTRS}); + + addInstancedTests({&SceneDataTest::transformations3DIntoArray, + &SceneDataTest::transformations3DIntoArrayTRS}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset, &SceneDataTest::meshesAsArray, &SceneDataTest::meshesAsArray, - &SceneDataTest::meshesAsArray, - &SceneDataTest::meshesIntoArrayInvalidSize, + &SceneDataTest::meshesAsArray}); + + addInstancedTests({&SceneDataTest::meshesIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::meshesIntoArrayInvalidSizeOrOffset, &SceneDataTest::meshMaterialsAsArray, &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsIntoArrayInvalidSize, + &SceneDataTest::meshMaterialsAsArray}); + + addInstancedTests({&SceneDataTest::meshMaterialsIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset, &SceneDataTest::lightsAsArray, &SceneDataTest::lightsAsArray, - &SceneDataTest::lightsAsArray, - &SceneDataTest::lightsIntoArrayInvalidSize, + &SceneDataTest::lightsAsArray}); + + addInstancedTests({&SceneDataTest::lightsIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::lightsIntoArrayInvalidSizeOrOffset, &SceneDataTest::camerasAsArray, &SceneDataTest::camerasAsArray, - &SceneDataTest::camerasAsArray, - &SceneDataTest::camerasIntoArrayInvalidSize, + &SceneDataTest::camerasAsArray}); + + addInstancedTests({&SceneDataTest::camerasIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::camerasIntoArrayInvalidSizeOrOffset, &SceneDataTest::skinsAsArray, &SceneDataTest::skinsAsArray, - &SceneDataTest::skinsAsArray, - &SceneDataTest::skinsIntoArrayInvalidSize, + &SceneDataTest::skinsAsArray}); + + addInstancedTests({&SceneDataTest::skinsIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::skinsIntoArrayInvalidSizeOrOffset, &SceneDataTest::mutableAccessNotAllowed, @@ -1675,6 +1739,7 @@ template void SceneDataTest::objectsAsArrayByIndex() { SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; + CORRADE_COMPARE_AS(scene.objectsAsArray(1), Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); @@ -1700,17 +1765,8 @@ template void SceneDataTest::objectsAsArrayByName() { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.objectsAsArray(SceneField::Mesh)), - Containers::arrayView(expected), - TestSuite::Compare::Container); - - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.objectsInto(SceneField::Mesh, out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::Mesh), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); } @@ -1741,7 +1797,99 @@ void SceneDataTest::objectsAsArrayLongType() { "Trade::SceneData::objectsInto(): indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via objects() instead\n"); } -void SceneDataTest::objectsIntoArrayInvalidSize() { +void SceneDataTest::objectsIntoArrayByIndex() { + 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; + UnsignedInt mesh; + } fields[] { + {15, 0}, + {37, 1}, + {44, 15} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, + view.slice(&Field::object), + view.slice(&Field::mesh)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.objectsInto(1, out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::object), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + Containers::Array out{data.size}; + CORRADE_COMPARE(scene.objectsInto(1, data.offset, out), data.expectedSize); + CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::objectsIntoArrayByName() { + 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; + UnsignedInt mesh; + } fields[] { + {15, 0}, + {37, 1}, + {44, 15} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, + view.slice(&Field::object), + view.slice(&Field::mesh)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.objectsInto(SceneField::Mesh, out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::object), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + Containers::Array out{data.size}; + CORRADE_COMPARE(scene.objectsInto(SceneField::Mesh, data.offset, out), data.expectedSize); + CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::objectsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -1762,9 +1910,13 @@ void SceneDataTest::objectsIntoArrayInvalidSize() { UnsignedInt destination[2]; scene.objectsInto(0, destination); scene.objectsInto(SceneField::Mesh, destination); + scene.objectsInto(0, 4, destination); + scene.objectsInto(SceneField::Mesh, 4, destination); CORRADE_COMPARE(out.str(), "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n"); + "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::objectsInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::objectsInto(): offset 4 out of bounds for a field of size 3\n"); } template void SceneDataTest::parentsAsArray() { @@ -1787,17 +1939,8 @@ template void SceneDataTest::parentsAsArray() { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; - Int expected[]{15, -1, 44}; - CORRADE_COMPARE_AS(arrayView(scene.parentsAsArray()), - Containers::arrayView(expected), - TestSuite::Compare::Container); - - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - Int out[3]; - scene.parentsInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.parentsAsArray(), + Containers::arrayView({15, -1, 44}), TestSuite::Compare::Container); } @@ -1829,7 +1972,53 @@ void SceneDataTest::parentsAsArrayLongType() { } #endif -void SceneDataTest::parentsIntoArrayInvalidSize() { +void SceneDataTest::parentsIntoArray() { + 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 parent; + } fields[] { + {1, 15}, + {0, -1}, + {4, 44} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}, + SceneFieldData{SceneField::Parent, + view.slice(&Field::object), + view.slice(&Field::parent)}, + }}; + + /* The offset-less overload should give back all data */ + { + Int out[3]; + scene.parentsInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::parent) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::parentsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -1849,8 +2038,10 @@ void SceneDataTest::parentsIntoArrayInvalidSize() { Error redirectError{&out}; Int destination[2]; scene.parentsInto(destination); + scene.parentsInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::parentsInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::transformations2DAsArray() { @@ -1908,23 +2099,12 @@ template void SceneDataTest::transformations2DAsArray() { components.slice(&Component::scaling)}, }}; - Matrix3 expected[]{ + 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}) - }; - CORRADE_COMPARE_AS(arrayView(scene.transformations2DAsArray()), - Containers::arrayView(expected), - TestSuite::Compare::Container); - - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - Matrix3 out[4]; - scene.transformations2DInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); + }), TestSuite::Compare::Container); } template void SceneDataTest::transformations2DAsArrayTRS() { @@ -2103,7 +2283,112 @@ template void SceneDataTest::transformations2DAsArrayBut3DTypeTRS() { "Trade::SceneData::transformations2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); } -void SceneDataTest::transformations2DIntoArrayInvalidSize() { +void SceneDataTest::transformations2DIntoArray() { + 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; + Matrix3 transformation; + } fields[] { + {1, Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f})}, + {0, Matrix3::rotation(35.0_degf)}, + {4, Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf)} + }; + + 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::Transformation, + view.slice(&Field::object), + view.slice(&Field::transformation)}, + }}; + + /* The offset-less overload should give back all data */ + { + Matrix3 out[3]; + scene.transformations2DInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::transformation) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::transformations2DIntoArrayTRS() { + 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; + 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)}, + }}; + + Matrix3 expected[]{ + Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f}), + Matrix3::rotation(35.0_degf), + Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf) + }; + + /* The offset-less overload should give back all data */ + { + Matrix3 out[3]; + scene.transformations2DInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + 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::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2123,8 +2408,10 @@ void SceneDataTest::transformations2DIntoArrayInvalidSize() { Error redirectError{&out}; Matrix3 destination[2]; scene.transformations2DInto(destination); + scene.transformations2DInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations2DInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::transformations3DAsArray() { @@ -2185,23 +2472,12 @@ template void SceneDataTest::transformations3DAsArray() { components.slice(&Component::scaling)}, }}; - Matrix4 expected[]{ + 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}) - }; - CORRADE_COMPARE_AS(arrayView(scene.transformations3DAsArray()), - Containers::arrayView(expected), - TestSuite::Compare::Container); - - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - Matrix4 out[4]; - scene.transformations3DInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); + }), TestSuite::Compare::Container); } template void SceneDataTest::transformations3DAsArrayTRS() { @@ -2380,7 +2656,112 @@ template void SceneDataTest::transformations3DAsArrayBut2DTypeTRS() { "Trade::SceneData::transformations3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); } -void SceneDataTest::transformations3DIntoArrayInvalidSize() { +void SceneDataTest::transformations3DIntoArray() { + 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; + Matrix4 transformation; + } fields[] { + {1, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f})}, + {0, Matrix4::rotationX(35.0_degf)}, + {4, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf)} + }; + + 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::Transformation, + view.slice(&Field::object), + view.slice(&Field::transformation)}, + }}; + + /* The offset-less overload should give back all data */ + { + Matrix4 out[3]; + scene.transformations3DInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::transformation) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::transformations3DIntoArrayTRS() { + 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; + 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)}, + }}; + + Matrix4 expected[]{ + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f}), + Matrix4::rotationX(35.0_degf), + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf) + }; + + /* The offset-less overload should give back all data */ + { + Matrix4 out[3]; + scene.transformations3DInto(out); + CORRADE_COMPARE_AS(Containers::arrayView(out), + Containers::arrayView(expected), + 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::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2400,8 +2781,10 @@ void SceneDataTest::transformations3DIntoArrayInvalidSize() { Error redirectError{&out}; Matrix4 destination[2]; scene.transformations3DInto(destination); + scene.transformations3DInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations3DInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::meshesAsArray() { @@ -2424,21 +2807,58 @@ template void SceneDataTest::meshesAsArray() { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.meshesAsArray()), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.meshesAsArray(), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); +} - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.meshesInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); +void SceneDataTest::meshesIntoArray() { + 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; + UnsignedInt mesh; + } fields[]{ + {1, 15}, + {0, 37}, + {4, 44} + }; + + 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::Mesh, + view.slice(&Field::object), + view.slice(&Field::mesh)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.meshesInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::mesh), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + 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), + TestSuite::Compare::Container); + } } -void SceneDataTest::meshesIntoArrayInvalidSize() { +void SceneDataTest::meshesIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2458,8 +2878,10 @@ void SceneDataTest::meshesIntoArrayInvalidSize() { 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(): 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() { @@ -2482,21 +2904,58 @@ template void SceneDataTest::meshMaterialsAsArray() { SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.meshMaterialsAsArray()), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.meshMaterialsAsArray(), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); +} - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.meshMaterialsInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - 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; + UnsignedInt meshMaterial; + } fields[]{ + {1, 15}, + {0, 37}, + {4, 44} + }; + + 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::MeshMaterial, + view.slice(&Field::object), + view.slice(&Field::meshMaterial)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.meshMaterialsInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::meshMaterial), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + Containers::Array out{data.size}; + CORRADE_COMPARE(scene.meshMaterialsInto(data.offset, out), data.expectedSize); + CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + view.slice(&Field::meshMaterial) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } } -void SceneDataTest::meshMaterialsIntoArrayInvalidSize() { +void SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2516,8 +2975,10 @@ void SceneDataTest::meshMaterialsIntoArrayInvalidSize() { Error redirectError{&out}; UnsignedInt destination[2]; scene.meshMaterialsInto(destination); + scene.meshMaterialsInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::meshMaterialsInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::lightsAsArray() { @@ -2540,21 +3001,58 @@ template void SceneDataTest::lightsAsArray() { SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.lightsAsArray()), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.lightsAsArray(), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); +} - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.lightsInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); +void SceneDataTest::lightsIntoArray() { + 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; + UnsignedInt light; + } fields[] { + {1, 15}, + {0, 37}, + {4, 44} + }; + + 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::Light, + view.slice(&Field::object), + view.slice(&Field::light)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.lightsInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::light) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } } -void SceneDataTest::lightsIntoArrayInvalidSize() { +void SceneDataTest::lightsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2574,8 +3072,10 @@ void SceneDataTest::lightsIntoArrayInvalidSize() { Error redirectError{&out}; UnsignedInt destination[2]; scene.lightsInto(destination); + scene.lightsInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::lightsInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::camerasAsArray() { @@ -2598,21 +3098,58 @@ template void SceneDataTest::camerasAsArray() { SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.camerasAsArray()), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.camerasAsArray(), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); +} - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.camerasInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); +void SceneDataTest::camerasIntoArray() { + 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; + UnsignedInt camera; + } fields[]{ + {1, 15}, + {0, 37}, + {4, 44} + }; + + 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::Camera, + view.slice(&Field::object), + view.slice(&Field::camera)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.camerasInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::camera) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } } -void SceneDataTest::camerasIntoArrayInvalidSize() { +void SceneDataTest::camerasIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2632,8 +3169,10 @@ void SceneDataTest::camerasIntoArrayInvalidSize() { Error redirectError{&out}; UnsignedInt destination[2]; scene.camerasInto(destination); + scene.camerasInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::camerasInto(): expected a view with 3 elements but got 2\n"); + "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"); } template void SceneDataTest::skinsAsArray() { @@ -2656,21 +3195,58 @@ template void SceneDataTest::skinsAsArray() { SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; - UnsignedInt expected[]{15, 37, 44}; - CORRADE_COMPARE_AS(arrayView(scene.skinsAsArray()), - Containers::arrayView(expected), + CORRADE_COMPARE_AS(scene.skinsAsArray(), + Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); +} - /* Test Into() as well as it only shares a common helper with AsArray() but - has different top-level code paths */ - UnsignedInt out[3]; - scene.skinsInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), - Containers::arrayView(expected), - TestSuite::Compare::Container); +void SceneDataTest::skinsIntoArray() { + 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; + UnsignedInt skin; + } fields[] { + {1, 15}, + {0, 37}, + {4, 44} + }; + + 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::Skin, + view.slice(&Field::object), + view.slice(&Field::skin)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt out[3]; + scene.skinsInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + view.slice(&Field::skin) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } } -void SceneDataTest::skinsIntoArrayInvalidSize() { +void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2690,8 +3266,10 @@ void SceneDataTest::skinsIntoArrayInvalidSize() { Error redirectError{&out}; UnsignedInt destination[2]; scene.skinsInto(destination); + scene.skinsInto(4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::skinsInto(): expected a view with 3 elements but got 2\n"); + "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"); } void SceneDataTest::mutableAccessNotAllowed() { @@ -2862,18 +3440,29 @@ void SceneDataTest::fieldNotFound() { scene.mutableField(sceneFieldCustom(666)); scene.parentsAsArray(); + scene.parentsInto(nullptr); + scene.parentsInto(0, nullptr); scene.transformations2DAsArray(); - scene.transformations3DAsArray(); - /* Test both AsArray() and Into() for transformations as they only share a - common helper but have different top-level code paths. They however have - the same assertion messages to save binary size a bit. */ scene.transformations2DInto(nullptr); + scene.transformations2DInto(0, nullptr); + 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.lightsAsArray(); + scene.lightsInto(nullptr); + scene.lightsInto(0, nullptr); scene.camerasAsArray(); + scene.camerasInto(nullptr); + scene.camerasInto(0, nullptr); scene.skinsAsArray(); + scene.skinsInto(nullptr); + scene.skinsInto(0, nullptr); CORRADE_COMPARE(out.str(), "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" @@ -2898,19 +3487,32 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::mutableField(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::mutableField(): field Trade::SceneField::Custom(666) not found\n" + /* AsArray() and Into() each share a common helper but have different + top-level code paths. They however have the same assertion messages + to save binary size a bit. */ + "Trade::SceneData::parentsInto(): field not found\n" + "Trade::SceneData::parentsInto(): field not found\n" "Trade::SceneData::parentsInto(): field not found\n" - /* Test both AsArray() and Into() for transformations as they only - share a common helper but have different top-level code paths. They - however have the same assertion messages to save binary size a - bit. */ "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" - "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" + "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" "Trade::SceneData::transformations2DInto(): 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::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::lightsInto(): field not found\n" + "Trade::SceneData::lightsInto(): field not found\n" "Trade::SceneData::lightsInto(): field not found\n" "Trade::SceneData::camerasInto(): field not found\n" + "Trade::SceneData::camerasInto(): field not found\n" + "Trade::SceneData::camerasInto(): field not found\n" + "Trade::SceneData::skinsInto(): field not found\n" + "Trade::SceneData::skinsInto(): field not found\n" "Trade::SceneData::skinsInto(): field not found\n"); } From 56047b030e09f9ba4aac4d9f960130bec9dcc3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 11 Sep 2021 16:30:51 +0200 Subject: [PATCH 08/49] Trade: make SceneField::MeshMaterial signed. Because the material index has to share the object mapping with a mesh, there needs to be a way to express meshes without associated materials. --- src/Magnum/Trade/SceneData.cpp | 57 +++++++++++++++++-------- src/Magnum/Trade/SceneData.h | 27 +++++++----- src/Magnum/Trade/Test/SceneDataTest.cpp | 42 +++++++++--------- 3 files changed, 77 insertions(+), 49 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index b757871940..02efa08ce0 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -1106,7 +1106,7 @@ Containers::Array SceneData::transformations3DAsArray() const { return out; } -void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +void SceneData::unsignedIndexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { /* fieldId, offset and destination.size() is assumed to be in bounds, checked by the callers */ @@ -1123,8 +1123,31 @@ void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::siz else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -Containers::Array SceneData::indexFieldAsArrayInternal(const UnsignedInt fieldId) const { +void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + + const SceneFieldData& field = _fields[fieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, destination.size()); + const auto destination1ui = Containers::arrayCast<2, Int>(destination); + + if(field._fieldType == SceneFieldType::Int) + Utility::copy(Containers::arrayCast(fieldData), destination); + else if(field._fieldType == SceneFieldType::Short) + Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1ui); + else if(field._fieldType == SceneFieldType::Byte) + Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1ui); + 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); + 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; } @@ -1135,7 +1158,7 @@ void SceneData::meshesInto(const Containers::StridedArrayView1D& de "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(), ); - indexFieldIntoInternal(fieldId, 0, destination); + unsignedIndexFieldIntoInternal(fieldId, 0, destination); } std::size_t SceneData::meshesInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1145,7 +1168,7 @@ std::size_t SceneData::meshesInto(const std::size_t offset, const Containers::St 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); - indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); return size; } @@ -1155,10 +1178,10 @@ Containers::Array SceneData::meshesAsArray() const { /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::meshesInto(): field not found", {}); - return indexFieldAsArrayInternal(fieldId); + return unsignedIndexFieldAsArrayInternal(fieldId); } -void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshMaterialsInto(): field not found", ); @@ -1167,7 +1190,7 @@ void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::meshMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshMaterialsInto(): field not found", {}); @@ -1178,7 +1201,7 @@ std::size_t SceneData::meshMaterialsInto(const std::size_t offset, const Contain return size; } -Containers::Array SceneData::meshMaterialsAsArray() const { +Containers::Array SceneData::meshMaterialsAsArray() const { const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant @@ -1193,7 +1216,7 @@ void SceneData::lightsInto(const Containers::StridedArrayView1D& de "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(), ); - indexFieldIntoInternal(fieldId, 0, destination); + unsignedIndexFieldIntoInternal(fieldId, 0, destination); } std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1203,7 +1226,7 @@ std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::St 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); - indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); return size; } @@ -1213,7 +1236,7 @@ Containers::Array SceneData::lightsAsArray() const { /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::lightsInto(): field not found", {}); - return indexFieldAsArrayInternal(fieldId); + return unsignedIndexFieldAsArrayInternal(fieldId); } void SceneData::camerasInto(const Containers::StridedArrayView1D& destination) const { @@ -1222,7 +1245,7 @@ void SceneData::camerasInto(const Containers::StridedArrayView1D& d "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(), ); - indexFieldIntoInternal(fieldId, 0, destination); + unsignedIndexFieldIntoInternal(fieldId, 0, destination); } std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1232,7 +1255,7 @@ std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::S 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); - indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); return size; } @@ -1242,7 +1265,7 @@ Containers::Array SceneData::camerasAsArray() const { /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::camerasInto(): field not found", {}); - return indexFieldAsArrayInternal(fieldId); + return unsignedIndexFieldAsArrayInternal(fieldId); } void SceneData::skinsInto(const Containers::StridedArrayView1D& destination) const { @@ -1251,7 +1274,7 @@ void SceneData::skinsInto(const Containers::StridedArrayView1D& des "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(), ); - indexFieldIntoInternal(fieldId, 0, destination); + unsignedIndexFieldIntoInternal(fieldId, 0, destination); } std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1261,7 +1284,7 @@ std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::Str 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); - indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); return size; } @@ -1271,7 +1294,7 @@ Containers::Array SceneData::skinsAsArray() const { /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::skinsInto(): field not found", {}); - return indexFieldAsArrayInternal(fieldId); + return unsignedIndexFieldAsArrayInternal(fieldId); } Containers::Array SceneData::releaseFieldData() { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 84ac44e7a2..8e7b7b034e 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -208,10 +208,10 @@ enum class SceneField: UnsignedInt { /** * ID of a material for a @ref SceneField::Mesh, corresponding to the ID - * passed to @ref AbstractImporter::material(). Type is usually - * @ref SceneFieldType::UnsignedInt, but can be also any of - * @relativeref{SceneFieldType,UnsignedByte} or - * @relativeref{SceneFieldType,UnsignedShort}. Expected to share the + * passed to @ref AbstractImporter::material() or @cpp -1 @ce if the mesh + * has no material associated. Type is usually @ref SceneFieldType::Int, + * 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() */ @@ -1492,7 +1492,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * newly-allocated array. The field is expected to exist. * @see @ref meshMaterialsInto(), @ref hasField() */ - Containers::Array meshMaterialsAsArray() const; + Containers::Array meshMaterialsAsArray() const; /** * @brief Mesh material IDs as 32-bit integers into a pre-allocated view @@ -1503,20 +1503,20 @@ class MAGNUM_TRADE_EXPORT SceneData { * @p destination is sized to contain exactly all data. * @see @ref fieldSize(SceneField) const */ - void meshMaterialsInto(const Containers::StridedArrayView1D& destination) 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 + * 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. * @see @ref fieldSize(SceneField) const */ - std::size_t meshMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t meshMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; /** * @brief Light IDs as 32-bit integers @@ -1689,8 +1689,10 @@ class MAGNUM_TRADE_EXPORT SceneData { MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, 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 transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, 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 indexFieldAsArrayInternal(const UnsignedInt fieldId) 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 indexFieldAsArrayInternal(const UnsignedInt fieldId) const; DataFlags _dataFlags; SceneObjectType _objectType; @@ -1857,13 +1859,16 @@ namespace Implementation { type == SceneFieldType::Quaternion || type == SceneFieldType::Quaterniond)) || ((name == SceneField::Mesh || - name == SceneField::MeshMaterial || name == SceneField::Light || name == SceneField::Camera || name == SceneField::Skin) && (type == SceneFieldType::UnsignedByte || type == SceneFieldType::UnsignedShort || type == SceneFieldType::UnsignedInt)) || + (name == SceneField::MeshMaterial && + (type == SceneFieldType::Byte || + type == SceneFieldType::Short || + type == SceneFieldType::Int)) || /* Custom fields can be anything */ isSceneFieldCustom(name); } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index c9630c62d4..fce55fd685 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -303,9 +303,9 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::meshesIntoArrayInvalidSizeOrOffset, - &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsAsArray}); + &SceneDataTest::meshMaterialsAsArray, + &SceneDataTest::meshMaterialsAsArray, + &SceneDataTest::meshMaterialsAsArray}); addInstancedTests({&SceneDataTest::meshMaterialsIntoArray}, Containers::arraySize(IntoArrayOffsetData)); @@ -1314,7 +1314,7 @@ void SceneDataTest::constructZeroFields() { void SceneDataTest::constructZeroObjects() { int importerState; SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials}, &importerState}; CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(!scene.fieldData().empty()); @@ -1327,7 +1327,7 @@ void SceneDataTest::constructZeroObjects() { /* Field property access by name */ CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedShort); - CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::UnsignedInt); + CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 0); CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 0); CORRADE_COMPARE(scene.objects(SceneField::Mesh).data(), nullptr); @@ -1381,7 +1381,7 @@ void SceneDataTest::constructDuplicateField() { /* Builtin fields are checked using a bitfield, as they have monotonic numbering */ SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; SceneFieldData meshesAgain{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; std::ostringstream out; @@ -1413,7 +1413,7 @@ void SceneDataTest::constructInconsistentObjectType() { #endif SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedShort, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedShort, nullptr, SceneFieldType::Int, nullptr}; std::ostringstream out; Error redirectError{&out}; @@ -1439,12 +1439,12 @@ void SceneDataTest::constructObjectDataNotContained() { }}; /* Second a view that's in a completely different location */ SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { - SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataOut, dataIn} }}; /* Verify the owning constructor does the checks as well */ SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { - SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataOut, dataIn} }}; /* And if we have no data at all, it doesn't try to dereference them but @@ -1486,7 +1486,7 @@ void SceneDataTest::constructFieldDataNotContained() { }}; /* Second a view that's in a completely different location */ SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { - SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataIn, dataOut} }}; /* Verify array size is taken into account as well. If not, the data would @@ -1496,7 +1496,7 @@ void SceneDataTest::constructFieldDataNotContained() { }}; /* Verify the owning constructor does the checks as well */ SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { - SceneFieldData{SceneField::MeshMaterial, dataIn, dataIn}, + SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataIn, dataOut} }}; /* Not checking for nullptr data, since that got checked for object view @@ -1614,8 +1614,8 @@ void SceneDataTest::constructMismatchedMeshMaterialView() { reinterpret_cast(data.data() + 0x0c), 3}; Containers::ArrayView meshMaterialObjectData{ reinterpret_cast(data.data() + 0x18), 3}; - Containers::ArrayView meshMaterialFieldData{ - reinterpret_cast(data.data() + 0x24), 3}; + Containers::ArrayView meshMaterialFieldData{ + reinterpret_cast(data.data() + 0x24), 3}; SceneFieldData meshes{SceneField::Mesh, meshObjectData, meshFieldData}; SceneFieldData meshMaterialsDifferent{SceneField::MeshMaterial, meshMaterialObjectData, meshMaterialFieldData}; @@ -2892,7 +2892,7 @@ template void SceneDataTest::meshMaterialsAsArray() { T meshMaterial; } fields[]{ {0, T(15)}, - {1, T(37)}, + {1, T(-1)}, {15, T(44)} }; @@ -2905,7 +2905,7 @@ template void SceneDataTest::meshMaterialsAsArray() { }}; CORRADE_COMPARE_AS(scene.meshMaterialsAsArray(), - Containers::arrayView({15, 37, 44}), + Containers::arrayView({15, -1, 44}), TestSuite::Compare::Container); } @@ -2919,10 +2919,10 @@ void SceneDataTest::meshMaterialsIntoArray() { struct Field { UnsignedInt object; - UnsignedInt meshMaterial; + Int meshMaterial; } fields[]{ {1, 15}, - {0, 37}, + {0, -1}, {4, 44} }; @@ -2938,7 +2938,7 @@ void SceneDataTest::meshMaterialsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt out[3]; + Int out[3]; scene.meshMaterialsInto(out); CORRADE_COMPARE_AS(Containers::stridedArrayView(out), view.slice(&Field::meshMaterial), @@ -2946,7 +2946,7 @@ void SceneDataTest::meshMaterialsIntoArray() { /* The offset variant only a subset */ } { - Containers::Array out{data.size}; + Containers::Array out{data.size}; CORRADE_COMPARE(scene.meshMaterialsInto(data.offset, out), data.expectedSize); CORRADE_COMPARE_AS(out.prefix(data.expectedSize), view.slice(&Field::meshMaterial) @@ -2962,7 +2962,7 @@ void SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset() { struct Field { UnsignedInt object; - UnsignedInt meshMaterial; + Int meshMaterial; } fields[3]{}; Containers::StridedArrayView1D view = fields; @@ -2973,7 +2973,7 @@ void SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - UnsignedInt destination[2]; + Int destination[2]; scene.meshMaterialsInto(destination); scene.meshMaterialsInto(4, destination); CORRADE_COMPARE(out.str(), 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 09/49] 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 02efa08ce0..03965be464 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 8e7b7b034e..89bf35590f 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 fce55fd685..3f3f16d47c 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" From c61ed94a5f0cf139b4e53a52b3d7753c2dfdcbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 18 Sep 2021 11:43:45 +0200 Subject: [PATCH 10/49] Trade: SceneData TRS components can each have different type, test that. --- src/Magnum/Trade/Test/SceneDataTest.cpp | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 3f3f16d47c..d3cf758b42 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -112,14 +112,14 @@ struct SceneDataTest: TestSuite::Tester { void parentsIntoArray(); void parentsIntoArrayInvalidSizeOrOffset(); template void transformations2DAsArray(); - template void transformations2DAsArrayTRS(); + template void transformations2DAsArrayTRS(); template void transformations2DAsArrayBut3DType(); template void transformations2DAsArrayBut3DTypeTRS(); void transformations2DIntoArray(); void transformations2DIntoArrayTRS(); void transformations2DIntoArrayInvalidSizeOrOffset(); template void transformations3DAsArray(); - template void transformations3DAsArrayTRS(); + template void transformations3DAsArrayTRS(); template void transformations3DAsArrayBut2DType(); template void transformations3DAsArrayBut2DTypeTRS(); void transformations3DIntoArray(); @@ -261,8 +261,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, - &SceneDataTest::transformations2DAsArrayTRS, - &SceneDataTest::transformations2DAsArrayTRS, + &SceneDataTest::transformations2DAsArrayTRS, + &SceneDataTest::transformations2DAsArrayTRS, &SceneDataTest::transformations2DAsArrayBut3DType, &SceneDataTest::transformations2DAsArrayBut3DType, &SceneDataTest::transformations2DAsArrayBut3DType, @@ -279,8 +279,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, - &SceneDataTest::transformations3DAsArrayTRS, - &SceneDataTest::transformations3DAsArrayTRS, + &SceneDataTest::transformations3DAsArrayTRS, + &SceneDataTest::transformations3DAsArrayTRS, &SceneDataTest::transformations3DAsArrayBut2DType, &SceneDataTest::transformations3DAsArrayBut2DType, &SceneDataTest::transformations3DAsArrayBut2DType, @@ -2097,30 +2097,30 @@ template void SceneDataTest::transformations2DAsArray() { }), TestSuite::Compare::Container); } -template void SceneDataTest::transformations2DAsArrayTRS() { - setTestCaseTemplateName(NameTraits::name()); +template void SceneDataTest::transformations2DAsArrayTRS() { + setTestCaseTemplateName({NameTraits::name(), NameTraits::name(), NameTraits::name()}); struct Field { UnsignedInt object; Math::Vector2 translation; - Math::Complex rotation; - Math::Vector2 scaling; + Math::Complex rotation; + Math::Vector2 scaling; } fields[]{ {1, {T(3.0), T(2.0)}, {}, - {T(1.0), T(1.0)}}, + {V(1.0), V(1.0)}}, {0, {}, - Math::Complex::rotation(Math::Deg{T(35.0)}), - {T(1.0), T(1.0)}}, + Math::Complex::rotation(Math::Deg{U(35.0)}), + {V(1.0), V(1.0)}}, {2, {}, /* Identity transformation here */ {}, - {T(1.0), T(1.0)}}, + {V(1.0), V(1.0)}}, {4, {}, {}, - {T(2.0), T(1.0)}}, + {V(2.0), V(1.0)}}, {7, {T(1.5), T(2.5)}, - Math::Complex::rotation(Math::Deg{T(-15.0)}), - {T(-0.5), T(4.0)}}, + Math::Complex::rotation(Math::Deg{U(-15.0)}), + {V(-0.5), V(4.0)}}, }; Containers::StridedArrayView1D view = fields; @@ -2470,30 +2470,30 @@ template void SceneDataTest::transformations3DAsArray() { }), TestSuite::Compare::Container); } -template void SceneDataTest::transformations3DAsArrayTRS() { - setTestCaseTemplateName(NameTraits::name()); +template void SceneDataTest::transformations3DAsArrayTRS() { + setTestCaseTemplateName({NameTraits::name(), NameTraits::name(), NameTraits::name()}); struct Field { UnsignedInt object; Math::Vector3 translation; - Math::Quaternion rotation; - Math::Vector3 scaling; + Math::Quaternion rotation; + Math::Vector3 scaling; } fields[]{ {1, {T(3.0), T(2.0), T(1.0)}, {}, - {T(1.0), T(1.0), T(1.0)}}, + {V(1.0), V(1.0), V(1.0)}}, {0, {}, - Math::Quaternion::rotation(Math::Deg{T(35.0)}, Math::Vector3::yAxis()), - {T(1.0), T(1.0), T(1.0)}}, + Math::Quaternion::rotation(Math::Deg{U(35.0)}, Math::Vector3::yAxis()), + {V(1.0), V(1.0), V(1.0)}}, {2, {}, /* Identity transformation here */ {}, - {T(1.0), T(1.0), T(1.0)}}, + {V(1.0), V(1.0), V(1.0)}}, {4, {}, {}, - {T(2.0), T(1.0), T(0.0)}}, + {V(2.0), V(1.0), V(0.0)}}, {7, {T(1.5), T(2.5), T(3.5)}, - Math::Quaternion::rotation(Math::Deg{T(-15.0)}, Math::Vector3::xAxis()), - {T(-0.5), T(4.0), T(-16.0)}}, + Math::Quaternion::rotation(Math::Deg{U(-15.0)}, Math::Vector3::xAxis()), + {V(-0.5), V(4.0), V(-16.0)}}, }; Containers::StridedArrayView1D view = fields; From be36c8a0715eb6dd5a02174412ce218110b58c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 18 Sep 2021 11:57:38 +0200 Subject: [PATCH 11/49] Trade: convenience access to separate TRS components in SceneData. Because just the combined transformation may not be enough for handling animations. For example. --- src/Magnum/Trade/SceneData.cpp | 276 ++++++++++++++- src/Magnum/Trade/SceneData.h | 126 ++++++- src/Magnum/Trade/Test/SceneDataTest.cpp | 449 +++++++++++++++++++++++- 3 files changed, 821 insertions(+), 30 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 03965be464..09d0147489 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -26,8 +26,10 @@ #include "SceneData.h" #include +#include #include +#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" #include "Magnum/Math/DualComplex.h" @@ -863,6 +865,26 @@ std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, U ~std::size_t{} : _fields[fieldToCheckForSize]._size; } +std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { + UnsignedInt fieldToCheckForSize = ~UnsignedInt{}; + translationFieldId = ~UnsignedInt{}; + rotationFieldId = ~UnsignedInt{}; + scalingFieldId = ~UnsignedInt{}; + for(std::size_t i = 0; i != _fields.size(); ++i) { + if(_fields[i]._name == SceneField::Translation) { + fieldToCheckForSize = translationFieldId = i; + } else if(_fields[i]._name == SceneField::Rotation) { + fieldToCheckForSize = rotationFieldId = i; + } else if(_fields[i]._name == SceneField::Scaling) { + fieldToCheckForSize = scalingFieldId = i; + } + } + + /* Assuming the caller fires an appropriate assertion */ + return fieldToCheckForSize == ~UnsignedInt{} ? + ~std::size_t{} : _fields[fieldToCheckForSize]._size; +} + void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const { /* *FieldId, offset and destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ @@ -960,10 +982,7 @@ void SceneData::transformations2DInto(const Containers::StridedArrayView1D& destination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(expectedSize != ~std::size_t{}, "Trade::SceneData::transformations2DInto(): no transformation-related field found", {}); CORRADE_ASSERT(offset <= expectedSize, @@ -985,6 +1004,128 @@ Containers::Array SceneData::transformations2DAsArray() const { return out; } +void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { + /* *FieldId, offset and *Destination.size() is assumed to be in bounds (or + an invalid field ID), checked by the callers */ + + /* Retrieve translation, if desired. If no field is present, output a zero + vector for all objects. */ + if(translationDestination) { + if(translationFieldId == ~UnsignedInt{}) { + constexpr Vector2 identity[]{Vector2{0.0f}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(translationDestination.size()), translationDestination); + } else { + const SceneFieldData& field = _fields[translationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size()); + + if(field._fieldType == SceneFieldType::Vector2) { + Utility::copy(Containers::arrayCast(fieldData), translationDestination); + } else if(field._fieldType == SceneFieldType::Vector2d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(translationDestination)); + } else if(field._fieldType == SceneFieldType::Vector3 || + field._fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D translation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } + + /* Retrieve rotation, if desired. If no field is present, output an + identity rotation for all objects. */ + if(rotationDestination) { + if(rotationFieldId == ~UnsignedInt{}) { + constexpr Complex identity[]{Complex{Math::IdentityInit}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(rotationDestination.size()), rotationDestination); + } else { + const SceneFieldData& field = _fields[rotationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size()); + + if(field._fieldType == SceneFieldType::Complex) { + Utility::copy(Containers::arrayCast(fieldData), rotationDestination); + } else if(field._fieldType == SceneFieldType::Complexd) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(rotationDestination)); + } else if(field._fieldType == SceneFieldType::Quaternion || + field._fieldType == SceneFieldType::Quaterniond) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D rotation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } + + /* Retrieve scaling, if desired. If no field is present, output an identity + scaling for all objects. */ + if(scalingDestination) { + if(scalingFieldId == ~UnsignedInt{}) { + constexpr Vector2 identity[]{Vector2{1.0f}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(scalingDestination.size()), scalingDestination); + } else { + const SceneFieldData& field = _fields[scalingFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size()); + + if(field._fieldType == SceneFieldType::Vector2) { + Utility::copy(Containers::arrayCast(fieldData), scalingDestination); + } else if(field._fieldType == SceneFieldType::Vector2d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(scalingDestination)); + } else if(field._fieldType == SceneFieldType::Vector3 || + field._fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D scaling type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } +} + +void SceneData::translationsRotationsScalings2DInto(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{}, + "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(), ); + 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 { + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + "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, {}); + 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); + translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, + translationDestination ? translationDestination.prefix(size) : nullptr, + rotationDestination ? rotationDestination.prefix(size) : nullptr, + scalingDestination ? scalingDestination.prefix(size) : nullptr); + return size; +} + +Containers::Array> SceneData::translationsRotationsScalings2DAsArray() const { + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + /* 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}; + /** @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)}; + translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut); + return out; +} + void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { /* *FieldId, offset and destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ @@ -1082,10 +1223,7 @@ void SceneData::transformations3DInto(const Containers::StridedArrayView1D& destination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(expectedSize != ~std::size_t{}, "Trade::SceneData::transformations3DInto(): no transformation-related field found", {}); CORRADE_ASSERT(offset <= expectedSize, @@ -1107,6 +1245,128 @@ Containers::Array SceneData::transformations3DAsArray() const { return out; } +void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { + /* *FieldId, offset and *Destination.size() is assumed to be in bounds (or + an invalid field ID), checked by the callers */ + + /* Retrieve translation, if desired. If no field is present, output a zero + vector for all objects. */ + if(translationDestination) { + if(translationFieldId == ~UnsignedInt{}) { + constexpr Vector3 identity[]{Vector3{0.0f}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(translationDestination.size()), translationDestination); + } else { + const SceneFieldData& field = _fields[translationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size()); + + if(field._fieldType == SceneFieldType::Vector3) { + Utility::copy(Containers::arrayCast(fieldData), translationDestination); + } else if(field._fieldType == SceneFieldType::Vector3d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(translationDestination)); + } else if(field._fieldType == SceneFieldType::Vector2 || + field._fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D translation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } + + /* Retrieve rotation, if desired. If no field is present, output an + identity rotation for all objects. */ + if(rotationDestination) { + if(rotationFieldId == ~UnsignedInt{}) { + constexpr Quaternion identity[]{Quaternion{Math::IdentityInit}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(rotationDestination.size()), rotationDestination); + } else { + const SceneFieldData& field = _fields[rotationFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size()); + + if(field._fieldType == SceneFieldType::Quaternion) { + Utility::copy(Containers::arrayCast(fieldData), rotationDestination); + } else if(field._fieldType == SceneFieldType::Quaterniond) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 4), Containers::arrayCast<2, Float>(rotationDestination)); + } else if(field._fieldType == SceneFieldType::Complex || + field._fieldType == SceneFieldType::Complexd) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D rotation type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } + + /* Retrieve scaling, if desired. If no field is present, output an identity + scaling for all objects. */ + if(scalingDestination) { + if(scalingFieldId == ~UnsignedInt{}) { + constexpr Vector3 identity[]{Vector3{1.0f}}; + Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(scalingDestination.size()), scalingDestination); + } else { + const SceneFieldData& field = _fields[scalingFieldId]; + const Containers::StridedArrayView1D fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size()); + + if(field._fieldType == SceneFieldType::Vector3) { + Utility::copy(Containers::arrayCast(fieldData), scalingDestination); + } else if(field._fieldType == SceneFieldType::Vector3d) { + Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(scalingDestination)); + } else if(field._fieldType == SceneFieldType::Vector2 || + field._fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D scaling type" << field._fieldType, ); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + } +} + +void SceneData::translationsRotationsScalings3DInto(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{}, + "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(), ); + 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 { + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + "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, {}); + 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); + translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, + translationDestination ? translationDestination.prefix(size) : nullptr, + rotationDestination ? rotationDestination.prefix(size) : nullptr, + scalingDestination ? scalingDestination.prefix(size) : nullptr); + return size; +} + +Containers::Array> SceneData::translationsRotationsScalings3DAsArray() const { + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(expectedSize != ~std::size_t{}, + /* 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}; + /** @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)}; + translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut); + return out; +} + void SceneData::unsignedIndexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { /* fieldId, offset and destination.size() is assumed to be in bounds, checked by the callers */ diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 89bf35590f..356a31c674 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -148,7 +148,9 @@ enum class SceneField: UnsignedInt { * or be provided just for a subset of it --- see its documentation for * details. * @see @ref SceneData::transformations2DAsArray(), - * @ref SceneData::transformations3DAsArray() + * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::translationsRotationsScalings2DAsArray(), + * @ref SceneData::translationsRotationsScalings3DAsArray() */ Translation, @@ -167,7 +169,9 @@ enum class SceneField: UnsignedInt { * or be provided just for a subset of it --- see its documentation for * details. * @see @ref SceneData::transformations2DAsArray(), - * @ref SceneData::transformations3DAsArray() + * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::translationsRotationsScalings2DAsArray(), + * @ref SceneData::translationsRotationsScalings3DAsArray() */ Rotation, @@ -186,7 +190,9 @@ enum class SceneField: UnsignedInt { * or be provided just for a subset of it --- see its documentation for * details. * @see @ref SceneData::transformations2DAsArray(), - * @ref SceneData::transformations3DAsArray() + * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::translationsRotationsScalings2DAsArray(), + * @ref SceneData::translationsRotationsScalings3DAsArray() */ Scaling, @@ -1116,6 +1122,8 @@ 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 translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common * fields converted to usual types, but note that these operations @@ -1192,11 +1200,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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(), @ref transformations2DAsArray(), - * @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. + * non-templated @ref parentsAsArray(), + * @ref transformations2DAsArray(), @ref transformations3DAsArray(), + * @ref translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common + * fields converted to usual types, but note that these operations + * involve extra allocation and data conversion. * @see @ref field(UnsignedInt) const, @ref mutableField(SceneField) */ template::value>::type> Containers::StridedArrayView1D field(SceneField name) const; @@ -1402,6 +1413,54 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) 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 + * @ref translationsRotationsScalings3DAsArray(). If the + * @ref SceneField::Translation field isn't present, the first returned + * value is a zero vector. If the @relativeref{SceneField,Rotation} + * field isn't present, the second value is an identity rotation. If + * the @relativeref{SceneField,Scaling} field isn't present, the third + * value is an identity scaling (@cpp 1.0f @ce in both dimensions). + * @see @ref translationsRotationsScalings2DInto(), @ref hasField(), + * @ref fieldType(SceneField) 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. + * @see @ref fieldSize(SceneField) const + */ + void translationsRotationsScalings2DInto(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 + * 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 + * that are not @cpp nullptr @ce are expected to have the same size. + * @see @ref fieldSize(SceneField) const + */ + std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + /** * @brief 3D transformations as 4x4 float matrices * @m_since_latest @@ -1445,6 +1504,54 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t transformations3DInto(std::size_t offset, 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 + * @ref translationsRotationsScalings2DAsArray(). If the + * @ref SceneField::Translation field isn't present, the first returned + * value is a zero vector. If the @relativeref{SceneField,Rotation} + * field isn't present, the second value is an identity rotation. If + * the @relativeref{SceneField,Scaling} field isn't present, the third + * value is an identity scaling (@cpp 1.0f @ce in all dimensions). + * @see @ref translationsRotationsScalings3DInto(), @ref hasField(), + * @ref fieldType(SceneField) 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. + * @see @ref fieldSize(SceneField) const + */ + void translationsRotationsScalings3DInto(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 + * 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 + * that are not @cpp nullptr @ce are expected to have the same size. + * @see @ref fieldSize(SceneField) const + */ + std::size_t translationsRotationsScalings3DInto(std::size_t offset, 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 @@ -1654,8 +1761,11 @@ 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) const; + MAGNUM_TRADE_LOCAL std::size_t 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; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index d3cf758b42..b09ba66e8d 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,7 @@ struct SceneDataTest: TestSuite::Tester { void transformations2DIntoArray(); void transformations2DIntoArrayTRS(); void transformations2DIntoArrayInvalidSizeOrOffset(); + void transformations2DIntoArrayInvalidSizeOrOffsetTRS(); template void transformations3DAsArray(); template void transformations3DAsArrayTRS(); template void transformations3DAsArrayBut2DType(); @@ -125,6 +127,7 @@ struct SceneDataTest: TestSuite::Tester { void transformations3DIntoArray(); void transformations3DIntoArrayTRS(); void transformations3DIntoArrayInvalidSizeOrOffset(); + void transformations3DIntoArrayInvalidSizeOrOffsetTRS(); template void meshesMaterialsAsArray(); void meshesMaterialsIntoArray(); void meshesMaterialsIntoArrayInvalidSizeOrOffset(); @@ -275,6 +278,7 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset, + &SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, @@ -293,6 +297,7 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset, + &SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS, &SceneDataTest::meshesMaterialsAsArray, &SceneDataTest::meshesMaterialsAsArray, &SceneDataTest::meshesMaterialsAsArray}); @@ -2147,6 +2152,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation @@ -2158,6 +2170,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { scaling @@ -2169,6 +2188,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } /* Pairs */ @@ -2184,6 +2210,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { translation, @@ -2196,6 +2229,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation, @@ -2208,6 +2248,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } /* All */ @@ -2224,6 +2271,13 @@ template void SceneDataTest::transformations2DAsArray 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}}, + })), TestSuite::Compare::Container); } } @@ -2252,25 +2306,43 @@ template void SceneDataTest::transformations2DAsArrayBut3DTypeTRS() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData translation{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + /* Because TRSAsArray() allocates an Array and then calls + TRSInto(), which skips views that are nullptr, we wouldn't get the + assertion for translations, as those are at offset 0, which would be + interpreted as an empty view if there were no elements. So we have to + supply at least one element to make all assertions trigger. */ + struct Field { + UnsignedInt object; + Math::Vector3 translation; + Math::Quaternion rotation; + Math::Vector3 scaling; + } data[1]; + Containers::StridedArrayView1D view = data; + SceneData translation{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} }}; - SceneData rotation{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + SceneData rotation{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)} }}; - SceneData scaling{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + SceneData scaling{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)} }}; std::ostringstream out; Error redirectError{&out}; translation.transformations2DAsArray(); + translation.translationsRotationsScalings2DAsArray(); rotation.transformations2DAsArray(); + rotation.translationsRotationsScalings2DAsArray(); scaling.transformations2DAsArray(); + scaling.translationsRotationsScalings2DAsArray(); CORRADE_COMPARE(out.str(), Utility::formatString( "Trade::SceneData::transformations2DInto(): field has a 3D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D translation type Trade::SceneFieldType::{0}\n" "Trade::SceneData::transformations2DInto(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformations2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformations2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); } void SceneDataTest::transformations2DIntoArray() { @@ -2368,6 +2440,50 @@ void SceneDataTest::transformations2DIntoArrayTRS() { Containers::arrayView(expected), TestSuite::Compare::Container); + /* Variant with TRS components */ + } { + 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), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), + view.slice(&Field::scaling), + TestSuite::Compare::Container); + + /* Variant with just translations */ + } { + Vector2 translationsOut[3]; + scene.translationsRotationsScalings2DInto(translationsOut, nullptr, nullptr); + CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), + 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), + 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), + 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}; @@ -2375,6 +2491,56 @@ void SceneDataTest::transformations2DIntoArrayTRS() { 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) + .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), + 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), + 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), + 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); } } @@ -2404,6 +2570,47 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::transformations2DInto(): offset 4 out of bounds for a field of size 3\n"); } +void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + Vector2 translation; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + 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); + CORRADE_COMPARE(out.str(), + "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(): 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"); +} + template void SceneDataTest::transformations3DAsArray() { setTestCaseTemplateName(NameTraits::name()); @@ -2520,6 +2727,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation @@ -2531,6 +2745,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { scaling @@ -2542,6 +2763,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } /* Pairs */ @@ -2557,6 +2785,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { translation, @@ -2569,6 +2804,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation, @@ -2581,6 +2823,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } /* All */ @@ -2597,6 +2846,13 @@ template void SceneDataTest::transformations3DAsArray 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}}, + })), TestSuite::Compare::Container); } } @@ -2625,25 +2881,43 @@ template void SceneDataTest::transformations3DAsArrayBut2DTypeTRS() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData translation{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + /* Because TRSAsArray() allocates an Array and then calls + TRSInto(), which skips views that are nullptr, we wouldn't get the + assertion for translations, as those are at offset 0, which would be + interpreted as an empty view if there were no elements. So we have to + supply at least one element to make all assertions trigger. */ + struct Field { + UnsignedInt object; + Math::Vector2 translation; + Math::Complex rotation; + Math::Vector2 scaling; + } data[1]; + Containers::StridedArrayView1D view = data; + SceneData translation{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} }}; - SceneData rotation{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + SceneData rotation{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)} }}; - SceneData scaling{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + SceneData scaling{SceneObjectType::UnsignedInt, 1, {}, data, { + SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)} }}; std::ostringstream out; Error redirectError{&out}; translation.transformations3DAsArray(); + translation.translationsRotationsScalings3DAsArray(); rotation.transformations3DAsArray(); + rotation.translationsRotationsScalings3DAsArray(); scaling.transformations3DAsArray(); + scaling.translationsRotationsScalings3DAsArray(); CORRADE_COMPARE(out.str(), Utility::formatString( "Trade::SceneData::transformations3DInto(): field has a 2D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D translation type Trade::SceneFieldType::{0}\n" "Trade::SceneData::transformations3DInto(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformations3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformations3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); } void SceneDataTest::transformations3DIntoArray() { @@ -2741,6 +3015,50 @@ void SceneDataTest::transformations3DIntoArrayTRS() { Containers::arrayView(expected), TestSuite::Compare::Container); + /* Variant with TRS components */ + } { + 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), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), + view.slice(&Field::scaling), + TestSuite::Compare::Container); + + /* Variant with just translations */ + } { + Vector3 translationsOut[3]; + scene.translationsRotationsScalings3DInto(translationsOut, nullptr, nullptr); + CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), + 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), + 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), + 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}; @@ -2748,6 +3066,56 @@ void SceneDataTest::transformations3DIntoArrayTRS() { 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) + .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), + 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), + 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), + 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); } } @@ -2777,6 +3145,47 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::transformations3DInto(): offset 4 out of bounds for a field of size 3\n"); } +void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + Vector2 translation; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + 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); + CORRADE_COMPARE(out.str(), + "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(): 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"); +} + template void SceneDataTest::meshesMaterialsAsArray() { setTestCaseTemplateName({NameTraits::name(), NameTraits::name()}); @@ -3428,9 +3837,15 @@ void SceneDataTest::fieldNotFound() { scene.transformations2DAsArray(); scene.transformations2DInto(nullptr); scene.transformations2DInto(0, nullptr); + scene.translationsRotationsScalings2DAsArray(); + scene.translationsRotationsScalings2DInto(nullptr, nullptr, nullptr); + scene.translationsRotationsScalings2DInto(0, nullptr, nullptr, nullptr); scene.transformations3DAsArray(); scene.transformations3DInto(nullptr); scene.transformations3DInto(0, nullptr); + scene.translationsRotationsScalings3DAsArray(); + scene.translationsRotationsScalings3DInto(nullptr, nullptr, nullptr); + scene.translationsRotationsScalings3DInto(0, nullptr, nullptr, nullptr); scene.meshesMaterialsAsArray(); scene.meshesMaterialsInto(nullptr, nullptr); scene.meshesMaterialsInto(0, nullptr, nullptr); @@ -3476,9 +3891,15 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" "Trade::SceneData::transformations2DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): 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::transformations3DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field 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" From 140e60a6acbd867e5c882726133ad14f3ad39145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 18 Sep 2021 18:46:31 +0200 Subject: [PATCH 12/49] Trade: convenience per-object field access in SceneData. --- src/Magnum/Trade/SceneData.cpp | 374 +++++++++++++++- src/Magnum/Trade/SceneData.h | 269 +++++++++++- src/Magnum/Trade/Test/SceneDataTest.cpp | 547 ++++++++++++++++++++++++ 3 files changed, 1154 insertions(+), 36 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 09d0147489..01ae0026d7 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -25,6 +25,8 @@ #include "SceneData.h" +#include +#include #include #include #include @@ -839,8 +841,8 @@ template void applyScaling(const Containers::St } -std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { - UnsignedInt fieldToCheckForSize = ~UnsignedInt{}; +std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const { + UnsignedInt fieldWithObjectMapping = ~UnsignedInt{}; transformationFieldId = ~UnsignedInt{}; translationFieldId = ~UnsignedInt{}; rotationFieldId = ~UnsignedInt{}; @@ -849,40 +851,46 @@ std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, U /* If we find a transformation field, we don't need to look any further */ if(_fields[i]._name == SceneField::Transformation) { - fieldToCheckForSize = transformationFieldId = i; + fieldWithObjectMapping = transformationFieldId = i; break; } else if(_fields[i]._name == SceneField::Translation) { - fieldToCheckForSize = translationFieldId = i; + fieldWithObjectMapping = translationFieldId = i; } else if(_fields[i]._name == SceneField::Rotation) { - fieldToCheckForSize = rotationFieldId = i; + fieldWithObjectMapping = rotationFieldId = i; } else if(_fields[i]._name == SceneField::Scaling) { - fieldToCheckForSize = scalingFieldId = i; + fieldWithObjectMapping = scalingFieldId = i; } } + if(fieldWithObjectMappingDestination) + *fieldWithObjectMappingDestination = fieldWithObjectMapping; + /* Assuming the caller fires an appropriate assertion */ - return fieldToCheckForSize == ~UnsignedInt{} ? - ~std::size_t{} : _fields[fieldToCheckForSize]._size; + return fieldWithObjectMapping == ~UnsignedInt{} ? + ~std::size_t{} : _fields[fieldWithObjectMapping]._size; } -std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { - UnsignedInt fieldToCheckForSize = ~UnsignedInt{}; +std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const { + UnsignedInt fieldWithObjectMapping = ~UnsignedInt{}; translationFieldId = ~UnsignedInt{}; rotationFieldId = ~UnsignedInt{}; scalingFieldId = ~UnsignedInt{}; for(std::size_t i = 0; i != _fields.size(); ++i) { if(_fields[i]._name == SceneField::Translation) { - fieldToCheckForSize = translationFieldId = i; + fieldWithObjectMapping = translationFieldId = i; } else if(_fields[i]._name == SceneField::Rotation) { - fieldToCheckForSize = rotationFieldId = i; + fieldWithObjectMapping = rotationFieldId = i; } else if(_fields[i]._name == SceneField::Scaling) { - fieldToCheckForSize = scalingFieldId = i; + fieldWithObjectMapping = scalingFieldId = i; } } + if(fieldWithObjectMappingDestination) + *fieldWithObjectMappingDestination = fieldWithObjectMapping; + /* Assuming the caller fires an appropriate assertion */ - return fieldToCheckForSize == ~UnsignedInt{} ? - ~std::size_t{} : _fields[fieldToCheckForSize]._size; + return fieldWithObjectMapping == ~UnsignedInt{} ? + ~std::size_t{} : _fields[fieldWithObjectMapping]._size; } void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1553,6 +1561,342 @@ Containers::Array SceneData::skinsAsArray() const { return unsignedIndexFieldAsArrayInternal(fieldId); } +namespace { + +template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { + const Containers::StridedArrayView1D objectsT = Containers::arrayCast(objects); + const std::size_t max = objectsT.size(); + /** @todo implement something faster than O(n) when field-specific flags + can annotate how the object mapping is done */ + for(std::size_t i = 0; i != max; ++i) + if(objectsT[i] == object) return i; + return max; +} + +} + +std::size_t SceneData::fieldFor(const SceneFieldData& field, const std::size_t offset, const UnsignedInt object) const { + const Containers::StridedArrayView1D objects = fieldDataObjectViewInternal(field, offset, field._size - offset); + if(field._objectType == SceneObjectType::UnsignedInt) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedShort) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedByte) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedLong) + return offset + findObject(objects, object); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Containers::Array SceneData::childrenFor(Int object) const { + CORRADE_ASSERT(object >= -1 && object < Long(_objectCount), + "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt parentFieldId = fieldFor(SceneField::Parent); + if(parentFieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& parentField = _fields[parentFieldId]; + + /* Figure out the parent object index to look for or -1 if we want + top-level objects */ + Int parentIndexToLookFor; + if(object == -1) parentIndexToLookFor = -1; + else { + const std::size_t parentObjectIndex = fieldFor(parentField, 0, object); + if(parentObjectIndex == parentField._size) return {}; + parentIndexToLookFor = parentObjectIndex; + } + + /* Collect IDs of all objects that reference this index */ + Containers::Array out; + for(std::size_t offset = 0; offset != parentField.size(); ++offset) { + Int parentIndex[1]; + parentsIntoInternal(parentFieldId, offset, parentIndex); + if(*parentIndex == parentIndexToLookFor) { + UnsignedInt child[1]; + /** @todo bleh slow, use the children <-> parent field proxying + when implemented */ + objectsIntoInternal(parentFieldId, offset, child); + arrayAppend(out, *child); + } + } + + return out; +} + +Containers::Optional SceneData::transformation2DFor(const UnsignedInt object) const { + 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 {}; + + #ifndef CORRADE_NO_ASSERT + if(transformationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[transformationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Matrix3x3 || + type == SceneFieldType::Matrix3x3d || + type == SceneFieldType::DualComplex || + type == SceneFieldType::DualComplexd, + "Trade::SceneData::transformation2DFor(): field has a 3D transformation type" << type, {}); + } else { + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[translationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector2 || + type == SceneFieldType::Vector2d, + "Trade::SceneData::transformation2DFor(): field has a 3D translation type" << type, {}); + } + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[rotationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Complex || + type == SceneFieldType::Complexd, + "Trade::SceneData::transformation2DFor(): field has a 3D rotation type" << type, {}); + } + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[scalingFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector2 || + type == SceneFieldType::Vector2d, + "Trade::SceneData::transformation2DFor(): field has a 3D scaling type" << type, {}); + } + } + #endif + + const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); + if(offset == _fields[fieldWithObjectMapping]._size) return {}; + + Matrix3 transformation[1]; + transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); + return *transformation; +} + +Containers::Optional> SceneData::translationRotationScaling2DFor(const UnsignedInt object) const { + 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 {}; + + #ifndef CORRADE_NO_ASSERT + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[translationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector2 || + type == SceneFieldType::Vector2d, + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type" << type, {}); + } + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[rotationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Complex || + type == SceneFieldType::Complexd, + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type" << type, {}); + } + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[scalingFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector2 || + type == SceneFieldType::Vector2d, + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type" << type, {}); + } + #endif + + const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); + if(offset == _fields[fieldWithObjectMapping]._size) return {}; + + Vector2 translation[1]; + Complex rotation[1]; + Vector2 scaling[1]; + translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translation, rotation, scaling); + return {InPlaceInit, *translation, *rotation, *scaling}; +} + +Containers::Optional SceneData::transformation3DFor(const UnsignedInt object) const { + 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 {}; + + #ifndef CORRADE_NO_ASSERT + if(transformationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[transformationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Matrix4x4 || + type == SceneFieldType::Matrix4x4d || + type == SceneFieldType::DualQuaternion || + type == SceneFieldType::DualQuaterniond, + "Trade::SceneData::transformation3DFor(): field has a 2D transformation type" << type, {}); + } else { + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[translationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector3 || + type == SceneFieldType::Vector3d, + "Trade::SceneData::transformation3DFor(): field has a 2D translation type" << type, {}); + } + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[rotationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Quaternion || + type == SceneFieldType::Quaterniond, + "Trade::SceneData::transformation3DFor(): field has a 2D rotation type" << type, {}); + } + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[scalingFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector3 || + type == SceneFieldType::Vector3d, + "Trade::SceneData::transformation3DFor(): field has a 2D scaling type" << type, {}); + } + } + #endif + + const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); + if(offset == _fields[fieldWithObjectMapping]._size) return {}; + + Matrix4 transformation[1]; + transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); + return *transformation; +} + +Containers::Optional> SceneData::translationRotationScaling3DFor(const UnsignedInt object) const { + 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 {}; + + #ifndef CORRADE_NO_ASSERT + if(translationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[translationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector3 || + type == SceneFieldType::Vector3d, + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type" << type, {}); + } + if(rotationFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[rotationFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Quaternion || + type == SceneFieldType::Quaterniond, + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type" << type, {}); + } + if(scalingFieldId != ~UnsignedInt{}) { + const SceneFieldType type = _fields[scalingFieldId]._fieldType; + CORRADE_ASSERT( + type == SceneFieldType::Vector3 || + type == SceneFieldType::Vector3d, + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type" << type, {}); + } + #endif + + const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); + if(offset == _fields[fieldWithObjectMapping]._size) return {}; + + Vector3 translation[1]; + Quaternion rotation[1]; + Vector3 scaling[1]; + translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translation, rotation, scaling); + return {InPlaceInit, *translation, *rotation, *scaling}; +} + +Containers::Array> SceneData::meshesMaterialsFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt meshFieldId = fieldFor(SceneField::Mesh); + if(meshFieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[meshFieldId]; + Containers::Array> out; + std::size_t offset = 0; + for(;;) { + offset = fieldFor(field, offset, object); + if(offset == field._size) break; + + UnsignedInt mesh[1]; + Int material[1]; + meshesMaterialsIntoInternal(meshFieldId, offset, mesh, material); + arrayAppend(out, InPlaceInit, *mesh, *material); + ++offset; + } + + return out; +} + +Containers::Array SceneData::lightsFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::Light); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + Containers::Array out; + std::size_t offset = 0; + for(;;) { + offset = fieldFor(field, offset, object); + if(offset == field._size) break; + + UnsignedInt index[1]; + unsignedIndexFieldIntoInternal(fieldId, offset, index); + arrayAppend(out, InPlaceInit, *index); + ++offset; + } + + return out; +} + +Containers::Array SceneData::camerasFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::Camera); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + Containers::Array out; + std::size_t offset = 0; + for(;;) { + offset = fieldFor(field, offset, object); + if(offset == field._size) break; + + UnsignedInt index[1]; + unsignedIndexFieldIntoInternal(fieldId, offset, index); + arrayAppend(out, InPlaceInit, *index); + ++offset; + } + + return out; +} + +Containers::Array SceneData::skinsFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::Skin); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + Containers::Array out; + std::size_t offset = 0; + for(;;) { + offset = fieldFor(field, offset, object); + if(offset == field._size) break; + + UnsignedInt index[1]; + unsignedIndexFieldIntoInternal(fieldId, offset, index); + arrayAppend(out, InPlaceInit, *index); + ++offset; + } + + return out; +} + Containers::Array SceneData::releaseFieldData() { Containers::Array out = std::move(_fields); _fields = {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 356a31c674..f462955182 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -102,7 +102,7 @@ enum class SceneField: UnsignedInt { * Note that the index points to the parent array itself and isn't the * actual object index --- the object mapping is stored in the * corresponding @ref SceneData::objects() array. - * @see @ref SceneData::parentsAsArray() + * @see @ref SceneData::parentsAsArray(), @ref SceneData::childrenFor() */ Parent = 1, @@ -129,7 +129,9 @@ enum class SceneField: UnsignedInt { * only for a subset of transformed objects, useful for example when only * certain objects have these properties animated. * @see @ref SceneData::transformations2DAsArray(), - * @ref SceneData::transformations3DAsArray() + * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::transformation2DFor(), + * @ref SceneData::transformation3DFor() */ Transformation, @@ -149,8 +151,12 @@ enum class SceneField: UnsignedInt { * details. * @see @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::transformation2DFor(), + * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), - * @ref SceneData::translationsRotationsScalings3DAsArray() + * @ref SceneData::translationsRotationsScalings3DAsArray(), + * @ref SceneData::translationRotationScaling2DFor(), + * @ref SceneData::translationRotationScaling3DFor() */ Translation, @@ -170,8 +176,12 @@ enum class SceneField: UnsignedInt { * details. * @see @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::transformation2DFor(), + * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), - * @ref SceneData::translationsRotationsScalings3DAsArray() + * @ref SceneData::translationsRotationsScalings3DAsArray(), + * @ref SceneData::translationRotationScaling2DFor(), + * @ref SceneData::translationRotationScaling3DFor() */ Rotation, @@ -191,8 +201,12 @@ enum class SceneField: UnsignedInt { * details. * @see @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), + * @ref SceneData::transformation2DFor(), + * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), - * @ref SceneData::translationsRotationsScalings3DAsArray() + * @ref SceneData::translationsRotationsScalings3DAsArray(), + * @ref SceneData::translationRotationScaling2DFor(), + * @ref SceneData::translationRotationScaling3DFor() */ Scaling, @@ -208,7 +222,8 @@ 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::meshesMaterialsAsArray() + * @see @ref SceneData::meshesMaterialsAsArray(), + * @ref SceneData::meshesMaterialsFor() */ Mesh, @@ -219,7 +234,8 @@ 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::meshesMaterialsAsArray() + * @see @ref SceneData::meshesMaterialsAsArray(), + * @ref SceneData::meshesMaterialsFor() */ MeshMaterial, @@ -230,7 +246,7 @@ enum class SceneField: UnsignedInt { * @relativeref{SceneFieldType,UnsignedByte} or * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple * lights associated. - * @see @ref SceneData::lightsAsArray() + * @see @ref SceneData::lightsAsArray(), @ref SceneData::lightsFor() */ Light, @@ -241,7 +257,7 @@ enum class SceneField: UnsignedInt { * @relativeref{SceneFieldType,UnsignedByte} or * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple * cameras associated. - * @see @ref SceneData::camerasAsArray() + * @see @ref SceneData::camerasAsArray(), @ref SceneData::camerasFor() */ Camera, @@ -252,7 +268,7 @@ enum class SceneField: UnsignedInt { * @relativeref{SceneFieldType,UnsignedByte} or * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple * skins associated. - * @see @ref SceneData::skinsAsArray() + * @see @ref SceneData::skinsAsArray(), @ref SceneData::skinsFor() */ Skin, @@ -1342,7 +1358,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * is larger than the max representable 32-bit value, this * function can't be used, only an appropriately typed * @ref field(SceneField) const. - * @see @ref parentsInto(), @ref hasField() + * @see @ref parentsInto(), @ref hasField(), @ref childrenFor() */ Containers::Array parentsAsArray() const; @@ -1385,7 +1401,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * to 2D, otherwise you're supposed to use * @ref transformations3DAsArray(). * @see @ref transformations2DInto(), @ref hasField(), - * @ref fieldType(SceneField) const + * @ref fieldType(SceneField) const, @ref transformation2DFor() */ Containers::Array transformations2DAsArray() const; @@ -1431,7 +1447,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * the @relativeref{SceneField,Scaling} field isn't present, the third * value is an identity scaling (@cpp 1.0f @ce in both dimensions). * @see @ref translationsRotationsScalings2DInto(), @ref hasField(), - * @ref fieldType(SceneField) const + * @ref fieldType(SceneField) const, + * @ref translationRotationScaling2DFor() */ Containers::Array> translationsRotationsScalings2DAsArray() const; @@ -1476,7 +1493,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * to 3D, otherwise you're supposed to use * @ref transformations2DAsArray(). * @see @ref transformations3DInto(), @ref hasField(), - * @ref fieldType(SceneField) const + * @ref fieldType(SceneField) const, @ref transformation3DFor() */ Containers::Array transformations3DAsArray() const; @@ -1522,7 +1539,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * the @relativeref{SceneField,Scaling} field isn't present, the third * value is an identity scaling (@cpp 1.0f @ce in all dimensions). * @see @ref translationsRotationsScalings3DInto(), @ref hasField(), - * @ref fieldType(SceneField) const + * @ref fieldType(SceneField) const, + * @ref translationRotationScaling3DFor() */ Containers::Array> translationsRotationsScalings3DAsArray() const; @@ -1563,7 +1581,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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() + * @see @ref meshesMaterialsInto(), @ref hasField(), + * @ref meshesMaterialsFor() */ Containers::Array> meshesMaterialsAsArray() const; @@ -1600,7 +1619,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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. - * @see @ref lightsInto(), @ref hasField() + * @see @ref lightsInto(), @ref hasField(), @ref lightsFor() */ Containers::Array lightsAsArray() const; @@ -1636,7 +1655,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref SceneField::Camera 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 camerasInto(), @ref hasField() + * @see @ref camerasInto(), @ref hasField(), @ref camerasFor() */ Containers::Array camerasAsArray() const; @@ -1672,7 +1691,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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. - * @see @ref skinsInto(), @ref hasField() + * @see @ref skinsInto(), @ref hasField(), @ref skinsFor() */ Containers::Array skinsAsArray() const; @@ -1700,6 +1719,209 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** + * @brief Children for given object + * @m_since_latest + * + * Looks up @p object in the object mapping array for + * @ref SceneField::Parent and returns a list of all object IDs that + * have it listed as the parent. The lookup is done in an + * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field + * count and @f$ n @f$ the size of the parent field, thus for + * retrieving parent/child info for many objects it's recommended to + * access the field data directly with @ref parentsAsArray() and + * related APIs. + * + * If the @ref SceneField::Parent field doesn't exist or there are no + * objects which would have @p object listed as their parent, returns + * an empty array. Pass @cpp -1 @ce to get a list of top-level objects. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Array childrenFor(Int object) const; + + /** + * @brief 2D transformation for given object + * @m_since_latest + * + * Looks up the @ref SceneField::Transformation field for @p object or + * combines it from a @ref SceneField::Translation, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} in the same way as + * @ref transformations2DAsArray(). The lookup is done in an + * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field + * count and @f$ n @f$ the size of the transformation field, thus for + * retrieving transformation info for many objects it's recommended to + * access the field data directly with @ref transformations2DAsArray() + * and related APIs. + * + * If neither @ref SceneField::Transformation nor any of + * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or + * @relativeref{SceneField,Scaling} is present, the fields represent a + * 3D transformation or there's no transformation for @p object, + * returns @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + * @see @ref translationRotationScaling2DFor() + */ + Containers::Optional transformation2DFor(UnsignedInt object) const; + + /** + * @brief 2D translation, rotation and scaling for given object + * @m_since_latest + * + * Looks up the @ref SceneField::Translation, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} fields for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the transformation + * field, thus for retrieving transformation info for many objects it's + * recommended to access the field data directly with + * @ref translationsRotationsScalings2DAsArray() and related APIs. + * + * If the @ref SceneField::Translation field isn't present, the first + * returned value is a zero vector. If the + * @relativeref{SceneField,Rotation} field isn't present, the second + * value is an identity rotation. If the @relativeref{SceneField,Scaling} + * field isn't present, the third value is an identity scaling + * (@cpp 1.0f @ce in both dimensions). If neither of those fields is + * present, if any of the fields represents a 3D transformation or if + * there's no transformation for @p object, returns + * @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + * @see @ref transformation2DFor() + */ + Containers::Optional> translationRotationScaling2DFor(UnsignedInt object) const; + + /** + * @brief 3D transformation for given object + * @m_since_latest + * + * Looks up the @ref SceneField::Transformation field for @p object or + * combines it from a @ref SceneField::Translation, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} in the same way as + * @ref transformations3DAsArray(). The lookup is done in an + * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field + * count and @f$ n @f$ the size of the transformation field, thus for + * retrieving transformation info for many objects it's recommended to + * access the field data directly with @ref transformations3DAsArray() + * and related APIs. + * + * If neither @ref SceneField::Transformation nor any of + * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or + * @relativeref{SceneField,Scaling} is present, the fields represent a + * 2D transformation or there's no transformation for @p object, + * returns @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + * @see @ref translationRotationScaling3DFor() + */ + Containers::Optional transformation3DFor(UnsignedInt object) const; + + /** + * @brief 3D translation, rotation and scaling for given object + * @m_since_latest + * + * Looks up the @ref SceneField::Translation, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} fields for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the transformation + * field, thus for retrieving transformation info for many objects it's + * recommended to access the field data directly with + * @ref translationsRotationsScalings3DAsArray() and related APIs. + * + * If the @ref SceneField::Translation field isn't present, the first + * returned value is a zero vector. If the + * @relativeref{SceneField,Rotation} field isn't present, the second + * value is an identity rotation. If the @relativeref{SceneField,Scaling} + * field isn't present, the third value is an identity scaling + * (@cpp 1.0f @ce in all dimensions). If neither of those fields is + * present, if any of the fields represents a 2D transformation or if + * there's no transformation for @p object, returns + * @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + * @see @ref transformation3DFor() + */ + Containers::Optional> translationRotationScaling3DFor(UnsignedInt object) const; + + /** + * @brief Meshes and materials for given object + * @m_since_latest + * + * Looks up all @ref SceneField::Mesh and @ref SceneField::MeshMaterial + * @relativeref{SceneField,Scaling} fields for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the mesh field, thus + * for retrieving mesh info for many objects it's recommended to access + * the field data directly with @ref meshesMaterialsAsArray() and + * related APIs. + * + * If the @ref SceneField::MeshMaterial field is not present, the + * second returned value is always @cpp -1 @ce. If + * @ref SceneField::Mesh is not present or if there's no mesh for + * @p object, returns an empty array. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Array> meshesMaterialsFor(UnsignedInt object) const; + + /** + * @brief Lights for given object + * @m_since_latest + * + * Looks up all @ref SceneField::Light fields for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the light field, + * thus for retrieving light info for many objects it's recommended to + * access the field data directly with @ref lightsAsArray() and related + * APIs. + * + * If the @ref SceneField::Light field is not present or if there's no + * light for @p object, returns an empty array. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Array lightsFor(UnsignedInt object) const; + + /** + * @brief Cameras for given object + * @m_since_latest + * + * Looks up all @ref SceneField::Camera fields for @p object. The + * lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with + * @f$ m @f$ being the field count and @f$ n @f$ the size of the camera + * field, thus for retrieving camera info for many objects it's + * recommended to access the field data directly with + * @ref camerasAsArray() and related APIs. + * + * If the @ref SceneField::Camera field is not present or if there's no + * camera for @p object, returns an empty array. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Array camerasFor(UnsignedInt object) const; + + /** + * @brief Skins for given object + * @m_since_latest + * + * Looks up all @ref SceneField::Skin fields for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the skin field, thus + * for retrieving skin info for many objects it's recommended to access + * the field data directly with @ref skinsAsArray() and related APIs. + * + * If the @ref SceneField::Skin field is not present or if there's no + * skin for @p object, returns an empty array. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Array skinsFor(UnsignedInt object) const; + /** * @brief Release field data storage * @m_since_latest @@ -1746,6 +1968,11 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Internal helper that doesn't assert, unlike fieldId() */ UnsignedInt fieldFor(SceneField name) const; + /* Returns the offset at which `object` is for field at index `id`, or + the end offset if the object is not found. The returned offset can + be then passed to fieldData{Object,Field}ViewInternal(). */ + std::size_t fieldFor(const SceneFieldData& field, std::size_t offset, UnsignedInt object) const; + /* Like objects() / field(), but returning just a 1D view, sliced from offset to offset + size. The parameterless overloads are equal to offset = 0 and size = field.size(). */ @@ -1760,8 +1987,8 @@ 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) const; - MAGNUM_TRADE_LOCAL std::size_t findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) 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 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; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index b09ba66e8d..797b0d589d 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 @@ -150,6 +151,25 @@ struct SceneDataTest: TestSuite::Tester { void fieldWrongType(); void fieldWrongArrayAccess(); + /* Different object types checked just for the childrenFor(), other APIs + use the same helper */ + template void childrenFor(); + void transformation2DFor(); + void transformation2DForTRS(); + template void transformation2DForBut3DType(); + template void transformation2DForBut3DTypeTRS(); + void transformation3DFor(); + void transformation3DForTRS(); + template void transformation3DForBut2DType(); + template void transformation3DForBut2DTypeTRS(); + void meshesMaterialsFor(); + void lightsFor(); + void camerasFor(); + void skinsFor(); + + void fieldForFieldMissing(); + void fieldForInvalidObject(); + void releaseFieldData(); void releaseData(); }; @@ -340,6 +360,34 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldWrongType, &SceneDataTest::fieldWrongArrayAccess, + &SceneDataTest::childrenFor, + &SceneDataTest::childrenFor, + &SceneDataTest::childrenFor, + &SceneDataTest::childrenFor, + &SceneDataTest::transformation2DFor, + &SceneDataTest::transformation2DForTRS, + &SceneDataTest::transformation2DForBut3DType, + &SceneDataTest::transformation2DForBut3DType, + &SceneDataTest::transformation2DForBut3DType, + &SceneDataTest::transformation2DForBut3DType, + &SceneDataTest::transformation2DForBut3DTypeTRS, + &SceneDataTest::transformation2DForBut3DTypeTRS, + &SceneDataTest::transformation3DFor, + &SceneDataTest::transformation3DForTRS, + &SceneDataTest::transformation3DForBut2DType, + &SceneDataTest::transformation3DForBut2DType, + &SceneDataTest::transformation3DForBut2DType, + &SceneDataTest::transformation3DForBut2DType, + &SceneDataTest::transformation3DForBut2DTypeTRS, + &SceneDataTest::transformation3DForBut2DTypeTRS, + &SceneDataTest::meshesMaterialsFor, + &SceneDataTest::lightsFor, + &SceneDataTest::camerasFor, + &SceneDataTest::skinsFor, + + &SceneDataTest::fieldForFieldMissing, + &SceneDataTest::fieldForInvalidObject, + &SceneDataTest::releaseFieldData, &SceneDataTest::releaseData}); } @@ -3992,6 +4040,505 @@ void SceneDataTest::fieldWrongArrayAccess() { "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n"); } +template void SceneDataTest::childrenFor() { + setTestCaseTemplateName(NameTraits::name()); + + struct Field { + T object; + Int parent; + } fields[]{ + {4, -1}, + {3, 0}, + {2, 1}, + {1, 0}, + {5, 0}, + {0, -1}, + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} + }}; + + /* Just one child */ + CORRADE_COMPARE_AS(scene.childrenFor(3), + Containers::arrayView({2}), + TestSuite::Compare::Container); + + /* More */ + CORRADE_COMPARE_AS(scene.childrenFor(-1), + Containers::arrayView({4, 0}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.childrenFor(4), + Containers::arrayView({3, 1, 5}), + TestSuite::Compare::Container); + + /* Object that is present in the parent array but has no children */ + CORRADE_COMPARE_AS(scene.childrenFor(5), + Containers::arrayView({}), + TestSuite::Compare::Container); + + /* Object that is not in the parent array at all */ + CORRADE_COMPARE_AS(scene.childrenFor(6), + Containers::arrayView({}), + TestSuite::Compare::Container); +} + +void SceneDataTest::transformation2DFor() { + const struct Field { + UnsignedInt object; + Matrix3 transformation; + } fields[] { + {1, Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f})}, + {0, Matrix3::rotation(35.0_degf)}, + {4, Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf)}, + {1, Matrix3::translation({1.0f, 2.0f})} /* duplicate, ignored */ + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} + }}; + + CORRADE_COMPARE(scene.transformation2DFor(4), + Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf)); + CORRADE_COMPARE(scene.transformation2DFor(0), + Matrix3::Matrix3::Matrix3::Matrix3::rotation(35.0_degf)); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.transformation2DFor(1), + Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f})); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.transformation2DFor(2), + Containers::NullOpt); +} + +void SceneDataTest::transformation2DForTRS() { + const 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}}, + {1, {1.0f, 2.0f}, {}, {1.0f, 1.0f}} /* duplicate, ignored */ + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + 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)} + }}; + + CORRADE_COMPARE(scene.transformation2DFor(4), + Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf)); + CORRADE_COMPARE(scene.translationRotationScaling2DFor(4), + Containers::triple(Vector2{3.0f, 2.0f}, Complex::rotation(35.0_degf), Vector2{1.0f})); + CORRADE_COMPARE(scene.transformation2DFor(0), + Matrix3::rotation(35.0_degf)); + CORRADE_COMPARE(scene.translationRotationScaling2DFor(0), + Containers::triple(Vector2{}, Complex::rotation(35.0_degf), Vector2{1.0f})); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.transformation2DFor(1), + Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f})); + CORRADE_COMPARE(scene.translationRotationScaling2DFor(1), + Containers::triple(Vector2{3.0f, 2.0f}, Complex{}, Vector2{1.5f, 2.0f})); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.transformation2DFor(2), + Containers::NullOpt); + CORRADE_COMPARE(scene.translationRotationScaling2DFor(2), + Containers::NullOpt); +} + +template void SceneDataTest::transformation2DForBut3DType() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.transformation2DFor(0); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformation2DFor(): field has a 3D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); +} + +template void SceneDataTest::transformation2DForBut3DTypeTRS() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + translation.transformation2DFor(0); + translation.translationRotationScaling2DFor(0); + rotation.transformation2DFor(0); + rotation.translationRotationScaling2DFor(0); + scaling.transformation2DFor(0); + scaling.translationRotationScaling2DFor(0); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformation2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::transformation2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformation2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); +} + +void SceneDataTest::transformation3DFor() { + const struct Field { + UnsignedInt object; + Matrix4 transformation; + } fields[] { + {1, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f})}, + {0, Matrix4::rotationX(35.0_degf)}, + {4, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf)}, + {1, Matrix4::translation({1.0f, 2.0f, 3.0f})} /* duplicate, ignored */ + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} + }}; + + CORRADE_COMPARE(scene.transformation3DFor(4), + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf)); + CORRADE_COMPARE(scene.transformation3DFor(0), + Matrix4::rotationX(35.0_degf)); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.transformation3DFor(1), + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f})); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.transformation3DFor(2), + Containers::NullOpt); +} + +void SceneDataTest::transformation3DForTRS() { + const 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}}, + {1, {1.0f, 2.0f, 3.0f}, Quaternion{}, Vector3{1.0f}} /* duplicate, ignored */ + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + 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)} + }}; + + CORRADE_COMPARE(scene.transformation3DFor(4), + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf)); + CORRADE_COMPARE(scene.translationRotationScaling3DFor(4), + Containers::triple(Vector3{3.0f, 2.0f, 1.0f}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), Vector3{1.0f})); + CORRADE_COMPARE(scene.transformation3DFor(0), + Matrix4::rotationX(35.0_degf)); + CORRADE_COMPARE(scene.translationRotationScaling3DFor(0), + Containers::triple(Vector3{}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), Vector3{1.0f})); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.transformation3DFor(1), + Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f})); + CORRADE_COMPARE(scene.translationRotationScaling3DFor(1), + Containers::triple(Vector3{3.0f, 2.0f, 1.0f}, Quaternion{}, Vector3{1.5f, 2.0f, 4.5f})); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.transformation3DFor(2), + Containers::NullOpt); + CORRADE_COMPARE(scene.translationRotationScaling3DFor(2), + Containers::NullOpt); +} + +template void SceneDataTest::transformation3DForBut2DType() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.transformation3DFor(0); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformation3DFor(): field has a 2D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); +} + +template void SceneDataTest::transformation3DForBut2DTypeTRS() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + translation.transformation3DFor(0); + translation.translationRotationScaling3DFor(0); + rotation.transformation3DFor(0); + rotation.translationRotationScaling3DFor(0); + scaling.transformation3DFor(0); + scaling.translationRotationScaling3DFor(0); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData::transformation3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::transformation3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" + "Trade::SceneData::transformation3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n" + "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); +} + +void SceneDataTest::meshesMaterialsFor() { + struct Field { + UnsignedInt object; + UnsignedInt mesh; + Int meshMaterial; + } fields[]{ + {4, 1, -1}, + {1, 3, 0}, + {2, 4, 1}, + {2, 5, -1}, + {2, 1, 0}, + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} + }}; + + /* Just one */ + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(1), + (Containers::arrayView>({{3, 0}})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(4), + (Containers::arrayView>({{1, -1}})), + TestSuite::Compare::Container); + + /* More */ + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2), + (Containers::arrayView>({ + {4, 1}, {5, -1}, {1, 0} + })), TestSuite::Compare::Container); + + /* Object that is not in the array at all */ + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(6), + (Containers::arrayView>({})), + TestSuite::Compare::Container); +} + +void SceneDataTest::lightsFor() { + struct Field { + UnsignedInt object; + UnsignedInt light; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4}, + {2, 5} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} + }}; + + /* Just one */ + CORRADE_COMPARE_AS(scene.lightsFor(1), + (Containers::arrayView({3})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.lightsFor(4), + (Containers::arrayView({1})), + TestSuite::Compare::Container); + + /* More */ + CORRADE_COMPARE_AS(scene.lightsFor(2), + (Containers::arrayView({ + 4, 5 + })), TestSuite::Compare::Container); + + /* Object that is not in the array at all */ + CORRADE_COMPARE_AS(scene.lightsFor(6), + (Containers::arrayView({})), + TestSuite::Compare::Container); +} + +void SceneDataTest::camerasFor() { + struct Field { + UnsignedInt object; + UnsignedInt camera; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4}, + {2, 5} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} + }}; + + /* Just one */ + CORRADE_COMPARE_AS(scene.camerasFor(1), + (Containers::arrayView({3})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasFor(4), + (Containers::arrayView({1})), + TestSuite::Compare::Container); + + /* More */ + CORRADE_COMPARE_AS(scene.camerasFor(2), + (Containers::arrayView({ + 4, 5 + })), TestSuite::Compare::Container); + + /* Object that is not in the array at all */ + CORRADE_COMPARE_AS(scene.camerasFor(6), + (Containers::arrayView({})), + TestSuite::Compare::Container); +} + +void SceneDataTest::skinsFor() { + struct Field { + UnsignedInt object; + UnsignedInt skin; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4}, + {2, 5} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} + }}; + + /* Just one */ + CORRADE_COMPARE_AS(scene.skinsFor(1), + (Containers::arrayView({3})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.skinsFor(4), + (Containers::arrayView({1})), + TestSuite::Compare::Container); + + /* More */ + CORRADE_COMPARE_AS(scene.skinsFor(2), + (Containers::arrayView({ + 4, 5 + })), TestSuite::Compare::Container); + + /* Object that is not in the array at all */ + CORRADE_COMPARE_AS(scene.skinsFor(6), + (Containers::arrayView({})), + TestSuite::Compare::Container); +} + +void SceneDataTest::fieldForFieldMissing() { + SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; + + CORRADE_COMPARE_AS(scene.childrenFor(6), + Containers::arrayView({}), + TestSuite::Compare::Container); + CORRADE_COMPARE(scene.transformation2DFor(6), Containers::NullOpt); + CORRADE_COMPARE(scene.translationRotationScaling2DFor(6), Containers::NullOpt); + CORRADE_COMPARE(scene.transformation3DFor(6), Containers::NullOpt); + CORRADE_COMPARE(scene.translationRotationScaling3DFor(6), Containers::NullOpt); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(6), + (Containers::arrayView>({})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.lightsFor(6), + Containers::arrayView({}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasFor(6), + Containers::arrayView({}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.skinsFor(6), + Containers::arrayView({}), + TestSuite::Compare::Container); +} + +void SceneDataTest::fieldForInvalidObject() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; + + std::ostringstream out; + Error redirectError{&out}; + scene.childrenFor(-2); + scene.childrenFor(7); + scene.transformation2DFor(7); + scene.translationRotationScaling2DFor(7); + scene.transformation3DFor(7); + scene.translationRotationScaling3DFor(7); + scene.meshesMaterialsFor(7); + scene.lightsFor(7); + scene.camerasFor(7); + scene.skinsFor(7); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n" + "Trade::SceneData::childrenFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::transformation2DFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::translationRotationScaling2DFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::transformation3DFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::translationRotationScaling3DFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::meshesMaterialsFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::lightsFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::camerasFor(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::skinsFor(): object 7 out of bounds for 7 objects\n"); +} + void SceneDataTest::releaseFieldData() { struct Field { UnsignedByte object; From 409b49391e326d1819d6fbef12ce1760e706e65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 24 Sep 2021 20:07:16 +0200 Subject: [PATCH 13/49] Trade: add a parentFor() object-oriented accessor as well. For a lack of better API right now, this will be used to check if an object belongs to a certain scene. In the future, when BitArray is a thing, there will be a dedicated field with a mask telling which objects belong to the scene and which not, with a fallback that goes through all field object mappings and collects objects that have at least one field. --- src/Magnum/Trade/SceneData.cpp | 22 +++++++++++- src/Magnum/Trade/SceneData.h | 27 ++++++++++++-- src/Magnum/Trade/Test/SceneDataTest.cpp | 48 ++++++++++++++++++++----- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 01ae0026d7..e1904cbe2a 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -1588,7 +1588,27 @@ std::size_t SceneData::fieldFor(const SceneFieldData& field, const std::size_t o else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -Containers::Array SceneData::childrenFor(Int object) const { +Containers::Optional SceneData::parentFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::Parent); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + const std::size_t offset = fieldFor(field, 0, object); + if(offset == field._size) return {}; + + Int index[1]; + parentsIntoInternal(fieldId, offset, index); + if(*index == -1) return -1; + + UnsignedInt parent[1]; + objectsIntoInternal(fieldId, *index, parent); + return Int(*parent); +} + +Containers::Array SceneData::childrenFor(const Int object) const { CORRADE_ASSERT(object >= -1 && object < Long(_objectCount), "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index f462955182..3e7b305445 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -102,7 +102,8 @@ enum class SceneField: UnsignedInt { * Note that the index points to the parent array itself and isn't the * actual object index --- the object mapping is stored in the * corresponding @ref SceneData::objects() array. - * @see @ref SceneData::parentsAsArray(), @ref SceneData::childrenFor() + * @see @ref SceneData::parentsAsArray(), @ref SceneData::parentFor(), + * @ref SceneData::childrenFor() */ Parent = 1, @@ -1358,7 +1359,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * is larger than the max representable 32-bit value, this * function can't be used, only an appropriately typed * @ref field(SceneField) const. - * @see @ref parentsInto(), @ref hasField(), @ref childrenFor() + * @see @ref parentsInto(), @ref hasField(), @ref parentFor(), + * @ref childrenFor() */ Containers::Array parentsAsArray() const; @@ -1719,6 +1721,26 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** + * @brief Parent for given object + * @m_since_latest + * + * Looks up the @ref SceneField::Parent field for @p object. The lookup + * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ + * being the field count and @f$ n @f$ the size of the parent field, + * thus for retrieving parent info for many objects it's recommended to + * access the field data directly with @ref parentsAsArray() and + * related APIs. + * + * If the @ref SceneField::Parent field is not present or if there's no + * parent for @p object, returns @ref Containers::NullOpt. If @p object + * is top-level, returns @cpp -1 @ce. + * + * The @p object is expected to be less than @ref objectCount(). + * @see @ref childrenFor() + */ + Containers::Optional parentFor(UnsignedInt object) const; + /** * @brief Children for given object * @m_since_latest @@ -1737,6 +1759,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * an empty array. Pass @cpp -1 @ce to get a list of top-level objects. * * The @p object is expected to be less than @ref objectCount(). + * @see @ref parentFor() */ Containers::Array childrenFor(Int object) const; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 797b0d589d..17e268625f 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -151,9 +151,10 @@ struct SceneDataTest: TestSuite::Tester { void fieldWrongType(); void fieldWrongArrayAccess(); - /* Different object types checked just for the childrenFor(), other APIs + /* Different object types checked just for the parentFor(), other APIs use the same helper */ - template void childrenFor(); + template void parentFor(); + void childrenFor(); void transformation2DFor(); void transformation2DForTRS(); template void transformation2DForBut3DType(); @@ -360,10 +361,11 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldWrongType, &SceneDataTest::fieldWrongArrayAccess, - &SceneDataTest::childrenFor, - &SceneDataTest::childrenFor, - &SceneDataTest::childrenFor, - &SceneDataTest::childrenFor, + &SceneDataTest::parentFor, + &SceneDataTest::parentFor, + &SceneDataTest::parentFor, + &SceneDataTest::parentFor, + &SceneDataTest::childrenFor, &SceneDataTest::transformation2DFor, &SceneDataTest::transformation2DForTRS, &SceneDataTest::transformation2DForBut3DType, @@ -4040,12 +4042,39 @@ void SceneDataTest::fieldWrongArrayAccess() { "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n"); } -template void SceneDataTest::childrenFor() { +template void SceneDataTest::parentFor() { setTestCaseTemplateName(NameTraits::name()); struct Field { T object; Int parent; + } fields[]{ + {3, -1}, + {4, 0}, + {2, 1}, + {4, 2} /* duplicate, ignored */ + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} + }}; + + CORRADE_COMPARE(scene.parentFor(2), 4); + CORRADE_COMPARE(scene.parentFor(3), -1); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.parentFor(4), 3); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.parentFor(1), Containers::NullOpt); +} + +void SceneDataTest::childrenFor() { + struct Field { + UnsignedInt object; + Int parent; } fields[]{ {4, -1}, {3, 0}, @@ -4056,7 +4085,7 @@ template void SceneDataTest::childrenFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; @@ -4486,6 +4515,7 @@ void SceneDataTest::skinsFor() { void SceneDataTest::fieldForFieldMissing() { SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; + CORRADE_COMPARE(scene.parentFor(6), Containers::NullOpt); CORRADE_COMPARE_AS(scene.childrenFor(6), Containers::arrayView({}), TestSuite::Compare::Container); @@ -4516,6 +4546,7 @@ void SceneDataTest::fieldForInvalidObject() { std::ostringstream out; Error redirectError{&out}; + scene.parentFor(7); scene.childrenFor(-2); scene.childrenFor(7); scene.transformation2DFor(7); @@ -4527,6 +4558,7 @@ void SceneDataTest::fieldForInvalidObject() { scene.camerasFor(7); scene.skinsFor(7); CORRADE_COMPARE(out.str(), + "Trade::SceneData::parentFor(): object 7 out of bounds for 7 objects\n" "Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n" "Trade::SceneData::childrenFor(): object 7 out of bounds for 7 objects\n" "Trade::SceneData::transformation2DFor(): object 7 out of bounds for 7 objects\n" From f73409410912dbbbe060b48816c4950efa9ede05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 26 Sep 2021 19:39:14 +0200 Subject: [PATCH 14/49] Trade: support storing pointers (and thus importer state) in SceneData. --- src/Magnum/Trade/SceneData.cpp | 65 ++++++- src/Magnum/Trade/SceneData.h | 120 +++++++++++- src/Magnum/Trade/Test/SceneDataTest.cpp | 232 +++++++++++++++++++++++- 3 files changed, 407 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index e1904cbe2a..56029e2694 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -95,6 +95,7 @@ Debug& operator<<(Debug& debug, const SceneField value) { _c(Light) _c(Camera) _c(Skin) + _c(ImporterState) #undef _c /* LCOV_EXCL_STOP */ @@ -202,6 +203,8 @@ Debug& operator<<(Debug& debug, const SceneFieldType value) { _c(Rad) _c(Radh) _c(Radd) + _c(Pointer) + _c(MutablePointer) #undef _c /* LCOV_EXCL_STOP */ } @@ -323,6 +326,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { return 96; case SceneFieldType::Matrix4x4d: return 128; + case SceneFieldType::Pointer: + case SceneFieldType::MutablePointer: + return sizeof(void*); } #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic pop @@ -368,7 +374,7 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object #ifndef CORRADE_NO_ASSERT /* Check various assumptions about field data */ - Math::BoolVector<11> fieldsPresent; /** @todo some constant for this */ + Math::BoolVector<12> fieldsPresent; /** @todo some constant for this */ const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType); UnsignedInt translationField = ~UnsignedInt{}; UnsignedInt rotationField = ~UnsignedInt{}; @@ -1561,6 +1567,47 @@ Containers::Array SceneData::skinsAsArray() const { return unsignedIndexFieldAsArrayInternal(fieldId); } +void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_INTERNAL_ASSERT(field._fieldType == SceneFieldType::Pointer || + field._fieldType == SceneFieldType::MutablePointer); + Utility::copy(Containers::arrayCast(fieldDataFieldViewInternal(field, offset, destination.size())), destination); +} + +void SceneData::importerStateInto(const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::importerStateInto(): field not found", ); + CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); + importerStateIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::importerStateInto(): field not found", {}); + CORRADE_ASSERT(offset <= _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {}); + const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset); + importerStateIntoInternal(fieldId, offset, destination.prefix(size)); + return size; +} + +Containers::Array SceneData::importerStateAsArray() const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::importerStateInto(): field not found", {}); + Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; + importerStateIntoInternal(fieldId, 0, out); + return out; +} + namespace { template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { @@ -1917,6 +1964,22 @@ Containers::Array SceneData::skinsFor(const UnsignedInt object) con return out; } +Containers::Optional SceneData::importerStateFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + const std::size_t offset = fieldFor(field, 0, object); + if(offset == field._size) return {}; + + const void* importerState[1]; + importerStateIntoInternal(fieldId, offset, importerState); + return *importerState; +} + Containers::Array SceneData::releaseFieldData() { Containers::Array out = std::move(_fields); _fields = {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 3e7b305445..27b86899e6 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -273,6 +273,18 @@ enum class SceneField: UnsignedInt { */ Skin, + /** + * Importer state for given object, per-object counterpart to + * scene-specific @ref SceneData::importerState(). Type is usually + * @ref SceneFieldType::Pointer but can be also + * @ref SceneFieldType::MutablePointer. An object should have only one + * importer state, altough this isn't enforced in any way, and which of the + * duplicate fields gets used is not defined. + * @see @ref SceneData::importerStateAsArray(), + * @ref SceneData::importerStateFor() + */ + ImporterState, + /** * This and all higher values are for importer-specific fields. Can be * of any type. See documentation of a particular importer for details. @@ -455,7 +467,21 @@ enum class SceneFieldType: UnsignedShort { Degd, /**< @relativeref{Magnum,Degh} */ Rad, /**< @relativeref{Magnum,Rad} */ Radh, /**< @relativeref{Magnum,Radh} */ - Radd /**< @relativeref{Magnum,Radd} */ + Radd, /**< @relativeref{Magnum,Radd} */ + + /** + * @cpp const void* @ce, type is not preserved. For convenience it's + * possible to retrieve the value by calling @cpp field() @ce + * with an arbitrary `T` but the user has to ensure the type is correct. + */ + Pointer, + + /** + * @cpp void* @ce, type is not preserved. For convenience it's possible to + * retrieve the value by calling @cpp field() @ce with an arbitrary `T` + * but the user has to ensure the type is correct. + */ + MutablePointer, }; /** @@ -1142,9 +1168,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), - * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common - * fields converted to usual types, but note that these operations - * involve extra allocation and data conversion. + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors to get common fields converted + * to usual types, but note that these operations involve extra + * allocation and data conversion. * @see @ref field(SceneField) const, @ref mutableField(UnsignedInt), * @ref fieldArraySize() */ @@ -1222,9 +1249,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), - * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common - * fields converted to usual types, but note that these operations - * involve extra allocation and data conversion. + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors to get common fields converted + * to usual types, but note that these operations involve extra + * allocation and data conversion. * @see @ref field(UnsignedInt) const, @ref mutableField(SceneField) */ template::value>::type> Containers::StridedArrayView1D field(SceneField name) const; @@ -1721,6 +1749,45 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** + * @brief Per-object importer state as `void` pointers + * @m_since_latest + * + * Convenience alternative to @ref field(SceneField) const with + * @ref SceneField::ImporterState as the argument that converts the + * field from an arbitrary underlying type and returns it in a + * newly-allocated array. The field is expected to exist. + * + * This is different from @ref importerState(), which returns importer + * state for the scene itself, not particular objects. + * @see @ref importerStateInto(), @ref hasField(), @ref importerStateFor() + */ + Containers::Array importerStateAsArray() const; + + /** + * @brief Per-object importer state as `void` pointers into a pre-allocated view + * @m_since_latest + * + * Like @ref importerStateAsArray(), but puts the result into + * @p destination instead of allocating a new array. Expects that + * @p destination is sized to contain exactly all data. + * @see @ref fieldSize(SceneField) const + */ + void importerStateInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief A subrange of per-object importer state as `void` pointers into a pre-allocated view + * @m_since_latest + * + * Compared to @ref importerStateInto(const Containers::StridedArrayView1D&) const + * extracts only a subrange of the field defined by @p offset and size + * of the @p destination view, returning the count of items actually + * extracted. The @p offset is expected to not be larger than the field + * size. + * @see @ref fieldSize(SceneField) const + */ + std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Parent for given object * @m_since_latest @@ -1945,6 +2012,25 @@ class MAGNUM_TRADE_EXPORT SceneData { */ Containers::Array skinsFor(UnsignedInt object) const; + /** + * @brief Importer state for given object + * @m_since_latest + * + * Looks up the @ref SceneField::ImporterState field for @p object. The + * lookup is done in a @f$ \mathcal{O}(m + n) @f$ complexity with + * @f$ m @f$ being the field count and @f$ n @f$ the size of the + * importer state field, thus for retrieving importer state info for + * many objects it's recommended to access the field data directly with + * @ref importerStateAsArray() and related APIs. + * + * If the @ref SceneField::ImporterState field is not present or if + * there's no importer state for @p object, returns + * @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Optional importerStateFor(UnsignedInt object) const; + /** * @brief Release field data storage * @m_since_latest @@ -1983,7 +2069,11 @@ class MAGNUM_TRADE_EXPORT SceneData { /** * @brief Importer-specific state * - * See @ref AbstractImporter::importerState() for more information. + * Scene-specific importer state. For object-specific importer state + * look for the @ref SceneField::ImporterState field or access it via + * @ref importerStateAsArray(), @ref importerStateFor() and related + * convenience functions. See @ref AbstractImporter::importerState() + * for general information about importer state pointers. */ const void* importerState() const { return _importerState; } @@ -2020,6 +2110,7 @@ class MAGNUM_TRADE_EXPORT SceneData { MAGNUM_TRADE_LOCAL void indexFieldIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL Containers::Array unsignedIndexFieldAsArrayInternal(const UnsignedInt fieldId) const; MAGNUM_TRADE_LOCAL void meshesMaterialsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; + MAGNUM_TRADE_LOCAL void importerStateIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; DataFlags _dataFlags; SceneObjectType _objectType; @@ -2142,6 +2233,16 @@ namespace Implementation { template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + template struct SceneFieldTypeFor { + constexpr static SceneFieldType type() { + return SceneFieldType::Pointer; + } + }; + template struct SceneFieldTypeFor { + constexpr static SceneFieldType type() { + return SceneFieldType::MutablePointer; + } + }; template constexpr SceneObjectType sceneObjectTypeFor() { static_assert(sizeof(T) == 0, "unsupported object type"); @@ -2196,6 +2297,9 @@ namespace Implementation { (type == SceneFieldType::Byte || type == SceneFieldType::Short || type == SceneFieldType::Int)) || + (name == SceneField::ImporterState && + (type == SceneFieldType::Pointer || + type == SceneFieldType::MutablePointer)) || /* Custom fields can be anything */ isSceneFieldCustom(name); } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 17e268625f..82dbb87f4b 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -141,6 +141,9 @@ struct SceneDataTest: TestSuite::Tester { template void skinsAsArray(); void skinsIntoArray(); void skinsIntoArrayInvalidSizeOrOffset(); + template void importerStateAsArray(); + void importerStateIntoArray(); + void importerStateIntoArrayInvalidSizeOrOffset(); void mutableAccessNotAllowed(); @@ -149,6 +152,7 @@ struct SceneDataTest: TestSuite::Tester { void fieldNotFound(); void fieldWrongType(); + void fieldWrongPointerType(); void fieldWrongArrayAccess(); /* Different object types checked just for the parentFor(), other APIs @@ -167,6 +171,7 @@ struct SceneDataTest: TestSuite::Tester { void lightsFor(); void camerasFor(); void skinsFor(); + void importerStateFor(); void fieldForFieldMissing(); void fieldForInvalidObject(); @@ -351,6 +356,13 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::skinsIntoArrayInvalidSizeOrOffset, + &SceneDataTest::importerStateAsArray, + &SceneDataTest::importerStateAsArray}); + + addInstancedTests({&SceneDataTest::importerStateIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset, &SceneDataTest::mutableAccessNotAllowed, @@ -359,6 +371,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldNotFound, &SceneDataTest::fieldWrongType, + &SceneDataTest::fieldWrongPointerType, &SceneDataTest::fieldWrongArrayAccess, &SceneDataTest::parentFor, @@ -386,6 +399,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::lightsFor, &SceneDataTest::camerasFor, &SceneDataTest::skinsFor, + &SceneDataTest::importerStateFor, &SceneDataTest::fieldForFieldMissing, &SceneDataTest::fieldForInvalidObject, @@ -494,6 +508,7 @@ void SceneDataTest::fieldTypeSize() { CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x3d), sizeof(Matrix3x3d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x4d), sizeof(Matrix3x4d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix4x4d), sizeof(Matrix4x4d)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Pointer), sizeof(const void*)); } void SceneDataTest::fieldTypeSizeInvalid() { @@ -1763,6 +1778,12 @@ _c(DualComplex) _c(DualComplexd) _c(DualQuaternion) _c(DualQuaterniond) +template struct NameTraits { + static const char* name() { return "Pointer"; } +}; +template struct NameTraits { + static const char* name() { return "MutablePointer"; } +}; #undef _c template void SceneDataTest::objectsAsArrayByIndex() { @@ -3714,6 +3735,107 @@ void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::skinsInto(): offset 4 out of bounds for a field of size 3\n"); } +template void SceneDataTest::importerStateAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + int a, b; + + struct Field { + UnsignedByte object; + T importerState; + } fields[]{ + {0, &a}, + {1, nullptr}, + {15, &b} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + CORRADE_COMPARE_AS(scene.importerStateAsArray(), + Containers::arrayView({&a, nullptr, &b}), + TestSuite::Compare::Container); +} + +void SceneDataTest::importerStateIntoArray() { + auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Both AsArray() and Into() share a common helper. The AsArray() test + above verified handling of various data types and this checks the + offset/size parameters of the Into() variant. */ + + int a, b; + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[]{ + {1, &a}, + {0, nullptr}, + {4, &b} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::ImporterState, + view.slice(&Field::object), + view.slice(&Field::importerState)}, + }}; + + /* The offset-less overload should give back all data */ + { + const void* out[3]; + scene.importerStateInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::importerState), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + Containers::Array out{data.size}; + CORRADE_COMPARE(scene.importerStateInto(data.offset, out), data.expectedSize); + CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + view.slice(&Field::importerState) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + const void* destination[2]; + scene.importerStateInto(destination); + scene.importerStateInto(4, destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::importerStateInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::importerStateInto(): offset 4 out of bounds for a field of size 3\n"); +} + void SceneDataTest::mutableAccessNotAllowed() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -3908,6 +4030,9 @@ void SceneDataTest::fieldNotFound() { scene.skinsAsArray(); scene.skinsInto(nullptr); scene.skinsInto(0, nullptr); + scene.importerStateAsArray(); + scene.importerStateInto(nullptr); + scene.importerStateInto(0, nullptr); CORRADE_COMPARE(out.str(), "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" @@ -3961,7 +4086,10 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::camerasInto(): field not found\n" "Trade::SceneData::skinsInto(): field not found\n" "Trade::SceneData::skinsInto(): field not found\n" - "Trade::SceneData::skinsInto(): field not found\n"); + "Trade::SceneData::skinsInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n"); } void SceneDataTest::fieldWrongType() { @@ -4003,6 +4131,78 @@ void SceneDataTest::fieldWrongType() { "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n"); } +void SceneDataTest::fieldWrongPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Thing { + UnsignedInt object; + Int* foobar; + const Int* importerState; + } things[2]; + Containers::StridedArrayView1D view = things; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, things, { + SceneFieldData{sceneFieldCustom(35), view.slice(&Thing::object), Containers::arrayCast<2, Int*>(view.slice(&Thing::foobar))}, + SceneFieldData{SceneField::ImporterState, view.slice(&Thing::object), view.slice(&Thing::importerState)}, + }}; + + /* These are fine (type is not checked) */ + scene.field(0); + scene.field(1); + scene.mutableField(0); + scene.mutableField(1); + scene.field(sceneFieldCustom(35)); + scene.field(SceneField::ImporterState); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(SceneField::ImporterState); + + std::ostringstream out; + Error redirectError{&out}; + scene.field(0); + scene.field(0); + scene.field(0); + scene.field(1); + scene.field(1); + scene.mutableField(0); + scene.mutableField(0); + scene.mutableField(0); + scene.mutableField(1); + scene.mutableField(1); + scene.field(sceneFieldCustom(35)); + scene.field(sceneFieldCustom(35)); + scene.field(sceneFieldCustom(35)); + scene.field(SceneField::ImporterState); + scene.field(SceneField::ImporterState); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(SceneField::ImporterState); + scene.mutableField(SceneField::ImporterState); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n"); +} + void SceneDataTest::fieldWrongArrayAccess() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -4512,6 +4712,36 @@ void SceneDataTest::skinsFor() { TestSuite::Compare::Container); } +void SceneDataTest::importerStateFor() { + int a, b, c; + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[]{ + {3, &a}, + {4, &b}, + {2, nullptr}, + {4, &c} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + CORRADE_COMPARE(scene.importerStateFor(2), nullptr); + CORRADE_COMPARE(scene.importerStateFor(3), &a); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.importerStateFor(4), &b); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.importerStateFor(1), Containers::NullOpt); +} + void SceneDataTest::fieldForFieldMissing() { SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; From c9ac4d6f822f06dbe2745398ba216ff6af862ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 4 Oct 2021 19:36:13 +0200 Subject: [PATCH 15/49] Trade: introduce SceneData::is2D() and is3D(). This moves the transformation type consistency checks directly into the constructor and makes the discovered dimension count available through a getter. As a result, a lot of duplicate assertions (and corresponding tests) could be removed while tightening the behavior -- before it was possible to have a 2D transformation matrix complemented with a 3D rotation quaternion and all asserts would pass, yet the user would be required to call transformations2DAsArray() *but* translationsRotationsScalings3DAsArray(). Since scenes can exist without any transformation field, there's both is2D() and is3D() and they both return false in that case. --- src/Magnum/Trade/SceneData.cpp | 270 ++++++--------- src/Magnum/Trade/SceneData.h | 82 ++++- src/Magnum/Trade/Test/SceneDataTest.cpp | 440 ++++++++++-------------- 3 files changed, 356 insertions(+), 436 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 56029e2694..d22c78a2b3 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -363,7 +363,7 @@ Containers::Array sceneFieldDataNonOwningArray(const Containers: return Containers::Array{const_cast(view.data()), view.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}; } -SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{objectType}, _objectCount{objectCount}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} { +SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{objectType}, _dimensions{}, _objectCount{objectCount}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} { /* Check that object type is large enough */ CORRADE_ASSERT( (objectType == SceneObjectType::UnsignedByte && objectCount <= 0xffull) || @@ -376,14 +376,19 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object /* Check various assumptions about field data */ Math::BoolVector<12> fieldsPresent; /** @todo some constant for this */ const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType); + #endif + UnsignedInt transformationField = ~UnsignedInt{}; UnsignedInt translationField = ~UnsignedInt{}; UnsignedInt rotationField = ~UnsignedInt{}; UnsignedInt scalingField = ~UnsignedInt{}; + #ifndef CORRADE_NO_ASSERT UnsignedInt meshField = ~UnsignedInt{}; UnsignedInt meshMaterialField = ~UnsignedInt{}; + #endif for(std::size_t i = 0; i != _fields.size(); ++i) { const SceneFieldData& field = _fields[i]; + #ifndef CORRADE_NO_ASSERT /* The object type has to be the same among all fields. Technically it wouldn't need to be, but if there's 60k objects then using a 8bit type for certain fields would mean only the first 256 objects can be @@ -441,22 +446,30 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object "Trade::SceneData: field data [" << Debug::nospace << fieldBegin << Debug::nospace << ":" << Debug::nospace << fieldEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); } } + #endif - /* Remember TRS and mesh/material fields to check their object mapping - consistency outside of the loop below */ - if(_fields[i]._name == SceneField::Translation) { + /* Remember TRS and mesh/material fields to figure out whether the + scene is 2D or 3D and check their object mapping consistency outside + of the loop below */ + if(_fields[i]._name == SceneField::Transformation) { + transformationField = i; + } else if(_fields[i]._name == SceneField::Translation) { translationField = i; } else if(_fields[i]._name == SceneField::Rotation) { rotationField = i; } else if(_fields[i]._name == SceneField::Scaling) { scalingField = i; - } else if(_fields[i]._name == SceneField::Mesh) { + } + #ifndef CORRADE_NO_ASSERT + else if(_fields[i]._name == SceneField::Mesh) { meshField = i; } else if(_fields[i]._name == SceneField::MeshMaterial) { meshMaterialField = i; } + #endif } + #ifndef CORRADE_NO_ASSERT /* Check that certain fields share the same object mapping. Printing as if all would be pointers (and not offset-only), it's not worth the extra effort just for an assert message. Also, compared to above, where @@ -486,6 +499,67 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object if(meshField != ~UnsignedInt{} && meshMaterialField != ~UnsignedInt{}) checkFieldObjectDataMatch(_fields[meshField], _fields[meshMaterialField]); #endif + + /* Decide about dimensionality based on transformation type, if present */ + if(transformationField != ~UnsignedInt{}) { + const SceneFieldType fieldType = _fields[transformationField]._fieldType; + if(fieldType == SceneFieldType::Matrix3x3 || + fieldType == SceneFieldType::Matrix3x3d || + fieldType == SceneFieldType::DualComplex || + fieldType == SceneFieldType::DualComplexd) + _dimensions = 2; + else if(fieldType == SceneFieldType::Matrix4x4 || + fieldType == SceneFieldType::Matrix4x4d || + fieldType == SceneFieldType::DualQuaternion || + fieldType == SceneFieldType::DualQuaterniond) + _dimensions = 3; + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Use TRS fields to decide about dimensionality, if the transformation + field is not present. If it is, verify that they match it. */ + if(translationField != ~UnsignedInt{}) { + const SceneFieldType fieldType = _fields[translationField]._fieldType; + if(fieldType == SceneFieldType::Vector2 || + fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT(!_dimensions || _dimensions == 2, + "Trade::SceneData: expected a 3D translation field but got" << fieldType, ); + _dimensions = 2; + } else if(fieldType == SceneFieldType::Vector3 || + fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT(!_dimensions || _dimensions == 3, + "Trade::SceneData: expected a 2D translation field but got" << fieldType, ); + _dimensions = 3; + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + if(rotationField != ~UnsignedInt{}) { + const SceneFieldType fieldType = _fields[rotationField]._fieldType; + if(fieldType == SceneFieldType::Complex || + fieldType == SceneFieldType::Complexd) { + CORRADE_ASSERT(!_dimensions || _dimensions == 2, + "Trade::SceneData: expected a 3D rotation field but got" << fieldType, ); + _dimensions = 2; + } else if(fieldType == SceneFieldType::Quaternion || + fieldType == SceneFieldType::Quaterniond) { + CORRADE_ASSERT(!_dimensions || _dimensions == 3, + "Trade::SceneData: expected a 2D rotation field but got" << fieldType, ); + _dimensions = 3; + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + if(scalingField != ~UnsignedInt{}) { + const SceneFieldType fieldType = _fields[scalingField]._fieldType; + if(fieldType == SceneFieldType::Vector2 || + fieldType == SceneFieldType::Vector2d) { + CORRADE_ASSERT(!_dimensions || _dimensions == 2, + "Trade::SceneData: expected a 3D scaling field but got" << fieldType, ); + _dimensions = 2; + } else if(fieldType == SceneFieldType::Vector3 || + fieldType == SceneFieldType::Vector3d) { + CORRADE_ASSERT(!_dimensions || _dimensions == 3, + "Trade::SceneData: expected a 2D scaling field but got" << fieldType, ); + _dimensions = 3; + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } } SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} @@ -903,6 +977,10 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi /* *FieldId, offset and destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ + /* If is2D() returned false as well, all *FieldId would be invalid, which + the caller is assumed to check. */ + CORRADE_ASSERT(!is3D(), "Trade::SceneData::transformations2DInto(): scene has a 3D transformation type", ); + /** @todo apply scalings as well if dual complex? */ /* Prefer the transformation field, if present */ @@ -919,11 +997,6 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualComplexd) { convertTransformation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Matrix4x4 || - field._fieldType == SceneFieldType::Matrix4x4d || - field._fieldType == SceneFieldType::DualQuaternion || - field._fieldType == SceneFieldType::DualQuaterniond) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D transformation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ /* If not, combine from TRS components */ @@ -941,9 +1014,6 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi applyScaling(fieldData, destination); } else if(field._fieldType == SceneFieldType::Vector2d) { applyScaling(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Vector3 || - field._fieldType == SceneFieldType::Vector3d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D scaling type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -956,9 +1026,6 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi applyRotation(fieldData, destination); } else if(field._fieldType == SceneFieldType::Complexd) { applyRotation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Quaternion || - field._fieldType == SceneFieldType::Quaterniond) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D rotation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -971,9 +1038,6 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi applyTranslation(fieldData, destination); } else if(field._fieldType == SceneFieldType::Vector2d) { applyTranslation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Vector3 || - field._fieldType == SceneFieldType::Vector3d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D translation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -1022,6 +1086,10 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr /* *FieldId, offset and *Destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ + /* If is2D() returned false as well, all *FieldId would be invalid, which + the caller is assumed to check. */ + CORRADE_ASSERT(!is3D(), "Trade::SceneData::translationsRotationsScalings2DInto(): scene has a 3D transformation type", ); + /* Retrieve translation, if desired. If no field is present, output a zero vector for all objects. */ if(translationDestination) { @@ -1036,9 +1104,6 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), translationDestination); } else if(field._fieldType == SceneFieldType::Vector2d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(translationDestination)); - } else if(field._fieldType == SceneFieldType::Vector3 || - field._fieldType == SceneFieldType::Vector3d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D translation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1057,9 +1122,6 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), rotationDestination); } else if(field._fieldType == SceneFieldType::Complexd) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(rotationDestination)); - } else if(field._fieldType == SceneFieldType::Quaternion || - field._fieldType == SceneFieldType::Quaterniond) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D rotation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1078,9 +1140,6 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), scalingDestination); } else if(field._fieldType == SceneFieldType::Vector2d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(scalingDestination)); - } else if(field._fieldType == SceneFieldType::Vector3 || - field._fieldType == SceneFieldType::Vector3d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D scaling type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1144,6 +1203,10 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi /* *FieldId, offset and destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ + /* If is3D() returned false as well, all *FieldId would be invalid, which + the caller is assumed to check. */ + CORRADE_ASSERT(!is2D(), "Trade::SceneData::transformations3DInto(): scene has a 2D transformation type", ); + /** @todo apply scalings as well if dual quat? */ /* Prefer the transformation field, if present */ @@ -1160,11 +1223,6 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualQuaterniond) { convertTransformation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Matrix3x3 || - field._fieldType == SceneFieldType::Matrix3x3d || - field._fieldType == SceneFieldType::DualComplex || - field._fieldType == SceneFieldType::DualComplexd) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D transformation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ /* If not, combine from TRS components */ @@ -1182,9 +1240,6 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi applyScaling(fieldData, destination); } else if(field._fieldType == SceneFieldType::Vector3d) { applyScaling(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Vector2 || - field._fieldType == SceneFieldType::Vector2d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D scaling type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -1197,9 +1252,6 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi applyRotation(fieldData, destination); } else if(field._fieldType == SceneFieldType::Quaterniond) { applyRotation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Complex || - field._fieldType == SceneFieldType::Complexd) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D rotation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -1212,9 +1264,6 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi applyTranslation(fieldData, destination); } else if(field._fieldType == SceneFieldType::Vector3d) { applyTranslation(fieldData, destination); - } else if(field._fieldType == SceneFieldType::Vector2 || - field._fieldType == SceneFieldType::Vector2d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D translation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -1263,6 +1312,10 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr /* *FieldId, offset and *Destination.size() is assumed to be in bounds (or an invalid field ID), checked by the callers */ + /* If is3D() returned false as well, all *FieldId would be invalid, which + the caller is assumed to check. */ + CORRADE_ASSERT(!is2D(), "Trade::SceneData::translationsRotationsScalings3DInto(): scene has a 2D transformation type", ); + /* Retrieve translation, if desired. If no field is present, output a zero vector for all objects. */ if(translationDestination) { @@ -1277,9 +1330,6 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), translationDestination); } else if(field._fieldType == SceneFieldType::Vector3d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(translationDestination)); - } else if(field._fieldType == SceneFieldType::Vector2 || - field._fieldType == SceneFieldType::Vector2d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D translation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1298,9 +1348,6 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), rotationDestination); } else if(field._fieldType == SceneFieldType::Quaterniond) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 4), Containers::arrayCast<2, Float>(rotationDestination)); - } else if(field._fieldType == SceneFieldType::Complex || - field._fieldType == SceneFieldType::Complexd) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D rotation type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1319,9 +1366,6 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr Utility::copy(Containers::arrayCast(fieldData), scalingDestination); } else if(field._fieldType == SceneFieldType::Vector3d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(scalingDestination)); - } else if(field._fieldType == SceneFieldType::Vector2 || - field._fieldType == SceneFieldType::Vector2d) { - CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D scaling type" << field._fieldType, ); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } @@ -1698,39 +1742,9 @@ Containers::Optional SceneData::transformation2DFor(const UnsignedInt o UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; - #ifndef CORRADE_NO_ASSERT - if(transformationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[transformationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Matrix3x3 || - type == SceneFieldType::Matrix3x3d || - type == SceneFieldType::DualComplex || - type == SceneFieldType::DualComplexd, - "Trade::SceneData::transformation2DFor(): field has a 3D transformation type" << type, {}); - } else { - if(translationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[translationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector2 || - type == SceneFieldType::Vector2d, - "Trade::SceneData::transformation2DFor(): field has a 3D translation type" << type, {}); - } - if(rotationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[rotationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Complex || - type == SceneFieldType::Complexd, - "Trade::SceneData::transformation2DFor(): field has a 3D rotation type" << type, {}); - } - if(scalingFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[scalingFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector2 || - type == SceneFieldType::Vector2d, - "Trade::SceneData::transformation2DFor(): field has a 3D scaling type" << type, {}); - } - } - #endif + /* If is2D() returned false as well, all *FieldId would be invalid, which + is handled above. */ + CORRADE_ASSERT(!is3D(), "Trade::SceneData::transformation2DFor(): scene has a 3D transformation type", {}); const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); if(offset == _fields[fieldWithObjectMapping]._size) return {}; @@ -1747,29 +1761,9 @@ Containers::Optional> SceneData::t UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; - #ifndef CORRADE_NO_ASSERT - if(translationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[translationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector2 || - type == SceneFieldType::Vector2d, - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type" << type, {}); - } - if(rotationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[rotationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Complex || - type == SceneFieldType::Complexd, - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type" << type, {}); - } - if(scalingFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[scalingFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector2 || - type == SceneFieldType::Vector2d, - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type" << type, {}); - } - #endif + /* If is2D() returned false as well, all *FieldId would be invalid, which + is handled above. */ + CORRADE_ASSERT(!is3D(), "Trade::SceneData::translationRotationScaling2DFor(): scene has a 3D transformation type", {}); const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); if(offset == _fields[fieldWithObjectMapping]._size) return {}; @@ -1788,39 +1782,9 @@ Containers::Optional SceneData::transformation3DFor(const UnsignedInt o UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; - #ifndef CORRADE_NO_ASSERT - if(transformationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[transformationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Matrix4x4 || - type == SceneFieldType::Matrix4x4d || - type == SceneFieldType::DualQuaternion || - type == SceneFieldType::DualQuaterniond, - "Trade::SceneData::transformation3DFor(): field has a 2D transformation type" << type, {}); - } else { - if(translationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[translationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector3 || - type == SceneFieldType::Vector3d, - "Trade::SceneData::transformation3DFor(): field has a 2D translation type" << type, {}); - } - if(rotationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[rotationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Quaternion || - type == SceneFieldType::Quaterniond, - "Trade::SceneData::transformation3DFor(): field has a 2D rotation type" << type, {}); - } - if(scalingFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[scalingFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector3 || - type == SceneFieldType::Vector3d, - "Trade::SceneData::transformation3DFor(): field has a 2D scaling type" << type, {}); - } - } - #endif + /* If is3D() returned false as well, all *FieldId would be invalid, which + is handled above. */ + CORRADE_ASSERT(!is2D(), "Trade::SceneData::transformation3DFor(): scene has a 2D transformation type", {}); const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); if(offset == _fields[fieldWithObjectMapping]._size) return {}; @@ -1837,29 +1801,9 @@ Containers::Optional> SceneData UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; - #ifndef CORRADE_NO_ASSERT - if(translationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[translationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector3 || - type == SceneFieldType::Vector3d, - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type" << type, {}); - } - if(rotationFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[rotationFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Quaternion || - type == SceneFieldType::Quaterniond, - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type" << type, {}); - } - if(scalingFieldId != ~UnsignedInt{}) { - const SceneFieldType type = _fields[scalingFieldId]._fieldType; - CORRADE_ASSERT( - type == SceneFieldType::Vector3 || - type == SceneFieldType::Vector3d, - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type" << type, {}); - } - #endif + /* If is3D() returned false as well, all *FieldId would be invalid, which + is handled above. */ + CORRADE_ASSERT(!is2D(), "Trade::SceneData::translationRotationScaling3DFor(): scene has a 2D transformation type", {}); const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); if(offset == _fields[fieldWithObjectMapping]._size) return {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 27b86899e6..8a9a5b9b1a 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -121,15 +121,18 @@ enum class SceneField: UnsignedInt { * * The transformation can be also represented by separate * @ref SceneField::Translation, @ref SceneField::Rotation and - * @ref SceneField::Scaling fields. If both @ref SceneField::Transformation - * and TRS fields are specified, it's expected that all objects that have - * TRS fields have a combined transformation field as well, and + * @ref SceneField::Scaling fields. All present transformation-related + * fields are expected to have the same dimensionality --- either all 2D or + * all 3D. If both @ref SceneField::Transformation and TRS fields are + * specified, it's expected that all objects that have TRS fields have a + * combined transformation field as well, and * @ref SceneData::transformations2DAsArray() / * @ref SceneData::transformations3DAsArray() then takes into account only * the combined transformation field. TRS fields can however be specified * only for a subset of transformed objects, useful for example when only * certain objects have these properties animated. - * @see @ref SceneData::transformations2DAsArray(), + * @see @ref SceneData::is2D(), @ref SceneData::is3D(), + * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor() @@ -146,11 +149,13 @@ enum class SceneField: UnsignedInt { * * The translation field usually is (but doesn't have to be) complemented * by a @ref SceneField::Rotation and @ref SceneField::Scaling, which, if - * present, are expected to all share the same object mapping view. The TRS + * present, are expected to all share the same object mapping view and have + * the same dimensionality, either all 2D or all 3D. The TRS * components can either completely replace @ref SceneField::Transformation * or be provided just for a subset of it --- see its documentation for * details. - * @see @ref SceneData::transformations2DAsArray(), + * @see @ref SceneData::is2D(), @ref SceneData::is3D(), + * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), @@ -171,11 +176,13 @@ enum class SceneField: UnsignedInt { * * The rotation field usually is (but doesn't have to be) complemented by a * @ref SceneField::Translation and @ref SceneField::Scaling, which, if - * present, are expected to all share the same object mapping view. The TRS + * present, are expected to all share the same object mapping view and have + * the same dimensionality, either all 2D or all 3D. The TRS * components can either completely replace @ref SceneField::Transformation * or be provided just for a subset of it --- see its documentation for * details. - * @see @ref SceneData::transformations2DAsArray(), + * @see @ref SceneData::is2D(), @ref SceneData::is3D(), + * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), @@ -196,11 +203,13 @@ enum class SceneField: UnsignedInt { * * The scaling field usually is (but doesn't have to be) complemented by a * @ref SceneField::Translation and @ref SceneField::Rotation, which, if - * present, are expected to all share the same object mapping view. The TRS + * present, are expected to all share the same object mapping view and have + * the same dimensionality, either all 2D or all 3D. The TRS * components can either completely replace @ref SceneField::Transformation * or be provided just for a subset of it --- see its documentation for * details. - * @see @ref SceneData::transformations2DAsArray(), + * @see @ref SceneData::is2D(), @ref SceneData::is3D(), + * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), @@ -993,9 +1002,47 @@ class MAGNUM_TRADE_EXPORT SceneData { */ UnsignedShort fieldArraySize(UnsignedInt id) const; + /** + * @brief Whether the scene is two-dimensional + * @m_since_latest + * + * Returns @cpp true @ce if the present + * @ref SceneField::Transformation, + * @relativeref{SceneField,Translation}, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} fields have a 2D type, + * @cpp false @ce otherwise. + * + * If there's no transformation-related field, the scene is treated as + * neither 2D nor 3D and both @ref is2D() and @ref is3D() return + * @cpp false @ce. On the other hand, a scene can't be both 2D and 3D. + * @see @ref hasField() + */ + bool is2D() const { return _dimensions == 2; } + + /** + * @brief Whether the scene is three-dimensional + * @m_since_latest + * + * Returns @cpp true @ce if the present + * @ref SceneField::Transformation, + * @relativeref{SceneField,Translation}, + * @relativeref{SceneField,Rotation} and + * @relativeref{SceneField,Scaling} fields have a 3D type, + * @cpp false @ce otherwise. + * + * If there's no transformation-related field, the scene is treated as + * neither 2D nor 3D and both @ref is2D() and @ref is3D() return + * @cpp false @ce. On the other hand, a scene can't be both 2D and 3D. + * @see @ref hasField() + */ + bool is3D() const { return _dimensions == 3; } + /** * @brief Whether the scene has given field * @m_since_latest + * + * @see @ref is2D(), @ref is3D() */ bool hasField(SceneField name) const; @@ -1430,7 +1477,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * expected to exist and they are expected to have a type corresponding * to 2D, otherwise you're supposed to use * @ref transformations3DAsArray(). - * @see @ref transformations2DInto(), @ref hasField(), + * @see @ref is2D(), @ref transformations2DInto(), @ref hasField(), * @ref fieldType(SceneField) const, @ref transformation2DFor() */ Containers::Array transformations2DAsArray() const; @@ -1476,8 +1523,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * field isn't present, the second value is an identity rotation. If * the @relativeref{SceneField,Scaling} field isn't present, the third * value is an identity scaling (@cpp 1.0f @ce in both dimensions). - * @see @ref translationsRotationsScalings2DInto(), @ref hasField(), - * @ref fieldType(SceneField) const, + * @see @ref is2D(), @ref translationsRotationsScalings2DInto(), + * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling2DFor() */ Containers::Array> translationsRotationsScalings2DAsArray() const; @@ -1522,7 +1569,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * expected to exist and they are expected to have a type corresponding * to 3D, otherwise you're supposed to use * @ref transformations2DAsArray(). - * @see @ref transformations3DInto(), @ref hasField(), + * @see @ref is3D(), @ref transformations3DInto(), @ref hasField(), * @ref fieldType(SceneField) const, @ref transformation3DFor() */ Containers::Array transformations3DAsArray() const; @@ -1568,8 +1615,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * field isn't present, the second value is an identity rotation. If * the @relativeref{SceneField,Scaling} field isn't present, the third * value is an identity scaling (@cpp 1.0f @ce in all dimensions). - * @see @ref translationsRotationsScalings3DInto(), @ref hasField(), - * @ref fieldType(SceneField) const, + * @see @ref is3D(), @ref translationsRotationsScalings3DInto(), + * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling3DFor() */ Containers::Array> translationsRotationsScalings3DAsArray() const; @@ -2114,7 +2161,8 @@ class MAGNUM_TRADE_EXPORT SceneData { DataFlags _dataFlags; SceneObjectType _objectType; - /* 2/6 bytes free */ + UnsignedByte _dimensions; + /* 1/5 bytes free */ UnsignedLong _objectCount; const void* _importerState; Containers::Array _fields; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 82dbb87f4b..306bdfc0a3 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -96,6 +96,7 @@ struct SceneDataTest: TestSuite::Tester { void constructObjectTypeTooSmall(); void constructNotOwnedFlagOwned(); void constructMismatchedTRSViews(); + template void constructMismatchedTRSDimensionality(); void constructMismatchedMeshMaterialView(); void constructCopy(); @@ -115,16 +116,14 @@ struct SceneDataTest: TestSuite::Tester { void parentsIntoArrayInvalidSizeOrOffset(); template void transformations2DAsArray(); template void transformations2DAsArrayTRS(); - template void transformations2DAsArrayBut3DType(); - template void transformations2DAsArrayBut3DTypeTRS(); + void transformations2DAsArrayBut3DType(); void transformations2DIntoArray(); void transformations2DIntoArrayTRS(); void transformations2DIntoArrayInvalidSizeOrOffset(); void transformations2DIntoArrayInvalidSizeOrOffsetTRS(); template void transformations3DAsArray(); template void transformations3DAsArrayTRS(); - template void transformations3DAsArrayBut2DType(); - template void transformations3DAsArrayBut2DTypeTRS(); + void transformations3DAsArrayBut2DType(); void transformations3DIntoArray(); void transformations3DIntoArrayTRS(); void transformations3DIntoArrayInvalidSizeOrOffset(); @@ -161,12 +160,10 @@ struct SceneDataTest: TestSuite::Tester { void childrenFor(); void transformation2DFor(); void transformation2DForTRS(); - template void transformation2DForBut3DType(); - template void transformation2DForBut3DTypeTRS(); + void transformation2DForBut3DType(); void transformation3DFor(); void transformation3DForTRS(); - template void transformation3DForBut2DType(); - template void transformation3DForBut2DTypeTRS(); + void transformation3DForBut2DType(); void meshesMaterialsFor(); void lightsFor(); void camerasFor(); @@ -253,6 +250,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructObjectTypeTooSmall, &SceneDataTest::constructNotOwnedFlagOwned, &SceneDataTest::constructMismatchedTRSViews, + &SceneDataTest::constructMismatchedTRSDimensionality, + &SceneDataTest::constructMismatchedTRSDimensionality, &SceneDataTest::constructMismatchedMeshMaterialView, &SceneDataTest::constructCopy, @@ -292,12 +291,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArrayTRS, &SceneDataTest::transformations2DAsArrayTRS, - &SceneDataTest::transformations2DAsArrayBut3DType, - &SceneDataTest::transformations2DAsArrayBut3DType, - &SceneDataTest::transformations2DAsArrayBut3DType, - &SceneDataTest::transformations2DAsArrayBut3DType, - &SceneDataTest::transformations2DAsArrayBut3DTypeTRS, - &SceneDataTest::transformations2DAsArrayBut3DTypeTRS}); + &SceneDataTest::transformations2DAsArrayBut3DType}); addInstancedTests({&SceneDataTest::transformations2DIntoArray, &SceneDataTest::transformations2DIntoArrayTRS}, @@ -311,12 +305,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArrayTRS, &SceneDataTest::transformations3DAsArrayTRS, - &SceneDataTest::transformations3DAsArrayBut2DType, - &SceneDataTest::transformations3DAsArrayBut2DType, - &SceneDataTest::transformations3DAsArrayBut2DType, - &SceneDataTest::transformations3DAsArrayBut2DType, - &SceneDataTest::transformations3DAsArrayBut2DTypeTRS, - &SceneDataTest::transformations3DAsArrayBut2DTypeTRS}); + &SceneDataTest::transformations3DAsArrayBut2DType}); addInstancedTests({&SceneDataTest::transformations3DIntoArray, &SceneDataTest::transformations3DIntoArrayTRS}, @@ -381,20 +370,10 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::childrenFor, &SceneDataTest::transformation2DFor, &SceneDataTest::transformation2DForTRS, - &SceneDataTest::transformation2DForBut3DType, - &SceneDataTest::transformation2DForBut3DType, - &SceneDataTest::transformation2DForBut3DType, - &SceneDataTest::transformation2DForBut3DType, - &SceneDataTest::transformation2DForBut3DTypeTRS, - &SceneDataTest::transformation2DForBut3DTypeTRS, + &SceneDataTest::transformation2DForBut3DType, &SceneDataTest::transformation3DFor, &SceneDataTest::transformation3DForTRS, - &SceneDataTest::transformation3DForBut2DType, - &SceneDataTest::transformation3DForBut2DType, - &SceneDataTest::transformation3DForBut2DType, - &SceneDataTest::transformation3DForBut2DType, - &SceneDataTest::transformation3DForBut2DTypeTRS, - &SceneDataTest::transformation3DForBut2DTypeTRS, + &SceneDataTest::transformation3DForBut2DType, &SceneDataTest::meshesMaterialsFor, &SceneDataTest::lightsFor, &SceneDataTest::camerasFor, @@ -1136,6 +1115,9 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.fieldCount(), 4); CORRADE_COMPARE(scene.importerState(), &importerState); + /* is2D() / is3D() exhaustively tested in transformations*DAsArray[TRS]() + and constructZeroFields() */ + /* Field property access by ID */ CORRADE_COMPARE(scene.fieldName(0), SceneField::Transformation); CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); @@ -1369,6 +1351,8 @@ void SceneDataTest::constructZeroFields() { CORRADE_COMPARE(scene.objectCount(), 37563); CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); CORRADE_COMPARE(scene.fieldCount(), 0); + CORRADE_VERIFY(!scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); } void SceneDataTest::constructZeroObjects() { @@ -1660,6 +1644,110 @@ void SceneDataTest::constructMismatchedTRSViews() { "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Rotation object data [0xcafe0000:0xcafe0008]\n"); } +template struct NameTraits; +#define _c(format) template<> struct NameTraits { \ + static const char* name() { return #format; } \ + }; +_c(UnsignedByte) +_c(Byte) +_c(UnsignedShort) +_c(Short) +_c(UnsignedInt) +_c(Int) +_c(UnsignedLong) +_c(Long) +_c(Float) +_c(Double) +_c(Vector2) +_c(Vector2d) +_c(Vector3) +_c(Vector3d) +_c(Matrix3) +_c(Matrix3d) +_c(Matrix4) +_c(Matrix4d) +_c(Complex) +_c(Complexd) +_c(Quaternion) +_c(Quaterniond) +_c(DualComplex) +_c(DualComplexd) +_c(DualQuaternion) +_c(DualQuaterniond) +template struct NameTraits { + static const char* name() { return "Pointer"; } +}; +template struct NameTraits { + static const char* name() { return "MutablePointer"; } +}; +#undef _c + +template void SceneDataTest::constructMismatchedTRSDimensionality() { + setTestCaseTemplateName(NameTraits::name()); + + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformations2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformations3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData translations2D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData translations3D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData rotations2D{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData rotations3D{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData scalings2D{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData scalings3D{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + + /* Test that all pairs get checked */ + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, translations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, rotations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, translations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, rotations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations2D, rotations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {rotations2D, scalings3D}}; + + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, translations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, rotations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, translations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, rotations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations3D, rotations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {rotations3D, scalings2D}}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n", + NameTraits>::name(), + NameTraits>::name(), + NameTraits>::name(), + NameTraits>::name())); +} + void SceneDataTest::constructMismatchedMeshMaterialView() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -1744,48 +1832,6 @@ void SceneDataTest::constructMove() { CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } -template struct NameTraits; -#define _c(format) template<> struct NameTraits { \ - static const char* name() { return #format; } \ - }; -_c(UnsignedByte) -_c(Byte) -_c(UnsignedShort) -_c(Short) -_c(UnsignedInt) -_c(Int) -_c(UnsignedLong) -_c(Long) -_c(Float) -_c(Double) -_c(Vector2) -_c(Vector2d) -_c(Vector3) -_c(Vector3d) -_c(Matrix3) -_c(Matrix3x3) -_c(Matrix3d) -_c(Matrix3x3d) -_c(Matrix4) -_c(Matrix4x4) -_c(Matrix4d) -_c(Matrix4x4d) -_c(Complex) -_c(Complexd) -_c(Quaternion) -_c(Quaterniond) -_c(DualComplex) -_c(DualComplexd) -_c(DualQuaternion) -_c(DualQuaterniond) -template struct NameTraits { - static const char* name() { return "Pointer"; } -}; -template struct NameTraits { - static const char* name() { return "MutablePointer"; } -}; -#undef _c - template void SceneDataTest::objectsAsArrayByIndex() { setTestCaseTemplateName(NameTraits::name()); @@ -2165,6 +2211,8 @@ template void SceneDataTest::transformations2DAsArray() { components.slice(&Component::scaling)}, }}; + 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), @@ -2216,6 +2264,8 @@ template void SceneDataTest::transformations2DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { translation }}; + CORRADE_VERIFY(scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ Matrix3::translation({3.0f, 2.0f}), Matrix3{Math::IdentityInit}, @@ -2234,6 +2284,8 @@ template void SceneDataTest::transformations2DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation }}; + CORRADE_VERIFY(scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ Matrix3{Math::IdentityInit}, Matrix3::rotation(35.0_degf), @@ -2252,6 +2304,8 @@ template void SceneDataTest::transformations2DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { scaling }}; + CORRADE_VERIFY(scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ Matrix3{Math::IdentityInit}, Matrix3{Math::IdentityInit}, @@ -2274,6 +2328,8 @@ template void SceneDataTest::transformations2DAsArray translation, rotation }}; + 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), @@ -2293,6 +2349,8 @@ template void SceneDataTest::transformations2DAsArray translation, scaling }}; + CORRADE_VERIFY(scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ Matrix3::translation({3.0f, 2.0f}), Matrix3{Math::IdentityInit}, @@ -2312,6 +2370,8 @@ template void SceneDataTest::transformations2DAsArray rotation, scaling }}; + CORRADE_VERIFY(scene.is2D()); + CORRADE_VERIFY(!scene.is3D()); CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ Matrix3{Math::IdentityInit}, Matrix3::rotation(35.0_degf), @@ -2335,6 +2395,8 @@ template void SceneDataTest::transformations2DAsArray rotation, scaling }}; + 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), @@ -2352,27 +2414,7 @@ template void SceneDataTest::transformations2DAsArray } } -template void SceneDataTest::transformations2DAsArrayBut3DType() { - setTestCaseTemplateName(NameTraits::name()); - - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} - }}; - - std::ostringstream out; - Error redirectError{&out}; - scene.transformations2DAsArray(); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformations2DInto(): field has a 3D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); -} - -template void SceneDataTest::transformations2DAsArrayBut3DTypeTRS() { - setTestCaseTemplateName(NameTraits::name()); - +void SceneDataTest::transformations2DAsArrayBut3DType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2380,40 +2422,19 @@ template void SceneDataTest::transformations2DAsArrayBut3DTypeTRS() { /* Because TRSAsArray() allocates an Array and then calls TRSInto(), which skips views that are nullptr, we wouldn't get the assertion for translations, as those are at offset 0, which would be - interpreted as an empty view if there were no elements. So we have to - supply at least one element to make all assertions trigger. */ - struct Field { - UnsignedInt object; - Math::Vector3 translation; - Math::Quaternion rotation; - Math::Vector3 scaling; - } data[1]; - Containers::StridedArrayView1D view = data; - SceneData translation{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} - }}; - SceneData rotation{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)} - }}; - SceneData scaling{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)} + interpreted as an empty view if there were no elements. Thus using + rotations instead. */ + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Quaternion, nullptr} }}; std::ostringstream out; Error redirectError{&out}; - translation.transformations2DAsArray(); - translation.translationsRotationsScalings2DAsArray(); - rotation.transformations2DAsArray(); - rotation.translationsRotationsScalings2DAsArray(); - scaling.transformations2DAsArray(); - scaling.translationsRotationsScalings2DAsArray(); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformations2DInto(): field has a 3D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::transformations2DInto(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformations2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + scene.transformations2DAsArray(); + scene.translationsRotationsScalings2DAsArray(); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformations2DInto(): scene has a 3D transformation type\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): scene has a 3D transformation type\n"); } void SceneDataTest::transformations2DIntoArray() { @@ -2740,6 +2761,8 @@ template void SceneDataTest::transformations3DAsArray() { components.slice(&Component::scaling)}, }}; + 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), @@ -2791,6 +2814,8 @@ template void SceneDataTest::transformations3DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { translation }}; + 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}, @@ -2809,6 +2834,8 @@ template void SceneDataTest::transformations3DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { rotation }}; + CORRADE_VERIFY(!scene.is2D()); + CORRADE_VERIFY(scene.is3D()); CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ Matrix4{Math::IdentityInit}, Matrix4::rotationY(35.0_degf), @@ -2827,6 +2854,8 @@ template void SceneDataTest::transformations3DAsArray SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { scaling }}; + CORRADE_VERIFY(!scene.is2D()); + CORRADE_VERIFY(scene.is3D()); CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ Matrix4{Math::IdentityInit}, Matrix4{Math::IdentityInit}, @@ -2849,6 +2878,8 @@ template void SceneDataTest::transformations3DAsArray translation, rotation }}; + 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), @@ -2868,6 +2899,8 @@ template void SceneDataTest::transformations3DAsArray translation, scaling }}; + 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}, @@ -2887,6 +2920,8 @@ template void SceneDataTest::transformations3DAsArray rotation, scaling }}; + CORRADE_VERIFY(!scene.is2D()); + CORRADE_VERIFY(scene.is3D()); CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ Matrix4{Math::IdentityInit}, Matrix4::rotationY(35.0_degf), @@ -2910,6 +2945,8 @@ template void SceneDataTest::transformations3DAsArray rotation, scaling }}; + 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), @@ -2927,27 +2964,7 @@ template void SceneDataTest::transformations3DAsArray } } -template void SceneDataTest::transformations3DAsArrayBut2DType() { - setTestCaseTemplateName(NameTraits::name()); - - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} - }}; - - std::ostringstream out; - Error redirectError{&out}; - scene.transformations3DAsArray(); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformations3DInto(): field has a 2D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); -} - -template void SceneDataTest::transformations3DAsArrayBut2DTypeTRS() { - setTestCaseTemplateName(NameTraits::name()); - +void SceneDataTest::transformations3DAsArrayBut2DType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2955,40 +2972,19 @@ template void SceneDataTest::transformations3DAsArrayBut2DTypeTRS() { /* Because TRSAsArray() allocates an Array and then calls TRSInto(), which skips views that are nullptr, we wouldn't get the assertion for translations, as those are at offset 0, which would be - interpreted as an empty view if there were no elements. So we have to - supply at least one element to make all assertions trigger. */ - struct Field { - UnsignedInt object; - Math::Vector2 translation; - Math::Complex rotation; - Math::Vector2 scaling; - } data[1]; - Containers::StridedArrayView1D view = data; - SceneData translation{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} - }}; - SceneData rotation{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)} - }}; - SceneData scaling{SceneObjectType::UnsignedInt, 1, {}, data, { - SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)} + interpreted as an empty view if there were no elements. Thus using + rotations instead. */ + SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Complex, nullptr} }}; std::ostringstream out; Error redirectError{&out}; - translation.transformations3DAsArray(); - translation.translationsRotationsScalings3DAsArray(); - rotation.transformations3DAsArray(); - rotation.translationsRotationsScalings3DAsArray(); - scaling.transformations3DAsArray(); - scaling.translationsRotationsScalings3DAsArray(); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformations3DInto(): field has a 2D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::transformations3DInto(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformations3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + scene.transformations3DAsArray(); + scene.translationsRotationsScalings3DAsArray(); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformations3DInto(): scene has a 2D transformation type\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): scene has a 2D transformation type\n"); } void SceneDataTest::transformations3DIntoArray() { @@ -4387,56 +4383,22 @@ void SceneDataTest::transformation2DForTRS() { Containers::NullOpt); } -template void SceneDataTest::transformation2DForBut3DType() { - setTestCaseTemplateName(NameTraits::name()); - +void SceneDataTest::transformation2DForBut3DType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr} }}; std::ostringstream out; Error redirectError{&out}; scene.transformation2DFor(0); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformation2DFor(): field has a 3D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); -} - -template void SceneDataTest::transformation2DForBut3DTypeTRS() { - setTestCaseTemplateName(NameTraits::name()); - - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - - std::ostringstream out; - Error redirectError{&out}; - translation.transformation2DFor(0); - translation.translationRotationScaling2DFor(0); - rotation.transformation2DFor(0); - rotation.translationRotationScaling2DFor(0); - scaling.transformation2DFor(0); - scaling.translationRotationScaling2DFor(0); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformation2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::transformation2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformation2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + scene.translationRotationScaling2DFor(0); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformation2DFor(): scene has a 3D transformation type\n" + "Trade::SceneData::translationRotationScaling2DFor(): scene has a 3D transformation type\n"); } void SceneDataTest::transformation3DFor() { @@ -4513,56 +4475,22 @@ void SceneDataTest::transformation3DForTRS() { Containers::NullOpt); } -template void SceneDataTest::transformation3DForBut2DType() { - setTestCaseTemplateName(NameTraits::name()); - +void SceneDataTest::transformation3DForBut2DType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor::type(), nullptr} + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr} }}; std::ostringstream out; Error redirectError{&out}; scene.transformation3DFor(0); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformation3DFor(): field has a 2D transformation type Trade::SceneFieldType::{}\n", NameTraits::name())); -} - -template void SceneDataTest::transformation3DForBut2DTypeTRS() { - setTestCaseTemplateName(NameTraits::name()); - - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr} - }}; - - std::ostringstream out; - Error redirectError{&out}; - translation.transformation3DFor(0); - translation.translationRotationScaling3DFor(0); - rotation.transformation3DFor(0); - rotation.translationRotationScaling3DFor(0); - scaling.transformation3DFor(0); - scaling.translationRotationScaling3DFor(0); - CORRADE_COMPARE(out.str(), Utility::formatString( - "Trade::SceneData::transformation3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::transformation3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n" - "Trade::SceneData::transformation3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n" - "Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits>::name(), NameTraits>::name())); + scene.translationRotationScaling3DFor(0); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::transformation3DFor(): scene has a 2D transformation type\n" + "Trade::SceneData::translationRotationScaling3DFor(): scene has a 2D transformation type\n"); } void SceneDataTest::meshesMaterialsFor() { From 67900c79a4b6a5c5f2a9f24b7f3a3e8c5a6e683e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 4 Oct 2021 19:59:29 +0200 Subject: [PATCH 16/49] Trade: SceneField::Skin now requires a transformation to be present. Even an empty field, it's there just to disambiguate whether the skin is 2D or 3D. --- src/Magnum/Trade/SceneData.cpp | 8 ++++++ src/Magnum/Trade/SceneData.h | 10 +++++--- src/Magnum/Trade/Test/SceneDataTest.cpp | 33 ++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index d22c78a2b3..8fa76a42d7 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -384,6 +384,7 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object #ifndef CORRADE_NO_ASSERT UnsignedInt meshField = ~UnsignedInt{}; UnsignedInt meshMaterialField = ~UnsignedInt{}; + UnsignedInt skinField = ~UnsignedInt{}; #endif for(std::size_t i = 0; i != _fields.size(); ++i) { const SceneFieldData& field = _fields[i]; @@ -465,6 +466,8 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object meshField = i; } else if(_fields[i]._name == SceneField::MeshMaterial) { meshMaterialField = i; + } else if(_fields[i]._name == SceneField::Skin) { + skinField = i; } #endif } @@ -560,6 +563,11 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object _dimensions = 3; } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } + + /* A skin field requires some transformation field to exist in order to + disambiguate between 2D and 3D */ + CORRADE_ASSERT(skinField == ~UnsignedInt{} || _dimensions, + "Trade::SceneData: a skin field requires some transformation field to be present in order to disambiguate between 2D and 3D", ); } SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 8a9a5b9b1a..0884eb174e 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -273,12 +273,14 @@ enum class SceneField: UnsignedInt { /** * ID of a skin associated with this object, corresponding to the ID - * passed to @ref AbstractImporter::skin(). Type is usually - * @ref SceneFieldType::UnsignedInt, but can be also any of - * @relativeref{SceneFieldType,UnsignedByte} or + * passed to @ref AbstractImporter::skin2D() or + * @ref AbstractImporter::skin3D(), depending on whether the scene has a 2D + * or 3D transformation. Type is usually @ref SceneFieldType::UnsignedInt, + * but can be also any of @relativeref{SceneFieldType,UnsignedByte} or * @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple * skins associated. - * @see @ref SceneData::skinsAsArray(), @ref SceneData::skinsFor() + * @see @ref SceneData::is2D(), @ref SceneData::is3D(), + * @ref SceneData::skinsAsArray(), @ref SceneData::skinsFor() */ Skin, diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 306bdfc0a3..95d69eedcb 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -98,6 +98,7 @@ struct SceneDataTest: TestSuite::Tester { void constructMismatchedTRSViews(); template void constructMismatchedTRSDimensionality(); void constructMismatchedMeshMaterialView(); + void constructAmbiguousSkinDimensions(); void constructCopy(); void constructMove(); @@ -253,6 +254,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructMismatchedTRSDimensionality, &SceneDataTest::constructMismatchedTRSDimensionality, &SceneDataTest::constructMismatchedMeshMaterialView, + &SceneDataTest::constructAmbiguousSkinDimensions, &SceneDataTest::constructCopy, &SceneDataTest::constructMove, @@ -1778,6 +1780,19 @@ void SceneDataTest::constructMismatchedMeshMaterialView() { "Trade::SceneData: Trade::SceneField::MeshMaterial object data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Mesh object data [0xcafe0000:0xcafe000c]\n"); } +void SceneDataTest::constructAmbiguousSkinDimensions() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Skin, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr} + }}; + CORRADE_COMPARE(out.str(), "Trade::SceneData: a skin field requires some transformation field to be present in order to disambiguate between 2D and 3D\n"); +} + void SceneDataTest::constructCopy() { CORRADE_VERIFY(!std::is_copy_constructible{}); CORRADE_VERIFY(!std::is_copy_assignable{}); @@ -3649,8 +3664,10 @@ template void SceneDataTest::skinsAsArray() { 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}, + /* To verify it isn't just picking the first ever field; also to + satisfy the requirement of having a transformation field to + disambiguate the dimensionality */ + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; @@ -3679,8 +3696,10 @@ void SceneDataTest::skinsIntoArray() { 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}, + /* To verify it isn't just picking the first ever field; also to + satisfy the requirement of having a transformation field to + disambiguate the dimensionality */ + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)}, @@ -3718,6 +3737,9 @@ void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + /* To satisfy the requirement of having a transformation field to + disambiguate the dimensionality */ + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; @@ -4617,6 +4639,9 @@ void SceneDataTest::skinsFor() { Containers::StridedArrayView1D view = fields; SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + /* To satisfy the requirement of having a transformation field to + disambiguate the dimensionality */ + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; From 4da64d61e2833a2761ab1f95921fd738181dfa8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 2 Sep 2021 16:59:59 +0200 Subject: [PATCH 17/49] Trade: reintroduce original deprecated SceneData APIs. Those now operate on top of the new data layout. --- doc/changelog.dox | 13 +++ src/Magnum/Trade/SceneData.cpp | 62 ++++++++++++ src/Magnum/Trade/SceneData.h | 33 ++++++ src/Magnum/Trade/Test/SceneDataTest.cpp | 129 +++++++++++++++++++++++- 4 files changed, 234 insertions(+), 3 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index db333ca6ba..28a708d8e9 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -632,6 +632,15 @@ See also: directly but rather generated on-the-fly from attribute data, which makes them less efficient than calling @ref Trade::MaterialData::hasAttribute() etc. +- @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D + children is deprecated in favor of the new scene representation. Use + @ref Trade::SceneData::SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + instead. +- @cpp Trade::SceneData::children2D() @ce and + @cpp Trade::SceneData::children3D() @ce are deprecated in favor of the + new scene representation. Use @ref Trade::SceneData::childrenFor() with + @cpp -1 @ce passed as the @p object argument to get a list of top-level + objects. - @ref Trade::AbstractImporter::material() now returns @ref Corrade::Containers::Optional instead of a @ref Corrade::Containers::Pointer, as the new @ref Trade::MaterialData class isn't polymorphic anymore. If @@ -805,6 +814,10 @@ See also: @cpp Trade::AbstractMaterialData @ce aliases to, doesn't have a @cpp virtual @ce destructor as subclasses with extra data members aren't a desired use case anymore. +- @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D + children that got deprecated in favor of + @ref Trade::SceneData::SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + no longer accepts a scene that has both 2D and 3D children. - @ref Trade::AbstractImporter::doDefaultScene() is now @cpp const @ce --- since the function is not expected to fail, no kind of complex lazy population can be done anyway. This is now consistent with `do*Count()` diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 8fa76a42d7..b5599796c8 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -39,6 +39,12 @@ #include "Magnum/Math/PackingBatch.h" #include "Magnum/Trade/Implementation/arrayUtilities.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include +#include +#endif + namespace Magnum { namespace Trade { Debug& operator<<(Debug& debug, const SceneObjectType value) { @@ -580,6 +586,48 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, dataFlags, data, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} +#ifdef MAGNUM_BUILD_DEPRECATED +SceneData::SceneData(std::vector children2D, std::vector children3D, const void* const importerState): _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{SceneObjectType::UnsignedInt}, _importerState{importerState} { + /* Assume nobody ever created a scene with both 2D and 3D children. + (PrimitiveImporter did, but that's an exception and blame goes on me.) + If this blows up for you, please complain. Or rather upgrade to the new + API which ... forces you to have a separate scene for 2D and 3D. But you + can share the data among the two, at least. */ + CORRADE_ASSERT(children2D.empty() || children3D.empty(), + "Trade::SceneData: it's no longer possible to have a scene with both 2D and 3D objects", ); + Containers::ArrayView children; + if(!children2D.empty()) { + _dimensions = 2; + children = children2D; + } else if(!children3D.empty()) { + _dimensions = 3; + children = children3D; + } else _dimensions = 0; + + /* Set object count to the max found child index. It's not great as it + doesn't take any nested object into account but SceneData created this + way is expected to be used only through the deprecated APIs anyway, + which don't care about this value. */ + _objectCount = children.empty() ? 0 : Math::max(children) + 1; + + /* Convert the vector with top-level object IDs to the parent field, where + all have -1 as a parent. This way the (also deprecated) children2D() / + children3D() will return the desired values. */ + Containers::ArrayView objects; + Containers::ArrayView parents; + _data = Containers::ArrayTuple{ + {NoInit, children.size(), objects}, + {NoInit, children.size(), parents}, + }; + _fields = {InPlaceInit, { + SceneFieldData{SceneField::Parent, objects, parents} + }}; + Utility::copy(children, objects); + constexpr Int parent[]{-1}; + Utility::copy(Containers::stridedArrayView(parent).broadcasted<0>(parents.size()), parents); +} +#endif + SceneData::SceneData(SceneData&&) noexcept = default; SceneData::~SceneData() = default; @@ -1932,6 +1980,20 @@ Containers::Optional SceneData::importerStateFor(const UnsignedInt return *importerState; } +#ifdef MAGNUM_BUILD_DEPRECATED +std::vector SceneData::children2D() const { + if(_dimensions != 2) return {}; + const Containers::Array children = childrenFor(-1); + return {children.begin(), children.end()}; +} + +std::vector SceneData::children3D() const { + if(_dimensions != 3) return {}; + const Containers::Array children = childrenFor(-1); + return {children.begin(), children.end()}; +} +#endif + Containers::Array SceneData::releaseFieldData() { Containers::Array out = std::move(_fields); _fields = {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 0884eb174e..e6e4a43446 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -36,6 +36,11 @@ #include "Magnum/Trade/Trade.h" #include "Magnum/Trade/visibility.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include +#endif + namespace Magnum { namespace Trade { /** @@ -821,6 +826,18 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Not noexcept because allocation happens inside */ explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, DataFlags dataFlags, Containers::ArrayView data, std::initializer_list fields, const void* importerState = nullptr); + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Constructor + * @param children2D Two-dimensional child objects + * @param children3D Three-dimensional child objects + * @param importerState Importer-specific state + * @m_deprecated_since_latest Use @ref SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + * instead. + */ + explicit CORRADE_DEPRECATED("use SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) instead") SceneData(std::vector children2D, std::vector children3D, const void* importerState = nullptr); + #endif + /** @brief Copying is not allowed */ SceneData(const SceneData&) = delete; @@ -2080,6 +2097,22 @@ class MAGNUM_TRADE_EXPORT SceneData { */ Containers::Optional importerStateFor(UnsignedInt object) const; + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Two-dimensional root scene objects + * @m_deprecated_since_latest Use @ref childrenFor() with `-1` passed + * as the @p object argument. + */ + CORRADE_DEPRECATED("use childrenFor() instead") std::vector children2D() const; + + /** + * @brief Three-dimensional root scene objects + * @m_deprecated_since_latest Use @ref childrenFor() with `-1` passed + * as the @p object argument. + */ + CORRADE_DEPRECATED("use childrenFor() instead") std::vector children3D() const; + #endif + /** * @brief Release field data storage * @m_since_latest diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 95d69eedcb..ecf3f3f51b 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -40,6 +40,11 @@ #include "Magnum/Math/Range.h" #include "Magnum/Trade/SceneData.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#include +#endif + namespace Magnum { namespace Trade { namespace Test { namespace { struct SceneDataTest: TestSuite::Tester { @@ -85,8 +90,11 @@ struct SceneDataTest: TestSuite::Tester { void construct(); void constructZeroFields(); void constructZeroObjects(); - void constructNotOwned(); + #ifdef MAGNUM_BUILD_DEPRECATED + void constructDeprecated(); + void constructDeprecatedBoth2DAnd3D(); + #endif void constructDuplicateField(); void constructDuplicateCustomField(); @@ -171,6 +179,10 @@ struct SceneDataTest: TestSuite::Tester { void skinsFor(); void importerStateFor(); + #ifdef MAGNUM_BUILD_DEPRECATED + void childrenDeprecated(); + #endif + void fieldForFieldMissing(); void fieldForInvalidObject(); @@ -198,6 +210,18 @@ const struct { {"offset at the end", 3, 10, 0} }; +#ifdef MAGNUM_BUILD_DEPRECATED +const struct { + const char* name; + bool is2D; + bool is3D; +} ChildrenDeprecatedData[]{ + {"2D", true, false}, + {"3D", false, true}, + {"neither", false, false} +}; +#endif + SceneDataTest::SceneDataTest() { addTests({&SceneDataTest::objectTypeSize, &SceneDataTest::objectTypeSizeInvalid, @@ -243,6 +267,13 @@ SceneDataTest::SceneDataTest() { addInstancedTests({&SceneDataTest::constructNotOwned}, Containers::arraySize(NotOwnedData)); + #ifdef MAGNUM_BUILD_DEPRECATED + addInstancedTests({&SceneDataTest::constructDeprecated}, + Containers::arraySize(ChildrenDeprecatedData)); + + addTests({&SceneDataTest::constructDeprecatedBoth2DAnd3D}); + #endif + addTests({&SceneDataTest::constructDuplicateField, &SceneDataTest::constructDuplicateCustomField, &SceneDataTest::constructInconsistentObjectType, @@ -380,9 +411,14 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::lightsFor, &SceneDataTest::camerasFor, &SceneDataTest::skinsFor, - &SceneDataTest::importerStateFor, + &SceneDataTest::importerStateFor}); + + #ifdef MAGNUM_BUILD_DEPRECATED + addInstancedTests({&SceneDataTest::childrenDeprecated}, + Containers::arraySize(ChildrenDeprecatedData)); + #endif - &SceneDataTest::fieldForFieldMissing, + addTests({&SceneDataTest::fieldForFieldMissing, &SceneDataTest::fieldForInvalidObject, &SceneDataTest::releaseFieldData, @@ -1419,6 +1455,56 @@ void SceneDataTest::constructNotOwned() { CORRADE_COMPARE(scene.mutableField(0)[2], 0); } +#ifdef MAGNUM_BUILD_DEPRECATED +void SceneDataTest::constructDeprecated() { + auto&& data = ChildrenDeprecatedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + int a; + CORRADE_IGNORE_DEPRECATED_PUSH + SceneData scene{ + data.is2D ? std::vector{5, 17, 36, 22} : std::vector{}, + data.is3D ? std::vector{5, 17, 36, 22} : std::vector{}, + &a}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + if(data.is2D || data.is3D) + CORRADE_COMPARE(scene.objectCount(), 37); + else + CORRADE_COMPARE(scene.objectCount(), 0); + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Mutable|DataFlag::Owned); + CORRADE_COMPARE(scene.importerState(), &a); + CORRADE_COMPARE(scene.fieldCount(), 1); + CORRADE_COMPARE(scene.fieldName(0), SceneField::Parent); + CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Int); + if(data.is2D || data.is3D) { + CORRADE_COMPARE_AS(scene.objects(0), + Containers::arrayView({5, 17, 36, 22}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(0), + Containers::arrayView({-1, -1, -1, -1}), + TestSuite::Compare::Container); + } else CORRADE_COMPARE(scene.fieldSize(SceneField::Parent), 0); + /* There's no transformation field that would disambiguate this, the state + is set directly */ + CORRADE_COMPARE(scene.is2D(), data.is2D); + CORRADE_COMPARE(scene.is3D(), data.is3D); +} + +void SceneDataTest::constructDeprecatedBoth2DAnd3D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH + SceneData scene{{5, 17}, {36, 22}}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::SceneData: it's no longer possible to have a scene with both 2D and 3D objects\n"); +} +#endif + void SceneDataTest::constructDuplicateField() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -4695,6 +4781,43 @@ void SceneDataTest::importerStateFor() { CORRADE_COMPARE(scene.importerStateFor(1), Containers::NullOpt); } +#ifdef MAGNUM_BUILD_DEPRECATED +void SceneDataTest::childrenDeprecated() { + auto&& data = ChildrenDeprecatedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + struct Field { + UnsignedByte object; + Short parent; + } fields[]{ + {5, -1}, + {2, 0}, + {3, 0}, + {0, -1}, + {1, 2}, + {4, -1} + }; + Containers::StridedArrayView1D view = fields; + + Containers::Array fieldData; + arrayAppend(fieldData, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}); + if(data.is2D) + arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector2, nullptr}); + if(data.is3D) + arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}); + + SceneData scene{SceneObjectType::UnsignedByte, 25, {}, fields, std::move(fieldData)}; + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene.children2D(), + (data.is2D ? std::vector{5, 0, 4} : std::vector{}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.children3D(), + (data.is3D ? std::vector{5, 0, 4} : std::vector{}), + TestSuite::Compare::Container); + CORRADE_IGNORE_DEPRECATED_POP +} +#endif + void SceneDataTest::fieldForFieldMissing() { SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; From e858ab2dd85c27150d3114e064392719a5ab7dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 11 Oct 2021 19:29:24 +0200 Subject: [PATCH 18/49] Trade: add scene{Object,Field}TypeAlignment(). Support utilities needed for SceneTools that will operate on arbitrary SceneData (adding/removing fields, objects...) without having to know what each field means and how it needs to be treated. Honestly the same would make sense for the VertexFormat enum as well. But not now, later. --- src/Magnum/Trade/SceneData.cpp | 130 ++++++++++++++++++++++++ src/Magnum/Trade/SceneData.h | 28 ++++- src/Magnum/Trade/Test/SceneDataTest.cpp | 47 ++++++--- 3 files changed, 188 insertions(+), 17 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index b5599796c8..cc83bb7105 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -82,6 +82,24 @@ UnsignedInt sceneObjectTypeSize(const SceneObjectType type) { CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {}); } +UnsignedInt sceneObjectTypeAlignment(const SceneObjectType type) { + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(type) { + case SceneObjectType::UnsignedByte: return 1; + case SceneObjectType::UnsignedShort: return 2; + case SceneObjectType::UnsignedInt: return 4; + case SceneObjectType::UnsignedLong: return 8; + } + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic pop + #endif + + CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeAlignment(): invalid type" << type, {}); +} + Debug& operator<<(Debug& debug, const SceneField value) { debug << "Trade::SceneField" << Debug::nospace; @@ -343,6 +361,118 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): invalid type" << type, {}); } +UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) { + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(type) { + case SceneFieldType::UnsignedByte: + case SceneFieldType::Vector2ub: + case SceneFieldType::Vector3ub: + case SceneFieldType::Vector4ub: + case SceneFieldType::Byte: + case SceneFieldType::Vector2b: + case SceneFieldType::Vector3b: + case SceneFieldType::Vector4b: + return 1; + case SceneFieldType::UnsignedShort: + case SceneFieldType::Vector2us: + case SceneFieldType::Vector3us: + case SceneFieldType::Vector4us: + case SceneFieldType::Short: + case SceneFieldType::Vector2s: + case SceneFieldType::Vector3s: + case SceneFieldType::Vector4s: + case SceneFieldType::Half: + case SceneFieldType::Vector2h: + case SceneFieldType::Vector3h: + case SceneFieldType::Vector4h: + case SceneFieldType::Matrix2x2h: + case SceneFieldType::Matrix2x3h: + case SceneFieldType::Matrix2x4h: + case SceneFieldType::Matrix3x2h: + case SceneFieldType::Matrix3x3h: + case SceneFieldType::Matrix3x4h: + case SceneFieldType::Matrix4x2h: + case SceneFieldType::Matrix4x3h: + case SceneFieldType::Matrix4x4h: + case SceneFieldType::Range1Dh: + case SceneFieldType::Range2Dh: + case SceneFieldType::Range3Dh: + case SceneFieldType::Degh: + case SceneFieldType::Radh: + return 2; + case SceneFieldType::UnsignedInt: + case SceneFieldType::Vector2ui: + case SceneFieldType::Vector3ui: + case SceneFieldType::Vector4ui: + case SceneFieldType::Int: + case SceneFieldType::Vector2i: + case SceneFieldType::Vector3i: + case SceneFieldType::Vector4i: + case SceneFieldType::Float: + case SceneFieldType::Vector2: + case SceneFieldType::Vector3: + case SceneFieldType::Vector4: + case SceneFieldType::Matrix2x2: + case SceneFieldType::Matrix2x3: + case SceneFieldType::Matrix2x4: + case SceneFieldType::Matrix3x2: + case SceneFieldType::Matrix3x3: + case SceneFieldType::Matrix3x4: + case SceneFieldType::Matrix4x2: + case SceneFieldType::Matrix4x3: + case SceneFieldType::Matrix4x4: + case SceneFieldType::Range1Di: + case SceneFieldType::Range2Di: + case SceneFieldType::Range3Di: + case SceneFieldType::Range1D: + case SceneFieldType::Range2D: + case SceneFieldType::Range3D: + case SceneFieldType::Complex: + case SceneFieldType::Quaternion: + case SceneFieldType::DualComplex: + case SceneFieldType::DualQuaternion: + case SceneFieldType::Deg: + case SceneFieldType::Rad: + return 4; + case SceneFieldType::UnsignedLong: + case SceneFieldType::Long: + case SceneFieldType::Double: + case SceneFieldType::Vector2d: + case SceneFieldType::Vector3d: + case SceneFieldType::Vector4d: + case SceneFieldType::Matrix2x2d: + case SceneFieldType::Matrix2x3d: + case SceneFieldType::Matrix2x4d: + case SceneFieldType::Matrix3x2d: + case SceneFieldType::Matrix3x3d: + case SceneFieldType::Matrix3x4d: + case SceneFieldType::Matrix4x2d: + case SceneFieldType::Matrix4x3d: + case SceneFieldType::Matrix4x4d: + case SceneFieldType::Range1Dd: + case SceneFieldType::Range2Dd: + case SceneFieldType::Range3Dd: + case SceneFieldType::Complexd: + case SceneFieldType::Quaterniond: + case SceneFieldType::DualComplexd: + case SceneFieldType::DualQuaterniond: + case SceneFieldType::Degd: + case SceneFieldType::Radd: + return 8; + case SceneFieldType::Pointer: + case SceneFieldType::MutablePointer: + return sizeof(void*); + } + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic pop + #endif + + CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeAlignment(): invalid type" << type, {}); +} + SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{objectData.data(), ~std::size_t{}}, objectData.size()[0], objectData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} { /* Yes, this calls into a constexpr function defined in the header -- because I feel that makes more sense than duplicating the full assert diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index e6e4a43446..a3f825854f 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneObjectType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneObjectTypeSize(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() + * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneObjectType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneObjectTypeSize(), @ref Magnum::sceneObjectTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() */ #include @@ -51,7 +51,8 @@ Type used for mapping fields to corresponding objects. Unlike @ref SceneFieldType that is different for different fields, the object type is the same for all fields, and is guaranteed to be large enough to fit all @ref SceneData::objectCount() objects. -@see @ref SceneData::objectType(), @ref sceneObjectTypeSize() +@see @ref SceneData::objectType(), @ref sceneObjectTypeSize(), + @ref sceneObjectTypeAlignment() */ enum class SceneObjectType: UnsignedByte { /* Zero used for an invalid value */ @@ -80,9 +81,19 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneObjectType value); /** @brief Size of given scene object type @m_since_latest + +@see @ref sceneObjectTypeAlignment() */ MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeSize(SceneObjectType type); +/** +@brief Alignment of given scene object type +@m_since_latest + +Returns the same value as @ref sceneObjectTypeSize(). +*/ +MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeAlignment(SceneObjectType type); + /** @brief Scene field name @m_since_latest @@ -368,7 +379,8 @@ constexpr UnsignedInt sceneFieldCustom(SceneField name) { A type in which a @ref SceneField is stored. See @ref SceneData for more information. -@see @ref SceneFieldData, @ref sceneFieldTypeSize() +@see @ref SceneFieldData, @ref sceneFieldTypeSize(), + @ref sceneFieldTypeAlignment() */ enum class SceneFieldType: UnsignedShort { /* Zero used for an invalid value */ @@ -509,9 +521,19 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldType value); /** @brief Size of given scene field type @m_since_latest + +@see @ref sceneFieldTypeAlignment() */ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type); +/** +@brief Alignment of given scene field type +@m_since_latest + +@see @ref sceneFieldTypeSize() +*/ +MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type); + /** @brief Scene field data @m_since_latest diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index ecf3f3f51b..e990ccf651 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -50,8 +50,8 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct SceneDataTest: TestSuite::Tester { explicit SceneDataTest(); - void objectTypeSize(); - void objectTypeSizeInvalid(); + void objectTypeSizeAlignment(); + void objectTypeSizeAlignmentInvalid(); void debugObjectType(); void customFieldName(); @@ -59,8 +59,8 @@ struct SceneDataTest: TestSuite::Tester { void customFieldNameNotCustom(); void debugFieldName(); - void fieldTypeSize(); - void fieldTypeSizeInvalid(); + void fieldTypeSizeAlignment(); + void fieldTypeSizeAlignmentInvalid(); void debugFieldType(); void constructField(); @@ -223,8 +223,8 @@ const struct { #endif SceneDataTest::SceneDataTest() { - addTests({&SceneDataTest::objectTypeSize, - &SceneDataTest::objectTypeSizeInvalid, + addTests({&SceneDataTest::objectTypeSizeAlignment, + &SceneDataTest::objectTypeSizeAlignmentInvalid, &SceneDataTest::debugObjectType, &SceneDataTest::customFieldName, @@ -232,8 +232,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::customFieldNameNotCustom, &SceneDataTest::debugFieldName, - &SceneDataTest::fieldTypeSize, - &SceneDataTest::fieldTypeSizeInvalid, + &SceneDataTest::fieldTypeSizeAlignment, + &SceneDataTest::fieldTypeSizeAlignmentInvalid, &SceneDataTest::debugFieldType, &SceneDataTest::constructField, @@ -427,14 +427,18 @@ SceneDataTest::SceneDataTest() { using namespace Math::Literals; -void SceneDataTest::objectTypeSize() { +void SceneDataTest::objectTypeSizeAlignment() { CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedByte), 1); + CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedByte), 1); CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedShort), 2); + CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedShort), 2); CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedInt), 4); + CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedInt), 4); CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedLong), 8); + CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedLong), 8); } -void SceneDataTest::objectTypeSizeInvalid() { +void SceneDataTest::objectTypeSizeAlignmentInvalid() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -443,11 +447,15 @@ void SceneDataTest::objectTypeSizeInvalid() { Error redirectError{&out}; sceneObjectTypeSize(SceneObjectType{}); + sceneObjectTypeAlignment(SceneObjectType{}); sceneObjectTypeSize(SceneObjectType(0x73)); + sceneObjectTypeAlignment(SceneObjectType(0x73)); CORRADE_COMPARE(out.str(), "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x0)\n" - "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x73)\n"); + "Trade::sceneObjectTypeAlignment(): invalid type Trade::SceneObjectType(0x0)\n" + "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x73)\n" + "Trade::sceneObjectTypeAlignment(): invalid type Trade::SceneObjectType(0x73)\n"); } void SceneDataTest::debugObjectType() { @@ -506,7 +514,7 @@ void SceneDataTest::debugFieldName() { CORRADE_COMPARE(out.str(), "Trade::SceneField::Transformation Trade::SceneField::Custom(73) Trade::SceneField(0xdeadda7)\n"); } -void SceneDataTest::fieldTypeSize() { +void SceneDataTest::fieldTypeSizeAlignment() { /* Test at least one of every size */ CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Byte), sizeof(Byte)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Degh), sizeof(Degh)); @@ -526,9 +534,16 @@ void SceneDataTest::fieldTypeSize() { CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x4d), sizeof(Matrix3x4d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix4x4d), sizeof(Matrix4x4d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Pointer), sizeof(const void*)); + + /* Test at least one of every alignment */ + CORRADE_COMPARE(sceneFieldTypeAlignment(SceneFieldType::Vector3ub), alignof(UnsignedByte)); + CORRADE_COMPARE(sceneFieldTypeAlignment(SceneFieldType::Matrix3x3h), alignof(Half)); + CORRADE_COMPARE(sceneFieldTypeAlignment(SceneFieldType::Range3Di), alignof(UnsignedInt)); + CORRADE_COMPARE(sceneFieldTypeAlignment(SceneFieldType::DualComplexd), alignof(Double)); + CORRADE_COMPARE(sceneFieldTypeAlignment(SceneFieldType::Pointer), alignof(const void*)); } -void SceneDataTest::fieldTypeSizeInvalid() { +void SceneDataTest::fieldTypeSizeAlignmentInvalid() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -537,11 +552,15 @@ void SceneDataTest::fieldTypeSizeInvalid() { Error redirectError{&out}; sceneFieldTypeSize(SceneFieldType{}); + sceneFieldTypeAlignment(SceneFieldType{}); sceneFieldTypeSize(SceneFieldType(0xdead)); + sceneFieldTypeAlignment(SceneFieldType(0xdead)); CORRADE_COMPARE(out.str(), "Trade::sceneFieldTypeSize(): invalid type Trade::SceneFieldType(0x0)\n" - "Trade::sceneFieldTypeSize(): invalid type Trade::SceneFieldType(0xdead)\n"); + "Trade::sceneFieldTypeAlignment(): invalid type Trade::SceneFieldType(0x0)\n" + "Trade::sceneFieldTypeSize(): invalid type Trade::SceneFieldType(0xdead)\n" + "Trade::sceneFieldTypeAlignment(): invalid type Trade::SceneFieldType(0xdead)\n"); } void SceneDataTest::debugFieldType() { From 618fbeccaaa9c863cd88925647dea749f1a42b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Sep 2021 13:28:19 +0200 Subject: [PATCH 19/49] Trade: adapt AbstractImporter for the new SceneData workflow. Same as with MeshData2D/3D, the original ObjectData API and plugin interfaces are preserved to keep existing code as well as existing importer implementations working. As Magnum's own importers will get updated to the new SceneData workflow, a backward compatibility layer provided that translates it to the subset that the legacy ObjectData understands. With this commit, both existing plugin code can build (and test against) the new workflow, and any ports to the new workflow can test against the legacy interfaces. Except that for now the compatibility layer doesn't deal with objects that have more than one mesh or for example a light and a camera attached, this will be done in a separate step. --- doc/changelog.dox | 12 + doc/snippets/MagnumTrade.cpp | 28 +- src/Magnum/Trade/AbstractImporter.cpp | 319 ++- src/Magnum/Trade/AbstractImporter.h | 249 ++- src/Magnum/Trade/CMakeLists.txt | 22 +- src/Magnum/Trade/MeshObjectData2D.cpp | 6 + src/Magnum/Trade/MeshObjectData2D.h | 21 +- src/Magnum/Trade/MeshObjectData3D.cpp | 6 + src/Magnum/Trade/MeshObjectData3D.h | 19 +- src/Magnum/Trade/ObjectData2D.cpp | 24 + src/Magnum/Trade/ObjectData2D.h | 57 +- src/Magnum/Trade/ObjectData3D.cpp | 20 + src/Magnum/Trade/ObjectData3D.h | 57 +- src/Magnum/Trade/SceneData.h | 5 + .../Trade/Test/AbstractImporterTest.cpp | 1895 ++++++++++++++--- src/Magnum/Trade/Test/CMakeLists.txt | 8 +- src/Magnum/Trade/Test/ObjectData2DTest.cpp | 7 + src/Magnum/Trade/Test/ObjectData3DTest.cpp | 7 + src/Magnum/Trade/Trade.h | 8 +- 19 files changed, 2407 insertions(+), 363 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 28a708d8e9..cc3f4e7191 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -641,6 +641,18 @@ See also: new scene representation. Use @ref Trade::SceneData::childrenFor() with @cpp -1 @ce passed as the @p object argument to get a list of top-level objects. +- @cpp Trade::ObjectData*D @ce, @cpp Trade::MeshObjectData*D @ce classes, + @cpp Trade::ObjectInstanceType*D @ce, @cpp Trade::ObjectFlag*D @ce, + @cpp Trade::ObjectFlags*D @ce enums and the corresponding + @cpp Trade::AbstractImporter::object*DCount() @ce, + @cpp Trade::AbstractImporter::object*DForName() @ce, + @cpp Trade::AbstractImporter::object*DName() @ce and + @cpp Trade::AbstractImporter::object*D() @ce accessor APIs are deprecated + in favor of the unified representation in @ref Trade::SceneData and the + @ref Trade::AbstractImporter::objectCount(), + @relativeref{Trade::AbstractImporter,objectForName()} and + @relativeref{Trade::AbstractImporter,objectName()} accessors that are + shared for 2D and 3D. - @ref Trade::AbstractImporter::material() now returns @ref Corrade::Containers::Optional instead of a @ref Corrade::Containers::Pointer, as the new @ref Trade::MaterialData class isn't polymorphic anymore. If diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index e4a1fb3b17..6862caa3cb 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -47,8 +47,6 @@ #include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" -#include "Magnum/Trade/ObjectData2D.h" -#include "Magnum/Trade/MeshObjectData3D.h" #include "Magnum/Trade/PbrClearCoatMaterialData.h" #include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" #include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" @@ -66,9 +64,12 @@ #ifdef MAGNUM_BUILD_DEPRECATED #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ #include "Magnum/Trade/MeshData2D.h" #include "Magnum/Trade/MeshData3D.h" +#include "Magnum/Trade/MeshObjectData3D.h" +#include "Magnum/Trade/ObjectData2D.h" #endif #define DOXYGEN_ELLIPSIS(...) __VA_ARGS__ @@ -180,21 +181,6 @@ importer->openFile("scene.gltf"); // memory-maps all files } #endif -{ -Containers::Pointer importer; -Int materialIndex; -/* [AbstractImporter-usage-cast] */ -Containers::Pointer data = importer->object3D(12); -if(data && data->instanceType() == Trade::ObjectInstanceType3D::Mesh) { - auto& mesh = static_cast(*data); - - materialIndex = mesh.material(); - // ... -} -/* [AbstractImporter-usage-cast] */ -static_cast(materialIndex); -} - { Containers::Pointer importer; /* [AbstractImporter-setFileCallback] */ @@ -844,9 +830,9 @@ MeshTools::transformPointsInPlace(transformation, data.positions(0)); /* [MeshData2D-transform] */ CORRADE_IGNORE_DEPRECATED_POP } -#endif { +CORRADE_IGNORE_DEPRECATED_PUSH Trade::ObjectData2D& baz(); Trade::ObjectData2D& data = baz(); /* [ObjectData2D-transformation] */ @@ -855,9 +841,9 @@ Matrix3 transformation = Matrix3::scaling(data.scaling()); /* [ObjectData2D-transformation] */ static_cast(transformation); +CORRADE_IGNORE_DEPRECATED_POP } -#ifdef MAGNUM_BUILD_DEPRECATED { CORRADE_IGNORE_DEPRECATED_PUSH Trade::MeshData3D& bar(); @@ -871,9 +857,9 @@ MeshTools::transformVectorsInPlace(transformation, data.normals(0)); /* [MeshData3D-transform] */ CORRADE_IGNORE_DEPRECATED_POP } -#endif { +CORRADE_IGNORE_DEPRECATED_PUSH Trade::ObjectData3D& fizz(); Trade::ObjectData3D& data = fizz(); /* [ObjectData3D-transformation] */ @@ -882,6 +868,8 @@ Matrix4 transformation = Matrix4::scaling(data.scaling()); /* [ObjectData3D-transformation] */ static_cast(transformation); +CORRADE_IGNORE_DEPRECATED_POP } +#endif } diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index c470ac375c..3b027b9316 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -40,17 +40,21 @@ #include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" -#include "Magnum/Trade/ObjectData2D.h" -#include "Magnum/Trade/ObjectData3D.h" #include "Magnum/Trade/SceneData.h" #include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" #ifdef MAGNUM_BUILD_DEPRECATED +#include +#include + #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ #include "Magnum/Trade/MeshData2D.h" #include "Magnum/Trade/MeshData3D.h" +#include "Magnum/Trade/MeshObjectData2D.h" +#include "Magnum/Trade/MeshObjectData3D.h" #endif #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT @@ -95,6 +99,12 @@ AbstractImporter::AbstractImporter(PluginManager::Manager& man AbstractImporter::AbstractImporter(PluginManager::AbstractManager& manager, const std::string& plugin): PluginManager::AbstractManagingPlugin{manager, plugin} {} +#ifdef MAGNUM_BUILD_DEPRECATED +/* These twp needed because of the Array member */ +AbstractImporter::AbstractImporter(AbstractImporter&&) noexcept = default; +AbstractImporter::~AbstractImporter() = default; +#endif + void AbstractImporter::setFlags(ImporterFlags flags) { CORRADE_ASSERT(!isOpened(), "Trade::AbstractImporter::setFlags(): can't be set while a file is opened", ); @@ -268,6 +278,13 @@ UnsignedInt AbstractImporter::sceneCount() const { UnsignedInt AbstractImporter::doSceneCount() const { return 0; } +UnsignedLong AbstractImporter::objectCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::objectCount(): no file opened", {}); + return doObjectCount(); +} + +UnsignedLong AbstractImporter::doObjectCount() const { return 0; } + Int AbstractImporter::sceneForName(const std::string& name) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::sceneForName(): no file opened", -1); const Int id = doSceneForName(name); @@ -278,6 +295,16 @@ Int AbstractImporter::sceneForName(const std::string& name) { Int AbstractImporter::doSceneForName(const std::string&) { return -1; } +Long AbstractImporter::objectForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::objectForName(): no file opened", {}); + const Long id = doObjectForName(name); + CORRADE_ASSERT(id == -1 || UnsignedLong(id) < doObjectCount(), + "Trade::AbstractImporter::objectForName(): implementation-returned index" << id << "out of range for" << doObjectCount() << "entries", {}); + return id; +} + +Long AbstractImporter::doObjectForName(const std::string&) { return -1; } + std::string AbstractImporter::sceneName(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::sceneName(): no file opened", {}); CORRADE_ASSERT(id < doSceneCount(), "Trade::AbstractImporter::sceneName(): index" << id << "out of range for" << doSceneCount() << "entries", {}); @@ -286,10 +313,23 @@ std::string AbstractImporter::sceneName(const UnsignedInt id) { std::string AbstractImporter::doSceneName(UnsignedInt) { return {}; } +std::string AbstractImporter::objectName(const UnsignedLong id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::objectName(): no file opened", {}); + CORRADE_ASSERT(id < doObjectCount(), "Trade::AbstractImporter::objectName(): index" << id << "out of range for" << doObjectCount() << "entries", {}); + return doObjectName(id); +} + +std::string AbstractImporter::doObjectName(UnsignedLong) { return {}; } + Containers::Optional AbstractImporter::scene(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::scene(): no file opened", {}); CORRADE_ASSERT(id < doSceneCount(), "Trade::AbstractImporter::scene(): index" << id << "out of range for" << doSceneCount() << "entries", {}); - return doScene(id); + Containers::Optional scene = doScene(id); + CORRADE_ASSERT(!scene || ( + (!scene->_data.deleter() || scene->_data.deleter() == Implementation::nonOwnedArrayDeleter) && + (!scene->_fields.deleter() || scene->_fields.deleter() == reinterpret_cast(Implementation::nonOwnedArrayDeleter))), + "Trade::AbstractImporter::scene(): implementation is not allowed to use a custom Array deleter", {}); + return scene; } Containers::Optional AbstractImporter::doScene(UnsignedInt) { @@ -465,39 +505,176 @@ Containers::Optional AbstractImporter::camera(const std::string& nam return camera(id); /* not doCamera(), so we get the range checks also */ } +#ifdef MAGNUM_BUILD_DEPRECATED +struct AbstractImporter::CachedScenes { + UnsignedInt object2DCount{}; + UnsignedInt object3DCount{}; + Containers::Array> scenes; +}; + +void AbstractImporter::populateCachedScenes() { + if(_cachedScenes) return; + + _cachedScenes.emplace(); + _cachedScenes->scenes = Containers::Array>{sceneCount()}; + for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { + _cachedScenes->scenes[i] = scene(i); + + /* Return the 2D/3D object count based on which scenes are 2D and which + not. The objectCount() provided by the importer is ignored except + for the above, also because it doesn't take into account the + restriction for unique-functioning objects. */ + if(_cachedScenes->scenes[i]) { + if(_cachedScenes->scenes[i]->is2D()) + _cachedScenes->object2DCount = Math::max(_cachedScenes->object2DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); + if(_cachedScenes->scenes[i]->is3D()) + _cachedScenes->object3DCount = Math::max(_cachedScenes->object3DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); + } + } +} + UnsignedInt AbstractImporter::object2DCount() const { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DCount(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH return doObject2DCount(); + CORRADE_IGNORE_DEPRECATED_POP } -UnsignedInt AbstractImporter::doObject2DCount() const { return 0; } +UnsignedInt AbstractImporter::doObject2DCount() const { + /* I know, I know. A cleaner option would be to populate this during + openFile() / openData() but that would mean the backwards compatibility + overhead is there even if not using the deprecated APIs, which is way + worse than using this nasty hack in two places. */ + const_cast(*this).populateCachedScenes(); + return _cachedScenes->object2DCount; +} Int AbstractImporter::object2DForName(const std::string& name) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DForName(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH const Int id = doObject2DForName(name); CORRADE_ASSERT(id == -1 || UnsignedInt(id) < doObject2DCount(), "Trade::AbstractImporter::object2DForName(): implementation-returned index" << id << "out of range for" << doObject2DCount() << "entries", {}); return id; + CORRADE_IGNORE_DEPRECATED_POP } -Int AbstractImporter::doObject2DForName(const std::string&) { return -1; } +Int AbstractImporter::doObject2DForName(const std::string& name) { + /* Alias to the new interface. If it returns an ID that's larger than + reported 2D object count, then it's probably for a 3D object instead + -- ignore it in that case. */ + const Long id = doObjectForName(name); + CORRADE_IGNORE_DEPRECATED_PUSH + return id < doObject2DCount() ? id : -1; + CORRADE_IGNORE_DEPRECATED_POP +} std::string AbstractImporter::object2DName(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DName(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ASSERT(id < doObject2DCount(), "Trade::AbstractImporter::object2DName(): index" << id << "out of range for" << doObject2DCount() << "entries", {}); return doObject2DName(id); + CORRADE_IGNORE_DEPRECATED_POP } -std::string AbstractImporter::doObject2DName(UnsignedInt) { return {}; } +std::string AbstractImporter::doObject2DName(const UnsignedInt id) { + /* Alias to the new interface */ + return doObjectName(id); +} +CORRADE_IGNORE_DEPRECATED_PUSH Containers::Pointer AbstractImporter::object2D(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2D(): no file opened", {}); CORRADE_ASSERT(id < doObject2DCount(), "Trade::AbstractImporter::object2D(): index" << id << "out of range for" << doObject2DCount() << "entries", {}); return doObject2D(id); } -Containers::Pointer AbstractImporter::doObject2D(UnsignedInt) { - CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImporter::object2D(): not implemented", {}); +Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt id) { + /* The code is mostly the same between doObject2D() and doObject3D(), + except that the 2D variant has no lights. Attempting to unify the common + code would be more complex than just having a slightly modified copy. */ + + populateCachedScenes(); + + /* Find the first 2D scene with this object, which we'll detect from the + object count reported for the scene, whether it's 2D or 3D, and a + presence of a parent attribute. If a parent attribute is not present, it + means the object isn't a part of this scene, in which case we skip it. + It could also mean isn't a part of the hierarchy and is standalone + (skyboxes, scene-wide properties), which the legacy API had no way to + deal with anyway so ignoring those is fine. */ + std::size_t sceneCandidate = ~std::size_t{}; + for(std::size_t i = 0; i != _cachedScenes->scenes.size(); ++i) { + const Containers::Optional& scene = _cachedScenes->scenes[i]; + if(scene && scene->is2D() && id < scene->objectCount() && scene->parentFor(id)) { + sceneCandidate = i; + break; + } + } + + if(sceneCandidate == ~std::size_t{}) { + Error{} << "Trade::AbstractImporter::object2D(): object" << id << "not found in any 2D scene hierarchy"; + return {}; + } + + const SceneData& scene = *_cachedScenes->scenes[sceneCandidate]; + + ObjectFlags2D flags; + Containers::Optional transformation = scene.transformation2DFor(id); + Containers::Optional> trs = scene.translationRotationScaling2DFor(id); + if(trs) + flags |= ObjectFlag2D::HasTranslationRotationScaling; + /* If the object has neither a TRS nor a transformation field, assign an + identity transform to it */ + else if(!transformation) transformation = Matrix3{}; + + std::vector children; /* not const so we can move it */ + { + Containers::Array childrenArray = scene.childrenFor(id); + children = {childrenArray.begin(), childrenArray.end()}; + } + const Containers::Array> mesh = scene.meshesMaterialsFor(id); + const Containers::Array camera = scene.camerasFor(id); + const Containers::Array skin = scene.skinsFor(id); + const Containers::Optional importerState = scene.importerStateFor(id); + + /* All these should have at most 1 item as the old API doesn't have + any way to represent multi-function objects. */ + CORRADE_INTERNAL_ASSERT(camera.size() + mesh.size() <= 1); + + if(!mesh.empty()) { + return Containers::pointer(flags & ObjectFlag2D::HasTranslationRotationScaling ? + new MeshObjectData2D{std::move(children), + trs->first(), trs->second(), trs->third(), + mesh.front().first(), mesh.front().second(), + skin.empty() ? -1 : Int(skin.front()), + importerState ? *importerState : nullptr} : + new MeshObjectData2D{std::move(children), + *transformation, + mesh.front().first(), mesh.front().second(), + skin ? Int(*skin) : -1, + importerState ? *importerState : nullptr}); + } + + ObjectInstanceType2D instanceType; + UnsignedInt instance; + if(camera) { + instanceType = ObjectInstanceType2D::Camera; + instance = *camera; + } else { + instanceType = ObjectInstanceType2D::Empty; + instance = UnsignedInt(-1); /* Old APIs, you suck! */ + } + + return Containers::pointer(flags & ObjectFlag2D::HasTranslationRotationScaling ? + new ObjectData2D{std::move(children), + trs->first(), trs->second(), trs->third(), + instanceType, instance, + importerState ? *importerState : nullptr} : + new ObjectData2D{std::move(children), + *transformation, + instanceType, instance, + importerState ? *importerState : nullptr}); } Containers::Pointer AbstractImporter::object2D(const std::string& name) { @@ -509,40 +686,154 @@ Containers::Pointer AbstractImporter::object2D(const std::string& } return object2D(id); /* not doObject2D(), so we get the range checks also */ } +CORRADE_IGNORE_DEPRECATED_POP UnsignedInt AbstractImporter::object3DCount() const { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DCount(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH return doObject3DCount(); + CORRADE_IGNORE_DEPRECATED_POP } -UnsignedInt AbstractImporter::doObject3DCount() const { return 0; } +UnsignedInt AbstractImporter::doObject3DCount() const { + /* I know, I know. A cleaner option would be to populate this during + openFile() / openData() but that would mean the backwards compatibility + overhead is there even if not using the deprecated APIs, which is way + worse than using this nasty hack in two places. */ + const_cast(*this).populateCachedScenes(); + return _cachedScenes->object3DCount; +} Int AbstractImporter::object3DForName(const std::string& name) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DForName(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH const Int id = doObject3DForName(name); CORRADE_ASSERT(id == -1 || UnsignedInt(id) < doObject3DCount(), "Trade::AbstractImporter::object3DForName(): implementation-returned index" << id << "out of range for" << doObject3DCount() << "entries", {}); + CORRADE_IGNORE_DEPRECATED_POP return id; } -Int AbstractImporter::doObject3DForName(const std::string&) { return -1; } +Int AbstractImporter::doObject3DForName(const std::string& name) { + /* Alias to the new interface. If it returns an ID that's larger than + reported 3D object count, then it's probably for a 2D object instead + -- ignore it in that case. */ + const Long id = doObjectForName(name); + CORRADE_IGNORE_DEPRECATED_PUSH + return id < doObject3DCount() ? id : -1; + CORRADE_IGNORE_DEPRECATED_POP +} std::string AbstractImporter::object3DName(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DName(): no file opened", {}); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ASSERT(id < doObject3DCount(), "Trade::AbstractImporter::object3DName(): index" << id << "out of range for" << doObject3DCount() << "entries", {}); return doObject3DName(id); + CORRADE_IGNORE_DEPRECATED_POP } -std::string AbstractImporter::doObject3DName(UnsignedInt) { return {}; } +std::string AbstractImporter::doObject3DName(const UnsignedInt id) { + /* Alias to the new interface */ + return doObjectName(id); +} +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ Containers::Pointer AbstractImporter::object3D(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3D(): no file opened", {}); CORRADE_ASSERT(id < doObject3DCount(), "Trade::AbstractImporter::object3D(): index" << id << "out of range for" << doObject3DCount() << "entries", {}); return doObject3D(id); } -Containers::Pointer AbstractImporter::doObject3D(UnsignedInt) { - CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImporter::object3D(): not implemented", {}); +Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt id) { + /* The code is mostly the same between doObject2D() and doObject3D(), + except that the 2D variant has no lights. Attempting to unify the common + code would be more complex than just having a slightly modified copy. */ + + populateCachedScenes(); + + /* Find the first 3D scene with this object, which we'll detect from the + object count reported for the scene, whether it's 2D or 3D, and a + presence of a parent attribute. If a parent attribute is not present, it + means the object isn't a part of this scene, in which case we skip it. + It could also mean isn't a part of the hierarchy and is standalone + (skyboxes, scene-wide properties), which the legacy API had no way to + deal with anyway so ignoring those is fine. */ + std::size_t sceneCandidate = ~std::size_t{}; + for(std::size_t i = 0; i != _cachedScenes->scenes.size(); ++i) { + const Containers::Optional& scene = _cachedScenes->scenes[i]; + if(scene && scene->is3D() && id < scene->objectCount() && scene->parentFor(id)) { + sceneCandidate = i; + break; + } + } + + if(sceneCandidate == ~std::size_t{}) { + Error{} << "Trade::AbstractImporter::object3D(): object" << id << "not found in any 3D scene hierarchy"; + return {}; + } + + const SceneData& scene = *_cachedScenes->scenes[sceneCandidate]; + + ObjectFlags3D flags; + Containers::Optional transformation = scene.transformation3DFor(id); + Containers::Optional> trs = scene.translationRotationScaling3DFor(id); + if(trs) + flags |= ObjectFlag3D::HasTranslationRotationScaling; + /* If the object has neither a TRS nor a transformation field, assign an + identity transform to it */ + else if(!transformation) transformation = Matrix4{}; + + std::vector children; /* not const so we can move it */ + { + Containers::Array childrenArray = scene.childrenFor(id); + children = {childrenArray.begin(), childrenArray.end()}; + } + const Containers::Array> mesh = scene.meshesMaterialsFor(id); + const Containers::Array camera = scene.camerasFor(id); + const Containers::Array skin = scene.skinsFor(id); + const Containers::Array light = scene.lightsFor(id); + const Containers::Optional importerState = scene.importerStateFor(id); + + /* All these should have at most 1 item as the old API doesn't have + any way to represent multi-function objects. */ + CORRADE_INTERNAL_ASSERT(camera.size() + light.size() + mesh.size() <= 1); + + if(!mesh.empty()) { + return Containers::pointer(flags & ObjectFlag3D::HasTranslationRotationScaling ? + new MeshObjectData3D{std::move(children), + trs->first(), trs->second(), trs->third(), + mesh.front().first(), mesh.front().second(), + skin.empty() ? -1 : Int(skin.front()), + importerState ? *importerState : nullptr} : + new MeshObjectData3D{std::move(children), + *transformation, + mesh.front().first(), mesh.front().second(), + skin ? Int(*skin) : -1, + importerState ? *importerState : nullptr}); + } + + ObjectInstanceType3D instanceType; + UnsignedInt instance; + if(camera) { + instanceType = ObjectInstanceType3D::Camera; + instance = *camera; + } else if(light) { + instanceType = ObjectInstanceType3D::Light; + instance = *light; + } else { + instanceType = ObjectInstanceType3D::Empty; + instance = UnsignedInt(-1); /* Old APIs, you suck! */ + } + + return Containers::pointer(flags & ObjectFlag3D::HasTranslationRotationScaling ? + new ObjectData3D{std::move(children), + trs->first(), trs->second(), trs->third(), + instanceType, instance, + importerState ? *importerState : nullptr} : + new ObjectData3D{std::move(children), + *transformation, + instanceType, instance, + importerState ? *importerState : nullptr}); } Containers::Pointer AbstractImporter::object3D(const std::string& name) { @@ -554,6 +845,8 @@ Containers::Pointer AbstractImporter::object3D(const std::string& } return object3D(id); /* not doObject3D(), so we get the range checks also */ } +CORRADE_IGNORE_DEPRECATED_POP +#endif UnsignedInt AbstractImporter::skin2DCount() const { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::skin2DCount(): no file opened", {}); diff --git a/src/Magnum/Trade/AbstractImporter.h b/src/Magnum/Trade/AbstractImporter.h index 47c240250e..77500ae289 100644 --- a/src/Magnum/Trade/AbstractImporter.h +++ b/src/Magnum/Trade/AbstractImporter.h @@ -181,14 +181,11 @@ ID of a texture it uses). The following kinds of data can be imported: @ref meshCount(). Similarly as with images, each mesh can also have multiple levels (LODs or for example separate edge/face data), which are requested through the second parameter up to @ref meshLevelCount(). -- @ref ObjectData2D / @ref ObjectData3D using - @ref object2D(UnsignedInt) / @ref object3D(UnsignedInt) up to - @ref object2DCount() / @ref object3DCount(). An object can then reference - its child objects, mesh and a material, camera, light or a skin associated - with it via their IDs. - @ref SceneData using @ref scene(UnsignedInt) up to @ref sceneCount(), with the default scene index exposed through @ref defaultScene(). A scene then - references its child 2D/3D objects via their IDs. + contains all data for its objects such as transformations and parent/child + hierarchy, particular objects are associated with meshes, materials, + cameras, lights or skins via their IDs. - @ref SkinData2D / @ref SkinData3D using @ref skin2D(UnsignedInt) / @ref skin3D(UnsignedInt) up to @ref skin2DCount() / @ref skin3DCount() - @ref TextureData using @ref texture(UnsignedInt) up to @ref textureCount(). @@ -303,10 +300,8 @@ name doesn't exist. @ref mesh(const std::string&, UnsignedInt). Meshes themselves can have custom attributes, for which the name mapping can be retrieved using @ref meshAttributeName() and @ref meshAttributeForName(). -- Objects names using @ref object2DName() / @ref object3DName() & - @ref object2DForName() / @ref object3DForName(), imported with - @ref object2D(const std::string&) / @ref object3D(const std::string&) -- Scene names using @ref sceneName() & @ref sceneForName(), imported with +- Scene and object names using @ref sceneName() / @ref objectName() & + @ref sceneForName() / @ref objectForName(), imported with @ref scene(const std::string&). Scenes themselves can have custom fields, for which the name mapping can be retrieved using @ref sceneFieldName() and @ref sceneFieldForName(). @@ -335,10 +330,9 @@ expose internal state through various accessors: imported by @ref light() - @ref MeshData::importerState() can expose importer state for a mesh imported by @ref mesh() -- @ref ObjectData3D::importerState() can expose importer state for an object - imported by @ref object2D() or @ref object3D() - @ref SceneData::importerState() can expose importer state for a scene - imported by @ref scene() + imported by @ref scene(), per-object importer state can then be stored in + the @ref SceneField::ImporterState field - @ref SkinData::importerState() can expose importer state for a scene imported by @ref skin2D() or @ref skin3D() - @ref TextureData::importerState() can expose importer state for a texture @@ -349,20 +343,6 @@ Besides exposing internal state, importers that support the state using @ref openState(). See documentation of a particular importer for details about concrete types returned and accepted by these functions. -@subsection Trade-AbstractImporter-usage-casting Polymorphic imported data types - -Some data access functions return @relativeref{Corrade,Containers::Pointer} -instead of @relativeref{Corrade,Containers::Optional} because the result might -be a particular subclass of given type. Those functions are @ref object2D() -and @ref object3D(). You can cast the abstract base to a concrete type -depending on its reported type, for example: - -@snippet MagnumTrade.cpp AbstractImporter-usage-cast - -Another option is making use of the @ref Containers::pointerCast() utility, but -note that in that case the original @relativeref{Corrade,Containers::Pointer} -will have to be *moved into* a new instance, which might not be desirable. - @section Trade-AbstractImporter-data-dependency Data dependency The `*Data` instances returned from various functions *by design* have no @@ -493,6 +473,14 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi /** @brief Plugin manager constructor */ explicit AbstractImporter(PluginManager::AbstractManager& manager, const std::string& plugin); + #if defined(MAGNUM_BUILD_DEPRECATED) && !defined(DOXYGEN_GENERATING_OUTPUT) + /* These twp needed because of the Array member + (AnyImageImporter relies on the move), move assignment disabled by + AbstractPlugin already */ + AbstractImporter(AbstractImporter&&) noexcept; + ~AbstractImporter(); + #endif + /** @brief Features supported by this importer */ ImporterFeatures features() const { return doFeatures(); } @@ -723,6 +711,17 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ UnsignedInt sceneCount() const; + /** + * @brief Object count + * + * Total count of all (2D or 3D) objects in all scenes. An object can + * be present in multiple scenes at the same time. Fields corresponding + * to particular objects can be then accessed via the @ref SceneData + * class returned from @ref scene(UnsignedInt). Expects that a file is + * opened. + */ + UnsignedLong objectCount() const; + /** * @brief Scene for given name * @return Scene ID from range [0, @ref sceneCount()) or @cpp -1 @ce if @@ -733,6 +732,16 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ Int sceneForName(const std::string& name); + /** + * @brief Object ID for given name + * + * If no object for given name exists, returns @cpp -1 @ce. Expects + * that a file is opened. Object IDs are shared among all scenes, an + * object can be present in multiple scenes at the same time. + * @see @ref objectName() + */ + Long objectForName(const std::string& name); + /** * @brief Scene name * @param id Scene ID, from range [0, @ref sceneCount()). @@ -743,6 +752,18 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ std::string sceneName(UnsignedInt id); + /** + * @brief Object name + * @param id Object ID, from range [0, @ref objectCount()). + * + * Object IDs are shared among all scenes, an object can be present in + * multiple scenes at the same time. If the object has no name or the + * importer doesn't support object names, returns an empty string. + * Expects that a file is opened. + * @see @ref objectForName() + */ + std::string objectName(UnsignedLong id); + /** * @brief Scene * @param id Scene ID, from range [0, @ref sceneCount()). @@ -940,12 +961,15 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ Containers::Optional camera(const std::string& name); + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Two-dimensional object count * * Expects that a file is opened. + * @m_deprecated_since_latest Use @ref objectCount() instead, which is + * shared for both 2D and 3D objects. */ - UnsignedInt object2DCount() const; + CORRADE_DEPRECATED("use objectCount() instead") UnsignedInt object2DCount() const; /** * @brief Two-dimensional object for given name @@ -953,9 +977,11 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @cpp -1 @ce if no object for given name exists * * Expects that a file is opened. + * @m_deprecated_since_latest Use @ref objectForName() instead, which + * is shared for both 2D and 3D objects. * @see @ref object2DName(), @ref object2D(const std::string&) */ - Int object2DForName(const std::string& name); + CORRADE_DEPRECATED("use objectForName() instead") Int object2DForName(const std::string& name); /** * @brief Two-dimensional object name @@ -963,9 +989,11 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Expects that a file is opened. If the object has no name or the * importer doesn't support object names, returns an empty string. + * @m_deprecated_since_latest Use @ref objectName() instead, which is + * shared for both 2D and 3D objects. * @see @ref object2DForName() */ - std::string object2DName(UnsignedInt id); + CORRADE_DEPRECATED("use objectName() instead") std::string object2DName(UnsignedInt id); /** * @brief Two-dimensional object @@ -973,9 +1001,14 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Returns given object or @cpp nullptr @ce if importing failed. * Expects that a file is opened. + * @m_deprecated_since_latest Query object fields on the @ref SceneData + * object returned from @ref scene() instead, which is shared for + * both 2D and 3D objects. * @see @ref object2D(const std::string&) */ - Containers::Pointer object2D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + CORRADE_DEPRECATED("query object fields from scene() instead") Containers::Pointer object2D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Two-dimensional object for given name @@ -986,15 +1019,22 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @cpp -1 @ce, prints an error message and returns * @cpp nullptr @ce, otherwise propagates the result from * @ref object2D(UnsignedInt). Expects that a file is opened. + * @m_deprecated_since_latest Query object fields on the @ref SceneData + * object returned from @ref scene() instead, which is shared for + * both 2D and 3D objects. */ - Containers::Pointer object2D(const std::string& name); + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + CORRADE_DEPRECATED("query object fields from scene() instead") Containers::Pointer object2D(const std::string& name); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Three-dimensional object count * * Expects that a file is opened. + * @m_deprecated_since_latest Use @ref objectCount() instead, which is + * shared for both 2D and 3D objects. */ - UnsignedInt object3DCount() const; + CORRADE_DEPRECATED("use objectCount() instead") UnsignedInt object3DCount() const; /** * @brief Three-dimensional object for given name @@ -1002,9 +1042,11 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @cpp -1 @ce if no object for given name exists * * Expects that a file is opened. + * @m_deprecated_since_latest Use @ref objectForName() instead, which + * is shared for both 2D and 3D objects. * @see @ref object3DName(), @ref object3D(const std::string&) */ - Int object3DForName(const std::string& name); + CORRADE_DEPRECATED("use objectForName() instead") Int object3DForName(const std::string& name); /** * @brief Three-dimensional object name @@ -1012,9 +1054,11 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Expects that a file is opened. If the object has no name or the * importer doesn't support object names, returns an empty string. + * @m_deprecated_since_latest Use @ref objectName() instead, which is + * shared for both 2D and 3D objects. * @see @ref object3DForName() */ - std::string object3DName(UnsignedInt id); + CORRADE_DEPRECATED("use objectName() instead") std::string object3DName(UnsignedInt id); /** * @brief Three-dimensional object @@ -1022,9 +1066,14 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Returns given object or @cpp nullptr @ce if importing failed. * Expects that a file is opened. + * @m_deprecated_since_latest Query object fields on the @ref SceneData + * object returned from @ref scene() instead, which is shared for + * both 2D and 3D objects. * @see @ref object3D(const std::string&) */ - Containers::Pointer object3D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + CORRADE_DEPRECATED("query object fields from scene() instead") Containers::Pointer object3D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Three-dimensional object for given name @@ -1035,8 +1084,14 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @cpp -1 @ce, prints an error message and returns * @cpp nullptr @ce, otherwise propagates the result from * @ref object3D(UnsignedInt). Expects that a file is opened. + * @m_deprecated_since_latest Query object fields on the @ref SceneData + * object returned from @ref scene() instead, which is shared for + * both 2D and 3D objects. */ - Containers::Pointer object3D(const std::string& name); + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + CORRADE_DEPRECATED("query object fields from scene() instead") Containers::Pointer object3D(const std::string& name); + CORRADE_IGNORE_DEPRECATED_POP + #endif /** * @brief Two-dimensional skin count @@ -1772,6 +1827,16 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ virtual UnsignedInt doSceneCount() const; + /** + * @brief Implementation for @ref objectCount() + * + * Default implementation returns @cpp 0 @ce. This function isn't + * expected to fail --- if an import error occus, it should be handled + * preferably during @ref doScene() (with correct object count + * reported), and if not possible, already during file opening. + */ + virtual UnsignedLong doObjectCount() const; + /** * @brief Implementation for @ref sceneForName() * @@ -1779,6 +1844,13 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ virtual Int doSceneForName(const std::string& name); + /** + * @brief Implementation for @ref objectForName() + * + * Default implementation returns @cpp -1 @ce. + */ + virtual Long doObjectForName(const std::string& name); + /** * @brief Implementation for @ref sceneName() * @@ -1786,6 +1858,13 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi */ virtual std::string doSceneName(UnsignedInt id); + /** + * @brief Implementation for @ref objectName() + * + * Default implementation returns an empty string. + */ + virtual std::string doObjectName(UnsignedLong id); + /** @brief Implementation for @ref scene() */ virtual Containers::Optional doScene(UnsignedInt id); @@ -1887,59 +1966,121 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi /** @brief Implementation for @ref camera() */ virtual Containers::Optional doCamera(UnsignedInt id); + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Implementation for @ref object2DCount() * - * Default implementation returns @cpp 0 @ce. This function isn't - * expected to fail --- if an import error occus, it should be handled - * preferably during @ref doObject2D() (with correct object count - * reported), and if not possible, already during file opening. + * Default implementation returns @cpp 0 @ce. There weren't any + * importers in existence known to implement 2D scene import, so unlike + * @ref doObject3DCount() this function doesn't delegate to + * @ref doObjectCount(). + * @m_deprecated_since_latest Implement @ref doObjectCount() instead. */ + /* MSVC warns when overriding such methods and there's no way to + suppress that warning, making the RT build (which treats deprecation + warnings as errors) fail and other builds extremely noisy. So + disabling those on MSVC. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectCount() instead") + #endif virtual UnsignedInt doObject2DCount() const; /** * @brief Implementation for @ref object2DForName() * - * Default implementation returns @cpp -1 @ce. + * Default implementation returns @cpp -1 @ce. There weren't any + * importers in existence known to implement 2D scene import, so unlike + * @ref doObject3DForName() this function doesn't delegate to + * @ref doObjectForName(). + * @m_deprecated_since_latest Implement @ref doObjectForName() instead. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectForName() instead") + #endif /* See above */ virtual Int doObject2DForName(const std::string& name); /** * @brief Implementation for @ref object2DName() * - * Default implementation returns an empty string. + * Default implementation returns an empty string. There weren't any + * importers in existence known to implement 2D scene import, so unlike + * @ref doObject3DName() this function doesn't delegate to + * @ref doObjectName(). + * @m_deprecated_since_latest Implement @ref doObjectName() instead. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectName() instead") + #endif /* See above */ virtual std::string doObject2DName(UnsignedInt id); - /** @brief Implementation for @ref object2D() */ + /** + * @brief Implementation for @ref object2D() + * + * There weren't any importers in existence known to implement 2D scene + * import, so unlike @ref doObject3D() this function doesn't proxy + * per-object data returned from @ref doScene(). + * @m_deprecated_since_latest Implement @ref doScene() instead. + */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doScene() instead") + #endif /* See above */ virtual Containers::Pointer doObject2D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Implementation for @ref object3DCount() * - * Default implementation returns @cpp 0 @ce. This function isn't - * expected to fail --- if an import error occus, it should be handled - * preferably during @ref doObject3D() (with correct object count - * reported), and if not possible, already during file opening. + * Default implementation returns @ref doObjectCount() for backwards + * compatibility. + * @m_deprecated_since_latest Implement @ref doObjectCount() instead. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectCount() instead") + #endif /* See above */ virtual UnsignedInt doObject3DCount() const; /** * @brief Implementation for @ref object3DForName() * - * Default implementation returns @cpp -1 @ce. + * Default implementation returns @ref doObjectForName() for backwards + * compatibility. + * @m_deprecated_since_latest Implement @ref doObjectForName() instead. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectForName() instead") + #endif /* See above */ virtual Int doObject3DForName(const std::string& name); /** * @brief Implementation for @ref object3DName() * - * Default implementation returns an empty string. + * Default implementation returns @ref doObjectName() for backwards + * compatibility. + * @m_deprecated_since_latest Implement @ref doObjectName() instead. */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doObjectName() instead") + #endif /* See above */ virtual std::string doObject3DName(UnsignedInt id); - /** @brief Implementation for @ref object3D() */ + /** + * @brief Implementation for @ref object3D() + * + * Default implementation retrieves and caches scenes returned from + * @ref doScene(), finds the first scene that contains any fields for + * object @p id and then returns a subset of the data that's + * representable with a @ref ObjectData3D / @ref MeshObjectData3D + * instance. + * @m_deprecated_since_latest Implement @ref doScene() instead. + */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ + #if !(defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)) + CORRADE_DEPRECATED("implement doScene() instead") + #endif /* See above */ virtual Containers::Pointer doObject3D(UnsignedInt id); + CORRADE_IGNORE_DEPRECATED_POP + #endif /** * @brief Implementation for @ref skin2DCount() @@ -2368,6 +2509,12 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi const void* userData; /* GCC 4.8 complains loudly about missing initializers otherwise */ } _fileCallbackTemplate{nullptr, nullptr}; + + #ifdef MAGNUM_BUILD_DEPRECATED + struct CachedScenes; + Containers::Pointer _cachedScenes; + void populateCachedScenes(); + #endif }; #ifndef DOXYGEN_GENERATING_OUTPUT diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index ec032592ea..2a6fc229f5 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -28,8 +28,6 @@ find_package(Corrade REQUIRED PluginManager) set(MagnumTrade_SRCS ArrayAllocator.cpp Data.cpp - MeshObjectData2D.cpp - MeshObjectData3D.cpp TextureData.cpp) set(MagnumTrade_GracefulAssert_SRCS @@ -43,8 +41,6 @@ set(MagnumTrade_GracefulAssert_SRCS LightData.cpp MaterialData.cpp MeshData.cpp - ObjectData2D.cpp - ObjectData3D.cpp PbrClearCoatMaterialData.cpp PbrMetallicRoughnessMaterialData.cpp PbrSpecularGlossinessMaterialData.cpp @@ -66,10 +62,6 @@ set(MagnumTrade_HEADERS MaterialData.h MaterialLayerData.h MeshData.h - MeshObjectData2D.h - MeshObjectData3D.h - ObjectData2D.h - ObjectData3D.h PbrClearCoatMaterialData.h PbrMetallicRoughnessMaterialData.h PbrSpecularGlossinessMaterialData.h @@ -87,6 +79,9 @@ set(MagnumTrade_PRIVATE_HEADERS Implementation/materialAttributeProperties.hpp) if(MAGNUM_BUILD_DEPRECATED) + list(APPEND MagnumTrade_SRCS + MeshObjectData2D.cpp + MeshObjectData3D.cpp) list(APPEND MagnumTrade_GracefulAssert_SRCS # These have to be here instead of in MagnumTrade_SRCS because they # include MeshData.h and call (and thus instantiate) various functions @@ -96,12 +91,19 @@ if(MAGNUM_BUILD_DEPRECATED) # causing the tests to blow up. Happens only on the MSVC linker, but # let's be safe and do this everywhere. MeshData2D.cpp - MeshData3D.cpp) + MeshData3D.cpp + + ObjectData2D.cpp + ObjectData3D.cpp) list(APPEND MagnumTrade_HEADERS AbstractMaterialData.h MeshData2D.h - MeshData3D.h) + MeshData3D.h + MeshObjectData2D.h + MeshObjectData3D.h + ObjectData2D.h + ObjectData3D.h) endif() if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) diff --git a/src/Magnum/Trade/MeshObjectData2D.cpp b/src/Magnum/Trade/MeshObjectData2D.cpp index 2d478d8a34..0b587131ab 100644 --- a/src/Magnum/Trade/MeshObjectData2D.cpp +++ b/src/Magnum/Trade/MeshObjectData2D.cpp @@ -23,12 +23,18 @@ DEALINGS IN THE SOFTWARE. */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ + #include "MeshObjectData2D.h" namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ MeshObjectData2D::MeshObjectData2D(std::vector children, const Matrix3& transformation, const UnsignedInt instance, const Int material, const Int skin, const void* const importerState): ObjectData2D{std::move(children), transformation, ObjectInstanceType2D::Mesh, instance, importerState}, _material{material}, _skin{skin} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ MeshObjectData2D::MeshObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const UnsignedInt instance, const Int material, const Int skin, const void* const importerState): ObjectData2D{std::move(children), translation, rotation, scaling, ObjectInstanceType2D::Mesh, instance, importerState}, _material{material}, _skin{skin} {} +CORRADE_IGNORE_DEPRECATED_POP }} diff --git a/src/Magnum/Trade/MeshObjectData2D.h b/src/Magnum/Trade/MeshObjectData2D.h index 5171e55560..1e215f7722 100644 --- a/src/Magnum/Trade/MeshObjectData2D.h +++ b/src/Magnum/Trade/MeshObjectData2D.h @@ -25,21 +25,33 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file * @brief Class @ref Magnum::Trade::MeshObjectData2D */ +#endif + +#include "Magnum/configure.h" +#ifdef MAGNUM_BUILD_DEPRECATED #include "Magnum/Trade/ObjectData2D.h" +#ifndef _MAGNUM_NO_DEPRECATED_OBJECTDATA +CORRADE_DEPRECATED_FILE("use Magnum/Trade/SceneData.h and the SceneData class instead") +#endif + namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* MSVC warns on the inheritance */ + /** @brief Two-dimensional mesh object data +@m_deprecated_since_latest Use @ref SceneData instead. Provides access to material information for given mesh instance. @see @ref AbstractImporter::object2D(), @ref MeshObjectData3D */ -class MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { +class CORRADE_DEPRECATED("use SceneData instead") MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { public: /** * @brief Construct with combined transformation @@ -54,13 +66,11 @@ class MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { */ explicit MeshObjectData2D(std::vector children, const Matrix3& transformation, UnsignedInt instance, Int material, Int skin, const void* importerState = nullptr); - #ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief MeshObjectData2D(std::vector, const Matrix3&, UnsignedInt, Int, Int, const void*) * @m_deprecated_since_latest Use @ref MeshObjectData2D(std::vector, const Matrix3&, UnsignedInt, Int, Int, const void*) * instead. */ explicit CORRADE_DEPRECATED("use MeshObjectData2D(std::vector, const Matrix4&, UnsignedInt, Int, Int, const void*) instead") MeshObjectData2D(std::vector children, const Matrix3& transformation, UnsignedInt instance, Int material, const void* importerState = nullptr): MeshObjectData2D{std::move(children), transformation, instance, material, -1, importerState} {} - #endif /** * @brief Construct with separate transformations @@ -132,6 +142,11 @@ class MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { Int _material, _skin; }; +CORRADE_IGNORE_DEPRECATED_POP /* MSVC warns on the inheritance */ + }} +#else +#error use Magnum/Trade/SceneData.h and the SceneData class instead +#endif #endif diff --git a/src/Magnum/Trade/MeshObjectData3D.cpp b/src/Magnum/Trade/MeshObjectData3D.cpp index 00f58c2b20..67223b6354 100644 --- a/src/Magnum/Trade/MeshObjectData3D.cpp +++ b/src/Magnum/Trade/MeshObjectData3D.cpp @@ -23,12 +23,18 @@ DEALINGS IN THE SOFTWARE. */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ + #include "MeshObjectData3D.h" namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ MeshObjectData3D::MeshObjectData3D(std::vector children, const Matrix4& transformation, const UnsignedInt instance, const Int material, const Int skin, const void* const importerState): ObjectData3D{std::move(children), transformation, ObjectInstanceType3D::Mesh, instance, importerState}, _material{material}, _skin{skin} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ MeshObjectData3D::MeshObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const UnsignedInt instance, const Int material, const Int skin, const void* const importerState): ObjectData3D{std::move(children), translation, rotation, scaling, ObjectInstanceType3D::Mesh, instance, importerState}, _material{material}, _skin{skin} {} +CORRADE_IGNORE_DEPRECATED_POP }} diff --git a/src/Magnum/Trade/MeshObjectData3D.h b/src/Magnum/Trade/MeshObjectData3D.h index ad43aa5ba5..64236ccc6c 100644 --- a/src/Magnum/Trade/MeshObjectData3D.h +++ b/src/Magnum/Trade/MeshObjectData3D.h @@ -25,21 +25,33 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file * @brief Class @ref Magnum::Trade::MeshObjectData3D */ +#endif + +#include "Magnum/configure.h" +#ifdef MAGNUM_BUILD_DEPRECATED #include "Magnum/Trade/ObjectData3D.h" +#ifndef _MAGNUM_NO_DEPRECATED_OBJECTDATA +CORRADE_DEPRECATED_FILE("use Magnum/Trade/SceneData.h and the SceneData class instead") +#endif + namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* MSVC warns on the inheritance */ + /** @brief Three-dimensional mesh object data +@m_deprecated_since_latest Use @ref SceneData instead. Provides access to material information for given mesh instance. @see @ref AbstractImporter::object3D(), @ref MeshObjectData2D */ -class MAGNUM_TRADE_EXPORT MeshObjectData3D: public ObjectData3D { +class CORRADE_DEPRECATED("use SceneData instead") MAGNUM_TRADE_EXPORT MeshObjectData3D: public ObjectData3D { public: /** * @brief Construct with combined transformation @@ -132,6 +144,11 @@ class MAGNUM_TRADE_EXPORT MeshObjectData3D: public ObjectData3D { Int _material, _skin; }; +CORRADE_IGNORE_DEPRECATED_POP /* MSVC warns on the inheritance */ + }} +#else +#error use Magnum/Trade/SceneData.h and the SceneData class instead +#endif #endif diff --git a/src/Magnum/Trade/ObjectData2D.cpp b/src/Magnum/Trade/ObjectData2D.cpp index e2a2cc826b..d192bac223 100644 --- a/src/Magnum/Trade/ObjectData2D.cpp +++ b/src/Magnum/Trade/ObjectData2D.cpp @@ -23,62 +23,85 @@ DEALINGS IN THE SOFTWARE. */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ + #include "ObjectData2D.h" #include namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const ObjectInstanceType2D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _flags{}, _instance{Int(instance)}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData2D::ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const ObjectInstanceType2D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{instanceType}, _flags{ObjectFlag2D::HasTranslationRotationScaling}, _instance{Int(instance)}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType2D::Empty}, _flags{}, _instance{-1}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData2D::ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{ObjectInstanceType2D::Empty}, _flags{ObjectFlag2D::HasTranslationRotationScaling}, _instance{-1}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH /* MSVC warns here */ ObjectData2D::ObjectData2D(ObjectData2D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 noexcept #endif = default; +CORRADE_IGNORE_DEPRECATED_POP ObjectData2D::~ObjectData2D() = default; +CORRADE_IGNORE_DEPRECATED_PUSH /* GCC why you warn on return and not on param */ ObjectData2D& ObjectData2D::operator=(ObjectData2D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 noexcept #endif = default; +CORRADE_IGNORE_DEPRECATED_POP Vector2 ObjectData2D::translation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag2D::HasTranslationRotationScaling, "Trade::ObjectData2D::translation(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.translation; } Complex ObjectData2D::rotation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag2D::HasTranslationRotationScaling, "Trade::ObjectData2D::rotation(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.rotation; } Vector2 ObjectData2D::scaling() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag2D::HasTranslationRotationScaling, "Trade::ObjectData2D::scaling(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.scaling; } Matrix3 ObjectData2D::transformation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ if(_flags & ObjectFlag2D::HasTranslationRotationScaling) /* Has to be on a single line otherwise lcov reports an uncovered line. Ugh. */ return Matrix3::from(_transformation.trs.rotation.toMatrix(), _transformation.trs.translation)* Matrix3::scaling(_transformation.trs.scaling); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.matrix; } #ifndef DOXYGEN_GENERATING_OUTPUT +CORRADE_IGNORE_DEPRECATED_PUSH Debug& operator<<(Debug& debug, const ObjectInstanceType2D value) { debug << "Trade::ObjectInstanceType2D" << Debug::nospace; @@ -113,6 +136,7 @@ Debug& operator<<(Debug& debug, const ObjectFlags2D value) { return enumSetDebugOutput(debug, value, "Trade::ObjectFlags2D{}", { ObjectFlag2D::HasTranslationRotationScaling}); } +CORRADE_IGNORE_DEPRECATED_POP #endif }} diff --git a/src/Magnum/Trade/ObjectData2D.h b/src/Magnum/Trade/ObjectData2D.h index 5023dedbed..8f50c129d8 100644 --- a/src/Magnum/Trade/ObjectData2D.h +++ b/src/Magnum/Trade/ObjectData2D.h @@ -25,10 +25,17 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file * @brief Class @ref Magnum::Trade::ObjectData2D, enum @ref Magnum::Trade::ObjectInstanceType2D + * @m_deprecated_since_latest Use @ref Magnum/Trade/SceneData.h and the + * @relativeref{Magnum::Trade,SceneData} class instead. */ +#endif + +#include "Magnum/configure.h" +#ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Magnum.h" @@ -36,14 +43,19 @@ #include "Magnum/Math/Complex.h" #include "Magnum/Trade/visibility.h" +#ifndef _MAGNUM_NO_DEPRECATED_OBJECTDATA +CORRADE_DEPRECATED_FILE("use Magnum/Trade/SceneData.h and the SceneData class instead") +#endif + namespace Magnum { namespace Trade { /** @brief Type of instance held by given 2D object +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectData2D::instanceType() */ -enum class ObjectInstanceType2D: UnsignedByte { +enum class CORRADE_DEPRECATED_ENUM("use SceneData instead") ObjectInstanceType2D: UnsignedByte { Camera, /**< Camera instance (see @ref CameraData) */ /** @@ -57,10 +69,11 @@ enum class ObjectInstanceType2D: UnsignedByte { /** @brief 2D object flag +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectFlags2D, @ref ObjectData2D::flags() */ -enum class ObjectFlag2D: UnsignedByte { +enum class CORRADE_DEPRECATED_ENUM("use SceneData instead") ObjectFlag2D: UnsignedByte { /** * The object provides separate translation / rotation / scaling * properties. The @ref ObjectData2D::transformation() matrix returns them @@ -73,21 +86,27 @@ enum class ObjectFlag2D: UnsignedByte { /** @brief 2D object flags +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectData2D::flags() */ -typedef Containers::EnumSet ObjectFlags2D; +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ +typedef CORRADE_DEPRECATED("use SceneData instead") Containers::EnumSet ObjectFlags2D; +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ENUMSET_OPERATORS(ObjectFlags2D) +CORRADE_IGNORE_DEPRECATED_POP /** @brief Two-dimensional object data +@m_deprecated_since_latest Use @ref SceneData instead. Provides access to object transformation and hierarchy. @see @ref AbstractImporter::object2D(), @ref MeshObjectData2D, @ref ObjectData3D */ -class MAGNUM_TRADE_EXPORT ObjectData2D { +class CORRADE_DEPRECATED("use SceneData instead") MAGNUM_TRADE_EXPORT ObjectData2D { public: /** * @brief Construct with combined transformation @@ -97,7 +116,9 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { * @param instance Instance ID * @param importerState Importer-specific state */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ explicit ObjectData2D(std::vector children, const Matrix3& transformation, ObjectInstanceType2D instanceType, UnsignedInt instance, const void* importerState = nullptr); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Construct with separate transformations @@ -109,7 +130,9 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { * @param instance Instance ID * @param importerState Importer-specific state */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ explicit ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, ObjectInstanceType2D instanceType, UnsignedInt instance, const void* importerState = nullptr); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Construct empty instance with combined transformation @@ -163,7 +186,9 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { const std::vector& children() const { return _children; } /**< @overload */ /** @brief Flags */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectFlags2D flags() const { return _flags; } + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Translation (relative to parent) @@ -224,7 +249,9 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { * * @see @ref instance() */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectInstanceType2D instanceType() const { return _instanceType; } + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Instance ID @@ -256,21 +283,37 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { Vector2 scaling; } trs; } _transformation; + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectInstanceType2D _instanceType; ObjectFlags2D _flags; + CORRADE_IGNORE_DEPRECATED_POP Int _instance; const void* _importerState; }; -/** @debugoperatorenum{ObjectInstanceType2D} */ +CORRADE_IGNORE_DEPRECATED_PUSH +/** +@debugoperatorenum{ObjectInstanceType2D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectInstanceType2D value); -/** @debugoperatorenum{ObjectFlag2D} */ +/** +@debugoperatorenum{ObjectFlag2D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlag2D value); -/** @debugoperatorenum{ObjectFlags2D} */ +/** +@debugoperatorenum{ObjectFlags2D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlags2D value); +CORRADE_IGNORE_DEPRECATED_POP }} +#else +#error use Magnum/Trade/SceneData.h and the SceneData class instead +#endif #endif diff --git a/src/Magnum/Trade/ObjectData3D.cpp b/src/Magnum/Trade/ObjectData3D.cpp index 37d5b2f68d..b7eb50c0c5 100644 --- a/src/Magnum/Trade/ObjectData3D.cpp +++ b/src/Magnum/Trade/ObjectData3D.cpp @@ -23,62 +23,81 @@ DEALINGS IN THE SOFTWARE. */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ + #include "ObjectData3D.h" #include namespace Magnum { namespace Trade { +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const ObjectInstanceType3D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _flags{}, _instance{Int(instance)}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData3D::ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const ObjectInstanceType3D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{instanceType}, _flags{ObjectFlag3D::HasTranslationRotationScaling}, _instance{Int(instance)}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType3D::Empty}, _flags{}, _instance{-1}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectData3D::ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{ObjectInstanceType3D::Empty}, _flags{ObjectFlag3D::HasTranslationRotationScaling}, _instance{-1}, _importerState{importerState} {} +CORRADE_IGNORE_DEPRECATED_PUSH /* MSVC warns here */ ObjectData3D::ObjectData3D(ObjectData3D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 noexcept #endif = default; +CORRADE_IGNORE_DEPRECATED_POP ObjectData3D::~ObjectData3D() = default; +CORRADE_IGNORE_DEPRECATED_PUSH /* GCC why you warn on return and not on param */ ObjectData3D& ObjectData3D::operator=(ObjectData3D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 noexcept #endif = default; +CORRADE_IGNORE_DEPRECATED_POP Vector3 ObjectData3D::translation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag3D::HasTranslationRotationScaling, "Trade::ObjectData3D::translation(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.translation; } Quaternion ObjectData3D::rotation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag3D::HasTranslationRotationScaling, "Trade::ObjectData3D::rotation(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.rotation; } Vector3 ObjectData3D::scaling() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ CORRADE_ASSERT(_flags & ObjectFlag3D::HasTranslationRotationScaling, "Trade::ObjectData3D::scaling(): object has only a combined transformation", {}); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.trs.scaling; } Matrix4 ObjectData3D::transformation() const { + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ if(_flags & ObjectFlag3D::HasTranslationRotationScaling) /* Has to be on a single line otherwise lcov reports an uncovered line. Ugh. */ return Matrix4::from(_transformation.trs.rotation.toMatrix(), _transformation.trs.translation)* Matrix4::scaling(_transformation.trs.scaling); + CORRADE_IGNORE_DEPRECATED_POP return _transformation.matrix; } #ifndef DOXYGEN_GENERATING_OUTPUT +CORRADE_IGNORE_DEPRECATED_PUSH Debug& operator<<(Debug& debug, const ObjectInstanceType3D value) { debug << "Trade::ObjectInstanceType3D" << Debug::nospace; @@ -114,6 +133,7 @@ Debug& operator<<(Debug& debug, const ObjectFlags3D value) { return enumSetDebugOutput(debug, value, "Trade::ObjectFlags3D{}", { ObjectFlag3D::HasTranslationRotationScaling}); } +CORRADE_IGNORE_DEPRECATED_POP #endif }} diff --git a/src/Magnum/Trade/ObjectData3D.h b/src/Magnum/Trade/ObjectData3D.h index f2acd5b221..f96ab29659 100644 --- a/src/Magnum/Trade/ObjectData3D.h +++ b/src/Magnum/Trade/ObjectData3D.h @@ -25,10 +25,17 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file * @brief Class @ref Magnum::Trade::ObjectData3D, enum @ref Magnum::Trade::ObjectInstanceType3D + * @m_deprecated_since_latest Use @ref Magnum/Trade/SceneData.h and the + * @relativeref{Magnum::Trade,SceneData} class instead. */ +#endif + +#include "Magnum/configure.h" +#ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Magnum.h" @@ -36,14 +43,19 @@ #include "Magnum/Math/Quaternion.h" #include "Magnum/Trade/visibility.h" +#ifndef _MAGNUM_NO_DEPRECATED_OBJECTDATA +CORRADE_DEPRECATED_FILE("use Magnum/Trade/SceneData.h and the SceneData class instead") +#endif + namespace Magnum { namespace Trade { /** @brief Type of instance held by given 3D object +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectData3D::instanceType() */ -enum class ObjectInstanceType3D: UnsignedByte { +enum class CORRADE_DEPRECATED_ENUM("use SceneData instead") ObjectInstanceType3D: UnsignedByte { Camera, /**< Camera instance (see @ref CameraData) */ Light, /**< Light instance (see @ref LightData) */ @@ -58,10 +70,11 @@ enum class ObjectInstanceType3D: UnsignedByte { /** @brief 3D object flag +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectFlags3D, @ref ObjectData3D::flags() */ -enum class ObjectFlag3D: UnsignedByte { +enum class CORRADE_DEPRECATED_ENUM("use SceneData instead") ObjectFlag3D: UnsignedByte { /** * The object provides separate translation / rotation / scaling * properties. The @ref ObjectData3D::transformation() matrix returns them @@ -74,21 +87,27 @@ enum class ObjectFlag3D: UnsignedByte { /** @brief 3D object flags +@m_deprecated_since_latest Use @ref SceneData instead. @see @ref ObjectData3D::flags() */ -typedef Containers::EnumSet ObjectFlags3D; +CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ +typedef CORRADE_DEPRECATED("use SceneData instead") Containers::EnumSet ObjectFlags3D; +CORRADE_IGNORE_DEPRECATED_POP +CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ENUMSET_OPERATORS(ObjectFlags3D) +CORRADE_IGNORE_DEPRECATED_POP /** @brief Three-dimensional object data +@m_deprecated_since_latest Use @ref SceneData instead. Provides access to object transformation and hierarchy. @see @ref AbstractImporter::object3D(), @ref MeshObjectData3D, @ref ObjectData2D */ -class MAGNUM_TRADE_EXPORT ObjectData3D { +class CORRADE_DEPRECATED("use SceneData instead") MAGNUM_TRADE_EXPORT ObjectData3D { public: /** * @brief Construct with combined transformation @@ -98,7 +117,9 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { * @param instance Instance ID * @param importerState Importer-specific state */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ explicit ObjectData3D(std::vector children, const Matrix4& transformation, ObjectInstanceType3D instanceType, UnsignedInt instance, const void* importerState = nullptr); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Construct with separate transformations @@ -110,7 +131,9 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { * @param instance Instance ID * @param importerState Importer-specific state */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ explicit ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, ObjectInstanceType3D instanceType, UnsignedInt instance, const void* importerState = nullptr); + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Construct empty instance with combined transformation @@ -164,7 +187,9 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { const std::vector& children() const { return _children; } /**< @overload */ /** @brief Flags */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectFlags3D flags() const { return _flags; } + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Translation (relative to parent) @@ -225,7 +250,9 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { * * @see @ref instance() */ + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectInstanceType3D instanceType() const { return _instanceType; } + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Instance ID @@ -255,21 +282,37 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { Vector3 scaling; } trs; } _transformation; + CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ ObjectInstanceType3D _instanceType; ObjectFlags3D _flags; + CORRADE_IGNORE_DEPRECATED_POP Int _instance; const void* _importerState; }; -/** @debugoperatorenum{ObjectInstanceType3D} */ +CORRADE_IGNORE_DEPRECATED_PUSH +/** +@debugoperatorenum{ObjectInstanceType3D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectInstanceType3D value); -/** @debugoperatorenum{ObjectFlag3D} */ +/** +@debugoperatorenum{ObjectFlag3D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlag3D value); -/** @debugoperatorenum{ObjectFlags3D} */ +/** +@debugoperatorenum{ObjectFlags3D} +@m_deprecated_since_latest Use @ref SceneData instead. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlags3D value); +CORRADE_IGNORE_DEPRECATED_POP }} +#else +#error use Magnum/Trade/SceneData.h and the SceneData class instead +#endif #endif diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index a3f825854f..24a5bccf3f 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -2182,6 +2182,11 @@ class MAGNUM_TRADE_EXPORT SceneData { const void* importerState() const { return _importerState; } private: + /* For custom deleter checks. Not done in the constructors here because + the restriction is pointless when used outside of plugin + implementations. */ + friend AbstractImporter; + /* Internal helper that doesn't assert, unlike fieldId() */ UnsignedInt fieldFor(SceneField name) const; diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 05deeba0f9..09aa1ad70e 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -34,6 +34,8 @@ #include "Magnum/PixelFormat.h" #include "Magnum/FileCallback.h" +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/ArrayAllocator.h" @@ -41,18 +43,22 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MeshData.h" -#include "Magnum/Trade/MeshObjectData2D.h" -#include "Magnum/Trade/MeshObjectData3D.h" #include "Magnum/Trade/PhongMaterialData.h" #include "Magnum/Trade/SceneData.h" #include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" #ifdef MAGNUM_BUILD_DEPRECATED +#include +#include + #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ #include "Magnum/Trade/MeshData2D.h" #include "Magnum/Trade/MeshData3D.h" +#include "Magnum/Trade/MeshObjectData2D.h" +#include "Magnum/Trade/MeshObjectData3D.h" #endif #include "configure.h" @@ -109,11 +115,26 @@ struct AbstractImporterTest: TestSuite::Tester { void defaultSceneOutOfRange(); void scene(); + void object(); + #ifdef MAGNUM_BUILD_DEPRECATED + void sceneDeprecatedFallback2D(); + void sceneDeprecatedFallback3D(); + void sceneDeprecatedFallbackParentless2D(); + void sceneDeprecatedFallbackParentless3D(); + void sceneDeprecatedFallbackTransformless2D(); + void sceneDeprecatedFallbackTransformless3D(); + #endif void sceneNameNotImplemented(); + void objectNameNotImplemented(); void sceneForNameOutOfRange(); + void objectForNameOutOfRange(); void sceneNameOutOfRange(); + void objectNameOutOfRange(); void sceneNotImplemented(); void sceneOutOfRange(); + void sceneNonOwningDeleters(); + void sceneCustomDataDeleter(); + void sceneCustomFieldDataDeleter(); void sceneFieldName(); void sceneFieldNameNotImplemented(); @@ -144,19 +165,35 @@ struct AbstractImporterTest: TestSuite::Tester { void cameraNotImplemented(); void cameraOutOfRange(); + #ifdef MAGNUM_BUILD_DEPRECATED void object2D(); - void object2DNameNotImplemented(); + void object2DCountNotImplemented(); + void object2DCountNoFile(); + void object2DForNameNotImplemented(); + void object2DForNameNoFile(); void object2DForNameOutOfRange(); + void object2DByNameNotFound(); + void object2DNameNotImplemented(); + void object2DNameNoFile(); void object2DNameOutOfRange(); void object2DNotImplemented(); + void object2DNoFile(); void object2DOutOfRange(); void object3D(); - void object3DNameNotImplemented(); + void object3DCountNotImplemented(); + void object3DCountNoFile(); + void object3DForNameNotImplemented(); + void object3DForNameNoFile(); void object3DForNameOutOfRange(); + void object3DByNameNotFound(); + void object3DNameNotImplemented(); + void object3DNameNoFile(); void object3DNameOutOfRange(); void object3DNotImplemented(); + void object3DNoFile(); void object3DOutOfRange(); + #endif void skin2D(); void skin2DNameNotImplemented(); @@ -307,6 +344,8 @@ constexpr struct { {"verify the message", true} }; +using namespace Math::Literals; + AbstractImporterTest::AbstractImporterTest() { addTests({&AbstractImporterTest::construct, &AbstractImporterTest::constructWithPluginManagerReference, @@ -358,11 +397,26 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::defaultSceneNotImplemented, &AbstractImporterTest::scene, + &AbstractImporterTest::object, + #ifdef MAGNUM_BUILD_DEPRECATED + &AbstractImporterTest::sceneDeprecatedFallback2D, + &AbstractImporterTest::sceneDeprecatedFallback3D, + &AbstractImporterTest::sceneDeprecatedFallbackParentless2D, + &AbstractImporterTest::sceneDeprecatedFallbackParentless3D, + &AbstractImporterTest::sceneDeprecatedFallbackTransformless2D, + &AbstractImporterTest::sceneDeprecatedFallbackTransformless3D, + #endif &AbstractImporterTest::sceneForNameOutOfRange, + &AbstractImporterTest::objectForNameOutOfRange, &AbstractImporterTest::sceneNameNotImplemented, + &AbstractImporterTest::objectNameNotImplemented, &AbstractImporterTest::sceneNameOutOfRange, + &AbstractImporterTest::objectNameOutOfRange, &AbstractImporterTest::sceneNotImplemented, &AbstractImporterTest::sceneOutOfRange, + &AbstractImporterTest::sceneNonOwningDeleters, + &AbstractImporterTest::sceneCustomDataDeleter, + &AbstractImporterTest::sceneCustomFieldDataDeleter, &AbstractImporterTest::sceneFieldName, &AbstractImporterTest::sceneFieldNameNotImplemented, @@ -391,23 +445,45 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::cameraNameNotImplemented, &AbstractImporterTest::cameraNameOutOfRange, &AbstractImporterTest::cameraNotImplemented, - &AbstractImporterTest::cameraOutOfRange, + &AbstractImporterTest::cameraOutOfRange}); + + #ifdef MAGNUM_BUILD_DEPRECATED + addTests({&AbstractImporterTest::object2D, + &AbstractImporterTest::object2DCountNotImplemented, + &AbstractImporterTest::object2DCountNoFile, + &AbstractImporterTest::object2DForNameNotImplemented, + &AbstractImporterTest::object2DForNameNoFile, + &AbstractImporterTest::object2DForNameOutOfRange}); + + addInstancedTests({&AbstractImporterTest::object2DByNameNotFound}, + Containers::arraySize(ThingByNameData)); - &AbstractImporterTest::object2D, - &AbstractImporterTest::object2DForNameOutOfRange, - &AbstractImporterTest::object2DNameNotImplemented, + addTests({&AbstractImporterTest::object2DNameNotImplemented, + &AbstractImporterTest::object2DNameNoFile, &AbstractImporterTest::object2DNameOutOfRange, &AbstractImporterTest::object2DNotImplemented, + &AbstractImporterTest::object2DNoFile, &AbstractImporterTest::object2DOutOfRange, &AbstractImporterTest::object3D, - &AbstractImporterTest::object3DForNameOutOfRange, - &AbstractImporterTest::object3DNameNotImplemented, + &AbstractImporterTest::object3DCountNotImplemented, + &AbstractImporterTest::object3DCountNoFile, + &AbstractImporterTest::object3DForNameNotImplemented, + &AbstractImporterTest::object3DForNameNoFile, + &AbstractImporterTest::object3DForNameOutOfRange}); + + addInstancedTests({&AbstractImporterTest::object3DByNameNotFound}, + Containers::arraySize(ThingByNameData)); + + addTests({&AbstractImporterTest::object3DNameNotImplemented, + &AbstractImporterTest::object3DNameNoFile, &AbstractImporterTest::object3DNameOutOfRange, &AbstractImporterTest::object3DNotImplemented, - &AbstractImporterTest::object3DOutOfRange, + &AbstractImporterTest::object3DNoFile, + &AbstractImporterTest::object3DOutOfRange}); + #endif - &AbstractImporterTest::skin2D, + addTests({&AbstractImporterTest::skin2D, &AbstractImporterTest::skin2DForNameOutOfRange, &AbstractImporterTest::skin2DNameNotImplemented, &AbstractImporterTest::skin2DNameOutOfRange, @@ -1210,13 +1286,11 @@ void AbstractImporterTest::thingCountNotImplemented() { } importer; CORRADE_COMPARE(importer.sceneCount(), 0); + CORRADE_COMPARE(importer.objectCount(), 0); CORRADE_COMPARE(importer.animationCount(), 0); CORRADE_COMPARE(importer.lightCount(), 0); CORRADE_COMPARE(importer.cameraCount(), 0); - CORRADE_COMPARE(importer.object2DCount(), 0); - CORRADE_COMPARE(importer.object3DCount(), 0); - CORRADE_COMPARE(importer.skin2DCount(), 0); CORRADE_COMPARE(importer.skin3DCount(), 0); @@ -1244,13 +1318,11 @@ void AbstractImporterTest::thingCountNoFile() { Error redirectError{&out}; importer.sceneCount(); + importer.objectCount(); importer.animationCount(); importer.lightCount(); importer.cameraCount(); - importer.object2DCount(); - importer.object3DCount(); - importer.skin2DCount(); importer.skin3DCount(); @@ -1268,13 +1340,11 @@ void AbstractImporterTest::thingCountNoFile() { CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneCount(): no file opened\n" + "Trade::AbstractImporter::objectCount(): no file opened\n" "Trade::AbstractImporter::animationCount(): no file opened\n" "Trade::AbstractImporter::lightCount(): no file opened\n" "Trade::AbstractImporter::cameraCount(): no file opened\n" - "Trade::AbstractImporter::object2DCount(): no file opened\n" - "Trade::AbstractImporter::object3DCount(): no file opened\n" - "Trade::AbstractImporter::skin2DCount(): no file opened\n" "Trade::AbstractImporter::skin3DCount(): no file opened\n" @@ -1299,13 +1369,11 @@ void AbstractImporterTest::thingForNameNotImplemented() { } importer; CORRADE_COMPARE(importer.sceneForName(""), -1); + CORRADE_COMPARE(importer.objectForName(""), -1); CORRADE_COMPARE(importer.animationForName(""), -1); CORRADE_COMPARE(importer.lightForName(""), -1); CORRADE_COMPARE(importer.cameraForName(""), -1); - CORRADE_COMPARE(importer.object2DForName(""), -1); - CORRADE_COMPARE(importer.object3DForName(""), -1); - CORRADE_COMPARE(importer.skin2DForName(""), -1); CORRADE_COMPARE(importer.skin3DForName(""), -1); @@ -1333,13 +1401,11 @@ void AbstractImporterTest::thingForNameNoFile() { Error redirectError{&out}; importer.sceneForName(""); + importer.objectForName(""); importer.animationForName(""); importer.lightForName(""); importer.cameraForName(""); - importer.object2DForName(""); - importer.object3DForName(""); - importer.skin2DForName(""); importer.skin3DForName(""); @@ -1353,13 +1419,11 @@ void AbstractImporterTest::thingForNameNoFile() { CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneForName(): no file opened\n" + "Trade::AbstractImporter::objectForName(): no file opened\n" "Trade::AbstractImporter::animationForName(): no file opened\n" "Trade::AbstractImporter::lightForName(): no file opened\n" "Trade::AbstractImporter::cameraForName(): no file opened\n" - "Trade::AbstractImporter::object2DForName(): no file opened\n" - "Trade::AbstractImporter::object3DForName(): no file opened\n" - "Trade::AbstractImporter::skin2DForName(): no file opened\n" "Trade::AbstractImporter::skin3DForName(): no file opened\n" @@ -1386,19 +1450,16 @@ void AbstractImporterTest::thingByNameNotFound() { UnsignedInt doLightCount() const override { return 3; } UnsignedInt doCameraCount() const override { return 4; } - UnsignedInt doObject2DCount() const override { return 5; } - UnsignedInt doObject3DCount() const override { return 6; } - - UnsignedInt doSkin2DCount() const override { return 7; } - UnsignedInt doSkin3DCount() const override { return 8; } + UnsignedInt doSkin2DCount() const override { return 5; } + UnsignedInt doSkin3DCount() const override { return 6; } - UnsignedInt doMeshCount() const override { return 9; } - UnsignedInt doMaterialCount() const override { return 10; } - UnsignedInt doTextureCount() const override { return 11; } + UnsignedInt doMeshCount() const override { return 7; } + UnsignedInt doMaterialCount() const override { return 8; } + UnsignedInt doTextureCount() const override { return 9; } - UnsignedInt doImage1DCount() const override { return 12; } - UnsignedInt doImage2DCount() const override { return 13; } - UnsignedInt doImage3DCount() const override { return 14; } + UnsignedInt doImage1DCount() const override { return 10; } + UnsignedInt doImage2DCount() const override { return 11; } + UnsignedInt doImage3DCount() const override { return 12; } } importer; std::ostringstream out; @@ -1411,9 +1472,6 @@ void AbstractImporterTest::thingByNameNotFound() { CORRADE_VERIFY(!importer.light("foobar")); CORRADE_VERIFY(!importer.camera("foobar")); - CORRADE_VERIFY(!importer.object2D("foobar")); - CORRADE_VERIFY(!importer.object3D("foobar")); - CORRADE_VERIFY(!importer.skin2D("foobar")); CORRADE_VERIFY(!importer.skin3D("foobar")); @@ -1433,19 +1491,16 @@ void AbstractImporterTest::thingByNameNotFound() { "Trade::AbstractImporter::light(): light foobar not found among 3 entries\n" "Trade::AbstractImporter::camera(): camera foobar not found among 4 entries\n" - "Trade::AbstractImporter::object2D(): object foobar not found among 5 entries\n" - "Trade::AbstractImporter::object3D(): object foobar not found among 6 entries\n" + "Trade::AbstractImporter::skin2D(): skin foobar not found among 5 entries\n" + "Trade::AbstractImporter::skin3D(): skin foobar not found among 6 entries\n" - "Trade::AbstractImporter::skin2D(): skin foobar not found among 7 entries\n" - "Trade::AbstractImporter::skin3D(): skin foobar not found among 8 entries\n" + "Trade::AbstractImporter::mesh(): mesh foobar not found among 7 entries\n" + "Trade::AbstractImporter::material(): material foobar not found among 8 entries\n" + "Trade::AbstractImporter::texture(): texture foobar not found among 9 entries\n" - "Trade::AbstractImporter::mesh(): mesh foobar not found among 9 entries\n" - "Trade::AbstractImporter::material(): material foobar not found among 10 entries\n" - "Trade::AbstractImporter::texture(): texture foobar not found among 11 entries\n" - - "Trade::AbstractImporter::image1D(): image foobar not found among 12 entries\n" - "Trade::AbstractImporter::image2D(): image foobar not found among 13 entries\n" - "Trade::AbstractImporter::image3D(): image foobar not found among 14 entries\n"); + "Trade::AbstractImporter::image1D(): image foobar not found among 10 entries\n" + "Trade::AbstractImporter::image2D(): image foobar not found among 11 entries\n" + "Trade::AbstractImporter::image3D(): image foobar not found among 12 entries\n"); } } @@ -1468,9 +1523,6 @@ void AbstractImporterTest::thingNameNoFile() { importer.lightName(42); importer.cameraName(42); - importer.object2DName(42); - importer.object3DName(42); - importer.skin2DName(42); importer.skin3DName(42); @@ -1488,9 +1540,6 @@ void AbstractImporterTest::thingNameNoFile() { "Trade::AbstractImporter::lightName(): no file opened\n" "Trade::AbstractImporter::cameraName(): no file opened\n" - "Trade::AbstractImporter::object2DName(): no file opened\n" - "Trade::AbstractImporter::object3DName(): no file opened\n" - "Trade::AbstractImporter::skin2DName(): no file opened\n" "Trade::AbstractImporter::skin3DName(): no file opened\n" @@ -1527,11 +1576,6 @@ void AbstractImporterTest::thingNoFile() { importer.camera(42); importer.camera("foo"); - importer.object2D(42); - importer.object2D("foo"); - importer.object3D(42); - importer.object3D("foo"); - importer.skin2D(42); importer.skin2D("foo"); importer.skin3D(42); @@ -1564,11 +1608,6 @@ void AbstractImporterTest::thingNoFile() { "Trade::AbstractImporter::camera(): no file opened\n" "Trade::AbstractImporter::camera(): no file opened\n" - "Trade::AbstractImporter::object2D(): no file opened\n" - "Trade::AbstractImporter::object2D(): no file opened\n" - "Trade::AbstractImporter::object3D(): no file opened\n" - "Trade::AbstractImporter::object3D(): no file opened\n" - "Trade::AbstractImporter::skin2D(): no file opened\n" "Trade::AbstractImporter::skin2D(): no file opened\n" "Trade::AbstractImporter::skin3D(): no file opened\n" @@ -1673,39 +1712,29 @@ void AbstractImporterTest::scene() { } } -void AbstractImporterTest::sceneForNameOutOfRange() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - struct: AbstractImporter { - ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } - void doClose() override {} - - UnsignedInt doSceneCount() const override { return 8; } - Int doSceneForName(const std::string&) override { return 8; } - } importer; - - std::ostringstream out; - Error redirectError{&out}; - importer.sceneForName(""); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneForName(): implementation-returned index 8 out of range for 8 entries\n"); -} - -void AbstractImporterTest::sceneNameNotImplemented() { +void AbstractImporterTest::object() { struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doSceneCount() const override { return 8; } + UnsignedLong doObjectCount() const override { return 8; } + Long doObjectForName(const std::string& name) override { + if(name == "eighth") return 7; + return -1; + } + std::string doObjectName(UnsignedLong id) override { + if(id == 7) return "eighth"; + return {}; + } } importer; - CORRADE_COMPARE(importer.sceneName(7), ""); + CORRADE_COMPARE(importer.objectCount(), 8); + CORRADE_COMPARE(importer.objectForName("eighth"), 7); + CORRADE_COMPARE(importer.objectName(7), "eighth"); } -void AbstractImporterTest::sceneNameOutOfRange() { +void AbstractImporterTest::sceneForNameOutOfRange() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -1716,16 +1745,16 @@ void AbstractImporterTest::sceneNameOutOfRange() { void doClose() override {} UnsignedInt doSceneCount() const override { return 8; } + Int doSceneForName(const std::string&) override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; - - importer.sceneName(8); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneName(): index 8 out of range for 8 entries\n"); + importer.sceneForName(""); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneForName(): implementation-returned index 8 out of range for 8 entries\n"); } -void AbstractImporterTest::sceneNotImplemented() { +void AbstractImporterTest::objectForNameOutOfRange() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -1735,155 +1764,1229 @@ void AbstractImporterTest::sceneNotImplemented() { bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doSceneCount() const override { return 8; } + UnsignedLong doObjectCount() const override { return 8; } + Long doObjectForName(const std::string&) override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; - - importer.scene(7); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::scene(): not implemented\n"); + importer.objectForName(""); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::objectForName(): implementation-returned index 8 out of range for 8 entries\n"); } -void AbstractImporterTest::sceneOutOfRange() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif +#ifdef MAGNUM_BUILD_DEPRECATED +void AbstractImporterTest::sceneDeprecatedFallback2D() { + /* Need to test the following combinations: + + - few objects in the root + - an object with one child, with more than one, with none + - an object with a mesh and a material + - an object with a mesh and without a material + - an object with a mesh and a skin + - an object with a skin but no mesh + - an object with a camera + - an object with TRS transformation + - an object with TRS transformation and a mesh + - an object with nothing except parent / transformation + */ + + struct Transform { + UnsignedInt object; + Int parent; + Matrix3 transformation; + }; + struct Trs { + UnsignedInt object; + Vector2 translation; + Complex rotation; + Vector2 scaling; + }; + struct Mesh { + UnsignedInt object; + UnsignedShort mesh; + Short meshMaterial; + }; + struct Index { + UnsignedInt object; + UnsignedInt id; + }; + struct ImporterState { + UnsignedInt object; + const void* importerState; + }; + Containers::StridedArrayView1D transformations; + Containers::StridedArrayView1D trs; + Containers::StridedArrayView1D meshes; + Containers::StridedArrayView1D cameras; + Containers::StridedArrayView1D skins; + Containers::StridedArrayView1D importerState; + Containers::Array data = Containers::ArrayTuple{ + {NoInit, 6, transformations}, + {NoInit, 2, trs}, + {NoInit, 2, meshes}, + {NoInit, 1, cameras}, + {NoInit, 2, skins}, + {NoInit, 3, importerState} + }; - struct: AbstractImporter { - ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } - void doClose() override {} + int a, b, c; - UnsignedInt doSceneCount() const override { return 8; } - } importer; + /* Object 3 is in the root, has a camera attached, TRS and children 5 + 4. + Because of the TRS, the actual transformation gets ignored. Has importer + state. */ + transformations[0] = {3, -1, Matrix3::rotation(75.0_degf)}; + trs[0] = {3, {0.0f, 3.0f}, Complex::rotation(15.0_degf), Vector2{1.0f}}; + cameras[0] = {3, 15}; + importerState[0] = {3, &a}; - std::ostringstream out; - Error redirectError{&out}; + /* Object 5 is a child of object 3 (which is at index 0), has a skin (which + gets ignored by the legacy interface) */ + transformations[1] = {5, 0, Matrix3::rotation(-15.0_degf)}; + skins[0] = {5, 226}; - importer.scene(8); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::scene(): index 8 out of range for 8 entries\n"); -} + /* Object 1 is a child of object 2 (which will be at index 3) */ + transformations[2] = {1, 3, Matrix3::translation({1.0f, 0.5f})*Matrix3::rotation(15.0_degf)}; -void AbstractImporterTest::sceneFieldName() { - struct: AbstractImporter { - ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return false; } - void doClose() override {} + /* Object 2 is in the root, has object 1 as a child but nothing else */ + transformations[3] = {2, -1, {}}; - SceneField doSceneFieldForName(const std::string& name) override { - if(name == "OctreeCell") return sceneFieldCustom(100037); - return SceneField{}; - } + /* Object 0 is in the root, has a mesh without a material and no children */ + transformations[4] = {0, -1, Matrix3::rotation(30.0_degf)}; + meshes[0] = {0, 33, -1}; - std::string doSceneFieldName(UnsignedInt id) override { - if(id == 100037) return "OctreeCell"; - return ""; - } - } importer; + /* Object 4 has TRS also, a mesh with a material and a skin and is a child + of object 3 (which is at index 0). The transformation gets ignored + again. Has importer state. */ + transformations[5] = {4, 0, Matrix3::translation(Vector2::xAxis(5.0f))}; + trs[1] = {4, {}, {}, {1.5f, -0.5f}}; + meshes[1] = {4, 27, 46}; + skins[1] = {4, 72}; + importerState[1] = {4, &b}; - CORRADE_COMPARE(importer.sceneFieldForName("OctreeCell"), sceneFieldCustom(100037)); - CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), "OctreeCell"); -} + /* Object 6 has neither a transformation nor a parent, only an importer + state. It should get ignored. */ + importerState[2] = {6, &c}; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} -void AbstractImporterTest::sceneFieldNameNotImplemented() { - struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return false; } + bool doIsOpened() const override { return true; } void doClose() override {} - } importer; - CORRADE_COMPARE(importer.sceneFieldForName(""), SceneField{}); - CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), ""); -} + UnsignedInt doSceneCount() const override { return 3; } + UnsignedLong doObjectCount() const override { return 7; } + Long doObjectForName(const std::string& name) override { + if(name == "sixth") return 5; + return -1; + } + std::string doObjectName(UnsignedLong id) override { + if(id == 5) return "sixth"; + return {}; + } + Containers::Optional doScene(UnsignedInt id) override { + /* This one has seven objects, but no fields for them so it should + get skipped */ + if(id == 0) + return SceneData{SceneObjectType::UnsignedByte, 7, nullptr, {}}; + /* This one has no objects, so it should get skipped as well + without even querying any fieldFor() API (as those would + assert) */ + if(id == 1) + return SceneData{SceneObjectType::UnsignedShort, 0, nullptr, {}}; + /* This one is the one */ + if(id == 2) + return SceneData{SceneObjectType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } -void AbstractImporterTest::sceneFieldNameNotCustom() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 7, std::move(data), { + SceneFieldData{SceneField::Parent, + transformations.slice(&Transform::object), + transformations.slice(&Transform::parent)}, + SceneFieldData{SceneField::Transformation, + transformations.slice(&Transform::object), + transformations.slice(&Transform::transformation)}, + SceneFieldData{SceneField::Translation, + trs.slice(&Trs::object), + trs.slice(&Trs::translation)}, + SceneFieldData{SceneField::Rotation, + trs.slice(&Trs::object), + trs.slice(&Trs::rotation)}, + SceneFieldData{SceneField::Scaling, + trs.slice(&Trs::object), + trs.slice(&Trs::scaling)}, + SceneFieldData{SceneField::Mesh, + meshes.slice(&Mesh::object), + meshes.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, + meshes.slice(&Mesh::object), + meshes.slice(&Mesh::meshMaterial)}, + SceneFieldData{SceneField::Camera, + cameras.slice(&Index::object), + cameras.slice(&Index::id)}, + SceneFieldData{SceneField::Skin, + skins.slice(&Index::object), + skins.slice(&Index::id)}, + SceneFieldData{SceneField::ImporterState, + importerState.slice(&ImporterState::object), importerState.slice(&ImporterState::importerState)} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 3); + + Containers::Optional scene = importer.scene(2); + CORRADE_VERIFY(scene); - struct: AbstractImporter { - ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return false; } - void doClose() override {} + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + (std::vector{3, 2, 0}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{}), + TestSuite::Compare::Container); - SceneField doSceneFieldForName(const std::string&) override { - return SceneField::Translation; - } - } importer; + CORRADE_COMPARE(importer.object2DCount(), 7); + CORRADE_COMPARE(importer.object2DForName("sixth"), 5); + CORRADE_COMPARE(importer.object2DName(5), "sixth"); - std::ostringstream out; - Error redirectError{&out}; - importer.sceneFieldForName("OctreeCell"); - importer.sceneFieldName(SceneField::Translation); - CORRADE_COMPARE(out.str(), - "Trade::AbstractImporter::sceneFieldForName(): implementation-returned Trade::SceneField::Translation is neither custom nor invalid\n" - "Trade::AbstractImporter::sceneFieldName(): Trade::SceneField::Translation is not custom\n"); + CORRADE_COMPARE(importer.object3DCount(), 0); + CORRADE_COMPARE(importer.object3DForName("sixth"), -1); + + { + Containers::Pointer o = importer.object2D(0); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 33); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3::rotation(30.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + CORRADE_COMPARE(mo.skin(), -1); + } { + Containers::Pointer o = importer.object2D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3::translation({1.0f, 0.5f})*Matrix3::rotation(15.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(2); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{1}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(3); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), &a); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Camera); + CORRADE_COMPARE(o->instance(), 15); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); + CORRADE_COMPARE(o->transformation(), Matrix3::translation({0.0f, 3.0f})*Matrix3::rotation(15.0_degf)); + CORRADE_COMPARE(o->translation(), (Vector2{0.0f, 3.0f})); + CORRADE_COMPARE(o->rotation(), Complex::rotation(15.0_degf)); + CORRADE_COMPARE(o->scaling(), (Vector2{1.0f})); + CORRADE_COMPARE_AS(o->children(), + (std::vector{5, 4}), + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(4); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), &b); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 27); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); + CORRADE_COMPARE(o->transformation(), Matrix3::scaling({1.5f, -0.5f})); + CORRADE_COMPARE(o->translation(), Vector2{}); + CORRADE_COMPARE(o->rotation(), Complex{}); + CORRADE_COMPARE(o->scaling(), (Vector2{1.5, -0.5f})); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 46); + CORRADE_COMPARE(mo.skin(), 72); + } { + Containers::Pointer o = importer.object2D("sixth"); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3::rotation(-15.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + /* This one is not contained in any parent hierarchy, so it fails to + import */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!importer.object2D(6)); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2D(): object 6 not found in any 2D scene hierarchy\n"); + } + CORRADE_IGNORE_DEPRECATED_POP } -void AbstractImporterTest::animation() { - struct: AbstractImporter { +void AbstractImporterTest::sceneDeprecatedFallback3D() { + /* Need to test the following combinations: + + - few objects in the root + - an object with one child, with more than one, with none + - an object with a mesh and a material + - an object with a mesh and without a material + - an object with a mesh and a skin + - an object with a skin but no mesh + - an object with a camera + - an object with a light + - an object with TRS transformation + - an object with TRS transformation and a mesh + - an object with nothing except parent / transformation + */ + + struct Transform { + UnsignedInt object; + Int parent; + Matrix4 transformation; + }; + struct Trs { + UnsignedInt object; + Vector3 translation; + Quaternion rotation; + Vector3 scaling; + }; + struct Mesh { + UnsignedInt object; + UnsignedShort mesh; + Short meshMaterial; + }; + struct Index { + UnsignedInt object; + UnsignedInt id; + }; + struct ImporterState { + UnsignedInt object; + const void* importerState; + }; + Containers::StridedArrayView1D transformations; + Containers::StridedArrayView1D trs; + Containers::StridedArrayView1D meshes; + Containers::StridedArrayView1D cameras; + Containers::StridedArrayView1D lights; + Containers::StridedArrayView1D skins; + Containers::StridedArrayView1D importerState; + Containers::Array data = Containers::ArrayTuple{ + {NoInit, 6, transformations}, + {NoInit, 2, trs}, + {NoInit, 2, meshes}, + {NoInit, 1, cameras}, + {NoInit, 1, lights}, + {NoInit, 2, skins}, + {NoInit, 3, importerState} + }; + + int a, b, c; + + /* Object 3 is in the root, has a camera attached, TRS and children 5 + 4. + Because of the TRS, the actual transformation gets ignored. Has importer + state. */ + transformations[0] = {3, -1, Matrix4::rotationX(75.0_degf)}; + trs[0] = {3, {0.0f, 0.0f, 3.0f}, Quaternion::rotation(15.0_degf, Vector3::xAxis()), Vector3{1.0f}}; + cameras[0] = {3, 15}; + importerState[0] = {3, &a}; + + /* Object 5 is a child of object 3 (which is at index 0), has a skin (which + gets ignored by the legacy interface) */ + transformations[1] = {5, 0, Matrix4::rotationY(-15.0_degf)}; + skins[0] = {5, 226}; + + /* Object 1 is a child of object 2 (which will be at index 3), has a light. */ + transformations[2] = {1, 3, Matrix4::translation({1.0f, 0.0f, 1.0f})*Matrix4::rotationZ(15.0_degf)}; + lights[0] = {1, 113}; + + /* Object 2 is in the root, has object 1 as a child but nothing else */ + transformations[3] = {2, -1, {}}; + + /* Object 0 is in the root, has a mesh without a material and no children */ + transformations[4] = {0, -1, Matrix4::rotationX(30.0_degf)}; + meshes[0] = {0, 33, -1}; + + /* Object 4 has TRS also, a mesh with a material and a skin and is a child + of object 3 (which is at index 0). The transformation gets ignored + again. Has importer state. */ + transformations[5] = {4, 0, Matrix4::translation(Vector3::xAxis(5.0f))}; + trs[1] = {4, {}, {}, {1.5f, 3.0f, -0.5f}}; + meshes[1] = {4, 27, 46}; + skins[1] = {4, 72}; + importerState[1] = {4, &b}; + + /* Object 6 has neither a transformation nor a parent, only an importer + state. It should get ignored. */ + importerState[2] = {6, &c}; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doAnimationCount() const override { return 8; } - Int doAnimationForName(const std::string& name) override { - if(name == "eighth") return 7; + UnsignedInt doSceneCount() const override { return 3; } + UnsignedLong doObjectCount() const override { return 7; } + Long doObjectForName(const std::string& name) override { + if(name == "sixth") return 5; return -1; } - std::string doAnimationName(UnsignedInt id) override { - if(id == 7) return "eighth"; + std::string doObjectName(UnsignedLong id) override { + if(id == 5) return "sixth"; return {}; } - Containers::Optional doAnimation(UnsignedInt id) override { - /* Verify that initializer list is converted to an array with - the default deleter and not something disallowed */ - if(id == 7) return AnimationData{nullptr, { - AnimationTrackData{AnimationTrackType::Vector3, - AnimationTrackTargetType::Scaling3D, 0, {}} - }, &state}; - return AnimationData{{}, {}}; + Containers::Optional doScene(UnsignedInt id) override { + /* This one has seven objects, but no fields for them so it should + get skipped */ + if(id == 0) + return SceneData{SceneObjectType::UnsignedByte, 7, nullptr, {}}; + /* This one has no objects, so it should get skipped as well + without even querying any fieldFor() API (as those would + assert) */ + if(id == 1) + return SceneData{SceneObjectType::UnsignedShort, 0, nullptr, {}}; + /* This one is the one */ + if(id == 2) + return SceneData{SceneObjectType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } - } importer; - CORRADE_COMPARE(importer.animationCount(), 8); - CORRADE_COMPARE(importer.animationForName("eighth"), 7); - CORRADE_COMPARE(importer.animationName(7), "eighth"); + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 7, std::move(data), { + SceneFieldData{SceneField::Parent, + transformations.slice(&Transform::object), + transformations.slice(&Transform::parent)}, + SceneFieldData{SceneField::Transformation, + transformations.slice(&Transform::object), + transformations.slice(&Transform::transformation)}, + SceneFieldData{SceneField::Translation, + trs.slice(&Trs::object), + trs.slice(&Trs::translation)}, + SceneFieldData{SceneField::Rotation, + trs.slice(&Trs::object), + trs.slice(&Trs::rotation)}, + SceneFieldData{SceneField::Scaling, + trs.slice(&Trs::object), + trs.slice(&Trs::scaling)}, + SceneFieldData{SceneField::Mesh, + meshes.slice(&Mesh::object), + meshes.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, + meshes.slice(&Mesh::object), + meshes.slice(&Mesh::meshMaterial)}, + SceneFieldData{SceneField::Camera, + cameras.slice(&Index::object), + cameras.slice(&Index::id)}, + SceneFieldData{SceneField::Light, + lights.slice(&Index::object), + lights.slice(&Index::id)}, + SceneFieldData{SceneField::Skin, + skins.slice(&Index::object), + skins.slice(&Index::id)}, + SceneFieldData{SceneField::ImporterState, + importerState.slice(&ImporterState::object), importerState.slice(&ImporterState::importerState)} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 3); + + Containers::Optional scene = importer.scene(2); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{3, 2, 0}), + TestSuite::Compare::Container); + + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object2DForName("sixth"), -1); + + CORRADE_COMPARE(importer.object3DCount(), 7); + CORRADE_COMPARE(importer.object3DForName("sixth"), 5); + CORRADE_COMPARE(importer.object3DName(5), "sixth"); { - auto data = importer.animation(7); - CORRADE_VERIFY(data); - CORRADE_COMPARE(data->importerState(), &state); + Containers::Pointer o = importer.object3D(0); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 33); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4::rotationX(30.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + CORRADE_COMPARE(mo.skin(), -1); } { - auto data = importer.animation("eighth"); - CORRADE_VERIFY(data); - CORRADE_COMPARE(data->importerState(), &state); + Containers::Pointer o = importer.object3D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Light); + CORRADE_COMPARE(o->instance(), 113); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4::translation({1.0f, 0.0f, 1.0f})*Matrix4::rotationZ(15.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(2); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{1}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(3); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), &a); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Camera); + CORRADE_COMPARE(o->instance(), 15); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); + CORRADE_COMPARE(o->transformation(), Matrix4::translation({0.0f, 0.0f, 3.0f})*Matrix4::rotationX(15.0_degf)); + CORRADE_COMPARE(o->translation(), (Vector3{0.0f, 0.0f, 3.0f})); + CORRADE_COMPARE(o->rotation(), Quaternion::rotation(15.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o->scaling(), (Vector3{1.0f})); + CORRADE_COMPARE_AS(o->children(), + (std::vector{5, 4}), + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(4); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), &b); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 27); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); + CORRADE_COMPARE(o->transformation(), Matrix4::scaling({1.5f, 3.0f, -0.5f})); + CORRADE_COMPARE(o->translation(), Vector3{}); + CORRADE_COMPARE(o->rotation(), Quaternion{}); + CORRADE_COMPARE(o->scaling(), (Vector3{1.5, 3.0f, -0.5f})); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 46); + CORRADE_COMPARE(mo.skin(), 72); + } { + Containers::Pointer o = importer.object3D("sixth"); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4::rotationY(-15.0_degf)); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + /* This one is not contained in any parent hierarchy, so it fails to + import */ + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!importer.object3D(6)); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3D(): object 6 not found in any 3D scene hierarchy\n"); } + CORRADE_IGNORE_DEPRECATED_POP } -void AbstractImporterTest::animationForNameOutOfRange() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif +void AbstractImporterTest::sceneDeprecatedFallbackParentless2D() { + /* As the Parent field is currently used to distinguish which objects + belong to which scene, its absence means the objects are advertised, but + aren't listed as children of any scene, and retrieving them will fail */ + /** @todo adapt when there's a dedicated way to distinguish which objects + belong to which scene */ - struct: AbstractImporter { - ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } - void doClose() override {} + struct Field { + UnsignedInt object; + Matrix3 transformation; + } fields[]{ + {5, Matrix3{}}, + {2, Matrix3{}}, + }; - UnsignedInt doAnimationCount() const override { return 8; } - Int doAnimationForName(const std::string&) override { return 8; } - } importer; + Containers::StridedArrayView1D view = fields; - std::ostringstream out; - Error redirectError{&out}; - importer.animationForName(""); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::animationForName(): implementation-returned index 8 out of range for 8 entries\n"); -} + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} -void AbstractImporterTest::animationNameNotImplemented() { - struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedLong doObjectCount() const override { return 6; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + } + + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + SceneFieldData{SceneField::Transformation, + view.slice(&Field::object), + view.slice(&Field::transformation)} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 1); + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 6); + CORRADE_COMPARE(importer.object3DCount(), 0); + CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional scene = importer.scene(0); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{}), + TestSuite::Compare::Container); + CORRADE_IGNORE_DEPRECATED_POP + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(!importer.object2D(0)); + CORRADE_VERIFY(!importer.object2D(1)); + CORRADE_VERIFY(!importer.object2D(2)); + CORRADE_VERIFY(!importer.object2D(3)); + CORRADE_VERIFY(!importer.object2D(4)); + CORRADE_VERIFY(!importer.object2D(5)); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object2D(): object 0 not found in any 2D scene hierarchy\n" + "Trade::AbstractImporter::object2D(): object 1 not found in any 2D scene hierarchy\n" + "Trade::AbstractImporter::object2D(): object 2 not found in any 2D scene hierarchy\n" + "Trade::AbstractImporter::object2D(): object 3 not found in any 2D scene hierarchy\n" + "Trade::AbstractImporter::object2D(): object 4 not found in any 2D scene hierarchy\n" + "Trade::AbstractImporter::object2D(): object 5 not found in any 2D scene hierarchy\n"); +} + +void AbstractImporterTest::sceneDeprecatedFallbackParentless3D() { + /* As the Parent field is currently used to distinguish which objects + belong to which scene, its absence means the objects are advertised, but + aren't listed as children of any scene, and retrieving them will fail */ + /** @todo adapt when there's a dedicated way to distinguish which objects + belong to which scene */ + + struct Field { + UnsignedInt object; + Matrix4 transformation; + } fields[]{ + {5, Matrix4{}}, + {2, Matrix4{}}, + }; + + Containers::StridedArrayView1D view = fields; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedLong doObjectCount() const override { return 6; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + } + + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + SceneFieldData{SceneField::Transformation, + view.slice(&Field::object), + view.slice(&Field::transformation)} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 1); + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 6); + CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional scene = importer.scene(0); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{}), + TestSuite::Compare::Container); + CORRADE_IGNORE_DEPRECATED_POP + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(!importer.object3D(0)); + CORRADE_VERIFY(!importer.object3D(1)); + CORRADE_VERIFY(!importer.object3D(2)); + CORRADE_VERIFY(!importer.object3D(3)); + CORRADE_VERIFY(!importer.object3D(4)); + CORRADE_VERIFY(!importer.object3D(5)); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object3D(): object 0 not found in any 3D scene hierarchy\n" + "Trade::AbstractImporter::object3D(): object 1 not found in any 3D scene hierarchy\n" + "Trade::AbstractImporter::object3D(): object 2 not found in any 3D scene hierarchy\n" + "Trade::AbstractImporter::object3D(): object 3 not found in any 3D scene hierarchy\n" + "Trade::AbstractImporter::object3D(): object 4 not found in any 3D scene hierarchy\n" + "Trade::AbstractImporter::object3D(): object 5 not found in any 3D scene hierarchy\n"); +} + +void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { + /* If no transformation field is present, for backwards compatibility we + assume the objects are 3D -- the only plugin that has a 2D scene is + PrimitiveImporter and it has the transformation field. */ + + struct Field { + UnsignedInt object; + Int parent; + } fields[]{ + {5, -1}, + {2, 0}, + {3, 0}, + {1, -1} + }; + + Containers::StridedArrayView1D view = fields; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedLong doObjectCount() const override { return 6; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + } + + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + SceneFieldData{SceneField::Parent, + view.slice(&Field::object), + view.slice(&Field::parent)}, + /* Required in order to have the scene recognized as 2D */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 1); + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 6); + CORRADE_COMPARE(importer.object3DCount(), 0); + CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional scene = importer.scene(0); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + (std::vector{5, 1}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + std::vector{}, + TestSuite::Compare::Container); + + { + Containers::Pointer o = importer.object2D(5); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3{}); + CORRADE_COMPARE_AS(o->children(), + (std::vector{2, 3}), + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(2); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(3); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->transformation(), Matrix3{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } + CORRADE_IGNORE_DEPRECATED_POP +} +void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { + /* If no transformation field is present, for backwards compatibility we + assume the objects are 3D -- the only plugin that has a 2D scene is + PrimitiveImporter and it has the transformation field. */ + + struct Field { + UnsignedInt object; + Int parent; + } fields[]{ + {5, -1}, + {2, 0}, + {3, 0}, + {1, -1} + }; + + Containers::StridedArrayView1D view = fields; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedLong doObjectCount() const override { return 6; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + } + + private: + SceneData _data; + } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + SceneFieldData{SceneField::Parent, + view.slice(&Field::object), + view.slice(&Field::parent)}, + /* Required in order to have the scene recognized as 3D */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr} + }}}; + + CORRADE_COMPARE(importer.sceneCount(), 1); + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 6); + CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional scene = importer.scene(0); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{5, 1}), + TestSuite::Compare::Container); + + { + Containers::Pointer o = importer.object3D(5); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4{}); + CORRADE_COMPARE_AS(o->children(), + (std::vector{2, 3}), + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(2); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(3); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->importerState(), nullptr); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(o->instance(), -1); + CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->transformation(), Matrix4{}); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } + CORRADE_IGNORE_DEPRECATED_POP +} +#endif + +void AbstractImporterTest::sceneNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 8; } + } importer; + + CORRADE_COMPARE(importer.sceneName(7), ""); +} + +void AbstractImporterTest::objectNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedLong doObjectCount() const override { return 8; } + } importer; + + CORRADE_COMPARE(importer.objectName(7), ""); +} + +void AbstractImporterTest::sceneNameOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.sceneName(8); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::sceneName(): index 8 out of range for 8 entries\n"); +} + +void AbstractImporterTest::objectNameOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedLong doObjectCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.objectName(8); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::objectName(): index 8 out of range for 8 entries\n"); +} + +void AbstractImporterTest::sceneNotImplemented() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.scene(7); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::scene(): not implemented\n"); +} + +void AbstractImporterTest::sceneOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.scene(8); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::scene(): index 8 out of range for 8 entries\n"); +} + +void AbstractImporterTest::sceneNonOwningDeleters() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 0, + Containers::Array{data, 1, Implementation::nonOwnedArrayDeleter}, + sceneFieldDataNonOwningArray(fields)}; + } + + char data[1]; + SceneFieldData fields[1]{ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr} + }; + } importer; + + auto data = importer.scene(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(static_cast(data->data()), importer.data); + CORRADE_COMPARE(static_cast(data->fieldData()), importer.fields); +} + +void AbstractImporterTest::sceneCustomDataDeleter() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + Int doSceneForName(const std::string&) override { return 0; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 0, + Containers::Array{data, 1, [](char*, std::size_t) {}}, + {}}; + } + + char data[1]; + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.scene(0); + importer.scene(""); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::scene(): implementation is not allowed to use a custom Array deleter\n" + "Trade::AbstractImporter::scene(): implementation is not allowed to use a custom Array deleter\n"); +} + +void AbstractImporterTest::sceneCustomFieldDataDeleter() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + Int doSceneForName(const std::string&) override { return 0; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneObjectType::UnsignedInt, 0, nullptr, Containers::Array{&parents, 1, [](SceneFieldData*, std::size_t) {}}}; + } + + SceneFieldData parents{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.scene(0); + importer.scene(""); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::scene(): implementation is not allowed to use a custom Array deleter\n" + "Trade::AbstractImporter::scene(): implementation is not allowed to use a custom Array deleter\n" + ); +} + +void AbstractImporterTest::sceneFieldName() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + SceneField doSceneFieldForName(const std::string& name) override { + if(name == "OctreeCell") return sceneFieldCustom(100037); + return SceneField{}; + } + + std::string doSceneFieldName(UnsignedInt id) override { + if(id == 100037) return "OctreeCell"; + return ""; + } + } importer; + + CORRADE_COMPARE(importer.sceneFieldForName("OctreeCell"), sceneFieldCustom(100037)); + CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), "OctreeCell"); +} + +void AbstractImporterTest::sceneFieldNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + CORRADE_COMPARE(importer.sceneFieldForName(""), SceneField{}); + CORRADE_COMPARE(importer.sceneFieldName(sceneFieldCustom(100037)), ""); +} + +void AbstractImporterTest::sceneFieldNameNotCustom() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + SceneField doSceneFieldForName(const std::string&) override { + return SceneField::Translation; + } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + importer.sceneFieldForName("OctreeCell"); + importer.sceneFieldName(SceneField::Translation); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::sceneFieldForName(): implementation-returned Trade::SceneField::Translation is neither custom nor invalid\n" + "Trade::AbstractImporter::sceneFieldName(): Trade::SceneField::Translation is not custom\n"); +} + +void AbstractImporterTest::animation() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doAnimationCount() const override { return 8; } + Int doAnimationForName(const std::string& name) override { + if(name == "eighth") return 7; + return -1; + } + std::string doAnimationName(UnsignedInt id) override { + if(id == 7) return "eighth"; + return {}; + } + Containers::Optional doAnimation(UnsignedInt id) override { + /* Verify that initializer list is converted to an array with + the default deleter and not something disallowed */ + if(id == 7) return AnimationData{nullptr, { + AnimationTrackData{AnimationTrackType::Vector3, + AnimationTrackTargetType::Scaling3D, 0, {}} + }, &state}; + return AnimationData{{}, {}}; + } + } importer; + + CORRADE_COMPARE(importer.animationCount(), 8); + CORRADE_COMPARE(importer.animationForName("eighth"), 7); + CORRADE_COMPARE(importer.animationName(7), "eighth"); + + { + auto data = importer.animation(7); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->importerState(), &state); + } { + auto data = importer.animation("eighth"); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->importerState(), &state); + } +} + +void AbstractImporterTest::animationForNameOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doAnimationCount() const override { return 8; } + Int doAnimationForName(const std::string&) override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + importer.animationForName(""); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::animationForName(): implementation-returned index 8 out of range for 8 entries\n"); +} + +void AbstractImporterTest::animationNameNotImplemented() { + struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} @@ -2265,80 +3368,175 @@ void AbstractImporterTest::cameraNameOutOfRange() { CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::cameraName(): index 8 out of range for 8 entries\n"); } -void AbstractImporterTest::cameraNotImplemented() { +void AbstractImporterTest::cameraNotImplemented() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doCameraCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.camera(7); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::camera(): not implemented\n"); +} + +void AbstractImporterTest::cameraOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doCameraCount() const override { return 8; } + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.camera(8); + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::camera(): index 8 out of range for 8 entries\n"); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +void AbstractImporterTest::object2D() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doObject2DCount() const override { return 8; } + Int doObject2DForName(const std::string& name) override { + if(name == "eighth") return 7; + return -1; + } + std::string doObject2DName(UnsignedInt id) override { + if(id == 7) return "eighth"; + return {}; + } + CORRADE_IGNORE_DEPRECATED_PUSH + Containers::Pointer doObject2D(UnsignedInt id) override { + if(id == 7) return Containers::pointer(new ObjectData2D{{}, {}, &state}); + return {}; + } + CORRADE_IGNORE_DEPRECATED_POP + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 8); + CORRADE_COMPARE(importer.object2DForName("eighth"), 7); + CORRADE_COMPARE(importer.object2DName(7), "eighth"); + + { + auto data = importer.object2D(7); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->importerState(), &state); + } { + auto data = importer.object2D("eighth"); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->importerState(), &state); + } + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object2DCountNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object2DCountNoFile() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } + bool doIsOpened() const override { return false; } void doClose() override {} - - UnsignedInt doCameraCount() const override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; - importer.camera(7); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::camera(): not implemented\n"); + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object2DCount(); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DCount(): no file opened\n"); } -void AbstractImporterTest::cameraOutOfRange() { +void AbstractImporterTest::object2DForNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DForName(""), -1); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object2DForNameNoFile() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } + bool doIsOpened() const override { return false; } void doClose() override {} - - UnsignedInt doCameraCount() const override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; - importer.camera(8); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::camera(): index 8 out of range for 8 entries\n"); + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object2DForName(""); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DForName(): no file opened\n"); } -void AbstractImporterTest::object2D() { +void AbstractImporterTest::object2DByNameNotFound() { + auto&& data = ThingByNameData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doObject2DCount() const override { return 8; } - Int doObject2DForName(const std::string& name) override { - if(name == "eighth") return 7; - return -1; - } - std::string doObject2DName(UnsignedInt id) override { - if(id == 7) return "eighth"; - return {}; - } - Containers::Pointer doObject2D(UnsignedInt id) override { - if(id == 7) return Containers::pointer(new ObjectData2D{{}, {}, &state}); - return {}; - } + UnsignedInt doObject2DCount() const override { return 5; } } importer; - CORRADE_COMPARE(importer.object2DCount(), 8); - CORRADE_COMPARE(importer.object2DForName("eighth"), 7); - CORRADE_COMPARE(importer.object2DName(7), "eighth"); - + std::ostringstream out; { - auto data = importer.object2D(7); - CORRADE_VERIFY(data); - CORRADE_COMPARE(data->importerState(), &state); - } { - auto data = importer.object2D("eighth"); - CORRADE_VERIFY(data); - CORRADE_COMPARE(data->importerState(), &state); + Containers::Optional redirectError; + if(data.checkMessage) redirectError.emplace(&out); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(!importer.object2D("foobar")); + CORRADE_IGNORE_DEPRECATED_POP } + + if(data.checkMessage) CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object2D(): object foobar not found among 5 entries\n"); } void AbstractImporterTest::object2DForNameOutOfRange() { @@ -2357,7 +3555,9 @@ void AbstractImporterTest::object2DForNameOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object2DForName(""); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DForName(): implementation-returned index 8 out of range for 8 entries\n"); } @@ -2370,7 +3570,29 @@ void AbstractImporterTest::object2DNameNotImplemented() { UnsignedInt doObject2DCount() const override { return 8; } } importer; + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(importer.object2DName(7), ""); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object2DNameNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object2DName(42); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DName(): no file opened\n"); } void AbstractImporterTest::object2DNameOutOfRange() { @@ -2389,7 +3611,9 @@ void AbstractImporterTest::object2DNameOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object2DName(8); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DName(): index 8 out of range for 8 entries\n"); } @@ -2403,14 +3627,44 @@ void AbstractImporterTest::object2DNotImplemented() { bool doIsOpened() const override { return true; } void doClose() override {} + UnsignedInt doSceneCount() const override { return 1; } UnsignedInt doObject2DCount() const override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object2D(7); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2D(): not implemented\n"); + CORRADE_IGNORE_DEPRECATED_POP + /* It delegates to scene(), but since the assert is graceful and returns a + null optional, it errors out immediately after */ + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::scene(): not implemented\n" + "Trade::AbstractImporter::object2D(): object 7 not found in any 2D scene hierarchy\n"); +} + +void AbstractImporterTest::object2DNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object2D(42); + importer.object2D("foo"); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object2D(): no file opened\n" + "Trade::AbstractImporter::object2D(): no file opened\n"); } void AbstractImporterTest::object2DOutOfRange() { @@ -2429,7 +3683,9 @@ void AbstractImporterTest::object2DOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object2D(8); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2D(): index 8 out of range for 8 entries\n"); } @@ -2448,12 +3704,15 @@ void AbstractImporterTest::object3D() { if(id == 7) return "eighth"; return {}; } + CORRADE_IGNORE_DEPRECATED_PUSH Containers::Pointer doObject3D(UnsignedInt id) override { if(id == 7) return Containers::pointer(new ObjectData3D{{}, {}, &state}); return {}; } + CORRADE_IGNORE_DEPRECATED_POP } importer; + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(importer.object3DCount(), 8); CORRADE_COMPARE(importer.object3DForName("eighth"), 7); CORRADE_COMPARE(importer.object3DName(7), "eighth"); @@ -2467,6 +3726,97 @@ void AbstractImporterTest::object3D() { CORRADE_VERIFY(data); CORRADE_COMPARE(data->importerState(), &state); } + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object3DCountNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object3DCount(), 0); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object3DCountNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object3DCount(); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3DCount(): no file opened\n"); +} + +void AbstractImporterTest::object3DForNameNotImplemented() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object3DForName(""), -1); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object3DForNameNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object2DForName(""); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object2DForName(): no file opened\n"); +} + +void AbstractImporterTest::object3DByNameNotFound() { + auto&& data = ThingByNameData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doObject3DCount() const override { return 6; } + } importer; + + std::ostringstream out; + { + Containers::Optional redirectError; + if(data.checkMessage) redirectError.emplace(&out); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(!importer.object3D("foobar")); + CORRADE_IGNORE_DEPRECATED_POP + } + + if(data.checkMessage) CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object3D(): object foobar not found among 6 entries\n"); } void AbstractImporterTest::object3DForNameOutOfRange() { @@ -2485,7 +3835,9 @@ void AbstractImporterTest::object3DForNameOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object3DForName(""); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3DForName(): implementation-returned index 8 out of range for 8 entries\n"); } @@ -2498,7 +3850,29 @@ void AbstractImporterTest::object3DNameNotImplemented() { UnsignedInt doObject3DCount() const override { return 8; } } importer; + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(importer.object3DName(7), ""); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::object3DNameNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object3DName(42); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3DName(): no file opened\n"); } void AbstractImporterTest::object3DNameOutOfRange() { @@ -2517,7 +3891,9 @@ void AbstractImporterTest::object3DNameOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object3DName(8); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3DName(): index 8 out of range for 8 entries\n"); } @@ -2531,14 +3907,44 @@ void AbstractImporterTest::object3DNotImplemented() { bool doIsOpened() const override { return true; } void doClose() override {} + UnsignedInt doSceneCount() const override { return 1; } UnsignedInt doObject3DCount() const override { return 8; } } importer; std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object3D(7); - CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3D(): not implemented\n"); + CORRADE_IGNORE_DEPRECATED_POP + /* It delegates to scene(), but since the assert is graceful and returns a + null optional, it errors out immediately after */ + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::scene(): not implemented\n" + "Trade::AbstractImporter::object3D(): object 7 not found in any 3D scene hierarchy\n"); +} + +void AbstractImporterTest::object3DNoFile() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_IGNORE_DEPRECATED_PUSH + importer.object3D(42); + importer.object3D("foo"); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::object3D(): no file opened\n" + "Trade::AbstractImporter::object3D(): no file opened\n"); } void AbstractImporterTest::object3DOutOfRange() { @@ -2557,9 +3963,12 @@ void AbstractImporterTest::object3DOutOfRange() { std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH importer.object3D(8); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3D(): index 8 out of range for 8 entries\n"); } +#endif void AbstractImporterTest::skin2D() { struct: AbstractImporter { diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index 75103d4619..7114361465 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -54,8 +54,6 @@ corrade_add_test(TradeImageDataTest ImageDataTest.cpp LIBRARIES MagnumTradeTestL corrade_add_test(TradeLightDataTest LightDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeMaterialDataTest MaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeMeshDataTest MeshDataTest.cpp LIBRARIES MagnumTradeTestLib) -corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES MagnumTradeTestLib) -corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePbrClearCoatMaterialDataTest PbrClearCoatMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePbrMetallicRoughnessMate___Test PbrMetallicRoughnessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePbrSpecularGlossinessMat___Test PbrSpecularGlossinessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) @@ -81,8 +79,6 @@ set_target_properties( TradeImageDataTest TradeLightDataTest TradeMaterialDataTest - TradeObjectData2DTest - TradeObjectData3DTest TradePbrClearCoatMaterialDataTest TradePbrMetallicRoughnessMate___Test TradePbrSpecularGlossinessMat___Test @@ -94,9 +90,13 @@ set_target_properties( if(MAGNUM_BUILD_DEPRECATED) corrade_add_test(TradeMeshData2DTest MeshData2DTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeMeshData3DTest MeshData3DTest.cpp LIBRARIES MagnumTrade) + corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES MagnumTradeTestLib) + corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTradeTestLib) set_target_properties( TradeMeshData2DTest TradeMeshData3DTest + TradeObjectData2DTest + TradeObjectData3DTest PROPERTIES FOLDER "Magnum/Trade/Test") endif() diff --git a/src/Magnum/Trade/Test/ObjectData2DTest.cpp b/src/Magnum/Trade/Test/ObjectData2DTest.cpp index a4ad55b573..48e0390153 100644 --- a/src/Magnum/Trade/Test/ObjectData2DTest.cpp +++ b/src/Magnum/Trade/Test/ObjectData2DTest.cpp @@ -23,6 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +/* There's no better way to disable file deprecation warnings */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA + #include #include #include @@ -51,6 +54,8 @@ class ObjectData2DTest: public TestSuite::Tester { void debugFlags(); }; +CORRADE_IGNORE_DEPRECATED_PUSH + ObjectData2DTest::ObjectData2DTest() { addTests({&ObjectData2DTest::constructEmpty, &ObjectData2DTest::constructEmptyTransformations, @@ -264,6 +269,8 @@ void ObjectData2DTest::debugFlags() { CORRADE_COMPARE(o.str(), "Trade::ObjectFlag2D::HasTranslationRotationScaling Trade::ObjectFlags2D{}\n"); } +CORRADE_IGNORE_DEPRECATED_POP + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjectData2DTest) diff --git a/src/Magnum/Trade/Test/ObjectData3DTest.cpp b/src/Magnum/Trade/Test/ObjectData3DTest.cpp index 2b39a4eaaf..3fe3a6e84d 100644 --- a/src/Magnum/Trade/Test/ObjectData3DTest.cpp +++ b/src/Magnum/Trade/Test/ObjectData3DTest.cpp @@ -23,6 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +/* There's no better way to disable file deprecation warnings */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA + #include #include #include @@ -70,6 +73,8 @@ ObjectData3DTest::ObjectData3DTest() { &ObjectData3DTest::debugFlags}); } +CORRADE_IGNORE_DEPRECATED_PUSH + using namespace Math::Literals; void ObjectData3DTest::constructEmpty() { @@ -278,6 +283,8 @@ void ObjectData3DTest::debugFlags() { CORRADE_COMPARE(o.str(), "Trade::ObjectFlag3D::HasTranslationRotationScaling Trade::ObjectFlags3D{}\n"); } +CORRADE_IGNORE_DEPRECATED_POP + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjectData3DTest) diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 46f98370c3..fdd47b9c78 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -89,11 +89,11 @@ class MeshData; #ifdef MAGNUM_BUILD_DEPRECATED class CORRADE_DEPRECATED("use MeshData instead") MeshData2D; class CORRADE_DEPRECATED("use MeshData instead") MeshData3D; +class CORRADE_DEPRECATED("use SceneData instead") MeshObjectData2D; +class CORRADE_DEPRECATED("use SceneData instead") MeshObjectData3D; +class CORRADE_DEPRECATED("use SceneData instead") ObjectData2D; +class CORRADE_DEPRECATED("use SceneData instead") ObjectData3D; #endif -class MeshObjectData2D; -class MeshObjectData3D; -class ObjectData2D; -class ObjectData3D; class PbrClearCoatMaterialData; class PbrMetallicRoughnessMaterialData; class PbrSpecularGlossinessMaterialData; From 505538d155a0cbe25b999c1eb1f5b8a764b06be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Sep 2021 13:47:31 +0200 Subject: [PATCH 20/49] Trade: add an explicitly defaulted deinlined SkinData destructor. Apparently now that we don't use ObjectData anymore, SkinData was kinda relying on them to include Matrix3/Matrix4. Hah. --- src/Magnum/Trade/SkinData.cpp | 2 ++ src/Magnum/Trade/SkinData.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Magnum/Trade/SkinData.cpp b/src/Magnum/Trade/SkinData.cpp index 775a197ac9..797cea200a 100644 --- a/src/Magnum/Trade/SkinData.cpp +++ b/src/Magnum/Trade/SkinData.cpp @@ -43,6 +43,8 @@ template SkinData::SkinData(DataFlags, const template SkinData::SkinData(SkinData&&) noexcept = default; +template SkinData::~SkinData() = default; + template SkinData& SkinData::operator=(SkinData&&) noexcept = default; template Containers::Array SkinData::releaseJointData() { diff --git a/src/Magnum/Trade/SkinData.h b/src/Magnum/Trade/SkinData.h index a2a8293442..c956191a6b 100644 --- a/src/Magnum/Trade/SkinData.h +++ b/src/Magnum/Trade/SkinData.h @@ -82,6 +82,8 @@ template class SkinData { /** @brief Move constructor */ SkinData(SkinData&& other) noexcept; + ~SkinData(); + /** @brief Copying is not allowed */ SkinData& operator=(const SkinData&) = delete; From 4602248cd022d96c859c4ddfd5359a648d9a9fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Sep 2021 17:01:32 +0200 Subject: [PATCH 21/49] Trade: ensure the deprecated SceneData constructor uses default deleters. Otherwise the instances wouldn't be usable from AbstractImporter subclasses. --- src/Magnum/Trade/SceneData.cpp | 8 +++++--- src/Magnum/Trade/Test/SceneDataTest.cpp | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index cc83bb7105..69456234d1 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -749,9 +749,11 @@ SceneData::SceneData(std::vector children2D, std::vector{1}; + _fields[0] = SceneFieldData{SceneField::Parent, objects, parents}; Utility::copy(children, objects); constexpr Int parent[]{-1}; Utility::copy(Containers::stridedArrayView(parent).broadcasted<0>(parents.size()), parents); diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index e990ccf651..660371e8ea 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -1508,6 +1508,11 @@ void SceneDataTest::constructDeprecated() { is set directly */ CORRADE_COMPARE(scene.is2D(), data.is2D); CORRADE_COMPARE(scene.is3D(), data.is3D); + + /* The deleters have to be trivial, otherwise this instance wouldn't be + usable from an AbstractImporter */ + CORRADE_VERIFY(!scene.releaseFieldData().deleter()); + CORRADE_VERIFY(!scene.releaseData().deleter()); } void SceneDataTest::constructDeprecatedBoth2DAnd3D() { From b9cb395d75a13431ca5d2d9f163df0f091a3b648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 24 Sep 2021 19:30:59 +0200 Subject: [PATCH 22/49] AnySceneImporter: adapt to SceneData redesign. --- .../AnySceneImporter/AnySceneImporter.cpp | 12 ++++++++++-- .../AnySceneImporter/AnySceneImporter.h | 9 +++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp index 1fd7439401..a583007eff 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp @@ -38,8 +38,6 @@ #include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" -#include "Magnum/Trade/ObjectData2D.h" -#include "Magnum/Trade/ObjectData3D.h" #include "Magnum/Trade/SceneData.h" #include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" @@ -47,9 +45,12 @@ #ifdef MAGNUM_BUILD_DEPRECATED #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ +#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ #include "Magnum/Trade/MeshData2D.h" #include "Magnum/Trade/MeshData3D.h" +#include "Magnum/Trade/ObjectData2D.h" +#include "Magnum/Trade/ObjectData3D.h" #endif namespace Magnum { namespace Trade { @@ -174,8 +175,11 @@ Containers::Optional AnySceneImporter::doAnimation(const Unsigned Int AnySceneImporter::doDefaultScene() const { return _in->defaultScene(); } UnsignedInt AnySceneImporter::doSceneCount() const { return _in->sceneCount(); } +UnsignedLong AnySceneImporter::doObjectCount() const { return _in->objectCount(); } Int AnySceneImporter::doSceneForName(const std::string& name) { return _in->sceneForName(name); } +Long AnySceneImporter::doObjectForName(const std::string& name) { return _in->objectForName(name); } std::string AnySceneImporter::doSceneName(const UnsignedInt id) { return _in->sceneName(id); } +std::string AnySceneImporter::doObjectName(const UnsignedLong id) { return _in->objectName(id); } Containers::Optional AnySceneImporter::doScene(const UnsignedInt id) { return _in->scene(id); } UnsignedInt AnySceneImporter::doLightCount() const { return _in->lightCount(); } @@ -188,6 +192,8 @@ Int AnySceneImporter::doCameraForName(const std::string& name) { return _in->cam std::string AnySceneImporter::doCameraName(const UnsignedInt id) { return _in->cameraName(id); } Containers::Optional AnySceneImporter::doCamera(const UnsignedInt id) { return _in->camera(id); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH UnsignedInt AnySceneImporter::doObject2DCount() const { return _in->object2DCount(); } Int AnySceneImporter::doObject2DForName(const std::string& name) { return _in->object2DForName(name); } std::string AnySceneImporter::doObject2DName(const UnsignedInt id) { return _in->object2DName(id); } @@ -197,6 +203,8 @@ UnsignedInt AnySceneImporter::doObject3DCount() const { return _in->object3DCoun Int AnySceneImporter::doObject3DForName(const std::string& name) { return _in->object3DForName(name); } std::string AnySceneImporter::doObject3DName(const UnsignedInt id) { return _in->object3DName(id); } Containers::Pointer AnySceneImporter::doObject3D(const UnsignedInt id) { return _in->object3D(id); } +CORRADE_IGNORE_DEPRECATED_POP +#endif UnsignedInt AnySceneImporter::doSkin2DCount() const { return _in->skin2DCount(); } Int AnySceneImporter::doSkin2DForName(const std::string& name) { return _in->skin2DForName(name); } diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h index 836d504d67..bb84c95cfe 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h @@ -167,8 +167,11 @@ class MAGNUM_ANYSCENEIMPORTER_EXPORT AnySceneImporter: public AbstractImporter { MAGNUM_ANYSCENEIMPORTER_LOCAL Int doDefaultScene() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doSceneCount() const override; + MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedLong doObjectCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doSceneForName(const std::string& name) override; + MAGNUM_ANYSCENEIMPORTER_LOCAL Long doObjectForName(const std::string& name) override; MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doSceneName(UnsignedInt id) override; + MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doObjectName(UnsignedLong id) override; MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Optional doScene(UnsignedInt id) override; MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doLightCount() const override; @@ -181,15 +184,21 @@ class MAGNUM_ANYSCENEIMPORTER_EXPORT AnySceneImporter: public AbstractImporter { MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doCameraName(UnsignedInt id) override; MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Optional doCamera(UnsignedInt id) override; + #ifdef MAGNUM_BUILD_DEPRECATED MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doObject2DCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doObject2DForName(const std::string& name) override; MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doObject2DName(UnsignedInt id) override; + CORRADE_IGNORE_DEPRECATED_PUSH MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Pointer doObject2D(UnsignedInt id) override; + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doObject3DCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doObject3DForName(const std::string& name) override; MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doObject3DName(UnsignedInt id) override; + CORRADE_IGNORE_DEPRECATED_PUSH MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Pointer doObject3D(UnsignedInt id) override; + CORRADE_IGNORE_DEPRECATED_POP + #endif MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doSkin2DCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doSkin2DForName(const std::string& name) override; From 26d2f3e9ab7547f7bc4c625d7f6103cf433f23d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 29 Sep 2021 16:34:01 +0200 Subject: [PATCH 23/49] sceneconverter: print contents of the new SceneData. Was waiting for this to happen, as going through all the objects would have been too messy. Plugins implemented using the legacy API will now only show the parent field in SceneData, but since those eventually get all ported, that's fine. --- src/Magnum/MeshTools/sceneconverter.cpp | 118 +++++++++++++++++++----- 1 file changed, 96 insertions(+), 22 deletions(-) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 7f7d01db8f..3d7eca5379 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include "Magnum/PixelFormat.h" #include "Magnum/Implementation/converterUtilities.h" #include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix4.h" #include "Magnum/Math/FunctionsBatch.h" #include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/Trade/AbstractImporter.h" @@ -44,7 +46,7 @@ #include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" -#include "Magnum/Trade/MeshObjectData3D.h" +#include "Magnum/Trade/SceneData.h" #include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" #include "Magnum/Trade/AbstractSceneConverter.h" @@ -213,6 +215,7 @@ bool isInfoRequested(const Utility::Arguments& args) { args.isSet("info-lights") || args.isSet("info-materials") || args.isSet("info-meshes") || + args.isSet("info-scenes") || args.isSet("info-skins") || args.isSet("info-textures") || args.isSet("info"); @@ -242,6 +245,7 @@ int main(int argc, char** argv) { .addBooleanOption("info-lights").setHelp("info-lights", "print info about images in the input file and exit") .addBooleanOption("info-materials").setHelp("info-materials", "print info about materials in the input file and exit") .addBooleanOption("info-meshes").setHelp("info-meshes", "print info about meshes in the input file and exit") + .addBooleanOption("info-scenes").setHelp("info-scenes", "print info about textures in the input file and exit") .addBooleanOption("info-skins").setHelp("info-skins", "print info about skins in the input file and exit") .addBooleanOption("info-textures").setHelp("info-textures", "print info about textures in the input file and exit") .addBooleanOption("info").setHelp("info", "print info about everything in the input file and exit, same as specifying all other --info-* options together") @@ -372,30 +376,79 @@ used.)") std::string name; }; + struct SceneFieldInfo { + Trade::SceneField name; + std::string customName; + Trade::SceneFieldType type; + UnsignedInt arraySize; + std::size_t size; + }; + + struct SceneInfo { + UnsignedInt scene; + Trade::SceneObjectType objectType; + UnsignedLong objectCount; + Containers::Array fields; + std::size_t dataSize; + std::string name; + /** @todo object names? */ + }; + /* Parse everything first to avoid errors interleaved with output */ - /* Scene properties. Currently just counting how much is each mesh / - light / material shared. Texture reference count is calculated when - parsing materials. */ + /* Scene properties, together with counting how much is each mesh / + light / material / skin shared. Texture reference count is + calculated when parsing materials. */ + Containers::Array sceneInfos; Containers::Array materialReferenceCount{importer->materialCount()}; Containers::Array lightReferenceCount{importer->lightCount()}; Containers::Array meshReferenceCount{importer->meshCount()}; Containers::Array skinReferenceCount{importer->skin3DCount()}; - for(UnsignedInt i = 0; i != importer->object3DCount(); ++i) { - Containers::Pointer object = importer->object3D(i); - if(!object) continue; - if(object->instanceType() == Trade::ObjectInstanceType3D::Mesh) { - auto& meshObject = static_cast(*object); - if(std::size_t(meshObject.instance()) < meshReferenceCount.size()) - ++meshReferenceCount[meshObject.instance()]; - if(std::size_t(meshObject.material()) < materialReferenceCount.size()) - ++materialReferenceCount[meshObject.material()]; - if(std::size_t(meshObject.skin()) < skinReferenceCount.size()) - ++skinReferenceCount[meshObject.skin()]; - } else if(object->instanceType() == Trade::ObjectInstanceType3D::Light) { - if(std::size_t(object->instance()) < lightReferenceCount.size()) - ++lightReferenceCount[object->instance()]; + if(args.isSet("info") || args.isSet("info-scenes") || args.isSet("info-materials") || args.isSet("info-lights") || args.isSet("info-meshes") || args.isSet("info-skins")) for(UnsignedInt i = 0; i != importer->sceneCount(); ++i) { + Containers::Optional scene = importer->scene(i); + if(!scene) continue; + + SceneInfo info{}; + info.scene = i; + info.objectType = scene->objectType(); + info.objectCount = scene->objectCount(); + info.dataSize = scene->data().size(); + info.name = importer->sceneName(i); + 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(meshMaterial.first() < meshReferenceCount.size()) + ++meshReferenceCount[meshMaterial.first()]; + if(UnsignedInt(meshMaterial.second()) < materialReferenceCount.size()) + ++materialReferenceCount[meshMaterial.second()]; + } + + if(name == Trade::SceneField::Skin) for(const UnsignedInt skin: scene->skinsAsArray()) { + if(skin < skinReferenceCount.size()) + ++skinReferenceCount[skin]; + /** @todo 2D/3D distinction */ + } + + if(name == Trade::SceneField::Light) for(const UnsignedInt light: scene->lightsAsArray()) { + if(light < lightReferenceCount.size()) + ++lightReferenceCount[light]; + } + + arrayAppend(info.fields, InPlaceInit, + name, + Trade::isSceneFieldCustom(name) ? + importer->sceneFieldName(name) : "", + scene->fieldType(j), + scene->fieldArraySize(j), + scene->fieldSize(j)); } + + /* Add it to the array only if scene info was requested. We're + going through this loop also if just light / material / mesh / + skin info is requested, to gather reference count */ + if(args.isSet("info") || args.isSet("info-scenes")) + arrayAppend(sceneInfos, std::move(info)); } /* Animation properties */ @@ -608,6 +661,27 @@ used.)") if(args.isSet("info") || args.isSet("info-images")) imageInfos = Trade::Implementation::imageInfo(*importer, error, compactImages); + for(const SceneInfo& info: sceneInfos) { + Debug d; + d << "Scene" << info.scene << Debug::nospace << ":"; + if(!info.name.empty()) d << info.name; + d << Debug::newline; + d << " " << info.objectCount << "objects," << info.objectType + << "(" << Debug::nospace << Utility::formatString("{:.1f}", info.dataSize/1024.0f) << "kB)"; + + for(const SceneFieldInfo& field: info.fields) { + d << Debug::newline << " " << field.name; + if(Trade::isSceneFieldCustom(field.name)) { + d << "(" << Debug::nospace << field.customName + << Debug::nospace << ")"; + } + d << "@" << field.type; + if(field.arraySize) + d << Debug::nospace << Utility::formatString("[{}]", field.arraySize); + d << Debug::nospace << "," << field.size << "entries"; + } + } + for(const AnimationInfo& info: animationInfos) { Debug d; d << "Animation" << info.animation << Debug::nospace << ":"; @@ -641,7 +715,7 @@ used.)") d << "Skin" << info.skin; /* Print reference count only if there actually is a scene, otherwise this information is useless */ - if(importer->object3DCount()) + if(importer->objectCount()) d << Utility::formatString("(referenced by {} objects)", info.references); d << Debug::nospace << ":"; if(!info.name.empty()) d << info.name; @@ -654,7 +728,7 @@ used.)") d << "Light" << info.light; /* Print reference count only if there actually is a scene, otherwise this information is useless */ - if(importer->object3DCount()) + if(importer->objectCount()) d << Utility::formatString("(referenced by {} objects)", info.references); d << Debug::nospace << ":"; if(!info.name.empty()) d << info.name; @@ -673,7 +747,7 @@ used.)") d << "Material" << info.material; /* Print reference count only if there actually is a scene, otherwise this information is useless */ - if(importer->object3DCount()) + if(importer->objectCount()) d << Utility::formatString("(referenced by {} objects)", info.references); d << Debug::nospace << ":"; if(!info.name.empty()) d << info.name; @@ -756,7 +830,7 @@ used.)") d << "Mesh" << info.mesh; /* Print reference count only if there actually is a scene, otherwise this information is useless */ - if(importer->object3DCount()) + if(importer->objectCount()) d << Utility::formatString("(referenced by {} objects)", info.references); d << Debug::nospace << ":"; if(!info.name.empty()) d << info.name; From b192c4c3a3d01e00990bd376dac929d6762630b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 13 Oct 2021 13:02:05 +0200 Subject: [PATCH 24/49] Trade: internal tool for creating SceneData out of loose views. To become the central piece of an upcoming SceneTools library, now I need it just to implement duplication of objects that have more than one mesh/light/camera/... assignment. --- src/Magnum/Trade/CMakeLists.txt | 3 +- src/Magnum/Trade/Implementation/sceneTools.h | 193 +++++++++++ src/Magnum/Trade/Test/CMakeLists.txt | 2 + src/Magnum/Trade/Test/SceneToolsTest.cpp | 324 +++++++++++++++++++ 4 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Trade/Implementation/sceneTools.h create mode 100644 src/Magnum/Trade/Test/SceneToolsTest.cpp diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index 2a6fc229f5..e5b68f2724 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -76,7 +76,8 @@ set(MagnumTrade_HEADERS set(MagnumTrade_PRIVATE_HEADERS Implementation/arrayUtilities.h Implementation/converterUtilities.h - Implementation/materialAttributeProperties.hpp) + Implementation/materialAttributeProperties.hpp + Implementation/sceneTools.h) if(MAGNUM_BUILD_DEPRECATED) list(APPEND MagnumTrade_SRCS diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h new file mode 100644 index 0000000000..98f3e41b55 --- /dev/null +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -0,0 +1,193 @@ +#ifndef Magnum_Trade_Implementation_sceneTools_h +#define Magnum_Trade_Implementation_sceneTools_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +#include "Magnum/Math/PackingBatch.h" +#include "Magnum/Trade/SceneData.h" + +namespace Magnum { namespace Trade { namespace Implementation { + +/* These two are needed because there (obviously) isn't any overload of + castInto with the same input and output type */ +template void copyOrCastInto(const Containers::StridedArrayView1D& src, const Containers::StridedArrayView1D& dst) { + Math::castInto(Containers::arrayCast<2, const T>(src), Containers::arrayCast<2, U>(dst)); +} +template void copyOrCastInto(const Containers::StridedArrayView1D& src, const Containers::StridedArrayView1D& dst) { + Utility::copy(src, dst); +} + +template void sceneCombineCopyObjects(const Containers::ArrayView fields, const Containers::ArrayView> itemViews, const Containers::ArrayView> itemViewMappings) { + std::size_t latestMapping = 0; + for(std::size_t i = 0; i != fields.size(); ++i) { + /* If there are no aliased object mappings, itemViewMappings should be + monotonically increasing. If it's not, it means the mapping is + shared with something earlier and it got already copied -- skip. */ + const std::size_t mapping = itemViewMappings[i].first(); + if(i && mapping <= latestMapping) continue; + latestMapping = mapping; + + /* If the field has null object data, no need to copy anything. This + covers reserved fields but also fields of zero size. */ + if(!fields[i].objectData()) continue; + + const Containers::StridedArrayView1D src = fields[i].objectData(); + const Containers::StridedArrayView1D dst = Containers::arrayCast<1, T>(itemViews[mapping]); + if(fields[i].objectType() == SceneObjectType::UnsignedByte) + copyOrCastInto(Containers::arrayCast(src), dst); + else if(fields[i].objectType() == SceneObjectType::UnsignedShort) + copyOrCastInto(Containers::arrayCast(src), dst); + else if(fields[i].objectType() == SceneObjectType::UnsignedInt) + copyOrCastInto(Containers::arrayCast(src), dst); + else if(fields[i].objectType() == SceneObjectType::UnsignedLong) + copyOrCastInto(Containers::arrayCast(src), dst); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } +} + +/* Combine fields of varying object type together into a SceneData of a single + given objectType. The fields are expected to point to existing object/field + memory, which will be then copied to the resulting scene. If you supply a + field with null object or field data, the object or field data will not get + copied, only a placeholder for copying the data later will be allocated. If + you however need to have placeholder object data shared among + Offset-only fields are not allowed. + + The resulting fields are always tightly packed (not interleaved). + + If multiple fields share the same object mapping views, those are preserved, + however they have to have the exact same length. Sharing object mappings + with different lengths will assert. */ +/** @todo when published, add an initializer_list overload and turn all + internal asserts into (tested!) message asserts */ +inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLong objectCount, const Containers::ArrayView fields) { + const std::size_t objectTypeSize = sceneObjectTypeSize(objectType); + const std::size_t objectTypeAlignment = sceneObjectTypeAlignment(objectType); + + /* Go through all fields and collect ArrayTuple allocations for these */ + std::unordered_map objectMappings; + Containers::Array items; + Containers::Array> itemViewMappings{NoInit, fields.size()}; + + /* The item views are referenced from ArrayTuple::Item, not using a + growable array in order to avoid an accidental reallocation */ + /** @todo once never-reallocating allocators are present, use them instead + of the manual offset */ + Containers::Array> itemViews{fields.size()*2}; + std::size_t itemViewOffset = 0; + + for(std::size_t i = 0; i != fields.size(); ++i) { + const SceneFieldData& field = fields[i]; + CORRADE_INTERNAL_ASSERT(!field.isOffsetOnly()); + + /* Object data. Allocate if the view is a placeholder of if it wasn't + used by other fields yet. */ + std::pair::iterator, bool> inserted; + if(field.objectData().data()) + inserted = objectMappings.emplace(field.objectData().data(), itemViewOffset); + if(field.objectData().data() && !inserted.second) { + itemViewMappings[i].first() = inserted.first->second; + /* Expect that fields sharing the same object mapping view have the + exact same length (the length gets stored in the output view + during the ArrayTuple::Item construction). + + We could just ignore the sharing in that case, but that'd only + lead to misery down the line -- imagine a field that shares the + first two items with a mesh and a material object mapping. If it + would be the last, it gets duplicated and everything is great, + however if it's the first then both mesh and the material get + duplicated, and that then asserts inside the SceneData + constructor, as those are always expected to share. + + One option that would solve this would be to store pointer+size + in the objectMappings map (and then only mappings that share + also the same size would be shared), another would be to use the + longest used view (and then the shorter prefixes would share + with it). The ultimate option would be to have some range map + where it'd be possible to locate also arbitrary subranges, not + just prefixes. A whole other topic altogether is checking for + the same stride, which is not done at all. + + This might theoretically lead to assertions also when two + compile-time arrays share a common prefix and get deduplicated + by the compiler. But that's unlikely, at least for the internal + use case we have right now. */ + CORRADE_INTERNAL_ASSERT(itemViews[inserted.first->second].size()[0] == field.size()); + } else { + itemViewMappings[i].first() = itemViewOffset; + arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), objectTypeSize, objectTypeAlignment, itemViews[itemViewOffset]); + ++itemViewOffset; + } + + /* Field data. No aliasing here right now, no sharing between object + and field data either. */ + /** @todo field aliasing might be useful at some point */ + itemViewMappings[i].second() = itemViewOffset; + arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), sceneFieldTypeSize(field.fieldType())*(field.fieldArraySize() ? field.fieldArraySize() : 1), sceneFieldTypeAlignment(field.fieldType()), itemViews[itemViewOffset]); + ++itemViewOffset; + } + + /* Allocate the data */ + Containers::Array outData = Containers::ArrayTuple{items}; + CORRADE_INTERNAL_ASSERT(!outData.deleter()); + + /* Copy the object data over and cast them as necessary */ + if(objectType == SceneObjectType::UnsignedByte) + sceneCombineCopyObjects(fields, itemViews, itemViewMappings); + else if(objectType == SceneObjectType::UnsignedShort) + sceneCombineCopyObjects(fields, itemViews, itemViewMappings); + else if(objectType == SceneObjectType::UnsignedInt) + sceneCombineCopyObjects(fields, itemViews, itemViewMappings); + else if(objectType == SceneObjectType::UnsignedLong) + sceneCombineCopyObjects(fields, itemViews, itemViewMappings); + + /* Copy the field data over. No special handling needed here. */ + for(std::size_t i = 0; i != fields.size(); ++i) { + /* If the field has null field data, no need to copy anything. This + covers reserved fields but also fields of zero size. */ + if(!fields[i].fieldData()) continue; + + /** @todo isn't there some less awful way to create a 2D view, sigh */ + Utility::copy(Containers::arrayCast<2, const char>(fields[i].fieldData(), sceneFieldTypeSize(fields[i].fieldType())*(fields[i].fieldArraySize() ? fields[i].fieldArraySize() : 1)), itemViews[itemViewMappings[i].second()]); + } + + /* Map the fields to the new data */ + Containers::Array outFields{fields.size()}; + for(std::size_t i = 0; i != fields.size(); ++i) { + outFields[i] = SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize()}; + } + + return SceneData{objectType, objectCount, std::move(outData), std::move(outFields)}; +} + +}}} + +#endif diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index 7114361465..521be6d740 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -59,6 +59,7 @@ corrade_add_test(TradePbrMetallicRoughnessMate___Test PbrMetallicRoughnessMateri corrade_add_test(TradePbrSpecularGlossinessMat___Test PbrSpecularGlossinessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradePhongMaterialDataTest PhongMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTradeTestLib) +corrade_add_test(TradeSceneToolsTest SceneToolsTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeSkinDataTest SkinDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) @@ -84,6 +85,7 @@ set_target_properties( TradePbrSpecularGlossinessMat___Test TradePhongMaterialDataTest TradeSceneDataTest + TradeSceneToolsTest TradeTextureDataTest PROPERTIES FOLDER "Magnum/Trade/Test") diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp new file mode 100644 index 0000000000..f5fda2dfa1 --- /dev/null +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -0,0 +1,324 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/Math/Complex.h" +#include "Magnum/Math/Vector2.h" +#include "Magnum/Trade/Implementation/sceneTools.h" + +namespace Magnum { namespace Trade { namespace Test { namespace { + +struct SceneToolsTest: TestSuite::Tester { + explicit SceneToolsTest(); + + void combine(); + void combineAlignment(); + void combineObjectsShared(); + void combineObjectsPlaceholderFieldPlaceholder(); + void combineObjectSharedFieldPlaceholder(); +}; + +struct { + const char* name; + SceneObjectType objectType; +} CombineData[]{ + {"UnsignedByte output", SceneObjectType::UnsignedByte}, + {"UnsignedShort output", SceneObjectType::UnsignedShort}, + {"UnsignedInt output", SceneObjectType::UnsignedInt}, + {"UnsignedLong output", SceneObjectType::UnsignedLong}, +}; + +SceneToolsTest::SceneToolsTest() { + addInstancedTests({&SceneToolsTest::combine}, + Containers::arraySize(CombineData)); + + addTests({&SceneToolsTest::combineAlignment, + &SceneToolsTest::combineObjectsShared, + &SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder, + &SceneToolsTest::combineObjectSharedFieldPlaceholder}); +} + +using namespace Math::Literals; + +void SceneToolsTest::combine() { + auto&& data = CombineData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Testing the four possible object types, it should be possible to combine + them */ + + const UnsignedInt meshObjects[]{45, 78, 23}; + const UnsignedByte meshes[]{3, 5, 17}; + + const UnsignedShort parentObjects[]{33, 25}; + const Short parents[]{-1, 0}; + + const UnsignedByte translationObjects[]{16}; + const Vector2d translations[]{{1.5, -0.5}}; + + const UnsignedLong fooObjects[]{15, 23}; + const Int foos[]{0, 1, 2, 3}; + + SceneData scene = Implementation::sceneCombine(data.objectType, 167, Containers::arrayView({ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + SceneFieldData{SceneField::Parent, Containers::arrayView(parentObjects), Containers::arrayView(parents)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationObjects), Containers::arrayView(translations)}, + /* Array field */ + SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooObjects), Containers::StridedArrayView2D{foos, {2, 2}}}, + /* Empty field */ + SceneFieldData{SceneField::Camera, Containers::ArrayView{}, Containers::ArrayView{}} + })); + + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(scene.objectType(), data.objectType); + CORRADE_COMPARE(scene.objectCount(), 167); + CORRADE_COMPARE(scene.fieldCount(), 5); + + CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh); + CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldArraySize(0), 0); + CORRADE_COMPARE_AS(scene.objectsAsArray(0), Containers::arrayView({ + 45, 78, 23 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(0), + Containers::arrayView(meshes), + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); + CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Short); + CORRADE_COMPARE(scene.fieldArraySize(1), 0); + CORRADE_COMPARE_AS(scene.objectsAsArray(1), Containers::arrayView({ + 33, 25 + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(1), + Containers::arrayView(parents), + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldName(2), SceneField::Translation); + CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::Vector2d); + CORRADE_COMPARE(scene.fieldArraySize(2), 0); + CORRADE_COMPARE_AS(scene.objectsAsArray(2), + Containers::arrayView({16}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(2), + Containers::arrayView(translations), + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(15)); + CORRADE_COMPARE(scene.fieldType(3), SceneFieldType::Int); + CORRADE_COMPARE(scene.fieldArraySize(3), 2); + CORRADE_COMPARE_AS(scene.objectsAsArray(3), + Containers::arrayView({15, 23}), + TestSuite::Compare::Container); + /** @todo clean up once it's possible to compare multidimensional + containers */ + CORRADE_COMPARE_AS(scene.field(3)[0], + (Containers::StridedArrayView2D{foos, {2, 2}})[0], + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(3)[1], + (Containers::StridedArrayView2D{foos, {2, 2}})[1], + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldName(4), SceneField::Camera); + CORRADE_COMPARE(scene.fieldType(4), SceneFieldType::UnsignedShort); + CORRADE_COMPARE(scene.fieldSize(4), 0); + CORRADE_COMPARE(scene.fieldArraySize(4), 0); +} + +void SceneToolsTest::combineAlignment() { + const UnsignedShort meshObjects[]{15, 23, 47}; + const UnsignedByte meshes[]{0, 1, 2}; + const UnsignedShort translationObjects[]{5}; /* 1 byte padding before */ + const Vector2d translations[]{{1.5, 3.0}}; /* 4 byte padding before */ + + SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedShort, 167, Containers::arrayView({ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationObjects), Containers::arrayView(translations)} + })); + + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.objectCount(), 167); + CORRADE_COMPARE(scene.fieldCount(), 2); + + CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh); + CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldArraySize(0), 0); + CORRADE_COMPARE_AS(scene.objects(0), + Containers::arrayView(meshObjects), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(0), + Containers::arrayView(meshes), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(reinterpret_cast(scene.objects(0).data()), 2, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.objects(0).data(), scene.data()); + CORRADE_COMPARE(scene.objects(0).stride()[0], 2); + CORRADE_COMPARE_AS(reinterpret_cast(scene.field(0).data()), 1, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.field(0).data(), scene.data() + 3*2); + CORRADE_COMPARE(scene.field(0).stride()[0], 1); + + CORRADE_COMPARE(scene.fieldName(1), SceneField::Translation); + CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Vector2d); + CORRADE_COMPARE(scene.fieldArraySize(1), 0); + CORRADE_COMPARE_AS(scene.objects(1), + Containers::arrayView(translationObjects), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(1), + Containers::arrayView(translations), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(reinterpret_cast(scene.objects(1).data()), 2, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.objects(1).data(), scene.data() + 3*2 + 3 + 1); + CORRADE_COMPARE(scene.objects(1).stride()[0], 2); + CORRADE_COMPARE_AS(reinterpret_cast(scene.field(1).data()), 8, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.field(1).data(), scene.data() + 3*2 + 3 + 1 + 2 + 4); + CORRADE_COMPARE(scene.field(1).stride()[0], 16); +} + +void SceneToolsTest::combineObjectsShared() { + const UnsignedShort meshObjects[]{15, 23, 47}; + const UnsignedByte meshes[]{0, 1, 2}; + const Int meshMaterials[]{72, -1, 23}; + + const UnsignedShort translationRotationObjects[]{14, 22}; + const Vector2 translations[]{{-1.0f, 25.3f}, {2.2f, 2.1f}}; + const Complex rotations[]{Complex::rotation(35.0_degf), Complex::rotation(22.5_degf)}; + + SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedInt, 173, Containers::arrayView({ + /* Deliberately in an arbitrary order to avoid false assumptions like + fields sharing the same object mapping always being after each + other */ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationRotationObjects), Containers::arrayView(translations)}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::arrayView(meshMaterials)}, + SceneFieldData{SceneField::Rotation, Containers::arrayView(translationRotationObjects), Containers::arrayView(rotations)} + })); + + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.fieldCount(), 4); + + CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 3); + CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3); + CORRADE_COMPARE(scene.objects(SceneField::Mesh).data(), scene.objects(SceneField::MeshMaterial).data()); + + CORRADE_COMPARE(scene.fieldSize(SceneField::Translation), 2); + CORRADE_COMPARE(scene.fieldSize(SceneField::Rotation), 2); + CORRADE_COMPARE(scene.objects(SceneField::Translation).data(), scene.objects(SceneField::Rotation).data()); +} + +void SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder() { + const UnsignedShort meshObjects[]{15, 23, 47}; + const UnsignedByte meshes[]{0, 1, 2}; + + SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedShort, 173, Containers::arrayView({ + SceneFieldData{SceneField::Camera, Containers::ArrayView{nullptr, 1}, Containers::ArrayView{nullptr, 1}}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + /* Looks like sharing object mapping with the Camera field, but + actually both are placeholders */ + SceneFieldData{SceneField::Light, Containers::ArrayView{nullptr, 2}, Containers::ArrayView{nullptr, 2}}, + /* Array field */ + SceneFieldData{sceneFieldCustom(15), Containers::ArrayView{nullptr, 2}, Containers::StridedArrayView2D{{nullptr, 16}, {2, 4}}}, + })); + + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.fieldCount(), 4); + + CORRADE_COMPARE(scene.fieldType(SceneField::Camera), SceneFieldType::UnsignedShort); + CORRADE_COMPARE(scene.fieldSize(SceneField::Camera), 1); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Camera), 0); + CORRADE_COMPARE(scene.objects(SceneField::Camera).data(), scene.data()); + CORRADE_COMPARE(scene.objects(SceneField::Camera).stride()[0], 2); + CORRADE_COMPARE(scene.field(SceneField::Camera).data(), scene.data() + 2); + CORRADE_COMPARE(scene.field(SceneField::Camera).stride()[0], 2); + + CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); + CORRADE_COMPARE_AS(scene.objects(SceneField::Mesh), + Containers::arrayView(meshObjects), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(SceneField::Mesh), + Containers::arrayView(meshes), + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldType(SceneField::Light), SceneFieldType::UnsignedInt); + CORRADE_COMPARE(scene.fieldSize(SceneField::Light), 2); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Light), 0); + CORRADE_COMPARE(scene.objects(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1); + CORRADE_COMPARE(scene.objects(SceneField::Light).stride()[0], 2); + CORRADE_COMPARE(scene.field(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2); + CORRADE_COMPARE(scene.field(SceneField::Light).stride()[0], 4); + + CORRADE_COMPARE(scene.fieldType(sceneFieldCustom(15)), SceneFieldType::Short); + CORRADE_COMPARE(scene.fieldSize(sceneFieldCustom(15)), 2); + CORRADE_COMPARE(scene.fieldArraySize(sceneFieldCustom(15)), 4); + CORRADE_COMPARE(scene.objects(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4); + CORRADE_COMPARE(scene.objects(sceneFieldCustom(15)).stride()[0], 2); + CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4 + 2*2); + CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).stride()[0], 4*2); +} + +void SceneToolsTest::combineObjectSharedFieldPlaceholder() { + const UnsignedInt meshObjects[]{15, 23, 47}; + const UnsignedByte meshes[]{0, 1, 2}; + + SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedInt, 173, Containers::arrayView({ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::ArrayView{nullptr, 3}}, + })); + + CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.fieldCount(), 2); + + CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); + CORRADE_COMPARE_AS(scene.objects(0), + Containers::arrayView(meshObjects), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(0), + Containers::arrayView(meshes), + TestSuite::Compare::Container); + + CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::Int); + CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3); + CORRADE_COMPARE(scene.fieldArraySize(SceneField::MeshMaterial), 0); + CORRADE_COMPARE(scene.objects(SceneField::MeshMaterial).data(), scene.objects(SceneField::Mesh).data()); + CORRADE_COMPARE_AS(scene.objects(SceneField::MeshMaterial), + Containers::arrayView(meshObjects), + TestSuite::Compare::Container); + CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).data(), scene.data() + 3*4 + 3 + 1); + CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).stride()[0], 4); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::SceneToolsTest) From 6e1bb5f838deace22b892dc2078cf75273b93f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 3 Dec 2021 16:52:22 +0100 Subject: [PATCH 25/49] Trade: add public SceneData APIs for finding fields and objects. Using hasField() + fieldId() was a bad usage pattern leading to a double linear lookup, so there's now findFieldId() returning an Optional which covers both. Similarly for finding an object offset in an field, there's a findFieldObjectOffset() returning an Optional, fieldObjectOffset() asserting if an object is not found (for convenience to avoid explicit error handling on user side) and hasFieldObject(). The internal helpers were also renamed and the offset argument moved to be last for consistency. --- src/Magnum/Trade/SceneData.cpp | 259 +++++++++++++------- src/Magnum/Trade/SceneData.h | 299 ++++++++++++++++-------- src/Magnum/Trade/Test/SceneDataTest.cpp | 218 +++++++++++++++-- 3 files changed, 574 insertions(+), 202 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 69456234d1..1b2028e648 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -831,36 +831,154 @@ UnsignedShort SceneData::fieldArraySize(const UnsignedInt id) const { return _fields[id]._fieldArraySize; } -UnsignedInt SceneData::fieldFor(const SceneField name) const { +UnsignedInt SceneData::findFieldIdInternal(const SceneField name) const { for(std::size_t i = 0; i != _fields.size(); ++i) if(_fields[i]._name == name) return i; return ~UnsignedInt{}; } -bool SceneData::hasField(const SceneField name) const { - return fieldFor(name) != ~UnsignedInt{}; +Containers::Optional SceneData::findFieldId(const SceneField name) const { + const UnsignedInt fieldId = findFieldIdInternal(name); + return fieldId == ~UnsignedInt{} ? Containers::Optional{} : fieldId; } UnsignedInt SceneData::fieldId(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldId(): field" << name << "not found", {}); return fieldId; } +bool SceneData::hasField(const SceneField name) const { + return findFieldIdInternal(name) != ~UnsignedInt{}; +} + +namespace { + +template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { + const Containers::StridedArrayView1D objectsT = Containers::arrayCast(objects); + const std::size_t max = objectsT.size(); + /** @todo implement something faster than O(n) when field-specific flags + can annotate how the object mapping is done */ + for(std::size_t i = 0; i != max; ++i) + if(objectsT[i] == object) return i; + return max; +} + +} + +std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedInt object, const std::size_t offset) const { + const Containers::StridedArrayView1D objects = fieldDataObjectViewInternal(field, offset, field._size - offset); + if(field._objectType == SceneObjectType::UnsignedInt) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedShort) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedByte) + return offset + findObject(objects, object); + else if(field._objectType == SceneObjectType::UnsignedLong) + return offset + findObject(objects, object); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +Containers::Optional SceneData::findFieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::findFieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(offset <= field._size, + "Trade::SceneData::findFieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {}); + + const std::size_t found = findFieldObjectOffsetInternal(field, object, offset); + return found == field._size ? Containers::Optional{} : found; +} + +Containers::Optional SceneData::findFieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = findFieldIdInternal(fieldName); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::findFieldObjectOffset(): field" << fieldName << "not found", {}); + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(offset <= field._size, + "Trade::SceneData::findFieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {}); + + const std::size_t found = findFieldObjectOffsetInternal(field, object, offset); + return found == field._size ? Containers::Optional{} : found; +} + +std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::fieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(offset <= field._size, + "Trade::SceneData::fieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {}); + + const std::size_t found = findFieldObjectOffsetInternal(field, object, offset); + CORRADE_ASSERT(found != field._size, + "Trade::SceneData::fieldObjectOffset(): object" << object << "not found in field" << field._name << "starting at offset" << offset, {}); + return found; +} + +std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = findFieldIdInternal(fieldName); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::fieldObjectOffset(): field" << fieldName << "not found", {}); + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_ASSERT(offset <= field._size, + "Trade::SceneData::fieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {}); + + const std::size_t found = findFieldObjectOffsetInternal(field, object, offset); + CORRADE_ASSERT(found != field._size, + "Trade::SceneData::fieldObjectOffset(): object" << object << "not found in field" << field._name << "starting at offset" << offset, {}); + return found; +} + +bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(fieldId < _fields.size(), + "Trade::SceneData::hasFieldObject(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + + const SceneFieldData& field = _fields[fieldId]; + return findFieldObjectOffsetInternal(field, object, 0) != field._size; +} + +bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = findFieldIdInternal(fieldName); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::hasFieldObject(): field" << fieldName << "not found", {}); + + const SceneFieldData& field = _fields[fieldId]; + return findFieldObjectOffsetInternal(field, object, 0) != field._size; +} + SceneFieldType SceneData::fieldType(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {}); return _fields[fieldId]._fieldType; } std::size_t SceneData::fieldSize(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldSize(): field" << name << "not found", {}); return _fields[fieldId]._size; } UnsignedShort SceneData::fieldArraySize(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {}); return _fields[fieldId]._fieldArraySize; } @@ -894,7 +1012,7 @@ Containers::StridedArrayView2D SceneData::mutableObjects(const UnsignedInt } Containers::StridedArrayView2D SceneData::objects(const SceneField fieldName) const { - const UnsignedInt fieldId = fieldFor(fieldName); + const UnsignedInt fieldId = findFieldIdInternal(fieldName); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {}); return objects(fieldId); } @@ -902,7 +1020,7 @@ Containers::StridedArrayView2D SceneData::objects(const SceneField f Containers::StridedArrayView2D SceneData::mutableObjects(const SceneField fieldName) { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, "Trade::SceneData::mutableObjects(): data not mutable", {}); - const UnsignedInt fieldId = fieldFor(fieldName); + const UnsignedInt fieldId = findFieldIdInternal(fieldName); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {}); return mutableObjects(fieldId); } @@ -936,7 +1054,7 @@ Containers::StridedArrayView2D SceneData::mutableField(const UnsignedInt i } Containers::StridedArrayView2D SceneData::field(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::field(): field" << name << "not found", {}); return field(fieldId); @@ -945,7 +1063,7 @@ Containers::StridedArrayView2D SceneData::field(const SceneField nam Containers::StridedArrayView2D SceneData::mutableField(const SceneField name) { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, "Trade::SceneData::mutableField(): data not mutable", {}); - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableField(): field" << name << "not found", {}); return mutableField(fieldId); @@ -1000,21 +1118,21 @@ Containers::Array SceneData::objectsAsArray(const UnsignedInt field } void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objectsInto(): field" << name << "not found", ); objectsInto(fieldId, destination); } std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objectsInto(): field" << name << "not found", {}); return objectsInto(fieldId, offset, destination); } Containers::Array SceneData::objectsAsArray(const SceneField name) const { - const UnsignedInt fieldId = fieldFor(name); + const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ @@ -1043,7 +1161,7 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t } void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Parent); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, @@ -1052,7 +1170,7 @@ void SceneData::parentsInto(const Containers::StridedArrayView1D& destinati } std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Parent); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1063,7 +1181,7 @@ std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::S } Containers::Array SceneData::parentsAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::Parent); + 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 */ @@ -1664,7 +1782,7 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std /* 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); + const UnsignedInt materialFieldId = findFieldIdInternal(SceneField::MeshMaterial); if(materialFieldId == ~UnsignedInt{}) { constexpr Int invalid[]{-1}; Utility::copy(Containers::stridedArrayView(invalid).broadcasted<0>(meshMaterialDestination.size()), meshMaterialDestination); @@ -1673,7 +1791,7 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std } void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Mesh); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", ); CORRADE_ASSERT(!meshDestination || meshDestination.size() == _fields[fieldId]._size, @@ -1684,7 +1802,7 @@ void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Mesh); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1699,7 +1817,7 @@ std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Conta } Containers::Array> SceneData::meshesMaterialsAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::Mesh); + 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 */ @@ -1713,7 +1831,7 @@ Containers::Array> SceneData::meshesMaterials } void SceneData::lightsInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Light); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, @@ -1722,7 +1840,7 @@ void SceneData::lightsInto(const Containers::StridedArrayView1D& de } std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Light); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1733,7 +1851,7 @@ std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::St } Containers::Array SceneData::lightsAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::Light); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ @@ -1742,7 +1860,7 @@ Containers::Array SceneData::lightsAsArray() const { } void SceneData::camerasInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Camera); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, @@ -1751,7 +1869,7 @@ void SceneData::camerasInto(const Containers::StridedArrayView1D& d } std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Camera); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1762,7 +1880,7 @@ std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::S } Containers::Array SceneData::camerasAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::Camera); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ @@ -1771,7 +1889,7 @@ Containers::Array SceneData::camerasAsArray() const { } void SceneData::skinsInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Skin); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, @@ -1780,7 +1898,7 @@ void SceneData::skinsInto(const Containers::StridedArrayView1D& des } std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Skin); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1791,7 +1909,7 @@ std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::Str } Containers::Array SceneData::skinsAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::Skin); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ @@ -1810,7 +1928,7 @@ void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std:: } void SceneData::importerStateInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::importerStateInto(): field not found", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, @@ -1819,7 +1937,7 @@ void SceneData::importerStateInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::importerStateInto(): field not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, @@ -1830,7 +1948,7 @@ std::size_t SceneData::importerStateInto(const std::size_t offset, const Contain } Containers::Array SceneData::importerStateAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + 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 */ @@ -1840,42 +1958,15 @@ Containers::Array SceneData::importerStateAsArray() const { return out; } -namespace { - -template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { - const Containers::StridedArrayView1D objectsT = Containers::arrayCast(objects); - const std::size_t max = objectsT.size(); - /** @todo implement something faster than O(n) when field-specific flags - can annotate how the object mapping is done */ - for(std::size_t i = 0; i != max; ++i) - if(objectsT[i] == object) return i; - return max; -} - -} - -std::size_t SceneData::fieldFor(const SceneFieldData& field, const std::size_t offset, const UnsignedInt object) const { - const Containers::StridedArrayView1D objects = fieldDataObjectViewInternal(field, offset, field._size - offset); - if(field._objectType == SceneObjectType::UnsignedInt) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedShort) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedByte) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedLong) - return offset + findObject(objects, object); - else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ -} - Containers::Optional SceneData::parentFor(const UnsignedInt object) const { CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt fieldId = fieldFor(SceneField::Parent); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); if(fieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[fieldId]; - const std::size_t offset = fieldFor(field, 0, object); + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); if(offset == field._size) return {}; Int index[1]; @@ -1891,7 +1982,7 @@ Containers::Array SceneData::childrenFor(const Int object) const { CORRADE_ASSERT(object >= -1 && object < Long(_objectCount), "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt parentFieldId = fieldFor(SceneField::Parent); + const UnsignedInt parentFieldId = findFieldIdInternal(SceneField::Parent); if(parentFieldId == ~UnsignedInt{}) return {}; const SceneFieldData& parentField = _fields[parentFieldId]; @@ -1901,7 +1992,7 @@ Containers::Array SceneData::childrenFor(const Int object) const { Int parentIndexToLookFor; if(object == -1) parentIndexToLookFor = -1; else { - const std::size_t parentObjectIndex = fieldFor(parentField, 0, object); + const std::size_t parentObjectIndex = findFieldObjectOffsetInternal(parentField, object, 0); if(parentObjectIndex == parentField._size) return {}; parentIndexToLookFor = parentObjectIndex; } @@ -1934,8 +2025,9 @@ Containers::Optional SceneData::transformation2DFor(const UnsignedInt o is handled above. */ CORRADE_ASSERT(!is3D(), "Trade::SceneData::transformation2DFor(): scene has a 3D transformation type", {}); - const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); - if(offset == _fields[fieldWithObjectMapping]._size) return {}; + const SceneFieldData& field = _fields[fieldWithObjectMapping]; + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); + if(offset == field._size) return {}; Matrix3 transformation[1]; transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); @@ -1953,8 +2045,9 @@ Containers::Optional> SceneData::t is handled above. */ CORRADE_ASSERT(!is3D(), "Trade::SceneData::translationRotationScaling2DFor(): scene has a 3D transformation type", {}); - const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); - if(offset == _fields[fieldWithObjectMapping]._size) return {}; + const SceneFieldData& field = _fields[fieldWithObjectMapping]; + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); + if(offset == field._size) return {}; Vector2 translation[1]; Complex rotation[1]; @@ -1974,8 +2067,9 @@ Containers::Optional SceneData::transformation3DFor(const UnsignedInt o is handled above. */ CORRADE_ASSERT(!is2D(), "Trade::SceneData::transformation3DFor(): scene has a 2D transformation type", {}); - const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); - if(offset == _fields[fieldWithObjectMapping]._size) return {}; + const SceneFieldData& field = _fields[fieldWithObjectMapping]; + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); + if(offset == field._size) return {}; Matrix4 transformation[1]; transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); @@ -1993,8 +2087,9 @@ Containers::Optional> SceneData is handled above. */ CORRADE_ASSERT(!is2D(), "Trade::SceneData::translationRotationScaling3DFor(): scene has a 2D transformation type", {}); - const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); - if(offset == _fields[fieldWithObjectMapping]._size) return {}; + const SceneFieldData& field = _fields[fieldWithObjectMapping]; + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); + if(offset == field._size) return {}; Vector3 translation[1]; Quaternion rotation[1]; @@ -2007,14 +2102,14 @@ Containers::Array> SceneData::meshesMaterials CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt meshFieldId = fieldFor(SceneField::Mesh); + const UnsignedInt meshFieldId = findFieldIdInternal(SceneField::Mesh); if(meshFieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[meshFieldId]; Containers::Array> out; std::size_t offset = 0; for(;;) { - offset = fieldFor(field, offset, object); + offset = findFieldObjectOffsetInternal(field, object, offset); if(offset == field._size) break; UnsignedInt mesh[1]; @@ -2031,14 +2126,14 @@ Containers::Array SceneData::lightsFor(const UnsignedInt object) co CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt fieldId = fieldFor(SceneField::Light); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); if(fieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[fieldId]; Containers::Array out; std::size_t offset = 0; for(;;) { - offset = fieldFor(field, offset, object); + offset = findFieldObjectOffsetInternal(field, object, offset); if(offset == field._size) break; UnsignedInt index[1]; @@ -2054,14 +2149,14 @@ Containers::Array SceneData::camerasFor(const UnsignedInt object) c CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt fieldId = fieldFor(SceneField::Camera); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); if(fieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[fieldId]; Containers::Array out; std::size_t offset = 0; for(;;) { - offset = fieldFor(field, offset, object); + offset = findFieldObjectOffsetInternal(field, object, offset); if(offset == field._size) break; UnsignedInt index[1]; @@ -2077,14 +2172,14 @@ Containers::Array SceneData::skinsFor(const UnsignedInt object) con CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt fieldId = fieldFor(SceneField::Skin); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); if(fieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[fieldId]; Containers::Array out; std::size_t offset = 0; for(;;) { - offset = fieldFor(field, offset, object); + offset = findFieldObjectOffsetInternal(field, object, offset); if(offset == field._size) break; UnsignedInt index[1]; @@ -2100,11 +2195,11 @@ Containers::Optional SceneData::importerStateFor(const UnsignedInt CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); if(fieldId == ~UnsignedInt{}) return {}; const SceneFieldData& field = _fields[fieldId]; - const std::size_t offset = fieldFor(field, 0, object); + const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0); if(offset == field._size) return {}; const void* importerState[1]; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 24a5bccf3f..9ddbece3bd 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -1080,22 +1080,112 @@ class MAGNUM_TRADE_EXPORT SceneData { bool is3D() const { return _dimensions == 3; } /** - * @brief Whether the scene has given field + * @brief Find an absolute ID of a named field * @m_since_latest * - * @see @ref is2D(), @ref is3D() + * If @p name doesn't exist, returns @ref Containers::NullOpt. The + * lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with + * @f$ n @f$ being the field count. + * @see @ref hasField(), @ref fieldId() */ - bool hasField(SceneField name) const; + Containers::Optional findFieldId(SceneField name) const; /** * @brief Absolute ID of a named field * @m_since_latest * - * The @p name is expected to exist. + * Like @ref findFieldId(), but the @p name is expected to exist. * @see @ref hasField(), @ref fieldName(UnsignedInt) const */ UnsignedInt fieldId(SceneField name) const; + /** + * @brief Whether the scene has given field + * @m_since_latest + * + * @see @ref is2D(), @ref is3D() + */ + bool hasField(SceneField name) const; + + /** + * @brief Find offset of an object in given field + * @m_since_latest + * + * If @p object isn't present in @p fieldId starting at @p offset, + * returns @ref Containers::NullOpt. The @p fieldId is expected to be + * smaller than @ref fieldCount(), @p object smaller than + * @ref objectCount() and @p offset not larger than + * @ref fieldSize(UnsignedInt) const. + * + * The lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with + * @f$ n @f$ being the size of the field. + * + * You can also use @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * to directly find offset of an object in given named field. + * @see @ref hasFieldObject(UnsignedInt, UnsignedInt) const, + * @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const + */ + Containers::Optional findFieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const; + + /** + * @brief Find offset of an object in given named field + * @m_since_latest + * + * If @p object isn't present in @p fieldName starting at @p offset, + * returns @ref Containers::NullOpt. The @p fieldName is expected to + * exist, @p object is expected to be smaller than @ref objectCount() + * and @p offset not be larger than @ref fieldSize(SceneField) const. + * + * The lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with + * @f$ m @f$ being the field count and @f$ n @f$ the size of the field. + * + * @see @ref hasField(), @ref hasFieldObject(SceneField, UnsignedInt) const, + * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + */ + Containers::Optional findFieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const; + + /** + * @brief Offset of an object in given field + * @m_since_latest + * + * Like @ref findFieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const, + * but @p object is additionally expected to be present in @p fieldId + * starting at @p offset. + * + * You can also use @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * to directly get offset of an object in given named field. + */ + std::size_t fieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const; + + /** + * @brief Offset of an object in given named field + * @m_since_latest + * + * Like @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const, + * but @p object is additionally expected to be present in @p fieldName + * starting at @p offset. + */ + std::size_t fieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const; + + /** + * @brief Whether a scene field has given object + * @m_since_latest + * + * The @p fieldId is expected to be smaller than @ref fieldCount() and + * @p object smaller than @ref objectCount(). + */ + bool hasFieldObject(UnsignedInt fieldId, UnsignedInt object) const; + + /** + * @brief Whether a named scene field has given object + * @m_since_latest + * + * The @p fieldName is expected to exist and @p object is expected to + * be smaller than @ref objectCount(). + * @see @ref hasField() + */ + bool hasFieldObject(SceneField fieldName, UnsignedInt object) const; + /** * @brief Type of a named field * @m_since_latest @@ -1416,7 +1506,8 @@ 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. - * @see @ref fieldSize(UnsignedInt) const + * @see @ref fieldSize(UnsignedInt) const, + * @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const */ std::size_t objectsInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; @@ -1458,7 +1549,8 @@ 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. - * @see @ref fieldSize(SceneField) const + * @see @ref fieldSize(SceneField) const, + * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ std::size_t objectsInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D& destination) const; @@ -1500,7 +1592,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1543,7 +1636,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1592,7 +1686,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1635,7 +1730,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1684,7 +1780,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1725,7 +1822,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1761,7 +1859,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1797,7 +1896,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1833,7 +1933,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1872,7 +1973,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 + * @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; @@ -1880,12 +1982,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Parent for given object * @m_since_latest * - * Looks up the @ref SceneField::Parent field for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the parent field, - * thus for retrieving parent info for many objects it's recommended to - * access the field data directly with @ref parentsAsArray() and - * related APIs. + * Looks up the @ref SceneField::Parent field for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref parentsAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving parent + * info for many objects it's recommended to access the field data + * directly. * * If the @ref SceneField::Parent field is not present or if there's no * parent for @p object, returns @ref Containers::NullOpt. If @p object @@ -1901,13 +2004,12 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Looks up @p object in the object mapping array for - * @ref SceneField::Parent and returns a list of all object IDs that - * have it listed as the parent. The lookup is done in an - * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field - * count and @f$ n @f$ the size of the parent field, thus for - * retrieving parent/child info for many objects it's recommended to - * access the field data directly with @ref parentsAsArray() and - * related APIs. + * @ref SceneField::Parent equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const, + * converts the fields from an arbitrary underlying type the same way + * as @ref parentsAsArray(), returning a list of all object IDs that + * have it listed as the parent. See the lookup function documentation + * for operation complexity --- for retrieving parent/child info for + * many objects it's recommended to access the field data directly. * * If the @ref SceneField::Parent field doesn't exist or there are no * objects which would have @p object listed as their parent, returns @@ -1922,16 +2024,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief 2D transformation for given object * @m_since_latest * - * Looks up the @ref SceneField::Transformation field for @p object or - * combines it from a @ref SceneField::Translation, + * Looks up the @ref SceneField::Transformation field for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * or combines it from a @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and - * @relativeref{SceneField,Scaling} in the same way as - * @ref transformations2DAsArray(). The lookup is done in an - * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field - * count and @f$ n @f$ the size of the transformation field, thus for - * retrieving transformation info for many objects it's recommended to - * access the field data directly with @ref transformations2DAsArray() - * and related APIs. + * @relativeref{SceneField,Scaling}, converting the fields from + * arbitrary underlying types the same way as + * @ref transformations2DAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving + * transformation info for many objects it's recommended to access the + * field data directly. * * If neither @ref SceneField::Transformation nor any of * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or @@ -1950,12 +2052,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * * Looks up the @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and - * @relativeref{SceneField,Scaling} fields for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the transformation - * field, thus for retrieving transformation info for many objects it's - * recommended to access the field data directly with - * @ref translationsRotationsScalings2DAsArray() and related APIs. + * @relativeref{SceneField,Scaling} fields for @p object equivalently + * to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the fields from arbitrary underlying types the + * same way as @ref translationsRotationsScalings2DAsArray(). See the + * lookup function documentation for operation complexity --- for + * retrieving transformation info for many objects it's recommended to + * access the field data directly. * * If the @ref SceneField::Translation field isn't present, the first * returned value is a zero vector. If the @@ -1976,16 +2079,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief 3D transformation for given object * @m_since_latest * - * Looks up the @ref SceneField::Transformation field for @p object or - * combines it from a @ref SceneField::Translation, + * Looks up the @ref SceneField::Transformation field for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * or combines it from a @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and - * @relativeref{SceneField,Scaling} in the same way as - * @ref transformations3DAsArray(). The lookup is done in an - * @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field - * count and @f$ n @f$ the size of the transformation field, thus for - * retrieving transformation info for many objects it's recommended to - * access the field data directly with @ref transformations3DAsArray() - * and related APIs. + * @relativeref{SceneField,Scaling}, converting the fields from + * arbitrary underlying types the same way as + * @ref transformations3DAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving + * transformation info for many objects it's recommended to access the + * field data directly. * * If neither @ref SceneField::Transformation nor any of * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or @@ -2004,12 +2107,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * * Looks up the @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and - * @relativeref{SceneField,Scaling} fields for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the transformation - * field, thus for retrieving transformation info for many objects it's - * recommended to access the field data directly with - * @ref translationsRotationsScalings3DAsArray() and related APIs. + * @relativeref{SceneField,Scaling} fields for @p object equivalently + * to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the fields from arbitrary underlying types the + * same way as @ref translationsRotationsScalings2DAsArray(). See the + * lookup function documentation for operation complexity --- for + * retrieving transformation info for many objects it's recommended to + * access the field data directly. * * If the @ref SceneField::Translation field isn't present, the first * returned value is a zero vector. If the @@ -2031,12 +2135,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Looks up all @ref SceneField::Mesh and @ref SceneField::MeshMaterial - * @relativeref{SceneField,Scaling} fields for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the mesh field, thus - * for retrieving mesh info for many objects it's recommended to access - * the field data directly with @ref meshesMaterialsAsArray() and - * related APIs. + * @relativeref{SceneField,Scaling} fields for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref meshesMaterialsAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving mesh and + * material info for many objects it's recommended to access the field + * data directly. * * If the @ref SceneField::MeshMaterial field is not present, the * second returned value is always @cpp -1 @ce. If @@ -2051,12 +2156,12 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Lights for given object * @m_since_latest * - * Looks up all @ref SceneField::Light fields for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the light field, - * thus for retrieving light info for many objects it's recommended to - * access the field data directly with @ref lightsAsArray() and related - * APIs. + * Looks up all @ref SceneField::Light fields for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref lightsAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving light info + * for many objects it's recommended to access the field data directly. * * If the @ref SceneField::Light field is not present or if there's no * light for @p object, returns an empty array. @@ -2069,12 +2174,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Cameras for given object * @m_since_latest * - * Looks up all @ref SceneField::Camera fields for @p object. The - * lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with - * @f$ m @f$ being the field count and @f$ n @f$ the size of the camera - * field, thus for retrieving camera info for many objects it's - * recommended to access the field data directly with - * @ref camerasAsArray() and related APIs. + * Looks up all @ref SceneField::Camera fields for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref camerasAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving camera + * info for many objects it's recommended to access the field data + * directly. * * If the @ref SceneField::Camera field is not present or if there's no * camera for @p object, returns an empty array. @@ -2087,11 +2193,12 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Skins for given object * @m_since_latest * - * Looks up all @ref SceneField::Skin fields for @p object. The lookup - * is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ - * being the field count and @f$ n @f$ the size of the skin field, thus - * for retrieving skin info for many objects it's recommended to access - * the field data directly with @ref skinsAsArray() and related APIs. + * Looks up all @ref SceneField::Skin fields for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref skinsAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving skin info + * for many objects it's recommended to access the field data directly. * * If the @ref SceneField::Skin field is not present or if there's no * skin for @p object, returns an empty array. @@ -2104,12 +2211,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Importer state for given object * @m_since_latest * - * Looks up the @ref SceneField::ImporterState field for @p object. The - * lookup is done in a @f$ \mathcal{O}(m + n) @f$ complexity with - * @f$ m @f$ being the field count and @f$ n @f$ the size of the - * importer state field, thus for retrieving importer state info for - * many objects it's recommended to access the field data directly with - * @ref importerStateAsArray() and related APIs. + * Looks up the @ref SceneField::ImporterState field for @p object + * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + * and then converts the field from an arbitrary underlying type the + * same way as @ref importerStateAsArray(). See the lookup function + * documentation for operation complexity --- for retrieving importer + * state info for many objects it's recommended to access the field + * data directly. * * If the @ref SceneField::ImporterState field is not present or if * there's no importer state for @p object, returns @@ -2187,13 +2295,14 @@ class MAGNUM_TRADE_EXPORT SceneData { implementations. */ friend AbstractImporter; - /* Internal helper that doesn't assert, unlike fieldId() */ - UnsignedInt fieldFor(SceneField name) const; + /* Internal helper without the extra overhead from Optional, returns + ~UnsignedInt{} on failure */ + UnsignedInt findFieldIdInternal(SceneField name) const; /* Returns the offset at which `object` is for field at index `id`, or the end offset if the object is not found. The returned offset can be then passed to fieldData{Object,Field}ViewInternal(). */ - std::size_t fieldFor(const SceneFieldData& field, std::size_t offset, UnsignedInt object) const; + std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const; /* Like objects() / field(), but returning just a 1D view, sliced from offset to offset + size. The parameterless overloads are equal to @@ -2566,7 +2675,7 @@ template Containers::StridedArrayView1D SceneData::fiel if(!data.stride()[1]) return {}; #endif #ifndef CORRADE_NO_ASSERT - if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; + if(!checkFieldTypeCompatibility(_fields[findFieldIdInternal(name)], "Trade::SceneData::field():")) return {}; #endif return Containers::arrayCast<1, const T>(data); } @@ -2577,7 +2686,7 @@ template Containers::StridedArrayView2D(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; + if(!checkFieldTypeCompatibility(_fields[findFieldIdInternal(name)], "Trade::SceneData::field():")) return {}; #endif return Containers::arrayCast<2, const typename std::remove_extent::type>(data); } @@ -2588,7 +2697,7 @@ template Containers::StridedArrayView1D SceneData::mutableFie if(!data.stride()[1]) return {}; #endif #ifndef CORRADE_NO_ASSERT - if(!checkFieldTypeCompatibility(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; + if(!checkFieldTypeCompatibility(_fields[findFieldIdInternal(name)], "Trade::SceneData::mutableField():")) return {}; #endif return Containers::arrayCast<1, T>(data); } @@ -2599,7 +2708,7 @@ template Containers::StridedArrayView2D(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; + if(!checkFieldTypeCompatibility(_fields[findFieldIdInternal(name)], "Trade::SceneData::mutableField():")) return {}; #endif return Containers::arrayCast<2, typename std::remove_extent::type>(data); } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 660371e8ea..27e8cd0940 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -111,6 +111,11 @@ struct SceneDataTest: TestSuite::Tester { void constructCopy(); void constructMove(); + void findFieldId(); + template void findFieldObjectOffset(); + void findFieldObjectOffsetInvalidOffset(); + void fieldObjectOffsetNotFound(); + template void objectsAsArrayByIndex(); template void objectsAsArrayByName(); void objectsAsArrayLongType(); @@ -163,9 +168,7 @@ struct SceneDataTest: TestSuite::Tester { void fieldWrongPointerType(); void fieldWrongArrayAccess(); - /* Different object types checked just for the parentFor(), other APIs - use the same helper */ - template void parentFor(); + void parentFor(); void childrenFor(); void transformation2DFor(); void transformation2DForTRS(); @@ -184,7 +187,7 @@ struct SceneDataTest: TestSuite::Tester { #endif void fieldForFieldMissing(); - void fieldForInvalidObject(); + void findFieldObjectOffsetInvalidObject(); void releaseFieldData(); void releaseData(); @@ -290,6 +293,14 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructCopy, &SceneDataTest::constructMove, + &SceneDataTest::findFieldId, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffsetInvalidOffset, + &SceneDataTest::fieldObjectOffsetNotFound, + &SceneDataTest::objectsAsArrayByIndex, &SceneDataTest::objectsAsArrayByIndex, &SceneDataTest::objectsAsArrayByIndex, @@ -396,10 +407,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldWrongPointerType, &SceneDataTest::fieldWrongArrayAccess, - &SceneDataTest::parentFor, - &SceneDataTest::parentFor, - &SceneDataTest::parentFor, - &SceneDataTest::parentFor, + &SceneDataTest::parentFor, &SceneDataTest::childrenFor, &SceneDataTest::transformation2DFor, &SceneDataTest::transformation2DForTRS, @@ -419,7 +427,7 @@ SceneDataTest::SceneDataTest() { #endif addTests({&SceneDataTest::fieldForFieldMissing, - &SceneDataTest::fieldForInvalidObject, + &SceneDataTest::findFieldObjectOffsetInvalidObject, &SceneDataTest::releaseFieldData, &SceneDataTest::releaseData}); @@ -1298,15 +1306,6 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.mutableField(3)[0][1], 1.5f); /* Field property access by name */ - CORRADE_COMPARE(scene.fieldId(SceneField::Transformation), 0); - CORRADE_COMPARE(scene.fieldId(SceneField::Parent), 1); - CORRADE_COMPARE(scene.fieldId(SceneField::Mesh), 2); - CORRADE_COMPARE(scene.fieldId(sceneFieldCustom(37)), 3); - CORRADE_VERIFY(scene.hasField(SceneField::Transformation)); - CORRADE_VERIFY(scene.hasField(SceneField::Parent)); - CORRADE_VERIFY(scene.hasField(SceneField::Mesh)); - CORRADE_VERIFY(scene.hasField(sceneFieldCustom(37))); - CORRADE_VERIFY(!scene.hasField(SceneField::Skin)); CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4); CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); @@ -1957,6 +1956,151 @@ void SceneDataTest::constructMove() { CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } +void SceneDataTest::findFieldId() { + SceneData scene{SceneObjectType::UnsignedInt, 0, {}, nullptr, { + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedByte, nullptr} + }}; + + CORRADE_COMPARE(scene.findFieldId(SceneField::Parent), 0); + CORRADE_COMPARE(scene.findFieldId(SceneField::Mesh), 1); + CORRADE_COMPARE(scene.findFieldId(SceneField::MeshMaterial), Containers::NullOpt); + + CORRADE_COMPARE(scene.fieldId(SceneField::Parent), 0); + CORRADE_COMPARE(scene.fieldId(SceneField::Mesh), 1); + + CORRADE_VERIFY(scene.hasField(SceneField::Parent)); + CORRADE_VERIFY(scene.hasField(SceneField::Mesh)); + CORRADE_VERIFY(!scene.hasField(SceneField::MeshMaterial)); +} + +template void SceneDataTest::findFieldObjectOffset() { + setTestCaseTemplateName(NameTraits::name()); + + /** @todo update once field flags describing object order are present */ + + struct Field { + T object; + UnsignedInt mesh; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4}, + {0, 5}, + {2, 5} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + /* Test also with a completely empty field */ + SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + CORRADE_COMPARE(scene.findFieldObjectOffset(0, 4), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, 4), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 4), 0); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 1), 1); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2), 2); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 3), 4); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 5), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(1, 3), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 4), 0); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 1), 1); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2), 2); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 3), 4); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 5), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 3), Containers::NullOpt); + + CORRADE_COMPARE(scene.fieldObjectOffset(1, 4), 0); + CORRADE_COMPARE(scene.fieldObjectOffset(1, 1), 1); + CORRADE_COMPARE(scene.fieldObjectOffset(1, 2), 2); + CORRADE_COMPARE(scene.fieldObjectOffset(1, 2, 3), 4); + CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 4), 0); + CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 1), 1); + CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2), 2); + CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2, 3), 4); + + CORRADE_VERIFY(!scene.hasFieldObject(0, 4)); + CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, 4)); + CORRADE_VERIFY(scene.hasFieldObject(1, 4)); + CORRADE_VERIFY(scene.hasFieldObject(1, 1)); + CORRADE_VERIFY(scene.hasFieldObject(1, 2)); + CORRADE_VERIFY(!scene.hasFieldObject(1, 3)); + CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 4)); + CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 1)); + CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 2)); + CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Mesh, 3)); +} + +void SceneDataTest::findFieldObjectOffsetInvalidOffset() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt mesh; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.findFieldObjectOffset(0, 1, 4); + scene.findFieldObjectOffset(SceneField::Mesh, 1, 4); + scene.fieldObjectOffset(0, 1, 4); + scene.fieldObjectOffset(SceneField::Mesh, 1, 4); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::findFieldObjectOffset(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::findFieldObjectOffset(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::fieldObjectOffset(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::fieldObjectOffset(): offset 4 out of bounds for a field of size 3\n"); +} + +void SceneDataTest::fieldObjectOffsetNotFound() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + UnsignedInt mesh; + } fields[]{ + {4, 1}, + {1, 3}, + {2, 4}, + {0, 5}, + {2, 5} + }; + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + /* Test also with a completely empty field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + scene.fieldObjectOffset(0, 4); + scene.fieldObjectOffset(SceneField::Parent, 4); + scene.fieldObjectOffset(1, 1, 2); + scene.fieldObjectOffset(SceneField::Mesh, 1, 2); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::fieldObjectOffset(): object 4 not found in field Trade::SceneField::Parent starting at offset 0\n" + "Trade::SceneData::fieldObjectOffset(): object 4 not found in field Trade::SceneField::Parent starting at offset 0\n" + "Trade::SceneData::fieldObjectOffset(): object 1 not found in field Trade::SceneField::Mesh starting at offset 2\n" + "Trade::SceneData::fieldObjectOffset(): object 1 not found in field Trade::SceneField::Mesh starting at offset 2\n"); +} + template void SceneDataTest::objectsAsArrayByIndex() { setTestCaseTemplateName(NameTraits::name()); @@ -4108,6 +4252,9 @@ void SceneDataTest::fieldNotFound() { std::ostringstream out; Error redirectError{&out}; + scene.findFieldObjectOffset(2, 0); + scene.fieldObjectOffset(2, 0); + scene.hasFieldObject(2, 0); scene.fieldData(2); scene.fieldName(2); scene.fieldType(2); @@ -4121,6 +4268,9 @@ void SceneDataTest::fieldNotFound() { scene.mutableField(2); scene.fieldId(sceneFieldCustom(666)); + scene.findFieldObjectOffset(sceneFieldCustom(666), 0); + scene.fieldObjectOffset(sceneFieldCustom(666), 0); + scene.hasFieldObject(sceneFieldCustom(666), 0); scene.fieldType(sceneFieldCustom(666)); scene.fieldSize(sceneFieldCustom(666)); scene.fieldArraySize(sceneFieldCustom(666)); @@ -4162,6 +4312,9 @@ void SceneDataTest::fieldNotFound() { scene.importerStateInto(nullptr); scene.importerStateInto(0, 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" + "Trade::SceneData::hasFieldObject(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n" @@ -4175,6 +4328,9 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::findFieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::fieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::hasFieldObject(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldType(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldSize(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldArraySize(): field Trade::SceneField::Custom(666) not found\n" @@ -4370,11 +4526,9 @@ void SceneDataTest::fieldWrongArrayAccess() { "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n"); } -template void SceneDataTest::parentFor() { - setTestCaseTemplateName(NameTraits::name()); - +void SceneDataTest::parentFor() { struct Field { - T object; + UnsignedInt object; Int parent; } fields[]{ {3, -1}, @@ -4384,7 +4538,7 @@ template void SceneDataTest::parentFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; @@ -4867,15 +5021,23 @@ void SceneDataTest::fieldForFieldMissing() { TestSuite::Compare::Container); } -void SceneDataTest::fieldForInvalidObject() { +void SceneDataTest::findFieldObjectOffsetInvalidObject() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; + SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + }}; std::ostringstream out; Error redirectError{&out}; + scene.findFieldObjectOffset(0, 7); + scene.findFieldObjectOffset(SceneField::Parent, 7); + scene.fieldObjectOffset(0, 7); + scene.fieldObjectOffset(SceneField::Parent, 7); + scene.hasFieldObject(0, 7); + scene.hasFieldObject(SceneField::Parent, 7); scene.parentFor(7); scene.childrenFor(-2); scene.childrenFor(7); @@ -4888,6 +5050,12 @@ void SceneDataTest::fieldForInvalidObject() { scene.camerasFor(7); scene.skinsFor(7); CORRADE_COMPARE(out.str(), + "Trade::SceneData::findFieldObjectOffset(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::findFieldObjectOffset(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::fieldObjectOffset(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::fieldObjectOffset(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::hasFieldObject(): object 7 out of bounds for 7 objects\n" + "Trade::SceneData::hasFieldObject(): object 7 out of bounds for 7 objects\n" "Trade::SceneData::parentFor(): object 7 out of bounds for 7 objects\n" "Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n" "Trade::SceneData::childrenFor(): object 7 out of bounds for 7 objects\n" From 54042dacd065a7d8a976efa74f5c3aa9ce0f6803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 13 Oct 2021 19:08:00 +0200 Subject: [PATCH 26/49] Trade: this doesn't need to be exported. --- src/Magnum/Trade/SceneData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 9ddbece3bd..ea8d697e7c 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -2302,7 +2302,7 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Returns the offset at which `object` is for field at index `id`, or the end offset if the object is not found. The returned offset can be then passed to fieldData{Object,Field}ViewInternal(). */ - std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const; + MAGNUM_TRADE_LOCAL std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const; /* Like objects() / field(), but returning just a 1D view, sliced from offset to offset + size. The parameterless overloads are equal to From 9cdc5f0e5a3b853cd89e62e8a130fa43c88c049e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 13 Oct 2021 21:00:53 +0200 Subject: [PATCH 27/49] Trade: internal tool for making SceneData with single-function objects. Needed for backwards compatibility purposes currently, eventually it'll become a public API in the upcoming SceneTools library. --- src/Magnum/Trade/Implementation/sceneTools.h | 115 ++++++++++++++++ src/Magnum/Trade/Test/SceneToolsTest.cpp | 132 +++++++++++++++++++ 2 files changed, 247 insertions(+) diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index 98f3e41b55..db41c13732 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -28,9 +28,11 @@ #include #include #include +#include #include #include +#include "Magnum/Math/Functions.h" #include "Magnum/Math/PackingBatch.h" #include "Magnum/Trade/SceneData.h" @@ -188,6 +190,119 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo return SceneData{objectType, objectCount, std::move(outData), std::move(outFields)}; } +/* Creates a SceneData copy where each object has at most one of the fields + listed in the passed array. This is done by enlarging the parents array + and moving extraneous features to new objects that are marked as a child of + the original. No transformations or other fields are added for the new + objects. Fields that are connected together (such as meshes and materials) + are assumed to share the same object mapping with only one of them passed in + the fieldsToConvert array, which will result for all fields from the same + set being reassociated to the new object. + + Requies a SceneField::Parent to be present -- otherwise it wouldn't be + possible to know where to attach the new objects. */ +/** @todo when published, (again) add an initializer_list overload and turn all + internal asserts into (tested!) message asserts */ +inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Containers::ArrayView fieldsToConvert, const UnsignedInt newObjectOffset) { + /** @todo assert for really high object counts (where this cast would fail) */ + Containers::Array objectAttachmentCount{ValueInit, std::size_t(scene.objectCount())}; + for(const SceneField field: fieldsToConvert) { + CORRADE_INTERNAL_ASSERT(field != SceneField::Parent); + + /* Skip fields that are not present -- is it's not present, then it + definitely won't be responsible for multi-function objects */ + const Containers::Optional fieldId = scene.findFieldId(field); + if(!fieldId) continue; + + /** @todo use a statically-allocated array & Into() in a loop instead + once this is more than a private backwards-compatibility utility + where PERF WHATEVER WHO CARES */ + for(const UnsignedInt object: scene.objectsAsArray(*fieldId)) { + CORRADE_INTERNAL_ASSERT(object < objectAttachmentCount.size()); + ++objectAttachmentCount[object]; + } + } + + UnsignedInt objectsToAdd = 0; + for(const UnsignedInt count: objectAttachmentCount) + if(count > 1) objectsToAdd += count - 1; + + /* Ensure we don't overflow the 32-bit object count with the objects to + add. This should also cover the case when the parent field would not be + representable in 32 bits. */ + CORRADE_INTERNAL_ASSERT(newObjectOffset + objectsToAdd >= newObjectOffset); + + /* Copy the fields over, enlarging them as necessary */ + const UnsignedInt parentFieldId = scene.fieldId(SceneField::Parent); + Containers::Array fields{scene.fieldCount()}; + for(std::size_t i = 0; i != scene.fieldCount(); ++i) { + const SceneFieldData& field = scene.fieldData(i); + + /* If this is a parent, enlarge it for the newly added objects */ + if(field.name() == SceneField::Parent) { + /** @todo some nicer constructor for placeholders once this is in + public interest */ + fields[i] = SceneFieldData{SceneField::Parent, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}}; + + /* All other fields are copied as-is */ + } else fields[i] = field; + } + + /* Combine the fields into a new SceneData */ + SceneData out = sceneCombine(SceneObjectType::UnsignedInt, Math::max(scene.objectCount(), UnsignedLong(newObjectOffset) + objectsToAdd), fields); + + /* 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)); + + /* List new objects at the end of the extended parent field */ + const Containers::StridedArrayView1D newParentObjects = outParentObjects.suffix(scene.fieldSize(parentFieldId)); + const Containers::StridedArrayView1D newParents = outParents.suffix(scene.fieldSize(parentFieldId)); + for(std::size_t i = 0; i != newParentObjects.size(); ++i) { + newParentObjects[i] = newObjectOffset + i; + newParents[i] = -1; + } + + /* Clear the objectAttachmentCount array to reuse it below */ + /** @todo use a BitArray instead once it exists? */ + constexpr UnsignedInt zero[1]{}; + Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(scene.objectCount()), objectAttachmentCount); + + /* For objects with multiple fields move the extra fields to newly added + children */ + { + std::size_t newParentIndex = 0; + for(const SceneField field: fieldsToConvert) { + const Containers::Optional fieldId = scene.findFieldId(field); + if(!fieldId) continue; + + for(UnsignedInt& fieldObject: out.mutableObjects(*fieldId)) { + /* If the object is not new (could happen when an object + mapping array is shared among multiple fields, in which case + it *might* have been updated already to an ID larger than + the mapping array size) and it already has something + attached, then attach the field to a new object and make + that new object a child of the previous one. */ + if(fieldObject < objectAttachmentCount.size() && objectAttachmentCount[fieldObject]) { + /* Find an index of the old object and then use that index + to denote the parent of the new object */ + newParents[newParentIndex] = out.fieldObjectOffset(parentFieldId, fieldObject); + /* Assign the field to the new object */ + fieldObject = newParentObjects[newParentIndex]; + /* Move to the next reserved object */ + ++newParentIndex; + } else ++objectAttachmentCount[fieldObject]; + } + } + + CORRADE_INTERNAL_ASSERT(newParentIndex == objectsToAdd); + } + + return out; +} + }}} #endif diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index f5fda2dfa1..983ac8006e 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -41,6 +41,8 @@ struct SceneToolsTest: TestSuite::Tester { void combineObjectsShared(); void combineObjectsPlaceholderFieldPlaceholder(); void combineObjectSharedFieldPlaceholder(); + + void convertToSingleFunctionObjects(); }; struct { @@ -53,6 +55,15 @@ struct { {"UnsignedLong output", SceneObjectType::UnsignedLong}, }; +struct { + const char* name; + UnsignedLong originalObjectCount; + UnsignedLong expectedObjectCount; +} ConvertToSingleFunctionObjectsData[]{ + {"original object count smaller than new", 64, 67}, + {"original object count larger than new", 96, 96} +}; + SceneToolsTest::SceneToolsTest() { addInstancedTests({&SceneToolsTest::combine}, Containers::arraySize(CombineData)); @@ -61,6 +72,9 @@ SceneToolsTest::SceneToolsTest() { &SceneToolsTest::combineObjectsShared, &SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder, &SceneToolsTest::combineObjectSharedFieldPlaceholder}); + + addInstancedTests({&SceneToolsTest::convertToSingleFunctionObjects}, + Containers::arraySize(ConvertToSingleFunctionObjectsData)); } using namespace Math::Literals; @@ -319,6 +333,124 @@ void SceneToolsTest::combineObjectSharedFieldPlaceholder() { CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).stride()[0], 4); } +void SceneToolsTest::convertToSingleFunctionObjects() { + auto&& data = ConvertToSingleFunctionObjectsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Haha now I can use sceneCombine() to conveniently prepare the initial + state here, without having to mess with an ArrayTuple */ + + const UnsignedShort parentObjects[]{15, 21, 22, 23, 1}; + const Byte parents[]{-1, -1, 1, 2, -1}; + + /* Two objects have two and three mesh assignments respectively, meaning we + need three extra */ + const UnsignedShort meshObjects[]{15, 23, 23, 23, 1, 15, 21}; + const Containers::Pair meshesMaterials[]{ + {6, 4}, + {1, 0}, + {2, 3}, + {4, 2}, + {7, 2}, + {3, 1}, + {5, -1} + }; + + /* One camera is attached to an object that already has a mesh, meaning we + need a third extra object */ + const UnsignedShort cameraObjects[]{22, 1}; + const UnsignedInt cameras[]{1, 5}; + SceneData original = Implementation::sceneCombine(SceneObjectType::UnsignedShort, data.originalObjectCount, Containers::arrayView({ + SceneFieldData{SceneField::Parent, Containers::arrayView(parentObjects), Containers::arrayView(parents)}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::StridedArrayView1D{meshesMaterials, &meshesMaterials[0].first(), Containers::arraySize(meshesMaterials), sizeof(meshesMaterials[0])}}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::StridedArrayView1D{meshesMaterials, &meshesMaterials[0].second(), Containers::arraySize(meshesMaterials), sizeof(meshesMaterials[0])}}, + SceneFieldData{SceneField::Camera, Containers::arrayView(cameraObjects), Containers::arrayView(cameras)}, + })); + + SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original, Containers::arrayView({ + SceneField::Mesh, + /* Deliberately not including MeshMaterial in the list -- these should + get automatically updated as they share the same object mapping. + OTOH including them would break the output. */ + SceneField::Camera, + /* Include also a field that's not present -- it should get skipped */ + SceneField::ImporterState + }), 63); + + /* There should be three more objects, or the original count preserved if + it's large enough */ + CORRADE_COMPARE(scene.objectCount(), data.expectedObjectCount); + + /* Object 1 should have a new child that has the camera, as it has a mesh */ + CORRADE_COMPARE_AS(scene.childrenFor(1), + Containers::arrayView({66}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(1), + (Containers::arrayView>({{7, 2}})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasFor(1), + Containers::arrayView({}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasFor(66), + Containers::arrayView({5}), + TestSuite::Compare::Container); + + /* Object 15 should have a new child that has the second mesh */ + CORRADE_COMPARE_AS(scene.childrenFor(15), + Containers::arrayView({65}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(15), + (Containers::arrayView>({{6, 4}})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(65), + (Containers::arrayView>({{3, 1}})), + TestSuite::Compare::Container); + + /* Object 23 should have two new children that have the second and third + mesh */ + CORRADE_COMPARE_AS(scene.childrenFor(23), + Containers::arrayView({63, 64}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(23), + (Containers::arrayView>({{1, 0}})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(63), + (Containers::arrayView>({{2, 3}})), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(64), + (Containers::arrayView>({{4, 2}})), + TestSuite::Compare::Container); + + /* 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); + + /* 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_TEST_MAIN(Magnum::Trade::Test::SceneToolsTest) From 640fe8fea24675bc8a021a12e39085bd253c13c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 14 Oct 2021 00:09:10 +0200 Subject: [PATCH 28/49] Trade: process SceneData to single-function-objects for backwards compat. Now the code has all needed backwards compatibility in place. --- doc/changelog.dox | 13 + src/Magnum/Trade/AbstractImporter.cpp | 70 ++- .../Trade/Test/AbstractImporterTest.cpp | 405 ++++++++++++++++++ 3 files changed, 475 insertions(+), 13 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index cc3f4e7191..efb0a290a2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -830,6 +830,19 @@ See also: children that got deprecated in favor of @ref Trade::SceneData::SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) no longer accepts a scene that has both 2D and 3D children. +- The deprecated @cpp Trade::AbstractImporter::object*DCount() @ce, + @cpp Trade::AbstractImporter::object*DForName() @ce, + @cpp Trade::AbstractImporter::object*DName() @ce and + @cpp Trade::AbstractImporter::object*D() @ce accessors now behave different + for objects with multiple mesh assignments. This handling was originally + present in importer plugins themselves, but as the new + @ref Trade::SceneData representation supports multiple mesh/camera/... + assignments to a single object natively, the handling was moved to a single + place in the compatibility layer. Because the compatibility layer cannot + renumber object IDs, the newly added objects are not immediately following + the original ID but instead allocated at the end of the object ID range + reported by the importer. While the newly added objects have different IDs, + they retain the parent name like before. - @ref Trade::AbstractImporter::doDefaultScene() is now @cpp const @ce --- since the function is not expected to fail, no kind of complex lazy population can be done anyway. This is now consistent with `do*Count()` diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 3b027b9316..b000a55a7c 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -48,6 +48,8 @@ #include #include +#include "Magnum/Trade/Implementation/sceneTools.h" + #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ #define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ @@ -519,12 +521,20 @@ void AbstractImporter::populateCachedScenes() { _cachedScenes->scenes = Containers::Array>{sceneCount()}; for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { _cachedScenes->scenes[i] = scene(i); - - /* Return the 2D/3D object count based on which scenes are 2D and which - not. The objectCount() provided by the importer is ignored except - for the above, also because it doesn't take into account the - restriction for unique-functioning objects. */ if(_cachedScenes->scenes[i]) { + /* Convert the scene so that each object has only either a mesh + (potentially with a material and a skin), a camera or a light. + The tool requires SceneField::Parent to be present, however if + it's not then we treat the scene as empty in the backwards + compatibility code path anyway, so just skip the processing + altogether in that case. */ + if(_cachedScenes->scenes[i]->hasField(SceneField::Parent)) + _cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), objectCount()); + + /* Return the 2D/3D object count based on which scenes are 2D and + which not. The objectCount() provided by the importer is ignored + except for the above, also because it doesn't take into account + the restriction for unique-functioning objects. */ if(_cachedScenes->scenes[i]->is2D()) _cachedScenes->object2DCount = Math::max(_cachedScenes->object2DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); if(_cachedScenes->scenes[i]->is3D()) @@ -578,8 +588,23 @@ std::string AbstractImporter::object2DName(const UnsignedInt id) { } std::string AbstractImporter::doObject2DName(const UnsignedInt id) { - /* Alias to the new interface */ - return doObjectName(id); + /* Alias to the new interface if the ID is known to the new interface, + return an empty string for objects that got newly added in order to make + them single-functioning */ + if(id < doObjectCount()) return doObjectName(id); + + populateCachedScenes(); + for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { + if(!_cachedScenes->scenes[i] || + !_cachedScenes->scenes[i]->is2D() || + _cachedScenes->scenes[i]->objectCount() <= id) + continue; + + if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) + return doObjectName(*parent); + } + + return ""; } CORRADE_IGNORE_DEPRECATED_PUSH @@ -638,8 +663,10 @@ Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt const Containers::Array skin = scene.skinsFor(id); const Containers::Optional importerState = scene.importerStateFor(id); - /* All these should have at most 1 item as the old API doesn't have - any way to represent multi-function objects. */ + /* All these should have at most 1 item as the SceneData got processed to + have each object contain either just one mesh or one camera (materials + are implicitly shared with a mesh, skins also). Thus it doesn't matter + in which order we decide on the legacy object type. */ CORRADE_INTERNAL_ASSERT(camera.size() + mesh.size() <= 1); if(!mesh.empty()) { @@ -733,8 +760,23 @@ std::string AbstractImporter::object3DName(const UnsignedInt id) { } std::string AbstractImporter::doObject3DName(const UnsignedInt id) { - /* Alias to the new interface */ - return doObjectName(id); + /* Alias to the new interface if the ID is known to the new interface, + return an empty string for objects that got newly added in order to make + them single-functioning */ + if(id < doObjectCount()) return doObjectName(id); + + populateCachedScenes(); + for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { + if(!_cachedScenes->scenes[i] || + !_cachedScenes->scenes[i]->is3D() || + _cachedScenes->scenes[i]->objectCount() <= id) + continue; + + if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) + return doObjectName(*parent); + } + + return ""; } CORRADE_IGNORE_DEPRECATED_PUSH /* Clang doesn't warn, but GCC does */ @@ -794,8 +836,10 @@ Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt const Containers::Array light = scene.lightsFor(id); const Containers::Optional importerState = scene.importerStateFor(id); - /* All these should have at most 1 item as the old API doesn't have - any way to represent multi-function objects. */ + /* All these should have at most 1 item as the SceneData got processed to + have each object contain either just one mesh, one camera or one light + (materials are implicitly shared with a mesh, skins also). Thus it + doesn't matter in which order we decide on the legacy object type. */ CORRADE_INTERNAL_ASSERT(camera.size() + light.size() + mesh.size() <= 1); if(!mesh.empty()) { diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 09aa1ad70e..89e6baffff 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -51,6 +51,7 @@ #ifdef MAGNUM_BUILD_DEPRECATED #include #include +#include #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ #define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */ @@ -123,6 +124,8 @@ struct AbstractImporterTest: TestSuite::Tester { void sceneDeprecatedFallbackParentless3D(); void sceneDeprecatedFallbackTransformless2D(); void sceneDeprecatedFallbackTransformless3D(); + void sceneDeprecatedFallbackMultiFunctionObjects2D(); + void sceneDeprecatedFallbackMultiFunctionObjects3D(); #endif void sceneNameNotImplemented(); void objectNameNotImplemented(); @@ -405,6 +408,8 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::sceneDeprecatedFallbackParentless3D, &AbstractImporterTest::sceneDeprecatedFallbackTransformless2D, &AbstractImporterTest::sceneDeprecatedFallbackTransformless3D, + &AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D, + &AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D, #endif &AbstractImporterTest::sceneForNameOutOfRange, &AbstractImporterTest::objectForNameOutOfRange, @@ -2676,6 +2681,406 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { } CORRADE_IGNORE_DEPRECATED_POP } + +void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { + /* Mostly just a copy of SceneToolsTest::convertToSingleFunctionObjects() + except that here we can't use the convenience combining tool so it's + done by hand */ + + struct Parent { + UnsignedInt object; + Int parent; + }; + struct Mesh { + UnsignedInt object; + UnsignedInt mesh; + Int meshMaterial; + }; + struct Camera { + UnsignedInt object; + UnsignedInt camera; + }; + Containers::StridedArrayView1D parents; + Containers::StridedArrayView1D meshes; + Containers::StridedArrayView1D cameras; + Containers::Array dataData = Containers::ArrayTuple{ + {NoInit, 5, parents}, + {NoInit, 7, meshes}, + {NoInit, 2, cameras}, + }; + + Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); + Utility::copy({ + {15, 6, 4}, + {23, 1, 0}, + {23, 2, 3}, + {23, 4, 2}, + {1, 7, 2}, + {15, 3, 1}, + {21, 5, -1} + }, meshes); + Utility::copy({{22, 1}, {1, 5}}, cameras); + + SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { + SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, + SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, + SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, + /* Just to disambiguate this as a 2D scene */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, + }}; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 3; } + UnsignedLong doObjectCount() const override { return 63; } + std::string doObjectName(UnsignedLong id) override { + if(id == 1) return "object 1"; + if(id == 15) return "object 15"; + if(id == 23) return "object 23"; + if(id == 62) return "last"; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + Containers::Optional doScene(UnsignedInt id) override { + /* This scene should get skipped when querying names as it's not + 2D */ + if(id == 0) + return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, {}}; + /* This scene should get skipped when querying names as it has too + little objects */ + if(id == 1) + return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Matrix3x3, nullptr} + }}; + if(id == 2) + return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + + private: + SceneData _data; + } importer{std::move(data)}; + + CORRADE_COMPARE(importer.sceneCount(), 3); + + Containers::Optional scene = importer.scene(2); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + (std::vector{15, 21, 1}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + std::vector{}, + TestSuite::Compare::Container); + + /* Total object count reported by the importer plus four new added */ + CORRADE_COMPARE(importer.object2DCount(), 63 + 4); + CORRADE_COMPARE(importer.object3DCount(), 0); + + /* Object name should return parent names for the additional objects */ + CORRADE_COMPARE(importer.object2DName(62), "last"); + CORRADE_COMPARE(importer.object2DName(63), "object 23"); + CORRADE_COMPARE(importer.object2DName(64), "object 23"); + CORRADE_COMPARE(importer.object2DName(65), "object 15"); + CORRADE_COMPARE(importer.object2DName(66), "object 1"); + + /* Only 9 objects should exist in total, go in order. Usually the object + IDs will be contiguous so no such mess as this happens. */ + { + Containers::Pointer o = importer.object2D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 7); + CORRADE_COMPARE_AS(o->children(), + std::vector{66}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object2D(15); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 6); + CORRADE_COMPARE_AS(o->children(), + std::vector{65}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 4); + } { + Containers::Pointer o = importer.object2D(21); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 5); + CORRADE_COMPARE_AS(o->children(), + std::vector{22}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + } { + Containers::Pointer o = importer.object2D(22); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Camera); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + std::vector{23}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object2D(23); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + (std::vector{63, 64}), + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 0); + } { + Containers::Pointer o = importer.object2D(63); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 2); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 3); + } { + Containers::Pointer o = importer.object2D(64); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 4); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object2D(65); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 3); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 1); + } { + Containers::Pointer o = importer.object2D(66); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Camera); + CORRADE_COMPARE(o->instance(), 5); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { + /* Mostly just a copy of SceneToolsTest::convertToSingleFunctionObjects() + except that here we can't use the convenience combining tool so it's + done by hand */ + + struct Parent { + UnsignedInt object; + Int parent; + }; + struct Mesh { + UnsignedInt object; + UnsignedInt mesh; + Int meshMaterial; + }; + struct Camera { + UnsignedInt object; + UnsignedInt camera; + }; + Containers::StridedArrayView1D parents; + Containers::StridedArrayView1D meshes; + Containers::StridedArrayView1D cameras; + Containers::Array dataData = Containers::ArrayTuple{ + {NoInit, 5, parents}, + {NoInit, 7, meshes}, + {NoInit, 2, cameras}, + }; + + Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); + Utility::copy({ + {15, 6, 4}, + {23, 1, 0}, + {23, 2, 3}, + {23, 4, 2}, + {1, 7, 2}, + {15, 3, 1}, + {21, 5, -1} + }, meshes); + Utility::copy({{22, 1}, {1, 5}}, cameras); + + SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { + SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, + SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, + SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, + /* Just to disambiguate this as a 3D scene */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, + }}; + + struct Importer: AbstractImporter { + explicit Importer(SceneData&& data): _data{std::move(data)} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 3; } + UnsignedLong doObjectCount() const override { return 63; } + std::string doObjectName(UnsignedLong id) override { + if(id == 1) return "object 1"; + if(id == 15) return "object 15"; + if(id == 23) return "object 23"; + if(id == 62) return "last"; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + Containers::Optional doScene(UnsignedInt id) override { + /* This scene should get skipped when querying names as it's not + 2D */ + if(id == 0) + return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, {}}; + /* This scene should get skipped when querying names as it has too + little objects */ + if(id == 1) + return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, { + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Matrix4x4, nullptr} + }}; + if(id == 2) + return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + + private: + SceneData _data; + } importer{std::move(data)}; + + CORRADE_COMPARE(importer.sceneCount(), 3); + + Containers::Optional scene = importer.scene(2); + CORRADE_VERIFY(scene); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene->children3D(), + (std::vector{15, 21, 1}), + TestSuite::Compare::Container); + + /* Total object count reported by the importer plus four new added */ + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 63 + 4); + + /* Object name should return parent names for the additional objects */ + CORRADE_COMPARE(importer.object3DName(62), "last"); + CORRADE_COMPARE(importer.object3DName(63), "object 23"); + CORRADE_COMPARE(importer.object3DName(64), "object 23"); + CORRADE_COMPARE(importer.object3DName(65), "object 15"); + CORRADE_COMPARE(importer.object3DName(66), "object 1"); + + /* Only 9 objects should exist in total, go in order. Usually the object + IDs will be contiguous so no such mess as this happens. */ + { + Containers::Pointer o = importer.object3D(1); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 7); + CORRADE_COMPARE_AS(o->children(), + std::vector{66}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object3D(15); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 6); + CORRADE_COMPARE_AS(o->children(), + std::vector{65}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 4); + } { + Containers::Pointer o = importer.object3D(21); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 5); + CORRADE_COMPARE_AS(o->children(), + std::vector{22}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + } { + Containers::Pointer o = importer.object3D(22); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Camera); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + std::vector{23}, + TestSuite::Compare::Container); + } { + Containers::Pointer o = importer.object3D(23); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + (std::vector{63, 64}), + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 0); + } { + Containers::Pointer o = importer.object3D(63); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 2); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 3); + } { + Containers::Pointer o = importer.object3D(64); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 4); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object3D(65); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 3); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 1); + } { + Containers::Pointer o = importer.object3D(66); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Camera); + CORRADE_COMPARE(o->instance(), 5); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + } + CORRADE_IGNORE_DEPRECATED_POP +} #endif void AbstractImporterTest::sceneNameNotImplemented() { From cb3070f096e952d629e8453e64ab2ba5416265d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 7 Dec 2021 14:14:09 +0100 Subject: [PATCH 29/49] Trade: ensure newly added object IDs don't overlap each other. --- src/Magnum/Trade/AbstractImporter.cpp | 8 +- .../Trade/Test/AbstractImporterTest.cpp | 202 +++++++++++++++--- 2 files changed, 175 insertions(+), 35 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index b000a55a7c..b3a436042e 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -519,6 +519,8 @@ void AbstractImporter::populateCachedScenes() { _cachedScenes.emplace(); _cachedScenes->scenes = Containers::Array>{sceneCount()}; + + UnsignedLong newObjectOffset = objectCount(); for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { _cachedScenes->scenes[i] = scene(i); if(_cachedScenes->scenes[i]) { @@ -529,7 +531,7 @@ void AbstractImporter::populateCachedScenes() { compatibility code path anyway, so just skip the processing altogether in that case. */ if(_cachedScenes->scenes[i]->hasField(SceneField::Parent)) - _cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), objectCount()); + _cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), newObjectOffset); /* Return the 2D/3D object count based on which scenes are 2D and which not. The objectCount() provided by the importer is ignored @@ -539,6 +541,10 @@ void AbstractImporter::populateCachedScenes() { _cachedScenes->object2DCount = Math::max(_cachedScenes->object2DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); if(_cachedScenes->scenes[i]->is3D()) _cachedScenes->object3DCount = Math::max(_cachedScenes->object3DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); + + /* Ensure the newly added objects for each scene don't overlap each + other */ + newObjectOffset = Math::max(newObjectOffset, _cachedScenes->scenes[i]->objectCount()); } } } diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 89e6baffff..ffd4aabc16 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -2708,7 +2708,6 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { {NoInit, 7, meshes}, {NoInit, 2, cameras}, }; - Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); Utility::copy({ {15, 6, 4}, @@ -2721,6 +2720,23 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { }, meshes); Utility::copy({{22, 1}, {1, 5}}, cameras); + /* Second scene that also has a duplicate, to verify the newly added object + IDs don't conflict with each other. A potential downside is that + multi-primitive nodes shared by multiple scenes get duplicated, but + that's a smaller problem than two unrelated nodes sharing the same ID + (and thus having a wrong name, etc). */ + Containers::StridedArrayView1D parentsSecondary; + Containers::StridedArrayView1D meshesSecondary; + Containers::Array dataDataSecondary = Containers::ArrayTuple{ + {NoInit, 1, parentsSecondary}, + {NoInit, 2, meshesSecondary} + }; + Utility::copy({{30, -1}}, parentsSecondary); + Utility::copy({ + {30, 6, 2}, + {30, 1, -1} + }, meshesSecondary); + SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, @@ -2729,20 +2745,28 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { /* Just to disambiguate this as a 2D scene */ SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, }}; + SceneData dataSecondary{SceneObjectType::UnsignedInt, 31, std::move(dataDataSecondary), { + SceneFieldData{SceneField::Parent, parentsSecondary.slice(&Parent::object), parentsSecondary.slice(&Parent::parent)}, + SceneFieldData{SceneField::Mesh, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::meshMaterial)}, + /* Just to disambiguate this as a 2D scene */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, + }}; struct Importer: AbstractImporter { - explicit Importer(SceneData&& data): _data{std::move(data)} {} + explicit Importer(SceneData&& data, SceneData&& dataSecondary): _data{std::move(data)}, _dataSecondary{std::move(dataSecondary)} {} ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doSceneCount() const override { return 3; } + UnsignedInt doSceneCount() const override { return 4; } UnsignedLong doObjectCount() const override { return 63; } std::string doObjectName(UnsignedLong id) override { if(id == 1) return "object 1"; if(id == 15) return "object 15"; if(id == 23) return "object 23"; + if(id == 30) return "object 30 from secondary scene"; if(id == 62) return "last"; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } @@ -2759,14 +2783,33 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { }}; if(id == 2) return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + /* A secondary scene, which should have non-overlapping IDs for the + newly added objects */ + if(id == 3) + return SceneData{SceneObjectType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } private: - SceneData _data; - } importer{std::move(data)}; + SceneData _data, _dataSecondary; + } importer{std::move(data), std::move(dataSecondary)}; - CORRADE_COMPARE(importer.sceneCount(), 3); + CORRADE_COMPARE(importer.sceneCount(), 4); + + CORRADE_IGNORE_DEPRECATED_PUSH + /* Total object count reported by the importer plus four new added for the + first and one for the second scene */ + CORRADE_COMPARE(importer.object2DCount(), 63 + 4 + 1); + CORRADE_COMPARE(importer.object3DCount(), 0); + + /* Object name should return parent names for the additional objects */ + CORRADE_COMPARE(importer.object2DName(62), "last"); + CORRADE_COMPARE(importer.object2DName(63), "object 23"); + CORRADE_COMPARE(importer.object2DName(64), "object 23"); + CORRADE_COMPARE(importer.object2DName(65), "object 15"); + CORRADE_COMPARE(importer.object2DName(66), "object 1"); + CORRADE_COMPARE(importer.object2DName(67), "object 30 from secondary scene"); + CORRADE_IGNORE_DEPRECATED_POP Containers::Optional scene = importer.scene(2); CORRADE_VERIFY(scene); @@ -2779,17 +2822,6 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { std::vector{}, TestSuite::Compare::Container); - /* Total object count reported by the importer plus four new added */ - CORRADE_COMPARE(importer.object2DCount(), 63 + 4); - CORRADE_COMPARE(importer.object3DCount(), 0); - - /* Object name should return parent names for the additional objects */ - CORRADE_COMPARE(importer.object2DName(62), "last"); - CORRADE_COMPARE(importer.object2DName(63), "object 23"); - CORRADE_COMPARE(importer.object2DName(64), "object 23"); - CORRADE_COMPARE(importer.object2DName(65), "object 15"); - CORRADE_COMPARE(importer.object2DName(66), "object 1"); - /* Only 9 objects should exist in total, go in order. Usually the object IDs will be contiguous so no such mess as this happens. */ { @@ -2880,6 +2912,41 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); } CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional sceneSecondary = importer.scene(3); + CORRADE_VERIFY(sceneSecondary); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(sceneSecondary->children2D(), + std::vector{30}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(sceneSecondary->children3D(), + (std::vector{}), + TestSuite::Compare::Container); + + /* One additional duplicated object here */ + { + Containers::Pointer o = importer.object2D(30); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 6); + CORRADE_COMPARE_AS(o->children(), + std::vector{67}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object2D(67); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData2D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + } + CORRADE_IGNORE_DEPRECATED_POP } void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { @@ -2908,7 +2975,6 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { {NoInit, 7, meshes}, {NoInit, 2, cameras}, }; - Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); Utility::copy({ {15, 6, 4}, @@ -2921,6 +2987,23 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { }, meshes); Utility::copy({{22, 1}, {1, 5}}, cameras); + /* Second scene that also has a duplicate, to verify the newly added object + IDs don't conflict with each other. A potential downside is that + multi-primitive nodes shared by multiple scenes get duplicated, but + that's a smaller problem than two unrelated nodes sharing the same ID + (and thus having a wrong name, etc). */ + Containers::StridedArrayView1D parentsSecondary; + Containers::StridedArrayView1D meshesSecondary; + Containers::Array dataDataSecondary = Containers::ArrayTuple{ + {NoInit, 1, parentsSecondary}, + {NoInit, 2, meshesSecondary} + }; + Utility::copy({{30, -1}}, parentsSecondary); + Utility::copy({ + {30, 6, 2}, + {30, 1, -1} + }, meshesSecondary); + SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, @@ -2929,20 +3012,28 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { /* Just to disambiguate this as a 3D scene */ SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, }}; + SceneData dataSecondary{SceneObjectType::UnsignedInt, 31, std::move(dataDataSecondary), { + SceneFieldData{SceneField::Parent, parentsSecondary.slice(&Parent::object), parentsSecondary.slice(&Parent::parent)}, + SceneFieldData{SceneField::Mesh, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::mesh)}, + SceneFieldData{SceneField::MeshMaterial, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::meshMaterial)}, + /* Just to disambiguate this as a 3D scene */ + SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, + }}; struct Importer: AbstractImporter { - explicit Importer(SceneData&& data): _data{std::move(data)} {} + explicit Importer(SceneData&& data, SceneData&& dataSecondary): _data{std::move(data)}, _dataSecondary{std::move(dataSecondary)} {} ImporterFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return true; } void doClose() override {} - UnsignedInt doSceneCount() const override { return 3; } + UnsignedInt doSceneCount() const override { return 4; } UnsignedLong doObjectCount() const override { return 63; } std::string doObjectName(UnsignedLong id) override { if(id == 1) return "object 1"; if(id == 15) return "object 15"; if(id == 23) return "object 23"; + if(id == 30) return "object 30 from secondary scene"; if(id == 62) return "last"; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } @@ -2959,14 +3050,33 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { }}; if(id == 2) return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + /* A secondary scene, which should have non-overlapping IDs for the + newly added objects */ + if(id == 3) + return SceneData{SceneObjectType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } private: - SceneData _data; - } importer{std::move(data)}; + SceneData _data, _dataSecondary; + } importer{std::move(data), std::move(dataSecondary)}; - CORRADE_COMPARE(importer.sceneCount(), 3); + CORRADE_COMPARE(importer.sceneCount(), 4); + + CORRADE_IGNORE_DEPRECATED_PUSH + /* Total object count reported by the importer plus four new added for the + first and one for the second scene */ + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 63 + 4 + 1); + + /* Object name should return parent names for the additional objects */ + CORRADE_COMPARE(importer.object3DName(62), "last"); + CORRADE_COMPARE(importer.object3DName(63), "object 23"); + CORRADE_COMPARE(importer.object3DName(64), "object 23"); + CORRADE_COMPARE(importer.object3DName(65), "object 15"); + CORRADE_COMPARE(importer.object3DName(66), "object 1"); + CORRADE_COMPARE(importer.object3DName(67), "object 30 from secondary scene"); + CORRADE_IGNORE_DEPRECATED_POP Containers::Optional scene = importer.scene(2); CORRADE_VERIFY(scene); @@ -2979,17 +3089,6 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { (std::vector{15, 21, 1}), TestSuite::Compare::Container); - /* Total object count reported by the importer plus four new added */ - CORRADE_COMPARE(importer.object2DCount(), 0); - CORRADE_COMPARE(importer.object3DCount(), 63 + 4); - - /* Object name should return parent names for the additional objects */ - CORRADE_COMPARE(importer.object3DName(62), "last"); - CORRADE_COMPARE(importer.object3DName(63), "object 23"); - CORRADE_COMPARE(importer.object3DName(64), "object 23"); - CORRADE_COMPARE(importer.object3DName(65), "object 15"); - CORRADE_COMPARE(importer.object3DName(66), "object 1"); - /* Only 9 objects should exist in total, go in order. Usually the object IDs will be contiguous so no such mess as this happens. */ { @@ -3080,6 +3179,41 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); } CORRADE_IGNORE_DEPRECATED_POP + + Containers::Optional sceneSecondary = importer.scene(3); + CORRADE_VERIFY(sceneSecondary); + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(sceneSecondary->children2D(), + std::vector{}, + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(sceneSecondary->children3D(), + (std::vector{30}), + TestSuite::Compare::Container); + + /* One additional duplicated object here */ + { + Containers::Pointer o = importer.object3D(30); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 6); + CORRADE_COMPARE_AS(o->children(), + std::vector{67}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), 2); + } { + Containers::Pointer o = importer.object3D(67); + CORRADE_VERIFY(o); + CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(o->instance(), 1); + CORRADE_COMPARE_AS(o->children(), + std::vector{}, + TestSuite::Compare::Container); + MeshObjectData3D& mo = static_cast(*o); + CORRADE_COMPARE(mo.material(), -1); + } + CORRADE_IGNORE_DEPRECATED_POP } #endif From 8ef35d42bb03569d8d8880123f84b752b6c3d460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 14 Oct 2021 15:33:07 +0200 Subject: [PATCH 30/49] Trade: the compatibility default for transformless scenes should be TRS. That's what plugins implemented with the legacy API did, as it's more flexible than a matrix. --- src/Magnum/Trade/AbstractImporter.cpp | 16 +++++++++------ .../Trade/Test/AbstractImporterTest.cpp | 20 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index b3a436042e..2146519d24 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -653,11 +653,13 @@ Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt ObjectFlags2D flags; Containers::Optional transformation = scene.transformation2DFor(id); Containers::Optional> trs = scene.translationRotationScaling2DFor(id); + /* If the object has neither a TRS nor a transformation field, assign an + empty TRS transform. Not a matrix, because a TRS is more flexible and + thus more desired. */ + if(!transformation && !trs) + trs.emplace(Vector2{}, Complex{}, Vector2{1.0f}); if(trs) flags |= ObjectFlag2D::HasTranslationRotationScaling; - /* If the object has neither a TRS nor a transformation field, assign an - identity transform to it */ - else if(!transformation) transformation = Matrix3{}; std::vector children; /* not const so we can move it */ { @@ -825,11 +827,13 @@ Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt ObjectFlags3D flags; Containers::Optional transformation = scene.transformation3DFor(id); Containers::Optional> trs = scene.translationRotationScaling3DFor(id); + /* If the object has neither a TRS nor a transformation field, assign an + empty TRS transform. Not a matrix, because a TRS is more flexible and + thus more desired. */ + if(!transformation && !trs) + trs.emplace(Vector3{}, Quaternion{}, Vector3{1.0f}); if(trs) flags |= ObjectFlag3D::HasTranslationRotationScaling; - /* If the object has neither a TRS nor a transformation field, assign an - identity transform to it */ - else if(!transformation) transformation = Matrix4{}; std::vector children; /* not const so we can move it */ { diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index ffd4aabc16..d7d44c859f 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -2530,13 +2530,15 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { std::vector{}, TestSuite::Compare::Container); + /* If we have neither a matrix nor a TRS, having an identity TRS is better + as it's more flexible compared to a matrix */ { Containers::Pointer o = importer.object2D(5); CORRADE_VERIFY(o); CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix3{}); CORRADE_COMPARE_AS(o->children(), (std::vector{2, 3}), @@ -2547,7 +2549,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix3{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, @@ -2558,7 +2560,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix3{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, @@ -2569,7 +2571,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType2D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags2D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag2D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix3{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, @@ -2634,13 +2636,15 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { (std::vector{5, 1}), TestSuite::Compare::Container); + /* If we have neither a matrix nor a TRS, having an identity TRS is better + as it's more flexible compared to a matrix */ { Containers::Pointer o = importer.object3D(5); CORRADE_VERIFY(o); CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix4{}); CORRADE_COMPARE_AS(o->children(), (std::vector{2, 3}), @@ -2651,7 +2655,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix4{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, @@ -2662,7 +2666,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix4{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, @@ -2673,7 +2677,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { CORRADE_COMPARE(o->importerState(), nullptr); CORRADE_COMPARE(o->instanceType(), ObjectInstanceType3D::Empty); CORRADE_COMPARE(o->instance(), -1); - CORRADE_COMPARE(o->flags(), ObjectFlags3D{}); + CORRADE_COMPARE(o->flags(), ObjectFlag3D::HasTranslationRotationScaling); CORRADE_COMPARE(o->transformation(), Matrix4{}); CORRADE_COMPARE_AS(o->children(), std::vector{}, From 4adabf9c1fe5e337353d09f43a3589c4a2575147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Nov 2021 13:36:50 +0100 Subject: [PATCH 31/49] Trade: warn in deprecated SceneData::children*D() if there's no parent. Even though this API is deprecated and thus not meant to be used, most existing code is still using the previous APIs and relying on the backwards compatibiity interfaces. And I wasted quite some time debugging why the scene looks like empty. --- src/Magnum/Trade/SceneData.cpp | 12 +++++++ src/Magnum/Trade/Test/SceneDataTest.cpp | 45 ++++++++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 1b2028e648..46412f45b2 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -2210,12 +2210,24 @@ Containers::Optional SceneData::importerStateFor(const UnsignedInt #ifdef MAGNUM_BUILD_DEPRECATED std::vector SceneData::children2D() const { if(_dimensions != 2) return {}; + + /* Even though (or exactly because?) this API is deprecated, it's better to + warn than to spend several hours debugging what's wrong */ + if(!hasField(SceneField::Parent)) + Warning{} << "Trade::SceneData::children2D(): no parent field present, returned array will be empty"; + const Containers::Array children = childrenFor(-1); return {children.begin(), children.end()}; } std::vector SceneData::children3D() const { if(_dimensions != 3) return {}; + + /* Even though (or exactly because?) this API is deprecated, it's better to + warn than to spend several hours debugging what's wrong */ + if(!hasField(SceneField::Parent)) + Warning{} << "Trade::SceneData::children3D(): no parent field present, returned array will be empty"; + const Containers::Array children = childrenFor(-1); return {children.begin(), children.end()}; } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 27e8cd0940..1df17838a5 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -218,10 +218,14 @@ const struct { const char* name; bool is2D; bool is3D; + bool skipParent; } ChildrenDeprecatedData[]{ - {"2D", true, false}, - {"3D", false, true}, - {"neither", false, false} + {"2D", true, false, false}, + {"2D with no parent", true, false, true}, + {"3D", false, true, false}, + {"3D with no parent", false, true, true}, + {"neither", false, false, false}, + {"neither with no parent", false, false, true} }; #endif @@ -4978,21 +4982,38 @@ void SceneDataTest::childrenDeprecated() { Containers::StridedArrayView1D view = fields; Containers::Array fieldData; - arrayAppend(fieldData, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}); + if(!data.skipParent) + arrayAppend(fieldData, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}); if(data.is2D) arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector2, nullptr}); if(data.is3D) arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}); SceneData scene{SceneObjectType::UnsignedByte, 25, {}, fields, std::move(fieldData)}; - CORRADE_IGNORE_DEPRECATED_PUSH - CORRADE_COMPARE_AS(scene.children2D(), - (data.is2D ? std::vector{5, 0, 4} : std::vector{}), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.children3D(), - (data.is3D ? std::vector{5, 0, 4} : std::vector{}), - TestSuite::Compare::Container); - CORRADE_IGNORE_DEPRECATED_POP + + if(!data.skipParent) { + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE_AS(scene.children2D(), + (data.is2D ? std::vector{5, 0, 4} : std::vector{}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.children3D(), + (data.is3D ? std::vector{5, 0, 4} : std::vector{}), + TestSuite::Compare::Container); + CORRADE_IGNORE_DEPRECATED_POP + } else { + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_VERIFY(scene.children2D().empty()); + CORRADE_VERIFY(scene.children3D().empty()); + CORRADE_IGNORE_DEPRECATED_POP + if(data.is2D) + CORRADE_COMPARE(out.str(), "Trade::SceneData::children2D(): no parent field present, returned array will be empty\n"); + else if(data.is3D) + CORRADE_COMPARE(out.str(), "Trade::SceneData::children3D(): no parent field present, returned array will be empty\n"); + else + CORRADE_COMPARE(out.str(), ""); + } } #endif From e056ad3c99452bcede5b6b3abf2cf5e9140edca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Nov 2021 20:34:35 +0100 Subject: [PATCH 32/49] Trade: allow SceneField::Transformation to be a 3x2 or a 4x3 matrix. Another thought I had was about allowing a 2x2 / 3x3 matrix to be used for rotation, but there's the ambiguity with it possibly containing scaling / shear / whatever, which would then cause EXPLODING HEADACHES when converting to quaternions / complex numbers. --- src/Magnum/Trade/SceneData.cpp | 24 +++++++++ src/Magnum/Trade/SceneData.h | 10 ++++ src/Magnum/Trade/Test/SceneDataTest.cpp | 68 +++++++++++++++++++------ 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 46412f45b2..44b9c685be 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -644,11 +644,15 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object const SceneFieldType fieldType = _fields[transformationField]._fieldType; if(fieldType == SceneFieldType::Matrix3x3 || fieldType == SceneFieldType::Matrix3x3d || + fieldType == SceneFieldType::Matrix3x2 || + fieldType == SceneFieldType::Matrix3x2d || fieldType == SceneFieldType::DualComplex || fieldType == SceneFieldType::DualComplexd) _dimensions = 2; else if(fieldType == SceneFieldType::Matrix4x4 || fieldType == SceneFieldType::Matrix4x4d || + fieldType == SceneFieldType::Matrix4x3 || + fieldType == SceneFieldType::Matrix4x3d || fieldType == SceneFieldType::DualQuaternion || fieldType == SceneFieldType::DualQuaterniond) _dimensions = 3; @@ -1200,6 +1204,18 @@ template void convertTransformation(const Conta destination[i] = Destination{sourceT[i].toMatrix()}; } +/* Expands 2x3 to 3x3, 4x3 to 4x4, while potentially also converting floats to + doubles */ +/** @todo also have some optimized batched API for this? doing a strided copy + and a fill seemed silly as that would cause the whole memory go through + cache twice */ +template void expandTransformationMatrix(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination{Math::RectangularMatrix{sourceT[i]}}; +} + /** @todo these (or the float variants at least) should eventually be replaced with optimized batched APIs (applyTranslationsInto() updating just the last matrix column etc.) */ @@ -1299,6 +1315,10 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi Utility::copy(Containers::arrayCast(fieldData), destination); } else if(field._fieldType == SceneFieldType::Matrix3x3d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 9), destination1f); + } else if(field._fieldType == SceneFieldType::Matrix3x2) { + expandTransformationMatrix(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix3x2d) { + expandTransformationMatrix(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualComplex) { convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualComplexd) { @@ -1525,6 +1545,10 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi Utility::copy(Containers::arrayCast(fieldData), destination); } else if(field._fieldType == SceneFieldType::Matrix4x4d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 16), destination1f); + } else if(field._fieldType == SceneFieldType::Matrix4x3) { + expandTransformationMatrix(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix4x3d) { + expandTransformationMatrix(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualQuaternion) { convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualQuaterniond) { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index ea8d697e7c..9bb087d179 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -127,9 +127,15 @@ enum class SceneField: UnsignedInt { * Transformation. Type is usually @ref SceneFieldType::Matrix3x3 for 2D * and @ref SceneFieldType::Matrix4x4 for 3D, but can be also any of * @relativeref{SceneFieldType,Matrix3x3d}, + * @relativeref{SceneFieldType,Matrix3x2} or + * @relativeref{SceneFieldType,Matrix3x2d} (with the bottom row implicitly + * assumed to be @f$ \begin{pmatrix} 0 & 0 & 1 \end{pmatrix} @f$), * @relativeref{SceneFieldType,DualComplex} or * @relativeref{SceneFieldType,DualComplexd} for 2D and * @relativeref{SceneFieldType,Matrix4x4d}, + * @relativeref{SceneFieldType,Matrix4x3} or + * @relativeref{SceneFieldType,Matrix4x3d} (with the bottom row implicitly + * assumed to be @f$ \begin{pmatrix} 0 & 0 & 0 & 1 \end{pmatrix} @f$), * @relativeref{SceneFieldType,DualQuaternion} or * @relativeref{SceneFieldType,DualQuaterniond} for 3D. An object should * have only one transformation, altough this isn't enforced in any way, @@ -2490,6 +2496,10 @@ namespace Implementation { type == SceneFieldType::Matrix3x3d || type == SceneFieldType::Matrix4x4 || type == SceneFieldType::Matrix4x4d || + type == SceneFieldType::Matrix3x2 || + type == SceneFieldType::Matrix3x2d || + type == SceneFieldType::Matrix4x3 || + type == SceneFieldType::Matrix4x3d || type == SceneFieldType::DualComplex || type == SceneFieldType::DualComplexd || type == SceneFieldType::DualQuaternion || diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 1df17838a5..b29000a8e0 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -335,6 +335,8 @@ SceneDataTest::SceneDataTest() { addTests({&SceneDataTest::parentsIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArrayTRS, @@ -349,6 +351,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArrayTRS, @@ -1779,8 +1783,12 @@ _c(Vector3) _c(Vector3d) _c(Matrix3) _c(Matrix3d) +_c(Matrix3x2) +_c(Matrix3x2d) _c(Matrix4) _c(Matrix4d) +_c(Matrix4x3) +_c(Matrix4x3d) _c(Complex) _c(Complexd) _c(Quaternion) @@ -1805,8 +1813,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { #endif SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformations2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformations3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData translations2D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData translations3D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; @@ -1821,6 +1831,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, translations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, rotations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, translations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, rotations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, translations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, rotations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, scalings3D}}; @@ -1831,6 +1845,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, translations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, rotations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, translations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, rotations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, translations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, rotations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, scalings2D}}; @@ -1841,6 +1859,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" @@ -1851,6 +1873,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" @@ -2429,10 +2455,21 @@ void SceneDataTest::parentsIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::parentsInto(): offset 4 out of bounds for a field of size 3\n"); } +template struct TransformationTypeFor { + typedef T Type; +}; +template struct TransformationTypeFor> { + typedef Math::Matrix3 Type; +}; +template struct TransformationTypeFor> { + typedef Math::Matrix4 Type; +}; + template void SceneDataTest::transformations2DAsArray() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type U; + typedef typename TransformationTypeFor::Type TT; struct Transformation { UnsignedInt object; @@ -2451,12 +2488,12 @@ template void SceneDataTest::transformations2DAsArray() { {NoInit, 4, transformations}, {NoInit, 2, components} }; - transformations[0] = {1, T::translation({U(3.0), U(2.0)})}; - transformations[1] = {0, T::rotation(Math::Deg(35.0))}; - transformations[2] = {4, T::translation({U(1.5), U(2.5)})* - T::rotation(Math::Deg(-15.0))}; - transformations[3] = {5, T::rotation(Math::Deg(-15.0))* - T::translation({U(1.5), U(2.5)})}; + transformations[0] = {1, T{TT::translation({U(3.0), U(2.0)})}}; + transformations[1] = {0, T{TT::rotation(Math::Deg(35.0))}}; + transformations[2] = {4, T{TT::translation({U(1.5), U(2.5)})* + TT::rotation(Math::Deg(-15.0))}}; + transformations[3] = {5, T{TT::rotation(Math::Deg(-15.0))* + TT::translation({U(1.5), U(2.5)})}}; /* Object number 4 additionally has a scaling component (which isn't representable with dual complex numbers). It currently doesn't get added to the transformations returned from transformations2DInto() but that @@ -2980,6 +3017,7 @@ template void SceneDataTest::transformations3DAsArray() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type U; + typedef typename TransformationTypeFor::Type TT; struct Transformation { UnsignedInt object; @@ -2998,15 +3036,15 @@ template void SceneDataTest::transformations3DAsArray() { {NoInit, 4, transformations}, {NoInit, 2, components} }; - transformations[0] = {1, T::translation({U(3.0), U(2.0), U(-0.5)})}; - transformations[1] = {0, T::rotation(Math::Deg(35.0), - Math::Vector3::yAxis())}; - transformations[2] = {4, T::translation({U(1.5), U(2.5), U(0.75)})* - T::rotation(Math::Deg(-15.0), - Math::Vector3::xAxis())}; - transformations[3] = {5, T::rotation(Math::Deg(-15.0), - Math::Vector3::xAxis())* - T::translation({U(1.5), U(2.5), U(0.75)})}; + transformations[0] = {1, T{TT::translation({U(3.0), U(2.0), U(-0.5)})}}; + transformations[1] = {0, T{TT::rotation(Math::Deg(35.0), + Math::Vector3::yAxis())}}; + transformations[2] = {4, T{TT::translation({U(1.5), U(2.5), U(0.75)})* + TT::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())}}; + transformations[3] = {5, T{TT::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())* + TT::translation({U(1.5), U(2.5), U(0.75)})}}; /* Object number 4 additionally has a scaling component (which isn't representable with dual quaternions). It currently doesn't get added to the transformations returned from transformations2DInto() but that From 0366919199995c6deee0c07429d1b303525af0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Nov 2021 21:34:02 +0100 Subject: [PATCH 33/49] Trade: test SceneData::{parentFor,childrenFor}() with trivial parents. Hmm, doesn't look right. A bug somewhere! --- src/Magnum/Trade/Test/SceneDataTest.cpp | 61 +++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index b29000a8e0..2e87b43ef1 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -169,7 +169,9 @@ struct SceneDataTest: TestSuite::Tester { void fieldWrongArrayAccess(); void parentFor(); + void parentForTrivialParent(); void childrenFor(); + void childrenForTrivialParent(); void transformation2DFor(); void transformation2DForTRS(); void transformation2DForBut3DType(); @@ -416,7 +418,9 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldWrongArrayAccess, &SceneDataTest::parentFor, + &SceneDataTest::parentForTrivialParent, &SceneDataTest::childrenFor, + &SceneDataTest::childrenForTrivialParent, &SceneDataTest::transformation2DFor, &SceneDataTest::transformation2DForTRS, &SceneDataTest::transformation2DForBut3DType, @@ -4595,6 +4599,32 @@ void SceneDataTest::parentFor() { CORRADE_COMPARE(scene.parentFor(1), Containers::NullOpt); } +void SceneDataTest::parentForTrivialParent() { + /* Going a bit overboard with the arrays, it makes the view creation below + easier tho */ + struct Field { + UnsignedInt object[4]; + Int parent[1]; + } fields[]{{ + {3, 4, 2, 4 /* duplicate, ignored */}, {-1} + }}; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Parent, + Containers::stridedArrayView(fields->object), Containers::stridedArrayView(fields->parent).broadcasted<0>(4)} + }}; + + CORRADE_COMPARE(scene.parentFor(2), -1); + CORRADE_COMPARE(scene.parentFor(3), -1); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.parentFor(4), -1); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.parentFor(1), Containers::NullOpt); +} + void SceneDataTest::childrenFor() { struct Field { UnsignedInt object; @@ -4637,6 +4667,37 @@ void SceneDataTest::childrenFor() { TestSuite::Compare::Container); } +void SceneDataTest::childrenForTrivialParent() { + /* Going a bit overboard with the arrays, it makes the view creation below + easier tho */ + struct Field { + UnsignedInt object[4]; + Int parent[1]; + } fields[]{{ + {3, 4, 2, 4 /* duplicate, gets put to the output */}, {-1} + }}; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::Parent, + Containers::stridedArrayView(fields->object), Containers::stridedArrayView(fields->parent).broadcasted<0>(4)} + }}; + + /* Trivial children */ + CORRADE_COMPARE_AS(scene.childrenFor(-1), + Containers::arrayView({3, 4, 2, 4}), + TestSuite::Compare::Container); + + /* Object that is present in the parent array but has no children */ + CORRADE_COMPARE_AS(scene.childrenFor(4), + Containers::arrayView({}), + TestSuite::Compare::Container); + + /* Object that is not in the parent array */ + CORRADE_COMPARE_AS(scene.childrenFor(5), + Containers::arrayView({}), + TestSuite::Compare::Container); +} + void SceneDataTest::transformation2DFor() { const struct Field { UnsignedInt object; From 605d72c86c94a09deae58e938474469d30571c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Nov 2021 21:47:45 +0100 Subject: [PATCH 34/49] Trade: fix SceneData object view retrieval & test it more thoroughly. --- src/Magnum/Trade/SceneData.cpp | 2 +- src/Magnum/Trade/Test/SceneDataTest.cpp | 50 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 44b9c685be..de9d247c34 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -782,7 +782,7 @@ Containers::StridedArrayView1D SceneData::fieldDataObjectViewInterna /* We're *sure* the view is correct, so faking the view size */ {static_cast(field._isOffsetOnly ? _data.data() + field._objectData.offset : field._objectData.pointer) - + field._fieldStride*offset, ~std::size_t{}}, + + field._objectStride*offset, ~std::size_t{}}, size, field._objectStride}; } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 2e87b43ef1..57e20c4959 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -90,6 +90,7 @@ struct SceneDataTest: TestSuite::Tester { void construct(); void constructZeroFields(); void constructZeroObjects(); + void constructSpecialStrides(); void constructNotOwned(); #ifdef MAGNUM_BUILD_DEPRECATED void constructDeprecated(); @@ -271,7 +272,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::construct, &SceneDataTest::constructZeroFields, - &SceneDataTest::constructZeroObjects}); + &SceneDataTest::constructZeroObjects, + &SceneDataTest::constructSpecialStrides}); addInstancedTests({&SceneDataTest::constructNotOwned}, Containers::arraySize(NotOwnedData)); @@ -1446,6 +1448,52 @@ void SceneDataTest::constructZeroObjects() { CORRADE_COMPARE(scene.objects(SceneField::MeshMaterial).data(), nullptr); } +void SceneDataTest::constructSpecialStrides() { + Containers::StridedArrayView1D broadcastedData; + Containers::StridedArrayView1D nonBroadcastedData; + Containers::ArrayTuple data{ + {NoInit, 1, broadcastedData}, + {NoInit, 4, nonBroadcastedData} + }; + + broadcastedData[0] = 15; + nonBroadcastedData[0] = 1; + nonBroadcastedData[1] = 2; + nonBroadcastedData[2] = 3; + nonBroadcastedData[3] = 4; + + SceneFieldData broadcastedObjects{sceneFieldCustom(38), + broadcastedData.broadcasted<0>(4), nonBroadcastedData}; + SceneFieldData broadcastedFields{sceneFieldCustom(39), + nonBroadcastedData, broadcastedData.broadcasted<0>(4)}; + SceneFieldData flippedFields{sceneFieldCustom(40), + nonBroadcastedData.flipped<0>(), nonBroadcastedData.flipped<0>()}; + SceneData scene{SceneObjectType::UnsignedShort, 8, std::move(data), { + broadcastedObjects, broadcastedFields, flippedFields + }}; + + CORRADE_COMPARE_AS(scene.objects(0), + Containers::arrayView({15, 15, 15, 15}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(0), + Containers::arrayView({1, 2, 3, 4}), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(scene.objects(1), + Containers::arrayView({1, 2, 3, 4}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(1), + Containers::arrayView({15, 15, 15, 15}), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(scene.objects(2), + Containers::arrayView({4, 3, 2, 1}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(2), + Containers::arrayView({4, 3, 2, 1}), + TestSuite::Compare::Container); +} + void SceneDataTest::constructNotOwned() { auto&& instanceData = NotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); From 6fe0dd1078f654ad4e031694244bf6745abb5c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Nov 2021 19:56:03 +0100 Subject: [PATCH 35/49] Trade: report at least some *D object count if everything fails. As the comment says -- before the user code expected that if the scene hierarchy is broken, particular objects will fail to import. Now the whole scene fails to import, so we don't even get to know the actual (expanded) deprecated 2D/3D object count. To reduce the suffering, return at least the dimension-less object count there. It won't include the duplicates from the single-function-object conversion but better than reporting 0. --- src/Magnum/Trade/AbstractImporter.cpp | 11 +++++ .../Trade/Test/AbstractImporterTest.cpp | 43 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 2146519d24..cda75437ee 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -547,6 +547,17 @@ void AbstractImporter::populateCachedScenes() { newObjectOffset = Math::max(newObjectOffset, _cachedScenes->scenes[i]->objectCount()); } } + + /* If there are scenes but no objects (because for example all scenes + failed to import), use the dimension-less object count at least, and + assume the scene was 3D. Otherwise this may cause unexpected assertions + in code that expected proper object count to be reported even if a scene + contains errors. + + Not ideal, especially regarding the 3D assumption, but better than + nothing. */ + if(!_cachedScenes->scenes.empty() && !_cachedScenes->object2DCount && !_cachedScenes->object3DCount) + _cachedScenes->object3DCount = objectCount(); } UnsignedInt AbstractImporter::object2DCount() const { diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index d7d44c859f..627ba15a98 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -126,6 +126,8 @@ struct AbstractImporterTest: TestSuite::Tester { void sceneDeprecatedFallbackTransformless3D(); void sceneDeprecatedFallbackMultiFunctionObjects2D(); void sceneDeprecatedFallbackMultiFunctionObjects3D(); + void sceneDeprecatedFallbackObjectCountNoScenes(); + void sceneDeprecatedFallbackObjectCountAllSceneImportFailed(); #endif void sceneNameNotImplemented(); void objectNameNotImplemented(); @@ -410,6 +412,8 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::sceneDeprecatedFallbackTransformless3D, &AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D, &AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D, + &AbstractImporterTest::sceneDeprecatedFallbackObjectCountNoScenes, + &AbstractImporterTest::sceneDeprecatedFallbackObjectCountAllSceneImportFailed, #endif &AbstractImporterTest::sceneForNameOutOfRange, &AbstractImporterTest::objectForNameOutOfRange, @@ -3219,6 +3223,45 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { } CORRADE_IGNORE_DEPRECATED_POP } + +void AbstractImporterTest::sceneDeprecatedFallbackObjectCountNoScenes() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 0; } + UnsignedLong doObjectCount() const override { return 27; } + } importer; + + /* There's no scenes to get data or hierarchy from, so there are no + 2D/3D objects reported even though objectCount() says 27 */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 0); + CORRADE_IGNORE_DEPRECATED_POP +} + +void AbstractImporterTest::sceneDeprecatedFallbackObjectCountAllSceneImportFailed() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedLong doObjectCount() const override { return 27; } + Containers::Optional doScene(UnsignedInt) override { + return {}; + } + } importer; + + /* There's a scene but it failed to import, assume it was 3D and proxy the + objectCount() */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 0); + CORRADE_COMPARE(importer.object3DCount(), 27); + CORRADE_IGNORE_DEPRECATED_POP +} #endif void AbstractImporterTest::sceneNameNotImplemented() { From e0893f87ac610b0fd4cb1d5450431e95cd1c86d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 9 Nov 2021 19:59:26 +0100 Subject: [PATCH 36/49] Trade: another case for the deprecated path not being 100% compatible. Honestly I don't care much, this is just that the original PrimitiveImporter tests started to fail because they expected object3DForName() to return -1 for a 2D name, but that's no longer the case. It would be possible to fix this, but I doubt anyone ever relied on such behavior for 2D scenes, so just add a test that acknowledges this new behavior. --- src/Magnum/Trade/AbstractImporter.cpp | 12 +++- .../Trade/Test/AbstractImporterTest.cpp | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index cda75437ee..ec15b7ef21 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -589,7 +589,11 @@ Int AbstractImporter::object2DForName(const std::string& name) { Int AbstractImporter::doObject2DForName(const std::string& name) { /* Alias to the new interface. If it returns an ID that's larger than reported 2D object count, then it's probably for a 3D object instead - -- ignore it in that case. */ + -- ignore it in that case. Ideally this would be solved by checking if + the ID is actually present in a 2D scene (and same in doObject2DName()) + but that's a lot of extra code for just a backwards compatibility + feature that almost nobody needs. The only pre-existing 2D importer is + PrimitiveImporter, which is rarely used. */ const Long id = doObjectForName(name); CORRADE_IGNORE_DEPRECATED_PUSH return id < doObject2DCount() ? id : -1; @@ -763,7 +767,11 @@ Int AbstractImporter::object3DForName(const std::string& name) { Int AbstractImporter::doObject3DForName(const std::string& name) { /* Alias to the new interface. If it returns an ID that's larger than reported 3D object count, then it's probably for a 2D object instead - -- ignore it in that case. */ + -- ignore it in that case. Ideally this would be solved by checking if + the ID is actually present in a 3D scene (and same in doObject3DName()) + but that's a lot of extra code for just a backwards compatibility + feature that almost nobody needs. The only pre-existing 2D importer is + PrimitiveImporter, which is rarely used. */ const Long id = doObjectForName(name); CORRADE_IGNORE_DEPRECATED_PUSH return id < doObject3DCount() ? id : -1; diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 627ba15a98..d549cf7db0 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -128,6 +128,7 @@ struct AbstractImporterTest: TestSuite::Tester { void sceneDeprecatedFallbackMultiFunctionObjects3D(); void sceneDeprecatedFallbackObjectCountNoScenes(); void sceneDeprecatedFallbackObjectCountAllSceneImportFailed(); + void sceneDeprecatedFallbackBoth2DAnd3DScene(); #endif void sceneNameNotImplemented(); void objectNameNotImplemented(); @@ -414,6 +415,7 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D, &AbstractImporterTest::sceneDeprecatedFallbackObjectCountNoScenes, &AbstractImporterTest::sceneDeprecatedFallbackObjectCountAllSceneImportFailed, + &AbstractImporterTest::sceneDeprecatedFallbackBoth2DAnd3DScene, #endif &AbstractImporterTest::sceneForNameOutOfRange, &AbstractImporterTest::objectForNameOutOfRange, @@ -3262,6 +3264,60 @@ void AbstractImporterTest::sceneDeprecatedFallbackObjectCountAllSceneImportFaile CORRADE_COMPARE(importer.object3DCount(), 27); CORRADE_IGNORE_DEPRECATED_POP } + +void AbstractImporterTest::sceneDeprecatedFallbackBoth2DAnd3DScene() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 2; } + UnsignedLong doObjectCount() const override { return 7; } + Long doObjectForName(const std::string& name) override { + if(name == "sixth") return 5; + return -1; + } + std::string doObjectName(UnsignedLong id) override { + if(id == 5) return "sixth"; + return {}; + } + Containers::Optional doScene(UnsignedInt id) override { + if(id == 0) return SceneData{SceneObjectType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr}, + }}; + if(id == 1) return SceneData{SceneObjectType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + }}; + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + } importer; + + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DCount(), 7); + CORRADE_COMPARE(importer.object3DCount(), 7); + CORRADE_IGNORE_DEPRECATED_POP + + { + CORRADE_EXPECT_FAIL("No check for whether given object is 2D or 3D is done, so the names are reported for both 2D and 3D objects."); + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DForName("sixth"), -1); + CORRADE_COMPARE(importer.object2DName(5), ""); + CORRADE_COMPARE(importer.object3DForName("sixth"), -1); + CORRADE_COMPARE(importer.object3DName(5), ""); + CORRADE_IGNORE_DEPRECATED_POP + } { + /* Just to be sure, verify that the names get really reported for both + instead of some other weird shit happening */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(importer.object2DForName("sixth"), 5); + CORRADE_COMPARE(importer.object2DName(5), "sixth"); + CORRADE_COMPARE(importer.object3DForName("sixth"), 5); + CORRADE_COMPARE(importer.object3DName(5), "sixth"); + CORRADE_IGNORE_DEPRECATED_POP + } +} #endif void AbstractImporterTest::sceneNameNotImplemented() { From 98e023b0b582d0a675765f63277a5fe58b461719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Nov 2021 23:48:46 +0100 Subject: [PATCH 37/49] Trade: GCC 4.8, hello! --- src/Magnum/Trade/Test/SceneDataTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 57e20c4959..ef0e6a78eb 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -3006,7 +3006,7 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset() { struct Field { UnsignedInt object; Matrix3 transformation; - } fields[3]{}; + } fields[3]; /* GCC 4.8 ICEs if I do a {} here */ Containers::StridedArrayView1D view = fields; @@ -3032,7 +3032,7 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { struct Field { UnsignedInt object; Vector2 translation; - } fields[3]{}; + } fields[3]; /* GCC 4.8 ICEs if I do a {} here */ Containers::StridedArrayView1D view = fields; @@ -3557,7 +3557,7 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { struct Field { UnsignedInt object; Matrix4 transformation; - } fields[3]{}; + } fields[3]; /* GCC 4.8 ICEs if I do a {} here */ Containers::StridedArrayView1D view = fields; @@ -3583,7 +3583,7 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { struct Field { UnsignedInt object; Vector2 translation; - } fields[3]{}; + } fields[3]; /* GCC 4.8 ICEs if I do a {} here */ Containers::StridedArrayView1D view = fields; From 2b409ee0bad4205bb1227196eabcad510f4554f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Nov 2021 12:47:16 +0100 Subject: [PATCH 38/49] Trade: drop assertions for 64-bit object/parent values from SceneData. This has to be solved on a more systematic level, perhaps even by switching all types to be 64-bit. In the following commit all *AsArray() and *Int() functions will output the object IDs as well, meaning this would need to be handled in each and every API, which is a huge maintenance burden. As it's very unlikely that there actually will *ever* be >4G objects, one possible option would be to introduce some "object ID hash" field that would provide (contiguous?) remapping of the object ID to 32-bit values, and the Into() and AsArray() accessors would return this remapping instead of the original. But then again it'd cause issues with for example animation references that would still reference the original 64-bit value. --- src/Magnum/Trade/SceneData.cpp | 2 - src/Magnum/Trade/SceneData.h | 28 +--------- src/Magnum/Trade/Test/SceneDataTest.cpp | 68 +------------------------ 3 files changed, 4 insertions(+), 94 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index de9d247c34..4b1c6b34ec 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -1088,7 +1088,6 @@ void SceneData::objectsIntoInternal(const UnsignedInt fieldId, const std::size_t else if(field._objectType == SceneObjectType::UnsignedByte) Math::castInto(Containers::arrayCast<2, const UnsignedByte>(objectData, 1), destination1ui); else if(field._objectType == SceneObjectType::UnsignedLong) { - CORRADE_ASSERT(_objectCount <= 0xffffffffull, "Trade::SceneData::objectsInto(): indices for up to" << _objectCount << "objects can't fit into a 32-bit type, access them directly via objects() instead", ); Math::castInto(Containers::arrayCast<2, const UnsignedLong>(objectData, 1), destination1ui); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -1159,7 +1158,6 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t else if(field._fieldType == SceneFieldType::Byte) Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1i); else if(field._fieldType == SceneFieldType::Long) { - CORRADE_ASSERT(field._size <= 0xffffffffull, "Trade::SceneData::parentsInto(): parent indices for up to" << field._size << "objects can't fit into a 32-bit type, access them directly via field() instead", ); Math::castInto(Containers::arrayCast<2, const Long>(fieldData, 1), destination1i); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 9bb087d179..8e077cb545 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -60,16 +60,7 @@ enum class SceneObjectType: UnsignedByte { UnsignedByte = 1, /**< @relativeref{Magnum,UnsignedByte} */ UnsignedShort, /**< @relativeref{Magnum,UnsignedShort} */ UnsignedInt, /**< @relativeref{Magnum,UnsignedInt} */ - - /** - * @relativeref{Magnum,UnsignedLong}. Meant to be used only in rare cases - * for *really huge* scenes. If this type is used and - * @ref SceneData::objectCount() is larger than the max representable - * 32-bit value, the indices can't be retrieved using - * @ref SceneData::objectsAsArray(), but only using appropriately typed - * @ref SceneData::objects(). - */ - UnsignedLong + UnsignedLong /**< @relativeref{Magnum,UnsignedLong} */ }; /** @@ -109,7 +100,7 @@ enum class SceneField: UnsignedInt { /** * Parent index. Type is usually @ref SceneFieldType::Int, but can be also * any of @relativeref{SceneFieldType,Byte}, - * @relativeref{SceneFieldType,Short} or, rarely, a + * @relativeref{SceneFieldType,Short} or a * @relativeref{SceneFieldType,Long}. A value of @cpp -1 @ce means there's * no parent. An object should have only one parent, altough this isn't * enforced in any way, and which of the duplicate fields gets used is not @@ -1483,11 +1474,6 @@ 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(). - * @attention In the rare case when @ref objectType() is - * @ref SceneObjectType::UnsignedLong and @ref objectCount() is - * larger than the max representable 32-bit value, this function - * can't be used, only an appropriately typed - * @ref objects(UnsignedInt) const. * @see @ref objectsInto(UnsignedInt, const Containers::StridedArrayView1D&) const */ Containers::Array objectsAsArray(UnsignedInt fieldId) const; @@ -1525,11 +1511,6 @@ 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. - * @attention In the rare case when @ref objectType() is - * @ref SceneObjectType::UnsignedLong and @ref objectCount() is - * larger than the max representable 32-bit value, this function - * can't be used, only an appropriately typed - * @ref objects(SceneField) const. * @see @ref objectsInto(SceneField, const Containers::StridedArrayView1D&) const, * @ref hasField() */ @@ -1568,11 +1549,6 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref SceneField::Parent 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. - * @attention In the rare case when @ref fieldType(SceneField) const is - * @ref SceneFieldType::Long and @ref fieldSize(SceneField) const - * is larger than the max representable 32-bit value, this - * function can't be used, only an appropriately typed - * @ref field(SceneField) const. * @see @ref parentsInto(), @ref hasField(), @ref parentFor(), * @ref childrenFor() */ diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index ef0e6a78eb..f0af111d24 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -119,14 +119,10 @@ struct SceneDataTest: TestSuite::Tester { template void objectsAsArrayByIndex(); template void objectsAsArrayByName(); - void objectsAsArrayLongType(); void objectsIntoArrayByIndex(); void objectsIntoArrayByName(); void objectsIntoArrayInvalidSizeOrOffset(); template void parentsAsArray(); - #ifndef CORRADE_TARGET_32BIT - void parentsAsArrayLongType(); - #endif void parentsIntoArray(); void parentsIntoArrayInvalidSizeOrOffset(); template void transformations2DAsArray(); @@ -316,8 +312,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::objectsAsArrayByName, &SceneDataTest::objectsAsArrayByName, &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayLongType}); + &SceneDataTest::objectsAsArrayByName}); addInstancedTests({&SceneDataTest::objectsIntoArrayByIndex, &SceneDataTest::objectsIntoArrayByName}, @@ -327,11 +322,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, - &SceneDataTest::parentsAsArray, - #ifndef CORRADE_TARGET_32BIT - &SceneDataTest::parentsAsArrayLongType, - #endif - }); + &SceneDataTest::parentsAsArray}); addInstancedTests({&SceneDataTest::parentsIntoArray}, Containers::arraySize(IntoArrayOffsetData)); @@ -2233,33 +2224,6 @@ template void SceneDataTest::objectsAsArrayByName() { TestSuite::Compare::Container); } -void SceneDataTest::objectsAsArrayLongType() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - struct Field { - UnsignedLong object; - UnsignedByte mesh; - } fields[3]{}; - - Containers::StridedArrayView1D view = fields; - - SceneData scene{SceneObjectType::UnsignedLong, 0x100000000ull, {}, fields, { - SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} - }}; - - /* AsArray calls into IntoArray, which then has the assert, so this tests - both */ - std::ostringstream out; - Error redirectError{&out}; - scene.objectsAsArray(0); - scene.objectsAsArray(SceneField::Mesh); - CORRADE_COMPARE(out.str(), - "Trade::SceneData::objectsInto(): indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via objects() instead\n" - "Trade::SceneData::objectsInto(): indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via objects() instead\n"); -} - void SceneDataTest::objectsIntoArrayByIndex() { auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -2407,34 +2371,6 @@ template void SceneDataTest::parentsAsArray() { TestSuite::Compare::Container); } -#ifndef CORRADE_TARGET_32BIT -void SceneDataTest::parentsAsArrayLongType() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - struct Field { - UnsignedLong object; - Long parent; - }; - - Containers::Array data{nullptr, 0x100000000ull*sizeof(Field), [](char*, std::size_t) {}}; - Containers::StridedArrayView1D view = Containers::arrayCast(data); - - SceneData scene{SceneObjectType::UnsignedLong, 0x100000000ull, std::move(data), { - SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} - }}; - - /* AsArray calls into IntoArray, which then has the assert, so this tests - both */ - std::ostringstream out; - Error redirectError{&out}; - scene.parentsAsArray(); - CORRADE_COMPARE(out.str(), - "Trade::SceneData::parentsInto(): parent indices for up to 4294967296 objects can't fit into a 32-bit type, access them directly via field() instead\n"); -} -#endif - void SceneDataTest::parentsIntoArray() { auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; setTestCaseDescription(data.name); 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 39/49] 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 3d7eca5379..98fbd95f8c 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 db41c13732..fb032db54c 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 4b1c6b34ec..88cc20aac4 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 8e077cb545..b9b6d02beb 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 f0af111d24..57f4da2e43 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 983ac8006e..5fed43146e 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); } }}}} From 19c055d6508586ea847fe305bdea6b0752205513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 26 Nov 2021 20:43:51 +0100 Subject: [PATCH 40/49] Trade: remove the useless indirection from SceneField::Parent. This got originally added as some sort of a kludge to make it easy to go to the parent transformation, assuming Parent and Transformation share the same object mapping: parentTransformation = transformations[parents[i]] But after some ACTUAL REAL WORLD use, I realized that there's often a set of objects that have a Parent defined, and then another, completely disjoint, set of objects that have a transformation (for example certain nodes having no transformation at all because it's an identity). And so this parent indirection is not only useless, but in fact an additional complication. Let's say we make a map of the transformations, where transformationMap[i] is a transformation for object i: transformationMap = {} for j in range(len(transformations)): transformationMap[transformationObject[j]] = transformation[j] Then, with *no* assumptions about shared object mapping, the indirection would cause parent transformation retrieval to look like this: parentTransformation = transformationMap[parentObjects[parents[i]] While *without* the indirection, it'd be just parentTransformation = transformationMap[parents[i]] --- src/Magnum/Trade/Implementation/sceneTools.h | 5 +-- src/Magnum/Trade/SceneData.cpp | 20 ++------- src/Magnum/Trade/SceneData.h | 6 +-- .../Trade/Test/AbstractImporterTest.cpp | 44 +++++++++---------- src/Magnum/Trade/Test/SceneDataTest.cpp | 12 ++--- src/Magnum/Trade/Test/SceneToolsTest.cpp | 16 +++---- 6 files changed, 42 insertions(+), 61 deletions(-) diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index fb032db54c..45d451d5fb 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -285,9 +285,8 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con attached, then attach the field to a new object and make that new object a child of the previous one. */ if(fieldObject < objectAttachmentCount.size() && objectAttachmentCount[fieldObject]) { - /* Find an index of the old object and then use that index - to denote the parent of the new object */ - newParents[newParentIndex] = out.fieldObjectOffset(parentFieldId, fieldObject); + /* Use the old object as a parent of the new object */ + newParents[newParentIndex] = fieldObject; /* Assign the field to the new object */ fieldObject = newParentObjects[newParentIndex]; /* Move to the next reserved object */ diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 88cc20aac4..28a482d833 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -2073,11 +2073,7 @@ Containers::Optional SceneData::parentFor(const UnsignedInt object) const { Int index[1]; parentsIntoInternal(fieldId, offset, index); - if(*index == -1) return -1; - - UnsignedInt parent[1]; - objectsIntoInternal(fieldId, *index, parent); - return Int(*parent); + return *index; } Containers::Array SceneData::childrenFor(const Int object) const { @@ -2089,22 +2085,12 @@ Containers::Array SceneData::childrenFor(const Int object) const { const SceneFieldData& parentField = _fields[parentFieldId]; - /* Figure out the parent object index to look for or -1 if we want - top-level objects */ - Int parentIndexToLookFor; - if(object == -1) parentIndexToLookFor = -1; - else { - const std::size_t parentObjectIndex = findFieldObjectOffsetInternal(parentField, object, 0); - if(parentObjectIndex == parentField._size) return {}; - parentIndexToLookFor = parentObjectIndex; - } - - /* Collect IDs of all objects that reference this index */ + /* Collect IDs of all objects that reference this object */ Containers::Array out; for(std::size_t offset = 0; offset != parentField.size(); ++offset) { Int parentIndex[1]; parentsIntoInternal(parentFieldId, offset, parentIndex); - if(*parentIndex == parentIndexToLookFor) { + if(*parentIndex == object) { UnsignedInt child[1]; /** @todo bleh slow, use the children <-> parent field proxying when implemented */ diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index b9b6d02beb..6af7dc26c9 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -98,17 +98,13 @@ enum class SceneField: UnsignedInt { /* Zero used for an invalid value */ /** - * Parent index. Type is usually @ref SceneFieldType::Int, but can be also + * Parent object. Type is usually @ref SceneFieldType::Int, but can be also * any of @relativeref{SceneFieldType,Byte}, * @relativeref{SceneFieldType,Short} or a * @relativeref{SceneFieldType,Long}. A value of @cpp -1 @ce means there's * no parent. An object should have only one parent, altough this isn't * enforced in any way, and which of the duplicate fields gets used is not * defined. - * - * Note that the index points to the parent array itself and isn't the - * actual object index --- the object mapping is stored in the - * corresponding @ref SceneData::objects() array. * @see @ref SceneData::parentsAsArray(), @ref SceneData::parentFor(), * @ref SceneData::childrenFor() */ diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index d549cf7db0..3c6bfc9488 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -1850,13 +1850,13 @@ void AbstractImporterTest::sceneDeprecatedFallback2D() { cameras[0] = {3, 15}; importerState[0] = {3, &a}; - /* Object 5 is a child of object 3 (which is at index 0), has a skin (which - gets ignored by the legacy interface) */ - transformations[1] = {5, 0, Matrix3::rotation(-15.0_degf)}; + /* Object 5 is a child of object 3, has a skin (which gets ignored by the + legacy interface) */ + transformations[1] = {5, 3, Matrix3::rotation(-15.0_degf)}; skins[0] = {5, 226}; - /* Object 1 is a child of object 2 (which will be at index 3) */ - transformations[2] = {1, 3, Matrix3::translation({1.0f, 0.5f})*Matrix3::rotation(15.0_degf)}; + /* Object 1 is a child of object 2 */ + transformations[2] = {1, 2, Matrix3::translation({1.0f, 0.5f})*Matrix3::rotation(15.0_degf)}; /* Object 2 is in the root, has object 1 as a child but nothing else */ transformations[3] = {2, -1, {}}; @@ -1866,9 +1866,9 @@ void AbstractImporterTest::sceneDeprecatedFallback2D() { meshes[0] = {0, 33, -1}; /* Object 4 has TRS also, a mesh with a material and a skin and is a child - of object 3 (which is at index 0). The transformation gets ignored - again. Has importer state. */ - transformations[5] = {4, 0, Matrix3::translation(Vector2::xAxis(5.0f))}; + of object 3. The transformation gets ignored again. Has importer + state. */ + transformations[5] = {4, 3, Matrix3::translation(Vector2::xAxis(5.0f))}; trs[1] = {4, {}, {}, {1.5f, -0.5f}}; meshes[1] = {4, 27, 46}; skins[1] = {4, 72}; @@ -2121,13 +2121,13 @@ void AbstractImporterTest::sceneDeprecatedFallback3D() { cameras[0] = {3, 15}; importerState[0] = {3, &a}; - /* Object 5 is a child of object 3 (which is at index 0), has a skin (which - gets ignored by the legacy interface) */ - transformations[1] = {5, 0, Matrix4::rotationY(-15.0_degf)}; + /* Object 5 is a child of object 3, has a skin (which gets ignored by the + legacy interface) */ + transformations[1] = {5, 3, Matrix4::rotationY(-15.0_degf)}; skins[0] = {5, 226}; - /* Object 1 is a child of object 2 (which will be at index 3), has a light. */ - transformations[2] = {1, 3, Matrix4::translation({1.0f, 0.0f, 1.0f})*Matrix4::rotationZ(15.0_degf)}; + /* Object 1 is a child of object 2, has a light. */ + transformations[2] = {1, 2, Matrix4::translation({1.0f, 0.0f, 1.0f})*Matrix4::rotationZ(15.0_degf)}; lights[0] = {1, 113}; /* Object 2 is in the root, has object 1 as a child but nothing else */ @@ -2138,9 +2138,9 @@ void AbstractImporterTest::sceneDeprecatedFallback3D() { meshes[0] = {0, 33, -1}; /* Object 4 has TRS also, a mesh with a material and a skin and is a child - of object 3 (which is at index 0). The transformation gets ignored - again. Has importer state. */ - transformations[5] = {4, 0, Matrix4::translation(Vector3::xAxis(5.0f))}; + of object 3. The transformation gets ignored again. Has importer + state. */ + transformations[5] = {4, 3, Matrix4::translation(Vector3::xAxis(5.0f))}; trs[1] = {4, {}, {}, {1.5f, 3.0f, -0.5f}}; meshes[1] = {4, 27, 46}; skins[1] = {4, 72}; @@ -2489,8 +2489,8 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { Int parent; } fields[]{ {5, -1}, - {2, 0}, - {3, 0}, + {2, 5}, + {3, 5}, {1, -1} }; @@ -2595,8 +2595,8 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { Int parent; } fields[]{ {5, -1}, - {2, 0}, - {3, 0}, + {2, 5}, + {3, 5}, {1, -1} }; @@ -2718,7 +2718,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { {NoInit, 7, meshes}, {NoInit, 2, cameras}, }; - Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); + Utility::copy({{15, -1}, {21, -1}, {22, 21}, {23, 22}, {1, -1}}, parents); Utility::copy({ {15, 6, 4}, {23, 1, 0}, @@ -2985,7 +2985,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { {NoInit, 7, meshes}, {NoInit, 2, cameras}, }; - Utility::copy({{15, -1}, {21, -1}, {22, 1}, {23, 2}, {1, -1}}, parents); + Utility::copy({{15, -1}, {21, -1}, {22, 21}, {23, 22}, {1, -1}}, parents); Utility::copy({ {15, 6, 4}, {23, 1, 0}, diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 57f4da2e43..20c8d190de 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -4812,8 +4812,8 @@ void SceneDataTest::parentFor() { Int parent; } fields[]{ {3, -1}, - {4, 0}, - {2, 1}, + {4, 3}, + {2, 4}, {4, 2} /* duplicate, ignored */ }; Containers::StridedArrayView1D view = fields; @@ -4865,10 +4865,10 @@ void SceneDataTest::childrenFor() { Int parent; } fields[]{ {4, -1}, - {3, 0}, - {2, 1}, - {1, 0}, - {5, 0}, + {3, 4}, + {2, 3}, + {1, 4}, + {5, 4}, {0, -1}, }; Containers::StridedArrayView1D view = fields; diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index 5fed43146e..fabb93855e 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -90,7 +90,7 @@ void SceneToolsTest::combine() { const UnsignedByte meshes[]{3, 5, 17}; const UnsignedShort parentObjects[]{33, 25}; - const Short parents[]{-1, 0}; + const Short parents[]{-1, 33}; const UnsignedByte translationObjects[]{16}; const Vector2d translations[]{{1.5, -0.5}}; @@ -341,7 +341,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { state here, without having to mess with an ArrayTuple */ const UnsignedShort parentObjects[]{15, 21, 22, 23, 1}; - const Byte parents[]{-1, -1, 1, 2, -1}; + const Byte parents[]{-1, -1, 21, 22, -1}; /* Two objects have two and three mesh assignments respectively, meaning we need three extra */ @@ -426,13 +426,13 @@ void SceneToolsTest::convertToSingleFunctionObjects() { CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView>({ {15, -1}, {21, -1}, - {22, 1}, - {23, 2}, + {22, 21}, + {23, 22}, {1, -1}, - {63, 3}, - {64, 3}, - {65, 0}, - {66, 4} + {63, 23}, + {64, 23}, + {65, 15}, + {66, 1} })), TestSuite::Compare::Container); /* Meshes / materials have certain objects reassigned, field data stay the From 5df37d293427112c84faa1161e927a9148f25a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 3 Dec 2021 20:32:00 +0100 Subject: [PATCH 41/49] Trade: rename SceneData "objects" to "mapping". Now it's a field and its corresponding object mapping, instead of field and "objects": - Goes better with the concept that there's not really any materialized "object" anywhere, just fields mapped to them. - No more weird singular/plural difference between field() and objects(), it's field() and mapping() now. - The objectCount() that actually wasn't really object count is now a mappingBound(), an upper bound for object IDs contained in the object mapping views. Which is quite self-explanatory without having to mention every time that the range may be sparse. --- doc/changelog.dox | 4 +- src/Magnum/MeshTools/sceneconverter.cpp | 10 +- src/Magnum/Trade/AbstractImporter.cpp | 18 +- src/Magnum/Trade/Implementation/sceneTools.h | 75 +- src/Magnum/Trade/SceneData.cpp | 549 +++--- src/Magnum/Trade/SceneData.h | 498 ++--- .../Trade/Test/AbstractImporterTest.cpp | 98 +- src/Magnum/Trade/Test/SceneDataTest.cpp | 1642 ++++++++--------- src/Magnum/Trade/Test/SceneToolsTest.cpp | 210 +-- src/Magnum/Trade/Trade.h | 2 +- 10 files changed, 1555 insertions(+), 1551 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index efb0a290a2..26ca1c203f 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -634,7 +634,7 @@ See also: etc. - @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D children is deprecated in favor of the new scene representation. Use - @ref Trade::SceneData::SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + @ref Trade::SceneData::SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) instead. - @cpp Trade::SceneData::children2D() @ce and @cpp Trade::SceneData::children3D() @ce are deprecated in favor of the @@ -828,7 +828,7 @@ See also: desired use case anymore. - @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D children that got deprecated in favor of - @ref Trade::SceneData::SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + @ref Trade::SceneData::SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) no longer accepts a scene that has both 2D and 3D children. - The deprecated @cpp Trade::AbstractImporter::object*DCount() @ce, @cpp Trade::AbstractImporter::object*DForName() @ce, diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 98fbd95f8c..d538fb721c 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -386,8 +386,8 @@ used.)") struct SceneInfo { UnsignedInt scene; - Trade::SceneObjectType objectType; - UnsignedLong objectCount; + Trade::SceneMappingType mappingType; + UnsignedLong mappingBound; Containers::Array fields; std::size_t dataSize; std::string name; @@ -410,8 +410,8 @@ used.)") SceneInfo info{}; info.scene = i; - info.objectType = scene->objectType(); - info.objectCount = scene->objectCount(); + info.mappingType = scene->mappingType(); + info.mappingBound = scene->mappingBound(); info.dataSize = scene->data().size(); info.name = importer->sceneName(i); for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) { @@ -666,7 +666,7 @@ used.)") d << "Scene" << info.scene << Debug::nospace << ":"; if(!info.name.empty()) d << info.name; d << Debug::newline; - d << " " << info.objectCount << "objects," << info.objectType + d << " bound:" << info.mappingBound << "objects," << info.mappingType << "(" << Debug::nospace << Utility::formatString("{:.1f}", info.dataSize/1024.0f) << "kB)"; for(const SceneFieldInfo& field: info.fields) { diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index ec15b7ef21..f63e0b63f5 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -538,13 +538,13 @@ void AbstractImporter::populateCachedScenes() { except for the above, also because it doesn't take into account the restriction for unique-functioning objects. */ if(_cachedScenes->scenes[i]->is2D()) - _cachedScenes->object2DCount = Math::max(_cachedScenes->object2DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); + _cachedScenes->object2DCount = Math::max(_cachedScenes->object2DCount, UnsignedInt(_cachedScenes->scenes[i]->mappingBound())); if(_cachedScenes->scenes[i]->is3D()) - _cachedScenes->object3DCount = Math::max(_cachedScenes->object3DCount, UnsignedInt(_cachedScenes->scenes[i]->objectCount())); + _cachedScenes->object3DCount = Math::max(_cachedScenes->object3DCount, UnsignedInt(_cachedScenes->scenes[i]->mappingBound())); /* Ensure the newly added objects for each scene don't overlap each other */ - newObjectOffset = Math::max(newObjectOffset, _cachedScenes->scenes[i]->objectCount()); + newObjectOffset = Math::max(newObjectOffset, _cachedScenes->scenes[i]->mappingBound()); } } @@ -618,7 +618,7 @@ std::string AbstractImporter::doObject2DName(const UnsignedInt id) { for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { if(!_cachedScenes->scenes[i] || !_cachedScenes->scenes[i]->is2D() || - _cachedScenes->scenes[i]->objectCount() <= id) + _cachedScenes->scenes[i]->mappingBound() <= id) continue; if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) @@ -643,7 +643,7 @@ Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt populateCachedScenes(); /* Find the first 2D scene with this object, which we'll detect from the - object count reported for the scene, whether it's 2D or 3D, and a + mapping bound reported for the scene, whether it's 2D or 3D, and a presence of a parent attribute. If a parent attribute is not present, it means the object isn't a part of this scene, in which case we skip it. It could also mean isn't a part of the hierarchy and is standalone @@ -652,7 +652,7 @@ Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt std::size_t sceneCandidate = ~std::size_t{}; for(std::size_t i = 0; i != _cachedScenes->scenes.size(); ++i) { const Containers::Optional& scene = _cachedScenes->scenes[i]; - if(scene && scene->is2D() && id < scene->objectCount() && scene->parentFor(id)) { + if(scene && scene->is2D() && id < scene->mappingBound() && scene->parentFor(id)) { sceneCandidate = i; break; } @@ -796,7 +796,7 @@ std::string AbstractImporter::doObject3DName(const UnsignedInt id) { for(UnsignedInt i = 0; i != _cachedScenes->scenes.size(); ++i) { if(!_cachedScenes->scenes[i] || !_cachedScenes->scenes[i]->is3D() || - _cachedScenes->scenes[i]->objectCount() <= id) + _cachedScenes->scenes[i]->mappingBound() <= id) continue; if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) @@ -821,7 +821,7 @@ Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt populateCachedScenes(); /* Find the first 3D scene with this object, which we'll detect from the - object count reported for the scene, whether it's 2D or 3D, and a + mapping bound reported for the scene, whether it's 2D or 3D, and a presence of a parent attribute. If a parent attribute is not present, it means the object isn't a part of this scene, in which case we skip it. It could also mean isn't a part of the hierarchy and is standalone @@ -830,7 +830,7 @@ Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt std::size_t sceneCandidate = ~std::size_t{}; for(std::size_t i = 0; i != _cachedScenes->scenes.size(); ++i) { const Containers::Optional& scene = _cachedScenes->scenes[i]; - if(scene && scene->is3D() && id < scene->objectCount() && scene->parentFor(id)) { + if(scene && scene->is3D() && id < scene->mappingBound() && scene->parentFor(id)) { sceneCandidate = i; break; } diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index 45d451d5fb..6336dc43e0 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -59,29 +59,30 @@ template void sceneCombineCopyObjects(const Containers::ArrayView src = fields[i].objectData(); + const Containers::StridedArrayView1D src = fields[i].mappingData(); const Containers::StridedArrayView1D dst = Containers::arrayCast<1, T>(itemViews[mapping]); - if(fields[i].objectType() == SceneObjectType::UnsignedByte) + if(fields[i].mappingType() == SceneMappingType::UnsignedByte) copyOrCastInto(Containers::arrayCast(src), dst); - else if(fields[i].objectType() == SceneObjectType::UnsignedShort) + else if(fields[i].mappingType() == SceneMappingType::UnsignedShort) copyOrCastInto(Containers::arrayCast(src), dst); - else if(fields[i].objectType() == SceneObjectType::UnsignedInt) + else if(fields[i].mappingType() == SceneMappingType::UnsignedInt) copyOrCastInto(Containers::arrayCast(src), dst); - else if(fields[i].objectType() == SceneObjectType::UnsignedLong) + else if(fields[i].mappingType() == SceneMappingType::UnsignedLong) copyOrCastInto(Containers::arrayCast(src), dst); else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } } -/* Combine fields of varying object type together into a SceneData of a single - given objectType. The fields are expected to point to existing object/field - memory, which will be then copied to the resulting scene. If you supply a - field with null object or field data, the object or field data will not get - copied, only a placeholder for copying the data later will be allocated. If - you however need to have placeholder object data shared among - Offset-only fields are not allowed. +/* Combine fields of varying mapping type together into a SceneData of a single + given mappingType. The fields are expected to point to existing + mapping/field memory, which will be then copied to the resulting scene. If + you supply a field with null mapping or field data, the mapping or field + data will not get copied, only a placeholder for copying the data later will + be allocated. If you however need to have placeholder mapping data shared + among multiple fields you have to allocate them upfront. Offset-only fields + are not allowed. The resulting fields are always tightly packed (not interleaved). @@ -90,9 +91,9 @@ template void sceneCombineCopyObjects(const Containers::ArrayView fields) { - const std::size_t objectTypeSize = sceneObjectTypeSize(objectType); - const std::size_t objectTypeAlignment = sceneObjectTypeAlignment(objectType); +inline SceneData sceneCombine(const SceneMappingType mappingType, const UnsignedLong mappingBound, const Containers::ArrayView fields) { + const std::size_t mappingTypeSize = sceneMappingTypeSize(mappingType); + const std::size_t mappingTypeAlignment = sceneMappingTypeAlignment(mappingType); /* Go through all fields and collect ArrayTuple allocations for these */ std::unordered_map objectMappings; @@ -110,12 +111,12 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo const SceneFieldData& field = fields[i]; CORRADE_INTERNAL_ASSERT(!field.isOffsetOnly()); - /* Object data. Allocate if the view is a placeholder of if it wasn't + /* Mapping data. Allocate if the view is a placeholder of if it wasn't used by other fields yet. */ std::pair::iterator, bool> inserted; - if(field.objectData().data()) - inserted = objectMappings.emplace(field.objectData().data(), itemViewOffset); - if(field.objectData().data() && !inserted.second) { + if(field.mappingData().data()) + inserted = objectMappings.emplace(field.mappingData().data(), itemViewOffset); + if(field.mappingData().data() && !inserted.second) { itemViewMappings[i].first() = inserted.first->second; /* Expect that fields sharing the same object mapping view have the exact same length (the length gets stored in the output view @@ -145,7 +146,7 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo CORRADE_INTERNAL_ASSERT(itemViews[inserted.first->second].size()[0] == field.size()); } else { itemViewMappings[i].first() = itemViewOffset; - arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), objectTypeSize, objectTypeAlignment, itemViews[itemViewOffset]); + arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), mappingTypeSize, mappingTypeAlignment, itemViews[itemViewOffset]); ++itemViewOffset; } @@ -162,13 +163,13 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo CORRADE_INTERNAL_ASSERT(!outData.deleter()); /* Copy the object data over and cast them as necessary */ - if(objectType == SceneObjectType::UnsignedByte) + if(mappingType == SceneMappingType::UnsignedByte) sceneCombineCopyObjects(fields, itemViews, itemViewMappings); - else if(objectType == SceneObjectType::UnsignedShort) + else if(mappingType == SceneMappingType::UnsignedShort) sceneCombineCopyObjects(fields, itemViews, itemViewMappings); - else if(objectType == SceneObjectType::UnsignedInt) + else if(mappingType == SceneMappingType::UnsignedInt) sceneCombineCopyObjects(fields, itemViews, itemViewMappings); - else if(objectType == SceneObjectType::UnsignedLong) + else if(mappingType == SceneMappingType::UnsignedLong) sceneCombineCopyObjects(fields, itemViews, itemViewMappings); /* Copy the field data over. No special handling needed here. */ @@ -187,7 +188,7 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo outFields[i] = SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize()}; } - return SceneData{objectType, objectCount, std::move(outData), std::move(outFields)}; + return SceneData{mappingType, mappingBound, std::move(outData), std::move(outFields)}; } /* Creates a SceneData copy where each object has at most one of the fields @@ -205,7 +206,7 @@ inline SceneData sceneCombine(const SceneObjectType objectType, const UnsignedLo internal asserts into (tested!) message asserts */ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Containers::ArrayView fieldsToConvert, const UnsignedInt newObjectOffset) { /** @todo assert for really high object counts (where this cast would fail) */ - Containers::Array objectAttachmentCount{ValueInit, std::size_t(scene.objectCount())}; + Containers::Array objectAttachmentCount{ValueInit, std::size_t(scene.mappingBound())}; for(const SceneField field: fieldsToConvert) { CORRADE_INTERNAL_ASSERT(field != SceneField::Parent); @@ -217,7 +218,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con /** @todo use a statically-allocated array & Into() in a loop instead once this is more than a private backwards-compatibility utility where PERF WHATEVER WHO CARES */ - for(const UnsignedInt object: scene.objectsAsArray(*fieldId)) { + for(const UnsignedInt object: scene.mappingAsArray(*fieldId)) { CORRADE_INTERNAL_ASSERT(object < objectAttachmentCount.size()); ++objectAttachmentCount[object]; } @@ -249,25 +250,25 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con } /* Combine the fields into a new SceneData */ - SceneData out = sceneCombine(SceneObjectType::UnsignedInt, Math::max(scene.objectCount(), UnsignedLong(newObjectOffset) + objectsToAdd), fields); + SceneData out = sceneCombine(SceneMappingType::UnsignedInt, Math::max(scene.mappingBound(), UnsignedLong(newObjectOffset) + objectsToAdd), fields); /* Copy existing parent object/field data to a prefix of the output */ - const Containers::StridedArrayView1D outParentObjects = out.mutableObjects(parentFieldId); + const Containers::StridedArrayView1D outParentMapping = out.mutableMapping(parentFieldId); const Containers::StridedArrayView1D outParents = out.mutableField(parentFieldId); - CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParentObjects, outParents) == scene.fieldSize(parentFieldId)); + CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParentMapping, outParents) == scene.fieldSize(parentFieldId)); /* List new objects at the end of the extended parent field */ - const Containers::StridedArrayView1D newParentObjects = outParentObjects.suffix(scene.fieldSize(parentFieldId)); + const Containers::StridedArrayView1D newParentMapping = outParentMapping.suffix(scene.fieldSize(parentFieldId)); const Containers::StridedArrayView1D newParents = outParents.suffix(scene.fieldSize(parentFieldId)); - for(std::size_t i = 0; i != newParentObjects.size(); ++i) { - newParentObjects[i] = newObjectOffset + i; + for(std::size_t i = 0; i != newParentMapping.size(); ++i) { + newParentMapping[i] = newObjectOffset + i; newParents[i] = -1; } /* Clear the objectAttachmentCount array to reuse it below */ /** @todo use a BitArray instead once it exists? */ constexpr UnsignedInt zero[1]{}; - Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(scene.objectCount()), objectAttachmentCount); + Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(scene.mappingBound()), objectAttachmentCount); /* For objects with multiple fields move the extra fields to newly added children */ @@ -277,7 +278,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con const Containers::Optional fieldId = scene.findFieldId(field); if(!fieldId) continue; - for(UnsignedInt& fieldObject: out.mutableObjects(*fieldId)) { + for(UnsignedInt& fieldObject: out.mutableMapping(*fieldId)) { /* If the object is not new (could happen when an object mapping array is shared among multiple fields, in which case it *might* have been updated already to an ID larger than @@ -288,7 +289,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con /* Use the old object as a parent of the new object */ newParents[newParentIndex] = fieldObject; /* Assign the field to the new object */ - fieldObject = newParentObjects[newParentIndex]; + fieldObject = newParentMapping[newParentIndex]; /* Move to the next reserved object */ ++newParentIndex; } else ++objectAttachmentCount[fieldObject]; diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 28a482d833..44a19a77c7 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -47,12 +47,12 @@ namespace Magnum { namespace Trade { -Debug& operator<<(Debug& debug, const SceneObjectType value) { - debug << "Trade::SceneObjectType" << Debug::nospace; +Debug& operator<<(Debug& debug, const SceneMappingType value) { + debug << "Trade::SceneMappingType" << Debug::nospace; switch(value) { /* LCOV_EXCL_START */ - #define _c(value) case SceneObjectType::value: return debug << "::" #value; + #define _c(value) case SceneMappingType::value: return debug << "::" #value; _c(UnsignedByte) _c(UnsignedInt) _c(UnsignedShort) @@ -64,40 +64,40 @@ Debug& operator<<(Debug& debug, const SceneObjectType value) { return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } -UnsignedInt sceneObjectTypeSize(const SceneObjectType type) { +UnsignedInt sceneMappingTypeSize(const SceneMappingType type) { #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic push #pragma GCC diagnostic error "-Wswitch" #endif switch(type) { - case SceneObjectType::UnsignedByte: return 1; - case SceneObjectType::UnsignedShort: return 2; - case SceneObjectType::UnsignedInt: return 4; - case SceneObjectType::UnsignedLong: return 8; + case SceneMappingType::UnsignedByte: return 1; + case SceneMappingType::UnsignedShort: return 2; + case SceneMappingType::UnsignedInt: return 4; + case SceneMappingType::UnsignedLong: return 8; } #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic pop #endif - CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {}); + CORRADE_ASSERT_UNREACHABLE("Trade::sceneMappingTypeSize(): invalid type" << type, {}); } -UnsignedInt sceneObjectTypeAlignment(const SceneObjectType type) { +UnsignedInt sceneMappingTypeAlignment(const SceneMappingType type) { #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic push #pragma GCC diagnostic error "-Wswitch" #endif switch(type) { - case SceneObjectType::UnsignedByte: return 1; - case SceneObjectType::UnsignedShort: return 2; - case SceneObjectType::UnsignedInt: return 4; - case SceneObjectType::UnsignedLong: return 8; + case SceneMappingType::UnsignedByte: return 1; + case SceneMappingType::UnsignedShort: return 2; + case SceneMappingType::UnsignedInt: return 4; + case SceneMappingType::UnsignedLong: return 8; } #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic pop #endif - CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeAlignment(): invalid type" << type, {}); + CORRADE_ASSERT_UNREACHABLE("Trade::sceneMappingTypeAlignment(): invalid type" << type, {}); } Debug& operator<<(Debug& debug, const SceneField value) { @@ -473,7 +473,7 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) { CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeAlignment(): invalid type" << type, {}); } -SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{objectData.data(), ~std::size_t{}}, objectData.size()[0], objectData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} { +SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} { /* Yes, this calls into a constexpr function defined in the header -- because I feel that makes more sense than duplicating the full assert logic */ @@ -484,14 +484,14 @@ SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedA "Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType, ); #endif - if(objectData.size()[1] == 8) _objectType = SceneObjectType::UnsignedLong; - else if(objectData.size()[1] == 4) _objectType = SceneObjectType::UnsignedInt; - else if(objectData.size()[1] == 2) _objectType = SceneObjectType::UnsignedShort; - else if(objectData.size()[1] == 1) _objectType = SceneObjectType::UnsignedByte; - else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got" << objectData.size()[1], ); + if(mappingData.size()[1] == 8) _mappingType = SceneMappingType::UnsignedLong; + else if(mappingData.size()[1] == 4) _mappingType = SceneMappingType::UnsignedInt; + else if(mappingData.size()[1] == 2) _mappingType = SceneMappingType::UnsignedShort; + else if(mappingData.size()[1] == 1) _mappingType = SceneMappingType::UnsignedByte; + else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second mapping view dimension size 1, 2, 4 or 8 but got" << mappingData.size()[1], ); + CORRADE_ASSERT(mappingData.isContiguous<1>(), "Trade::SceneFieldData: second mapping view dimension is not contiguous", ); CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", ); - CORRADE_ASSERT(objectData.isContiguous<1>(), "Trade::SceneFieldData: second object view dimension is not contiguous", ); } Containers::Array sceneFieldDataNonOwningArray(const Containers::ArrayView view) { @@ -499,19 +499,19 @@ Containers::Array sceneFieldDataNonOwningArray(const Containers: return Containers::Array{const_cast(view.data()), view.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}; } -SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{objectType}, _dimensions{}, _objectCount{objectCount}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} { - /* Check that object type is large enough */ +SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mappingBound, Containers::Array&& data, Containers::Array&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _mappingType{mappingType}, _dimensions{}, _mappingBound{mappingBound}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} { + /* Check that mapping type is large enough */ CORRADE_ASSERT( - (objectType == SceneObjectType::UnsignedByte && objectCount <= 0xffull) || - (objectType == SceneObjectType::UnsignedShort && objectCount <= 0xffffull) || - (objectType == SceneObjectType::UnsignedInt && objectCount <= 0xffffffffull) || - objectType == SceneObjectType::UnsignedLong, - "Trade::SceneData:" << objectType << "is too small for" << objectCount << "objects", ); + (mappingType == SceneMappingType::UnsignedByte && mappingBound <= 0xffull) || + (mappingType == SceneMappingType::UnsignedShort && mappingBound <= 0xffffull) || + (mappingType == SceneMappingType::UnsignedInt && mappingBound <= 0xffffffffull) || + mappingType == SceneMappingType::UnsignedLong, + "Trade::SceneData:" << mappingType << "is too small for" << mappingBound << "objects", ); #ifndef CORRADE_NO_ASSERT /* Check various assumptions about field data */ Math::BoolVector<12> fieldsPresent; /** @todo some constant for this */ - const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType); + const UnsignedInt mappingTypeSize = sceneMappingTypeSize(_mappingType); #endif UnsignedInt transformationField = ~UnsignedInt{}; UnsignedInt translationField = ~UnsignedInt{}; @@ -526,17 +526,17 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object const SceneFieldData& field = _fields[i]; #ifndef CORRADE_NO_ASSERT - /* The object type has to be the same among all fields. Technically it + /* The mapping type has to be the same among all fields. Technically it wouldn't need to be, but if there's 60k objects then using a 8bit type for certain fields would mean only the first 256 objects can be referenced, which makes no practical sense, and to improve the situation there would need to be some additional per-field object - offset and ... it's simpler to just require the object type to be + offset and ... it's simpler to just require the mapping type to be large enough to reference all objects (checked outside of the loop above) and that it's the same for all fields. This also makes it more convenient for the user. */ - CORRADE_ASSERT(field._objectType == _objectType, - "Trade::SceneData: inconsistent object type, got" << field._objectType << "for field" << i << "but expected" << _objectType, ); + CORRADE_ASSERT(field._mappingType == _mappingType, + "Trade::SceneData: inconsistent mapping type, got" << field._mappingType << "for field" << i << "but expected" << _mappingType, ); /* We could check that object indices are in bounds, but that's rather expensive. OTOH it's fine if field size is larger than object count, @@ -558,7 +558,7 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object "Trade::SceneData: duplicate field" << _fields[i]._name, ); } - /* Check that both the object and field view fits into the provided + /* Check that both the mapping and field view fits into the provided data array. If the field is empty, we don't check anything -- accessing the memory would be invalid anyway and enforcing this would lead to unnecessary friction with optional fields. */ @@ -566,19 +566,19 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)* (field._fieldArraySize ? field._fieldArraySize : 1); if(field._isOffsetOnly) { - const std::size_t objectSize = field._objectData.offset + (field._size - 1)*field._objectStride + objectTypeSize; + const std::size_t mappingSize = field._mappingData.offset + (field._size - 1)*field._mappingStride + mappingTypeSize; const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize; - CORRADE_ASSERT(objectSize <= _data.size(), - "Trade::SceneData: offset-only object data of field" << i << "span" << objectSize << "bytes but passed data array has only" << _data.size(), ); + CORRADE_ASSERT(mappingSize <= _data.size(), + "Trade::SceneData: offset-only mapping data of field" << i << "span" << mappingSize << "bytes but passed data array has only" << _data.size(), ); CORRADE_ASSERT(fieldSize <= _data.size(), "Trade::SceneData: offset-only field data of field" << i << "span" << fieldSize << "bytes but passed data array has only" << _data.size(), ); } else { - const void* const objectBegin = field._objectData.pointer; + const void* const mappingBegin = field._mappingData.pointer; const void* const fieldBegin = field._fieldData.pointer; - const void* const objectEnd = static_cast(field._objectData.pointer) + (field._size - 1)*field._objectStride + objectTypeSize; + const void* const mappingEnd = static_cast(field._mappingData.pointer) + (field._size - 1)*field._mappingStride + mappingTypeSize; const void* const fieldEnd = static_cast(field._fieldData.pointer) + (field._size - 1)*field._fieldStride + fieldTypeSize; - CORRADE_ASSERT(objectBegin >= _data.begin() && objectEnd <= _data.end(), - "Trade::SceneData: object data [" << Debug::nospace << objectBegin << Debug::nospace << ":" << Debug::nospace << objectEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); + CORRADE_ASSERT(mappingBegin >= _data.begin() && mappingEnd <= _data.end(), + "Trade::SceneData: mapping data [" << Debug::nospace << mappingBegin << Debug::nospace << ":" << Debug::nospace << mappingEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); CORRADE_ASSERT(fieldBegin >= _data.begin() && fieldEnd <= _data.end(), "Trade::SceneData: field data [" << Debug::nospace << fieldBegin << Debug::nospace << ":" << Debug::nospace << fieldEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast(_data.end()) << Debug::nospace << "]", ); } @@ -614,29 +614,29 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object effort just for an assert message. Also, compared to above, where "begin" was always zero, here we're always comparing four values, so the message for offset-only wouldn't be simpler either. */ - const auto checkFieldObjectDataMatch = [](const SceneFieldData& a, const SceneFieldData& b) { - const std::size_t objectTypeSize = sceneObjectTypeSize(a._objectType); - const void* const aBegin = a._objectData.pointer; - const void* const bBegin = b._objectData.pointer; - const void* const aEnd = static_cast(a._objectData.pointer) + a._size*objectTypeSize; - const void* const bEnd = static_cast(b._objectData.pointer) + b._size*objectTypeSize; + const auto checkFieldMappingDataMatch = [](const SceneFieldData& a, const SceneFieldData& b) { + const std::size_t mappingTypeSize = sceneMappingTypeSize(a._mappingType); + const void* const aBegin = a._mappingData.pointer; + const void* const bBegin = b._mappingData.pointer; + const void* const aEnd = static_cast(a._mappingData.pointer) + a._size*mappingTypeSize; + const void* const bEnd = static_cast(b._mappingData.pointer) + b._size*mappingTypeSize; CORRADE_ASSERT(aBegin == bBegin && aEnd == bEnd, - "Trade::SceneData:" << b._name << "object data [" << Debug::nospace << bBegin << Debug::nospace << ":" << Debug::nospace << bEnd << Debug::nospace << "] is different from" << a._name << "object data [" << Debug::nospace << aBegin << Debug::nospace << ":" << Debug::nospace << aEnd << Debug::nospace << "]", ); + "Trade::SceneData:" << b._name << "mapping data [" << Debug::nospace << bBegin << Debug::nospace << ":" << Debug::nospace << bEnd << Debug::nospace << "] is different from" << a._name << "mapping data [" << Debug::nospace << aBegin << Debug::nospace << ":" << Debug::nospace << aEnd << Debug::nospace << "]", ); }; /* All present TRS fields should share the same object mapping */ if(translationField != ~UnsignedInt{}) { if(rotationField != ~UnsignedInt{}) - checkFieldObjectDataMatch(_fields[translationField], _fields[rotationField]); + checkFieldMappingDataMatch(_fields[translationField], _fields[rotationField]); if(scalingField != ~UnsignedInt{}) - checkFieldObjectDataMatch(_fields[translationField], _fields[scalingField]); + checkFieldMappingDataMatch(_fields[translationField], _fields[scalingField]); } if(rotationField != ~UnsignedInt{} && scalingField != ~UnsignedInt{}) - checkFieldObjectDataMatch(_fields[rotationField], _fields[scalingField]); + checkFieldMappingDataMatch(_fields[rotationField], _fields[scalingField]); /* Mesh and materials also */ if(meshField != ~UnsignedInt{} && meshMaterialField != ~UnsignedInt{}) - checkFieldObjectDataMatch(_fields[meshField], _fields[meshMaterialField]); + checkFieldMappingDataMatch(_fields[meshField], _fields[meshMaterialField]); #endif /* Decide about dimensionality based on transformation type, if present */ @@ -710,18 +710,18 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object "Trade::SceneData: a skin field requires some transformation field to be present in order to disambiguate between 2D and 3D", ); } -SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array&& data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} +SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mappingBound, Containers::Array&& data, const std::initializer_list fields, const void* const importerState): SceneData{mappingType, mappingBound, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} -SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView data, Containers::Array&& fields, const void* const importerState) noexcept: SceneData{objectType, objectCount, Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(fields), importerState} { +SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mappingBound, const DataFlags dataFlags, const Containers::ArrayView data, Containers::Array&& fields, const void* const importerState) noexcept: SceneData{mappingType, mappingBound, Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(fields), importerState} { CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), "Trade::SceneData: can't construct with non-owned data but" << dataFlags, ); _dataFlags = dataFlags; } -SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView data, const std::initializer_list fields, const void* const importerState): SceneData{objectType, objectCount, dataFlags, data, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} +SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mappingBound, const DataFlags dataFlags, const Containers::ArrayView data, const std::initializer_list fields, const void* const importerState): SceneData{mappingType, mappingBound, dataFlags, data, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} #ifdef MAGNUM_BUILD_DEPRECATED -SceneData::SceneData(std::vector children2D, std::vector children3D, const void* const importerState): _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{SceneObjectType::UnsignedInt}, _importerState{importerState} { +SceneData::SceneData(std::vector children2D, std::vector children3D, const void* const importerState): _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _mappingType {SceneMappingType::UnsignedInt}, _importerState{importerState} { /* Assume nobody ever created a scene with both 2D and 3D children. (PrimitiveImporter did, but that's an exception and blame goes on me.) If this blows up for you, please complain. Or rather upgrade to the new @@ -738,27 +738,27 @@ SceneData::SceneData(std::vector children2D, std::vector objects; + Containers::ArrayView mapping; Containers::ArrayView parents; _data = Containers::ArrayTuple{ - {NoInit, children.size(), objects}, + {NoInit, children.size(), mapping}, {NoInit, children.size(), parents}, }; /* Can't use InPlaceInit as that creates an Array with a non-default deleter, which then trips up on an assertion when such an instance gets returned from AbstractImporter */ _fields = Containers::Array{1}; - _fields[0] = SceneFieldData{SceneField::Parent, objects, parents}; - Utility::copy(children, objects); + _fields[0] = SceneFieldData{SceneField::Parent, mapping, parents}; + Utility::copy(children, mapping); constexpr Int parent[]{-1}; Utility::copy(Containers::stridedArrayView(parent).broadcasted<0>(parents.size()), parents); } @@ -776,18 +776,19 @@ Containers::ArrayView SceneData::mutableData() & { return _data; } -Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const { +Containers::StridedArrayView1D SceneData::fieldDataMappingViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const { CORRADE_INTERNAL_ASSERT(offset + size <= field._size); return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ {static_cast(field._isOffsetOnly ? - _data.data() + field._objectData.offset : field._objectData.pointer) - + field._objectStride*offset, ~std::size_t{}}, - size, field._objectStride}; + _data.data() + field._mappingData.offset : field._mappingData.pointer) + + field._mappingStride*offset, ~std::size_t{}}, + size, field._mappingStride + }; } -Containers::StridedArrayView1D SceneData::fieldDataObjectViewInternal(const SceneFieldData& field) const { - return fieldDataObjectViewInternal(field, 0, field._size); +Containers::StridedArrayView1D SceneData::fieldDataMappingViewInternal(const SceneFieldData& field) const { + return fieldDataMappingViewInternal(field, 0, field._size); } Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const { @@ -808,7 +809,7 @@ SceneFieldData SceneData::fieldData(const UnsignedInt id) const { CORRADE_ASSERT(id < _fields.size(), "Trade::SceneData::fieldData(): index" << id << "out of range for" << _fields.size() << "fields", SceneFieldData{}); const SceneFieldData& field = _fields[id]; - return SceneFieldData{field._name, field._objectType, fieldDataObjectViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize}; + return SceneFieldData{field._name, field._mappingType, fieldDataMappingViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize}; } SceneField SceneData::fieldName(const UnsignedInt id) const { @@ -858,34 +859,34 @@ bool SceneData::hasField(const SceneField name) const { namespace { -template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { - const Containers::StridedArrayView1D objectsT = Containers::arrayCast(objects); - const std::size_t max = objectsT.size(); +template std::size_t findObject(const Containers::StridedArrayView1D& mapping, const UnsignedInt object) { + const Containers::StridedArrayView1D mappingT = Containers::arrayCast(mapping); + const std::size_t max = mappingT.size(); /** @todo implement something faster than O(n) when field-specific flags can annotate how the object mapping is done */ for(std::size_t i = 0; i != max; ++i) - if(objectsT[i] == object) return i; + if(mappingT[i] == object) return i; return max; } } std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedInt object, const std::size_t offset) const { - const Containers::StridedArrayView1D objects = fieldDataObjectViewInternal(field, offset, field._size - offset); - if(field._objectType == SceneObjectType::UnsignedInt) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedShort) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedByte) - return offset + findObject(objects, object); - else if(field._objectType == SceneObjectType::UnsignedLong) - return offset + findObject(objects, object); + const Containers::StridedArrayView1D mapping = fieldDataMappingViewInternal(field, offset, field._size - offset); + if(field._mappingType == SceneMappingType::UnsignedInt) + return offset + findObject(mapping, object); + else if(field._mappingType == SceneMappingType::UnsignedShort) + return offset + findObject(mapping, object); + else if(field._mappingType == SceneMappingType::UnsignedByte) + return offset + findObject(mapping, object); + else if(field._mappingType == SceneMappingType::UnsignedLong) + return offset + findObject(mapping, object); else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } Containers::Optional SceneData::findFieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), "Trade::SceneData::findFieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); @@ -898,8 +899,8 @@ Containers::Optional SceneData::findFieldObjectOffset(const Unsigne } Containers::Optional SceneData::findFieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(fieldName); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, @@ -914,8 +915,8 @@ Containers::Optional SceneData::findFieldObjectOffset(const SceneFi } std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), "Trade::SceneData::fieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); @@ -930,8 +931,8 @@ std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const Unsign } std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(fieldName); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, @@ -948,8 +949,8 @@ std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const Unsig } bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), "Trade::SceneData::hasFieldObject(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); @@ -958,8 +959,8 @@ bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt obje } bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(fieldName); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, @@ -987,26 +988,26 @@ UnsignedShort SceneData::fieldArraySize(const SceneField name) const { return _fields[fieldId]._fieldArraySize; } -Containers::StridedArrayView2D SceneData::objects(const UnsignedInt fieldId) const { +Containers::StridedArrayView2D SceneData::mapping(const UnsignedInt fieldId) const { CORRADE_ASSERT(fieldId < _fields.size(), - "Trade::SceneData::objects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + "Trade::SceneData::mapping(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); const SceneFieldData& field = _fields[fieldId]; - /* Build a 2D view using information about object type size */ + /* Build a 2D view using information about mapping type size */ return Containers::arrayCast<2, const char>( - fieldDataObjectViewInternal(field), - sceneObjectTypeSize(field._objectType)); + fieldDataMappingViewInternal(field), + sceneMappingTypeSize(field._mappingType)); } -Containers::StridedArrayView2D SceneData::mutableObjects(const UnsignedInt fieldId) { +Containers::StridedArrayView2D SceneData::mutableMapping(const UnsignedInt fieldId) { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, - "Trade::SceneData::mutableObjects(): data not mutable", {}); + "Trade::SceneData::mutableMapping(): data not mutable", {}); CORRADE_ASSERT(fieldId < _fields.size(), - "Trade::SceneData::mutableObjects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + "Trade::SceneData::mutableMapping(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); const SceneFieldData& field = _fields[fieldId]; /* Build a 2D view using information about attribute type size */ const auto out = Containers::arrayCast<2, const char>( - fieldDataObjectViewInternal(field), - sceneObjectTypeSize(field._objectType)); + fieldDataMappingViewInternal(field), + sceneMappingTypeSize(field._mappingType)); /** @todo some arrayConstCast? UGH */ return Containers::StridedArrayView2D{ /* The view size is there only for a size assert, we're pretty sure the @@ -1015,25 +1016,25 @@ Containers::StridedArrayView2D SceneData::mutableObjects(const UnsignedInt out.size(), out.stride()}; } -Containers::StridedArrayView2D SceneData::objects(const SceneField fieldName) const { +Containers::StridedArrayView2D SceneData::mapping(const SceneField fieldName) const { const UnsignedInt fieldId = findFieldIdInternal(fieldName); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {}); - return objects(fieldId); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mapping(): field" << fieldName << "not found", {}); + return mapping(fieldId); } -Containers::StridedArrayView2D SceneData::mutableObjects(const SceneField fieldName) { +Containers::StridedArrayView2D SceneData::mutableMapping(const SceneField fieldName) { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, - "Trade::SceneData::mutableObjects(): data not mutable", {}); + "Trade::SceneData::mutableMapping(): data not mutable", {}); const UnsignedInt fieldId = findFieldIdInternal(fieldName); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {}); - return mutableObjects(fieldId); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableMapping(): field" << fieldName << "not found", {}); + return mutableMapping(fieldId); } Containers::StridedArrayView2D SceneData::field(const UnsignedInt id) const { CORRADE_ASSERT(id < _fields.size(), "Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {}); const SceneFieldData& field = _fields[id]; - /* Build a 2D view using information about object type size */ + /* Build a 2D view using information about mapping type size */ return Containers::arrayCast<2, const char>( fieldDataFieldViewInternal(field), sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1)); @@ -1073,74 +1074,74 @@ Containers::StridedArrayView2D SceneData::mutableField(const SceneField na return mutableField(fieldId); } -void SceneData::objectsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +void SceneData::mappingIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { /* fieldId, offset and destination.size() is assumed to be in bounds, checked by the callers */ const SceneFieldData& field = _fields[fieldId]; - const Containers::StridedArrayView1D objectData = fieldDataObjectViewInternal(field, offset, destination.size()); + const Containers::StridedArrayView1D mappingData = fieldDataMappingViewInternal(field, offset, destination.size()); const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination); - if(field._objectType == SceneObjectType::UnsignedInt) - Utility::copy(Containers::arrayCast(objectData), destination); - else if(field._objectType == SceneObjectType::UnsignedShort) - Math::castInto(Containers::arrayCast<2, const UnsignedShort>(objectData, 1), destination1ui); - else if(field._objectType == SceneObjectType::UnsignedByte) - Math::castInto(Containers::arrayCast<2, const UnsignedByte>(objectData, 1), destination1ui); - else if(field._objectType == SceneObjectType::UnsignedLong) { - Math::castInto(Containers::arrayCast<2, const UnsignedLong>(objectData, 1), destination1ui); + if(field._mappingType == SceneMappingType::UnsignedInt) + Utility::copy(Containers::arrayCast(mappingData), destination); + else if(field._mappingType == SceneMappingType::UnsignedShort) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(mappingData, 1), destination1ui); + else if(field._mappingType == SceneMappingType::UnsignedByte) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(mappingData, 1), destination1ui); + else if(field._mappingType == SceneMappingType::UnsignedLong) { + Math::castInto(Containers::arrayCast<2, const UnsignedLong>(mappingData, 1), destination1ui); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { +void SceneData::mappingInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const { CORRADE_ASSERT(fieldId < _fields.size(), - "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", ); + "Trade::SceneData::mappingInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", ); CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::objectsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - objectsIntoInternal(fieldId, 0, destination); + "Trade::SceneData::mappingInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); + mappingIntoInternal(fieldId, 0, destination); } -std::size_t SceneData::objectsInto(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::mappingInto(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { CORRADE_ASSERT(fieldId < _fields.size(), - "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + "Trade::SceneData::mappingInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::objectsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {}); + "Trade::SceneData::mappingInto(): 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); - objectsIntoInternal(fieldId, offset, destination.prefix(size)); + mappingIntoInternal(fieldId, offset, destination.prefix(size)); return size; } -Containers::Array SceneData::objectsAsArray(const UnsignedInt fieldId) const { +Containers::Array SceneData::mappingAsArray(const UnsignedInt fieldId) const { CORRADE_ASSERT(fieldId < _fields.size(), /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); + "Trade::SceneData::mappingInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {}); Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - objectsIntoInternal(fieldId, 0, out); + mappingIntoInternal(fieldId, 0, out); return out; } -void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D& destination) const { +void SceneData::mappingInto(const SceneField name, const Containers::StridedArrayView1D& destination) const { const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::objectsInto(): field" << name << "not found", ); - objectsInto(fieldId, destination); + "Trade::SceneData::mappingInto(): field" << name << "not found", ); + mappingInto(fieldId, destination); } -std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::mappingInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D& destination) const { const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::objectsInto(): field" << name << "not found", {}); - return objectsInto(fieldId, offset, destination); + "Trade::SceneData::mappingInto(): field" << name << "not found", {}); + return mappingInto(fieldId, offset, destination); } -Containers::Array SceneData::objectsAsArray(const SceneField name) const { +Containers::Array SceneData::mappingAsArray(const SceneField name) const { const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::objectsInto(): field" << name << "not found", {}); - return objectsAsArray(fieldId); + "Trade::SceneData::mappingInto(): field" << name << "not found", {}); + return mappingAsArray(fieldId); } void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1162,29 +1163,29 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::parentsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::parentsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::parentsInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); parentsIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", {}); 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::parentsInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); if(fieldDestination) parentsIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } @@ -1197,7 +1198,7 @@ Containers::Array> SceneData::parentsAsArray( "Trade::SceneData::parentsInto(): field not found", {}); 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)}); + mappingIntoInternal(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; } @@ -1368,20 +1369,20 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::transformations2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::transformations2DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations2DInto(): no transformation-related field found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations2DInto(): expected mapping destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldWithObjectMapping, 0, mappingDestination); transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, fieldDestination); } -std::size_t SceneData::transformations2DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::transformations2DInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, @@ -1389,10 +1390,10 @@ std::size_t SceneData::transformations2DInto(const std::size_t offset, const Con 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::transformations2DInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldWithObjectMapping, offset, mappingDestination.prefix(size)); if(fieldDestination) transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, fieldDestination.prefix(size)); return size; } @@ -1406,7 +1407,7 @@ Containers::Array> SceneData::transformat "Trade::SceneData::transformations2DInto(): no transformation-related field found", {}); 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)}); + mappingIntoInternal(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; } @@ -1474,24 +1475,24 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr } } -void SceneData::translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +void SceneData::translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings2DInto(): expected mapping destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldWithObjectMapping, 0, mappingDestination); translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination); } -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 { +std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, @@ -1499,20 +1500,20 @@ std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t off 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(!mappingDestination != !translationDestination || mappingDestination.size() == translationDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): mapping and translation destination views have different size," << mappingDestination.size() << "vs" << translationDestination.size(), {}); + CORRADE_ASSERT(!mappingDestination != !rotationDestination || mappingDestination.size() == rotationDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): mapping and rotation destination views have different size," << mappingDestination.size() << "vs" << rotationDestination.size(), {}); + CORRADE_ASSERT(!mappingDestination != !scalingDestination || mappingDestination.size() == scalingDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): mapping and scaling destination views have different size," << mappingDestination.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({objectDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); - if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); + const std::size_t size = Math::min(Math::max({mappingDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldWithObjectMapping, offset, mappingDestination.prefix(size)); translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translationDestination ? translationDestination.prefix(size) : nullptr, rotationDestination ? rotationDestination.prefix(size) : nullptr, @@ -1529,7 +1530,7 @@ Containers::Array>> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; /** @todo use slicing once Triple exposes members somehow */ - objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + mappingIntoInternal(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)}; @@ -1613,20 +1614,20 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::transformations3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::transformations3DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const std::size_t fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations3DInto(): no transformation-related field found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations3DInto(): expected mapping destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldWithObjectMapping, 0, mappingDestination); transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, fieldDestination); } -std::size_t SceneData::transformations3DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::transformations3DInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, @@ -1634,10 +1635,10 @@ std::size_t SceneData::transformations3DInto(const std::size_t offset, const Con 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::transformations3DInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldWithObjectMapping, offset, mappingDestination.prefix(size)); if(fieldDestination) transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, fieldDestination.prefix(size)); return size; } @@ -1651,7 +1652,7 @@ Containers::Array> SceneData::transformat "Trade::SceneData::transformations3DInto(): no transformation-related field found", {}); 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)}); + mappingIntoInternal(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; } @@ -1719,24 +1720,24 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr } } -void SceneData::translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +void SceneData::translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings3DInto(): expected mapping destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldWithObjectMapping, 0, mappingDestination); translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination); } -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 { +std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, @@ -1744,20 +1745,20 @@ std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t off 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(!mappingDestination != !translationDestination || mappingDestination.size() == translationDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): mapping and translation destination views have different size," << mappingDestination.size() << "vs" << translationDestination.size(), {}); + CORRADE_ASSERT(!mappingDestination != !rotationDestination || mappingDestination.size() == rotationDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): mapping and rotation destination views have different size," << mappingDestination.size() << "vs" << rotationDestination.size(), {}); + CORRADE_ASSERT(!mappingDestination != !scalingDestination || mappingDestination.size() == scalingDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): mapping and scaling destination views have different size," << mappingDestination.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({objectDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); - if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); + const std::size_t size = Math::min(Math::max({mappingDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldWithObjectMapping, offset, mappingDestination.prefix(size)); translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translationDestination ? translationDestination.prefix(size) : nullptr, rotationDestination ? rotationDestination.prefix(size) : nullptr, @@ -1774,7 +1775,7 @@ Containers::Array>> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; /** @todo use slicing once Triple exposes members somehow */ - objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + mappingIntoInternal(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)}; @@ -1819,7 +1820,7 @@ void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::siz 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)}); + mappingIntoInternal(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; } @@ -1843,35 +1844,35 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std } } -void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { +void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& mappingDestination, 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::meshesMaterialsInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); meshesMaterialsIntoInternal(fieldId, 0, meshDestination, meshMaterialDestination); } -std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { +std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, 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", {}); 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(!mappingDestination != !meshDestination || mappingDestination.size() == meshDestination.size(), + "Trade::SceneData::meshesMaterialsInto(): mapping and mesh destination views have different size," << mappingDestination.size() << "vs" << meshDestination.size(), {}); + CORRADE_ASSERT(!mappingDestination != !meshMaterialDestination || mappingDestination.size() == meshMaterialDestination.size(), + "Trade::SceneData::meshesMaterialsInto(): mapping and mesh material destination views have different size," << mappingDestination.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({objectDestination.size(), meshDestination.size(), meshMaterialDestination.size()}), fieldSize - offset); - if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + const std::size_t size = Math::min(Math::max({mappingDestination.size(), meshDestination.size(), meshMaterialDestination.size()}), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); meshesMaterialsIntoInternal(fieldId, offset, meshDestination ? meshDestination.prefix(size) : nullptr, meshMaterialDestination ? meshMaterialDestination.prefix(size) : nullptr); @@ -1886,36 +1887,36 @@ 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)}); + mappingIntoInternal(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& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::lightsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::lightsInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", {}); 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::lightsInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } @@ -1929,29 +1930,29 @@ Containers::Array> SceneData::lightsA return unsignedIndexFieldAsArrayInternal(fieldId); } -void SceneData::camerasInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::camerasInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::camerasInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", {}); 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::camerasInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } @@ -1965,29 +1966,29 @@ Containers::Array> SceneData::cameras return unsignedIndexFieldAsArrayInternal(fieldId); } -void SceneData::skinsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::skinsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::skinsInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", {}); 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::skinsInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } @@ -2011,29 +2012,29 @@ void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std:: Utility::copy(Containers::arrayCast(fieldDataFieldViewInternal(field, offset, destination.size())), destination); } -void SceneData::importerStateInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +void SceneData::importerStateInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::importerStateInto(): field not found", ); - 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(!mappingDestination || mappingDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): expected mapping destination view either empty or with" << _fields[fieldId]._size << "elements but got" << mappingDestination.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); + mappingIntoInternal(fieldId, 0, mappingDestination); importerStateIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { +std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::importerStateInto(): field not found", {}); 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)); + CORRADE_ASSERT(!mappingDestination != !fieldDestination|| mappingDestination.size() == fieldDestination.size(), + "Trade::SceneData::importerStateInto(): mapping and field destination views have different size," << mappingDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(mappingDestination.size(), fieldDestination.size()), fieldSize - offset); + if(mappingDestination) mappingIntoInternal(fieldId, offset, mappingDestination.prefix(size)); if(fieldDestination) importerStateIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } @@ -2055,14 +2056,14 @@ Containers::Array> SceneData::importe , 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)}); + mappingIntoInternal(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; } Containers::Optional SceneData::parentFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); if(fieldId == ~UnsignedInt{}) return {}; @@ -2077,8 +2078,8 @@ Containers::Optional SceneData::parentFor(const UnsignedInt object) const { } Containers::Array SceneData::childrenFor(const Int object) const { - CORRADE_ASSERT(object >= -1 && object < Long(_objectCount), - "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object >= -1 && object < Long(_mappingBound), + "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt parentFieldId = findFieldIdInternal(SceneField::Parent); if(parentFieldId == ~UnsignedInt{}) return {}; @@ -2094,7 +2095,7 @@ Containers::Array SceneData::childrenFor(const Int object) const { UnsignedInt child[1]; /** @todo bleh slow, use the children <-> parent field proxying when implemented */ - objectsIntoInternal(parentFieldId, offset, child); + mappingIntoInternal(parentFieldId, offset, child); arrayAppend(out, *child); } } @@ -2103,8 +2104,8 @@ Containers::Array SceneData::childrenFor(const Int object) const { } Containers::Optional SceneData::transformation2DFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::transformation2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::transformation2DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); @@ -2124,8 +2125,8 @@ Containers::Optional SceneData::transformation2DFor(const UnsignedInt o } Containers::Optional> SceneData::translationRotationScaling2DFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::translationRotationScaling2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::translationRotationScaling2DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); @@ -2147,8 +2148,8 @@ Containers::Optional> SceneData::t } Containers::Optional SceneData::transformation3DFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::transformation3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::transformation3DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); @@ -2168,8 +2169,8 @@ Containers::Optional SceneData::transformation3DFor(const UnsignedInt o } Containers::Optional> SceneData::translationRotationScaling3DFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::translationRotationScaling3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::translationRotationScaling3DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); @@ -2191,8 +2192,8 @@ Containers::Optional> SceneData } Containers::Array> SceneData::meshesMaterialsFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt meshFieldId = findFieldIdInternal(SceneField::Mesh); if(meshFieldId == ~UnsignedInt{}) return {}; @@ -2215,8 +2216,8 @@ Containers::Array> SceneData::meshesMaterials } Containers::Array SceneData::lightsFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); if(fieldId == ~UnsignedInt{}) return {}; @@ -2238,8 +2239,8 @@ Containers::Array SceneData::lightsFor(const UnsignedInt object) co } Containers::Array SceneData::camerasFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); if(fieldId == ~UnsignedInt{}) return {}; @@ -2261,8 +2262,8 @@ Containers::Array SceneData::camerasFor(const UnsignedInt object) c } Containers::Array SceneData::skinsFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); if(fieldId == ~UnsignedInt{}) return {}; @@ -2284,8 +2285,8 @@ Containers::Array SceneData::skinsFor(const UnsignedInt object) con } Containers::Optional SceneData::importerStateFor(const UnsignedInt object) const { - CORRADE_ASSERT(object < _objectCount, - "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + CORRADE_ASSERT(object < _mappingBound, + "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState); if(fieldId == ~UnsignedInt{}) return {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 6af7dc26c9..a384f3acf2 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneObjectType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneObjectTypeSize(), @ref Magnum::sceneObjectTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() + * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneMappingType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneMappingTypeSize(), @ref Magnum::sceneMappingTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() */ #include @@ -44,17 +44,17 @@ namespace Magnum { namespace Trade { /** -@brief Scene object type +@brief Scene object mapping type @m_since_latest Type used for mapping fields to corresponding objects. Unlike -@ref SceneFieldType that is different for different fields, the object type is -the same for all fields, and is guaranteed to be large enough to fit all -@ref SceneData::objectCount() objects. -@see @ref SceneData::objectType(), @ref sceneObjectTypeSize(), - @ref sceneObjectTypeAlignment() +@ref SceneFieldType that is different for different fields, the object mapping +type is the same for all fields, and is guaranteed to be large enough to fit +@ref SceneData::mappingBound() objects. +@see @ref SceneData::mappingType(), @ref sceneMappingTypeSize(), + @ref sceneMappingTypeAlignment() */ -enum class SceneObjectType: UnsignedByte { +enum class SceneMappingType: UnsignedByte { /* Zero used for an invalid value */ UnsignedByte = 1, /**< @relativeref{Magnum,UnsignedByte} */ @@ -64,26 +64,26 @@ enum class SceneObjectType: UnsignedByte { }; /** -@debugoperatorenum{SceneObjectType} +@debugoperatorenum{SceneMappingType} @m_since_latest */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneObjectType value); +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneMappingType value); /** -@brief Size of given scene object type +@brief Size of given scene object mapping type @m_since_latest -@see @ref sceneObjectTypeAlignment() +@see @ref sceneMappingTypeAlignment() */ -MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeSize(SceneObjectType type); +MAGNUM_TRADE_EXPORT UnsignedInt sceneMappingTypeSize(SceneMappingType type); /** -@brief Alignment of given scene object type +@brief Alignment of given scene object mapping type @m_since_latest -Returns the same value as @ref sceneObjectTypeSize(). +Returns the same value as @ref sceneMappingTypeSize(). */ -MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeAlignment(SceneObjectType type); +MAGNUM_TRADE_EXPORT UnsignedInt sceneMappingTypeAlignment(SceneMappingType type); /** @brief Scene field name @@ -543,94 +543,94 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * initialization of the field array for @ref SceneData, expected to be * replaced with concrete values later. */ - constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _isOffsetOnly{}, _objectType{}, _objectStride{}, _objectData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} + constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _isOffsetOnly{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} /** * @brief Type-erased constructor * @param name Field name - * @param objectType Object type - * @param objectData Object data + * @param mappingType Object mapping type + * @param mappingData Object mapping data * @param fieldType Field type * @param fieldData Field data * @param fieldArraySize Field array size. Use @cpp 0 @ce for * non-array fields. * - * Expects that @p objectData and @p fieldData have the same size, + * Expects that @p mappingData and @p fieldData have the same size, * @p fieldType corresponds to @p name and @p fieldArraySize is zero * for builtin fields. */ - constexpr explicit SceneFieldData(SceneField name, SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; /** * @brief Constructor * @param name Field name - * @param objectData Object data + * @param mappingData Object mapping data * @param fieldType Field type * @param fieldData Field data * @param fieldArraySize Field array size. Use @cpp 0 @ce for * non-array fields. * - * Expects that @p objectData and @p fieldData have the same size in - * the first dimension, that the second dimension of @p objectData is + * Expects that @p mappingData and @p fieldData have the same size in + * the first dimension, that the second dimension of @p mappingData is * contiguous and its size is either 1, 2, 4 or 8, corresponding to one - * of the @ref SceneObjectType values, that the second dimension of + * of the @ref SceneMappingType values, that the second dimension of * @p fieldData is contiguous and its size matches @p fieldType and * @p fieldArraySize and that @p fieldType corresponds to @p name and * @p fieldArraySize is zero for builtin attributes. */ - explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& objectData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; /** * @brief Constructor * @param name Field name - * @param objectData Object data + * @param mappingData Object mapping data * @param fieldData Field data * - * Detects @ref SceneObjectType based on @p T and @ref SceneFieldType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort). + * Detects @ref SceneMappingType based on @p T and @ref SceneFieldType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort). * For all types known by Magnum, the detected @ref SceneFieldType is * of the same name as the type (so e.g. @relativeref{Magnum,Vector3ui} * gets recognized as @ref SceneFieldType::Vector3ui). */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView1D& fieldData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, objectData, Containers::stridedArrayView(fieldData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, mappingData, Containers::stridedArrayView(fieldData)} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), fieldData} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), Containers::stridedArrayView(fieldData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), Containers::stridedArrayView(fieldData)} {} /** * @brief Construct an array field * @param name Field name - * @param objectData Object data + * @param mappingData Object mapping data * @param fieldData Field data * - * Detects @ref SceneObjectType based on @p T and @ref SceneFieldType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * Detects @ref SceneMappingType based on @p T and @ref SceneFieldType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) * with the @p fieldData second dimension size passed to * @p fieldArraySize. Expects that the second dimension of @p fieldData * is contiguous. At the moment only custom fields can be arrays, which * means this function can't be used with a builtin @p name. See * @ref SceneFieldData(SceneField, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) - * for details about @ref SceneObjectType and @ref SceneFieldType + * for details about @ref SceneMappingType and @ref SceneFieldType * detection. */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView2D& fieldData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& objectData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(objectData), fieldData} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {} /** * @brief Construct an offset-only field * @param name Field name * @param size Number of entries - * @param objectType Object type - * @param objectOffset Object data offset - * @param objectStride Object data stride + * @param mappingType Object mapping type + * @param mappingOffset Object mapping data offset + * @param mappingStride Object mapping data stride * @param fieldType Field type * @param fieldOffset Field data offset * @param fieldStride Field data stride @@ -644,24 +644,24 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @p name and @p fieldArraySize is zero for builtin attributes. * * Note that due to the @cpp constexpr @ce nature of this constructor, - * no @p objectType checks against @p objectStride or + * no @p mappingType checks against @p mappingStride or * @p fieldType / @p fieldArraySize checks against @p fieldStride can - * be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneObjectType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) * constructor if you want additional safeguards. * @see @ref isOffsetOnly(), @ref fieldArraySize(), - * @ref objectData(Containers::ArrayView) const, + * @ref mappingData(Containers::ArrayView) const, * @ref fieldData(Containers::ArrayView) const */ - explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneObjectType objectType, std::size_t objectOffset, std::ptrdiff_t objectStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0) noexcept; + explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0) noexcept; /** * @brief If the field is offset-only * * Returns @cpp true @ce if the field doesn't contain the data views * directly, but instead refers to unspecified external data. - * @see @ref objectData(Containers::ArrayView) const, + * @see @ref mappingData(Containers::ArrayView) const, * @ref fieldData(Containers::ArrayView) const, - * @ref SceneFieldData(SceneField, std::size_t, SceneObjectType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort) + * @ref SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort) */ constexpr bool isOffsetOnly() const { return _isOffsetOnly; } @@ -671,36 +671,36 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { /** @brief Number of entries */ constexpr UnsignedLong size() const { return _size; } - /** @brief Object type */ - constexpr SceneObjectType objectType() const { return _objectType; } + /** @brief Object mapping type */ + constexpr SceneMappingType mappingType() const { return _mappingType; } /** - * @brief Type-erased object data + * @brief Type-erased object mapping data * * Expects that the field is not offset-only, in that case use the - * @ref objectData(Containers::ArrayView) const overload + * @ref mappingData(Containers::ArrayView) const overload * instead. * @see @ref isOffsetOnly() */ - constexpr Containers::StridedArrayView1D objectData() const { + constexpr Containers::StridedArrayView1D mappingData() const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ - {_objectData.pointer, ~std::size_t{}}, _size, - (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::objectData(): the field is offset-only, supply a data array"), _objectStride)}; + {_mappingData.pointer, ~std::size_t{}}, _size, + (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array"), _mappingStride)}; } /** - * @brief Type-erased object data for an offset-only attribute + * @brief Type-erased object mapping data for an offset-only attribute * * If the field is not offset-only, the @p data parameter is ignored. - * @see @ref isOffsetOnly(), @ref objectData() const + * @see @ref isOffsetOnly(), @ref mappingData() const */ - Containers::StridedArrayView1D objectData(Containers::ArrayView data) const { + Containers::StridedArrayView1D mappingData(Containers::ArrayView data) const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ - data, _isOffsetOnly ? reinterpret_cast(data.data()) + _objectData.offset : _objectData.pointer, _size, _objectStride}; + data, _isOffsetOnly ? reinterpret_cast(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride}; } /** @brief Field type */ @@ -755,9 +755,9 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { UnsignedLong _size; SceneField _name; bool _isOffsetOnly; - SceneObjectType _objectType; - Short _objectStride; - Data _objectData; + SceneMappingType _mappingType; + Short _mappingStride; + Data _mappingData; SceneFieldType _fieldType; Short _fieldStride; @@ -788,58 +788,60 @@ class MAGNUM_TRADE_EXPORT SceneData { public: /** * @brief Construct scene data - * @param objectType Object type - * @param objectCount Total count of all objects in the scene - * @param data Data for all fields and objects + * @param mappingType Object mapping type + * @param mappingBound Upper bound on object mapping indices in the + * scene + * @param data Data for all fields and object mappings * @param fields Description of all scene field data * @param importerState Importer-specific state * @m_since_latest * - * The @p objectType is expected to be large enough to index all - * @p objectCount objects. The @p fields are expected to reference + * The @p mappingType is expected to be large enough to index + * @p mappingBound objects. The @p fields are expected to reference * (sparse) sub-ranges of @p data, each having an unique - * @ref SceneField and @ref SceneObjectType equal to @p objectType. + * @ref SceneField, and @ref SceneMappingType equal to @p mappingType. * Particular fields can have additional restrictions, see * documentation of @ref SceneField values for more information. * * The @ref dataFlags() are implicitly set to a combination of * @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned data - * use the @ref SceneData(SceneObjectType, UnsignedLong, DataFlags, Containers::ArrayView, Containers::Array&&, const void*) + * use the @ref SceneData(SceneMappingType, UnsignedLong, DataFlags, Containers::ArrayView, Containers::Array&&, const void*) * constructor or its variants instead. */ - explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, Containers::Array&& data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + explicit SceneData(SceneMappingType mappingType, UnsignedLong mappingBound, Containers::Array&& data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; /** * @overload * @m_since_latest */ /* Not noexcept because allocation happens inside */ - explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, Containers::Array&& data, std::initializer_list fields, const void* importerState = nullptr); + explicit SceneData(SceneMappingType mappingType, UnsignedLong mappingBound, Containers::Array&& data, std::initializer_list fields, const void* importerState = nullptr); /** * @brief Construct non-owned scene data - * @param objectType Object type - * @param objectCount Total count of all objects in the scene + * @param mappingType Object mapping type + * @param mappingBound Upper bound on object mapping indices in the + * scene * @param dataFlags Data flags - * @param data View on data for all fields and objects + * @param data View on data for all fields and object mappings * @param fields Description of all scene field data * @param importerState Importer-specific state * @m_since_latest * - * Compared to @ref SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + * Compared to @ref SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) * creates an instance that doesn't own the passed data. The * @p dataFlags parameter can contain @ref DataFlag::Mutable to * indicate the external data can be modified, and is expected to *not* * have @ref DataFlag::Owned set. */ - explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + explicit SceneData(SceneMappingType mappingType, UnsignedLong mappingBound, DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; /** * @overload * @m_since_latest */ /* Not noexcept because allocation happens inside */ - explicit SceneData(SceneObjectType objectType, UnsignedLong objectCount, DataFlags dataFlags, Containers::ArrayView data, std::initializer_list fields, const void* importerState = nullptr); + explicit SceneData(SceneMappingType mappingType, UnsignedLong mappingBound, DataFlags dataFlags, Containers::ArrayView data, std::initializer_list fields, const void* importerState = nullptr); #ifdef MAGNUM_BUILD_DEPRECATED /** @@ -847,10 +849,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @param children2D Two-dimensional child objects * @param children3D Three-dimensional child objects * @param importerState Importer-specific state - * @m_deprecated_since_latest Use @ref SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) + * @m_deprecated_since_latest Use @ref SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) * instead. */ - explicit CORRADE_DEPRECATED("use SceneData(SceneObjectType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) instead") SceneData(std::vector children2D, std::vector children3D, const void* importerState = nullptr); + explicit CORRADE_DEPRECATED("use SceneData(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) instead") SceneData(std::vector children2D, std::vector children3D, const void* importerState = nullptr); #endif /** @brief Copying is not allowed */ @@ -871,8 +873,8 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Data flags * @m_since_latest * - * @see @ref releaseData(), @ref mutableData(), @ref mutableField(), - * @ref mutableObjects() + * @see @ref releaseData(), @ref mutableData(), @ref mutableMapping(), + * @ref mutableField() */ DataFlags dataFlags() const { return _dataFlags; } @@ -910,20 +912,20 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Type used for object mapping * @m_since_latest * - * Type returned from @ref objects() and @ref mutableObjects(). It's + * Type returned from @ref mapping() and @ref mutableMapping(). It's * the same for all fields and is guaranteed to be large enough to fit - * all @ref objectCount() objects. + * @ref mappingBound() objects. */ - SceneObjectType objectType() const { return _objectType; } + SceneMappingType mappingType() const { return _mappingType; } /** - * @brief Total object count + * @brief Object mapping bound * @m_since_latest * - * Total number of objects contained in the scene. + * Upper bound on object mapping indices of all fields in the scene. * @see @ref fieldCount(), @ref fieldSize() */ - UnsignedLong objectCount() const { return _objectCount; } + UnsignedLong mappingBound() const { return _mappingBound; } /** * @brief Field count @@ -933,8 +935,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * a scene with no fields. Each @ref SceneField can be present only * once, however an object can have a certain field associated with it * multiple times with different values (for example an object having - * multiple meshes). See also @ref objectCount() which returns count of - * actual objects. + * multiple meshes). */ UnsignedInt fieldCount() const { return _fields.size(); } @@ -945,7 +946,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * Returns the raw data that are used as a base for all `field*()` * accessors, or @cpp nullptr @ce if the scene has no fields. In most * cases you don't want to access those directly, but rather use the - * @ref objects(), @ref field(), @ref fieldName(), @ref fieldType(), + * @ref mapping(), @ref field(), @ref fieldName(), @ref fieldType(), * @ref fieldSize() and @ref fieldArraySize() accessors. Compared to * those and to @ref fieldData(UnsignedInt) const, the * @ref SceneFieldData instances returned by this function may have @@ -967,7 +968,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * Returns the raw data that are used as a base for all `field*()` * accessors. In most cases you don't want to access those directly, - * but rather use the @ref objects(), @ref field(), @ref fieldName(), + * but rather use the @ref mapping(), @ref field(), @ref fieldName(), * @ref fieldType(), @ref fieldSize() and @ref fieldArraySize() * accessors. This is also the reason why there's no overload taking a * @ref SceneField, unlike the other accessors. @@ -998,7 +999,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p id is expected to be smaller than @ref fieldCount(). You can * also use @ref fieldType(SceneField) const to directly get a type of * given named field. - * @see @ref fieldName(), @ref objectType() + * @see @ref fieldName(), @ref mappingType() */ SceneFieldType fieldType(UnsignedInt id) const; @@ -1006,9 +1007,9 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Size of given field * @m_since_latest * - * Size of the view returned by @ref field() / @ref mutableField() and - * also @ref objects() / @ref mutableObjects() for given @p id. Since - * an object can have multiple entries of the same field (for example + * Size of the view returned by @ref mapping() / @ref mutableMapping() + * and @ref field() / @ref mutableField() for given @p id. Since an + * object can have multiple entries of the same field (for example * multiple meshes associated with an object), the size doesn't * necessarily match the number of objects having given field. * @@ -1107,7 +1108,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * If @p object isn't present in @p fieldId starting at @p offset, * returns @ref Containers::NullOpt. The @p fieldId is expected to be * smaller than @ref fieldCount(), @p object smaller than - * @ref objectCount() and @p offset not larger than + * @ref mappingBound() and @p offset not larger than * @ref fieldSize(UnsignedInt) const. * * The lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with @@ -1126,7 +1127,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * If @p object isn't present in @p fieldName starting at @p offset, * returns @ref Containers::NullOpt. The @p fieldName is expected to - * exist, @p object is expected to be smaller than @ref objectCount() + * exist, @p object is expected to be smaller than @ref mappingBound() * and @p offset not be larger than @ref fieldSize(SceneField) const. * * The lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @@ -1165,7 +1166,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * The @p fieldId is expected to be smaller than @ref fieldCount() and - * @p object smaller than @ref objectCount(). + * @p object smaller than @ref mappingBound(). */ bool hasFieldObject(UnsignedInt fieldId, UnsignedInt object) const; @@ -1174,7 +1175,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * The @p fieldName is expected to exist and @p object is expected to - * be smaller than @ref objectCount(). + * be smaller than @ref mappingBound(). * @see @ref hasField() */ bool hasFieldObject(SceneField fieldName, UnsignedInt object) const; @@ -1212,33 +1213,33 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p fieldId is expected to be smaller than @ref fieldCount(). The * second dimension represents the actual data type (its size is equal - * to @ref SceneObjectType size) and is guaranteed to be contiguous. - * Use the templated overload below to get the objects in a concrete + * to @ref SceneMappingType size) and is guaranteed to be contiguous. + * Use the templated overload below to get the mapping in a concrete * type. - * @see @ref mutableObjects(UnsignedInt), + * @see @ref mutableMapping(UnsignedInt), * @ref Corrade::Containers::StridedArrayView::isContiguous(), - * @ref sceneObjectTypeSize() + * @ref sceneMappingTypeSize() */ - Containers::StridedArrayView2D objects(UnsignedInt fieldId) const; + Containers::StridedArrayView2D mapping(UnsignedInt fieldId) const; /** * @brief Mutable object mapping data for given field * @m_since_latest * - * Like @ref objects(UnsignedInt) const, but returns a mutable view. + * Like @ref mapping(UnsignedInt) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ - Containers::StridedArrayView2D mutableObjects(UnsignedInt fieldId); + Containers::StridedArrayView2D mutableMapping(UnsignedInt fieldId); /** * @brief Object mapping for given field * @m_since_latest * * The @p fieldId is expected to be smaller than @ref fieldCount() and - * @p T is expected to correspond to @ref objectType(). + * @p T is expected to correspond to @ref mappingType(). * - * You can also use the non-templated @ref objectsAsArray() accessor + * You can also use the non-templated @ref mappingAsArray() accessor * (or the combined @ref parentsAsArray(), * @ref transformations2DAsArray(), @ref transformations3DAsArray(), * @ref translationsRotationsScalings2DAsArray(), @@ -1248,19 +1249,19 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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) + * @see @ref mutableMapping(UnsignedInt) */ - template Containers::StridedArrayView1D objects(UnsignedInt fieldId) const; + template Containers::StridedArrayView1D mapping(UnsignedInt fieldId) const; /** * @brief Mutable object mapping for given field * @m_since_latest * - * Like @ref objects(UnsignedInt) const, but returns a mutable view. + * Like @ref mapping(UnsignedInt) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ - template Containers::StridedArrayView1D mutableObjects(UnsignedInt fieldId); + template Containers::StridedArrayView1D mutableMapping(UnsignedInt fieldId); /** * @brief Object mapping data for given named field @@ -1268,32 +1269,33 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p fieldName is expected to exist. The second dimension * represents the actual data type (its size is equal to - * @ref SceneObjectType size) and is guaranteed to be contiguous. Use - * the templated overload below to get the objects in a concrete type. - * @see @ref hasField(), @ref objects(UnsignedInt) const, - * @ref mutableObjects(SceneField), + * @ref SceneMappingType size) and is guaranteed to be contiguous. Use + * the templated overload below to get the object mapping in a concrete + * type. + * @see @ref hasField(), @ref mapping(UnsignedInt) const, + * @ref mutableMapping(SceneField), * @ref Corrade::Containers::StridedArrayView::isContiguous() */ - Containers::StridedArrayView2D objects(SceneField fieldName) const; + Containers::StridedArrayView2D mapping(SceneField fieldName) const; /** * @brief Mutable object mapping data for given named field * @m_since_latest * - * Like @ref objects(SceneField) const, but returns a mutable view. + * Like @ref mapping(SceneField) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ - Containers::StridedArrayView2D mutableObjects(SceneField fieldName); + Containers::StridedArrayView2D mutableMapping(SceneField fieldName); /** * @brief Object mapping for given named field * @m_since_latest * * The @p fieldName is expected to exist and @p T is expected to - * correspond to @ref objectType(). + * correspond to @ref mappingType(). * - * You can also use the non-templated @ref objectsAsArray() accessor + * You can also use the non-templated @ref mappingAsArray() accessor * (or the combined @ref parentsAsArray(), * @ref transformations2DAsArray(), @ref transformations3DAsArray(), * @ref translationsRotationsScalings2DAsArray(), @@ -1303,20 +1305,20 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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) + * @see @ref hasField(), @ref mapping(UnsignedInt) const, + * @ref mutableMapping(UnsignedInt) */ - template Containers::StridedArrayView1D objects(SceneField fieldName) const; + template Containers::StridedArrayView1D mapping(SceneField fieldName) const; /** * @brief Mutable object mapping for given named field * @m_since_latest * - * Like @ref objects(SceneField) const, but returns a mutable view. + * Like @ref mapping(SceneField) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ - template Containers::StridedArrayView1D mutableObjects(SceneField fieldName); + template Containers::StridedArrayView1D mutableMapping(SceneField fieldName); /** * @brief Data for given field @@ -1485,7 +1487,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Convenience alternative to the templated - * @ref objects(UnsignedInt) const that converts the field from an + * @ref mapping(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(). * @@ -1498,15 +1500,15 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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 + * @see @ref mappingInto(UnsignedInt, const Containers::StridedArrayView1D&) const */ - Containers::Array objectsAsArray(UnsignedInt fieldId) const; + Containers::Array mappingAsArray(UnsignedInt fieldId) const; /** * @brief Object mapping for given field as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref objectsAsArray(UnsignedInt) const, but puts the result + * Like @ref mappingAsArray(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. * @@ -1521,13 +1523,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * * @see @ref fieldSize(UnsignedInt) const */ - void objectsInto(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; + void mappingInto(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; /** * @brief A subrange of object mapping for given field as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref objectsInto(UnsignedInt, const Containers::StridedArrayView1D&) const + * Compared to @ref mappingInto(UnsignedInt, const Containers::StridedArrayView1D&) const * extracts only a subrange of the object mapping 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 @@ -1545,14 +1547,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(UnsignedInt) const, * @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const */ - std::size_t objectsInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t mappingInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; /** * @brief Object mapping for given named field as 32-bit integers * @m_since_latest * * Convenience alternative to the templated - * @ref objects(SceneField) const that converts the field from an + * @ref mapping(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. * @@ -1565,16 +1567,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * @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, + * @see @ref mappingInto(SceneField, const Containers::StridedArrayView1D&) const, * @ref hasField() */ - Containers::Array objectsAsArray(SceneField fieldName) const; + Containers::Array mappingAsArray(SceneField fieldName) const; /** * @brief Object mapping for given named field as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref objectsAsArray(SceneField) const, but puts the result into + * Like @ref mappingAsArray(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. * @@ -1589,13 +1591,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * * @see @ref fieldSize(SceneField) const */ - void objectsInto(SceneField fieldName, const Containers::StridedArrayView1D& destination) const; + void mappingInto(SceneField fieldName, const Containers::StridedArrayView1D& destination) const; /** * @brief A subrange of object mapping for given named field as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref objectsInto(SceneField, const Containers::StridedArrayView1D&) const + * Compared to @ref mappingInto(SceneField, const Containers::StridedArrayView1D&) const * extracts only a subrange of the object mapping 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 @@ -1613,13 +1615,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t objectsInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t mappingInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D& destination) const; /** * @brief Parent indices as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1634,14 +1636,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * 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 + * @p mappingDestination 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. + * @ref mappingInto() with @ref SceneField::Parent. * @see @ref fieldSize(SceneField) const */ - void parentsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void parentsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of parent indices as 32-bit integers into a pre-allocated view @@ -1655,13 +1657,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief 2D transformations as 3x3 float matrices * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1682,17 +1684,17 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref transformations2DAsArray(), 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 + * @p mappingDestination 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 mappingInto() 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& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void transformations2DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of 2D transformations as 3x3 float matrices into a pre-allocated view @@ -1706,13 +1708,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief 2D transformations as float translation, rotation and scaling components * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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. @@ -1737,18 +1739,18 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref translationsRotationsScalings2DAsArray(), but puts the - * result into @p objectDestination, @p translationDestination, + * result into @p mappingDestination, @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 + * same as calling @ref mappingInto() 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& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + void translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& mappingDestination, 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 @@ -1762,13 +1764,13 @@ 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& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, 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 objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1789,17 +1791,17 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref transformations3DAsArray(), but puts the result into - * @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 + * @p mappingDestination 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 mappingInto() 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& objectDestination, const Containers::StridedArrayView1D& destination) const; + void transformations3DInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& destination) const; /** * @brief A subrange of 3D transformations as 4x4 float matrices into a pre-allocated view @@ -1813,13 +1815,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& destination) const; + std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& destination) const; /** * @brief 3D transformations as float translation, rotation and scaling components * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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. @@ -1844,18 +1846,18 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref translationsRotationsScalings3DAsArray(), but puts the - * result into @p objectDestination, @p translationDestination, + * result into @p mappingDestination, @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 + * same as calling @ref mappingInto() 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& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + void translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& mappingDestination, 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 @@ -1869,13 +1871,13 @@ 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& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + std::size_t translationsRotationsScalings3DInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, 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 objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1893,15 +1895,15 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref meshesMaterialsAsArray(), but puts the results into - * @p objectDestination, @p meshDestination and + * @p mappingDestination, @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. + * the same as calling @ref mappingInto() with @ref SceneField::Mesh. * @see @ref fieldSize(SceneField) const */ - void meshesMaterialsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; + void meshesMaterialsInto(const Containers::StridedArrayView1D& mappingDestination, 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 @@ -1915,13 +1917,13 @@ 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& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialsDestination) const; + std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialsDestination) const; /** * @brief Light IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1935,14 +1937,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * 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 + * @p mappingDestination 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& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void lightsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of light IDs as 32-bit integers into a pre-allocated view @@ -1956,13 +1958,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Camera IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -1976,14 +1978,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref camerasAsArray(), 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 + * @p mappingDestination 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. + * @ref mappingInto() with @ref SceneField::Camera. * @see @ref fieldSize(SceneField) const */ - void camerasInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void camerasInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of camera IDs as 32-bit integers into a pre-allocated view @@ -1997,13 +1999,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Skin IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -2017,14 +2019,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref skinsAsArray(), but puts the result into - * @p objectDestination and @p fieldDestination instead of allocating a + * @p mappingDestination 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. + * @ref mappingInto() with @ref SceneField::Skin. * @see @ref fieldSize(SceneField) const */ - void skinsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void skinsInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of skin IDs as 32-bit integers into a pre-allocated view @@ -2038,13 +2040,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Per-object importer state as `void` pointers * @m_since_latest * - * Convenience alternative to @ref objects(SceneField) const together + * Convenience alternative to @ref mapping(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 @@ -2061,14 +2063,14 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * Like @ref importerStateAsArray(), 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 + * @p mappingDestination 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. + * @ref mappingInto() with @ref SceneField::ImporterState. * @see @ref fieldSize(SceneField) const */ - void importerStateInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + void importerStateInto(const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of per-object importer state as `void` pointers into a pre-allocated view @@ -2082,7 +2084,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; + std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Parent for given object @@ -2100,7 +2102,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * parent for @p object, returns @ref Containers::NullOpt. If @p object * is top-level, returns @cpp -1 @ce. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref childrenFor() */ Containers::Optional parentFor(UnsignedInt object) const; @@ -2121,7 +2123,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * objects which would have @p object listed as their parent, returns * an empty array. Pass @cpp -1 @ce to get a list of top-level objects. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref parentFor() */ Containers::Array childrenFor(Int object) const; @@ -2147,7 +2149,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * 3D transformation or there's no transformation for @p object, * returns @ref Containers::NullOpt. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref translationRotationScaling2DFor() */ Containers::Optional transformation2DFor(UnsignedInt object) const; @@ -2176,7 +2178,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * there's no transformation for @p object, returns * @ref Containers::NullOpt. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref transformation2DFor() */ Containers::Optional> translationRotationScaling2DFor(UnsignedInt object) const; @@ -2202,7 +2204,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * 2D transformation or there's no transformation for @p object, * returns @ref Containers::NullOpt. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref translationRotationScaling3DFor() */ Containers::Optional transformation3DFor(UnsignedInt object) const; @@ -2231,7 +2233,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * there's no transformation for @p object, returns * @ref Containers::NullOpt. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). * @see @ref transformation3DFor() */ Containers::Optional> translationRotationScaling3DFor(UnsignedInt object) const; @@ -2254,7 +2256,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref SceneField::Mesh is not present or if there's no mesh for * @p object, returns an empty array. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array> meshesMaterialsFor(UnsignedInt object) const; @@ -2272,7 +2274,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * If the @ref SceneField::Light field is not present or if there's no * light for @p object, returns an empty array. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array lightsFor(UnsignedInt object) const; @@ -2291,7 +2293,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * If the @ref SceneField::Camera field is not present or if there's no * camera for @p object, returns an empty array. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array camerasFor(UnsignedInt object) const; @@ -2309,7 +2311,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * If the @ref SceneField::Skin field is not present or if there's no * skin for @p object, returns an empty array. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array skinsFor(UnsignedInt object) const; @@ -2329,7 +2331,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * there's no importer state for @p object, returns * @ref Containers::NullOpt. * - * The @p object is expected to be less than @ref objectCount(). + * The @p object is expected to be less than @ref mappingBound(). */ Containers::Optional importerStateFor(UnsignedInt object) const; @@ -2407,14 +2409,14 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Returns the offset at which `object` is for field at index `id`, or the end offset if the object is not found. The returned offset can - be then passed to fieldData{Object,Field}ViewInternal(). */ + be then passed to fieldData{Mapping,Field}ViewInternal(). */ MAGNUM_TRADE_LOCAL std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const; /* Like objects() / field(), but returning just a 1D view, sliced from offset to offset + size. The parameterless overloads are equal to offset = 0 and size = field.size(). */ - MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataObjectViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const; - MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataObjectViewInternal(const SceneFieldData& field) const; + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataMappingViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const; + MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataMappingViewInternal(const SceneFieldData& field) const; MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataFieldViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const; MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D fieldDataFieldViewInternal(const SceneFieldData& field) const; @@ -2422,7 +2424,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template bool checkFieldTypeCompatibility(const SceneFieldData& attribute, const char* prefix) const; #endif - MAGNUM_TRADE_LOCAL void objectsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; + MAGNUM_TRADE_LOCAL void mappingIntoInternal(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 UnsignedInt findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; MAGNUM_TRADE_LOCAL UnsignedInt findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; @@ -2437,10 +2439,10 @@ class MAGNUM_TRADE_EXPORT SceneData { MAGNUM_TRADE_LOCAL void importerStateIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; DataFlags _dataFlags; - SceneObjectType _objectType; + SceneMappingType _mappingType; UnsignedByte _dimensions; /* 1/5 bytes free */ - UnsignedLong _objectCount; + UnsignedLong _mappingBound; const void* _importerState; Containers::Array _fields; Containers::Array _data; @@ -2569,13 +2571,13 @@ namespace Implementation { } }; - template constexpr SceneObjectType sceneObjectTypeFor() { - static_assert(sizeof(T) == 0, "unsupported object type"); + template constexpr SceneMappingType sceneMappingTypeFor() { + static_assert(sizeof(T) == 0, "unsupported mapping type"); return {}; } #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type) \ - template<> constexpr SceneObjectType sceneObjectTypeFor() { return SceneObjectType::type; } + template<> constexpr SceneMappingType sceneMappingTypeFor() { return SceneMappingType::type; } _c(UnsignedByte) _c(UnsignedShort) _c(UnsignedInt) @@ -2638,16 +2640,16 @@ namespace Implementation { } } -constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize) noexcept: - _size{(CORRADE_CONSTEXPR_ASSERT(objectData.size() == fieldData.size(), - "Trade::SceneFieldData: expected object and field view to have the same size but got" << objectData.size() << "and" << fieldData.size()), objectData.size())}, +constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize) noexcept: + _size{(CORRADE_CONSTEXPR_ASSERT(mappingData.size() == fieldData.size(), + "Trade::SceneFieldData: expected mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldData.size()), mappingData.size())}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, _isOffsetOnly{false}, - _objectType{objectType}, - _objectStride{(CORRADE_CONSTEXPR_ASSERT(objectData.stride() >= -32768 && objectData.stride() <= 32767, - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got" << objectData.stride()), Short(objectData.stride()))}, - _objectData{objectData.data()}, + _mappingType{mappingType}, + _mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingData.stride() >= -32768 && mappingData.stride() <= 32767, + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingData.stride()), Short(mappingData.stride()))}, + _mappingData{mappingData.data()}, _fieldType{fieldType}, _fieldStride{(CORRADE_CONSTEXPR_ASSERT(fieldData.stride() >= -32768 && fieldData.stride() <= 32767, "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got" << fieldData.stride()), Short(fieldData.stride()))}, @@ -2655,27 +2657,27 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneObjec "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, _fieldData{fieldData.data()} {} -template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Implementation::sceneObjectTypeFor::type>(), objectData, Implementation::SceneFieldTypeFor::type>::type(), fieldData, 0} {} +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor::type>(), mappingData, Implementation::SceneFieldTypeFor::type>::type(), fieldData, 0} {} -template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& objectData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{ +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{ name, - Implementation::sceneObjectTypeFor::type>(), - objectData, + Implementation::sceneMappingTypeFor::type>(), + mappingData, Implementation::SceneFieldTypeFor::type>::type(), Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, /* Not using isContiguous<1>() as that's not constexpr */ (CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])) } {} -constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneObjectType objectType, const std::size_t objectOffset, const std::ptrdiff_t objectStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize) noexcept: +constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize) noexcept: _size{size}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, _isOffsetOnly{true}, - _objectType{objectType}, - _objectStride{(CORRADE_CONSTEXPR_ASSERT(objectStride >= -32768 && objectStride <= 32767, - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got" << objectStride), Short(objectStride))}, - _objectData{objectOffset}, + _mappingType{mappingType}, + _mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingStride >= -32768 && mappingStride <= 32767, + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingStride), Short(mappingStride))}, + _mappingData{mappingOffset}, _fieldType{fieldType}, _fieldStride{(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767, "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got" << fieldStride), Short(fieldStride))}, @@ -2683,43 +2685,43 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_ "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, _fieldData{fieldOffset} {} -template Containers::StridedArrayView1D SceneData::objects(const UnsignedInt fieldId) const { - Containers::StridedArrayView2D data = objects(fieldId); +template Containers::StridedArrayView1D SceneData::mapping(const UnsignedInt fieldId) const { + Containers::StridedArrayView2D data = mapping(fieldId); #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, - "Trade::SceneData::objects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + CORRADE_ASSERT(Implementation::sceneMappingTypeFor() == _mappingType, + "Trade::SceneData::mapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, const T>(data); } -template Containers::StridedArrayView1D SceneData::mutableObjects(const UnsignedInt fieldId) { - Containers::StridedArrayView2D data = mutableObjects(fieldId); +template Containers::StridedArrayView1D SceneData::mutableMapping(const UnsignedInt fieldId) { + Containers::StridedArrayView2D data = mutableMapping(fieldId); #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, - "Trade::SceneData::mutableObjects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + CORRADE_ASSERT(Implementation::sceneMappingTypeFor() == _mappingType, + "Trade::SceneData::mutableMapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, T>(data); } -template Containers::StridedArrayView1D SceneData::objects(const SceneField fieldName) const { - Containers::StridedArrayView2D data = objects(fieldName); +template Containers::StridedArrayView1D SceneData::mapping(const SceneField fieldName) const { + Containers::StridedArrayView2D data = mapping(fieldName); #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, - "Trade::SceneData::objects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + CORRADE_ASSERT(Implementation::sceneMappingTypeFor() == _mappingType, + "Trade::SceneData::mapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, const T>(data); } -template Containers::StridedArrayView1D SceneData::mutableObjects(const SceneField fieldName) { - Containers::StridedArrayView2D data = mutableObjects(fieldName); +template Containers::StridedArrayView1D SceneData::mutableMapping(const SceneField fieldName) { + Containers::StridedArrayView2D data = mutableMapping(fieldName); #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::sceneObjectTypeFor() == _objectType, - "Trade::SceneData::mutableObjects(): objects are" << _objectType << "but requested" << Implementation::sceneObjectTypeFor(), {}); + CORRADE_ASSERT(Implementation::sceneMappingTypeFor() == _mappingType, + "Trade::SceneData::mutableMapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, T>(data); } diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 3c6bfc9488..836f464177 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -1703,8 +1703,8 @@ void AbstractImporterTest::scene() { } Containers::Optional doScene(UnsignedInt id) override { if(id == 7) - return SceneData{SceneObjectType::UnsignedByte, 0, nullptr, {}, &state}; - return SceneData{SceneObjectType::UnsignedByte, 0, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedByte, 0, nullptr, {}, &state}; + return SceneData{SceneMappingType::UnsignedByte, 0, nullptr, {}}; } } importer; @@ -1899,21 +1899,21 @@ void AbstractImporterTest::sceneDeprecatedFallback2D() { /* This one has seven objects, but no fields for them so it should get skipped */ if(id == 0) - return SceneData{SceneObjectType::UnsignedByte, 7, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedByte, 7, nullptr, {}}; /* This one has no objects, so it should get skipped as well without even querying any fieldFor() API (as those would assert) */ if(id == 1) - return SceneData{SceneObjectType::UnsignedShort, 0, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedShort, 0, nullptr, {}}; /* This one is the one */ if(id == 2) - return SceneData{SceneObjectType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 7, std::move(data), { + } importer{SceneData{SceneMappingType::UnsignedInt, 7, std::move(data), { SceneFieldData{SceneField::Parent, transformations.slice(&Transform::object), transformations.slice(&Transform::parent)}, @@ -2171,21 +2171,21 @@ void AbstractImporterTest::sceneDeprecatedFallback3D() { /* This one has seven objects, but no fields for them so it should get skipped */ if(id == 0) - return SceneData{SceneObjectType::UnsignedByte, 7, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedByte, 7, nullptr, {}}; /* This one has no objects, so it should get skipped as well without even querying any fieldFor() API (as those would assert) */ if(id == 1) - return SceneData{SceneObjectType::UnsignedShort, 0, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedShort, 0, nullptr, {}}; /* This one is the one */ if(id == 2) - return SceneData{SceneObjectType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 7, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 7, std::move(data), { + } importer{SceneData{SceneMappingType::UnsignedInt, 7, std::move(data), { SceneFieldData{SceneField::Parent, transformations.slice(&Transform::object), transformations.slice(&Transform::parent)}, @@ -2356,12 +2356,12 @@ void AbstractImporterTest::sceneDeprecatedFallbackParentless2D() { UnsignedInt doSceneCount() const override { return 1; } UnsignedLong doObjectCount() const override { return 6; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + } importer{SceneData{SceneMappingType::UnsignedInt, 6, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} @@ -2431,12 +2431,12 @@ void AbstractImporterTest::sceneDeprecatedFallbackParentless3D() { UnsignedInt doSceneCount() const override { return 1; } UnsignedLong doObjectCount() const override { return 6; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + } importer{SceneData{SceneMappingType::UnsignedInt, 6, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} @@ -2506,17 +2506,17 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless2D() { UnsignedInt doSceneCount() const override { return 1; } UnsignedLong doObjectCount() const override { return 6; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + } importer{SceneData{SceneMappingType::UnsignedInt, 6, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}, /* Required in order to have the scene recognized as 2D */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr} + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr} }}}; CORRADE_COMPARE(importer.sceneCount(), 1); @@ -2612,17 +2612,17 @@ void AbstractImporterTest::sceneDeprecatedFallbackTransformless3D() { UnsignedInt doSceneCount() const override { return 1; } UnsignedLong doObjectCount() const override { return 6; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 6, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; } private: SceneData _data; - } importer{SceneData{SceneObjectType::UnsignedInt, 6, {}, fields, { + } importer{SceneData{SceneMappingType::UnsignedInt, 6, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}, /* Required in order to have the scene recognized as 3D */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr} + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr} }}}; CORRADE_COMPARE(importer.sceneCount(), 1); @@ -2747,20 +2747,20 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { {30, 1, -1} }, meshesSecondary); - SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { + SceneData data{SceneMappingType::UnsignedInt, 32, std::move(dataData), { SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, /* Just to disambiguate this as a 2D scene */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, }}; - SceneData dataSecondary{SceneObjectType::UnsignedInt, 31, std::move(dataDataSecondary), { + SceneData dataSecondary{SceneMappingType::UnsignedInt, 31, std::move(dataDataSecondary), { SceneFieldData{SceneField::Parent, parentsSecondary.slice(&Parent::object), parentsSecondary.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::meshMaterial)}, /* Just to disambiguate this as a 2D scene */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, }}; struct Importer: AbstractImporter { @@ -2784,19 +2784,19 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { /* This scene should get skipped when querying names as it's not 2D */ if(id == 0) - return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedByte, 32, nullptr, {}}; /* This scene should get skipped when querying names as it has too little objects */ if(id == 1) - return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Matrix3x3, nullptr} + return SceneData{SceneMappingType::UnsignedByte, 32, nullptr, { + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Matrix3x3, nullptr} }}; if(id == 2) - return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; /* A secondary scene, which should have non-overlapping IDs for the newly added objects */ if(id == 3) - return SceneData{SceneObjectType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } @@ -3014,20 +3014,20 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { {30, 1, -1} }, meshesSecondary); - SceneData data{SceneObjectType::UnsignedInt, 32, std::move(dataData), { + SceneData data{SceneMappingType::UnsignedInt, 32, std::move(dataData), { SceneFieldData{SceneField::Parent, parents.slice(&Parent::object), parents.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, /* Just to disambiguate this as a 3D scene */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, }}; - SceneData dataSecondary{SceneObjectType::UnsignedInt, 31, std::move(dataDataSecondary), { + SceneData dataSecondary{SceneMappingType::UnsignedInt, 31, std::move(dataDataSecondary), { SceneFieldData{SceneField::Parent, parentsSecondary.slice(&Parent::object), parentsSecondary.slice(&Parent::parent)}, SceneFieldData{SceneField::Mesh, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshesSecondary.slice(&Mesh::object), meshesSecondary.slice(&Mesh::meshMaterial)}, /* Just to disambiguate this as a 3D scene */ - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, }}; struct Importer: AbstractImporter { @@ -3051,19 +3051,19 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { /* This scene should get skipped when querying names as it's not 2D */ if(id == 0) - return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, {}}; + return SceneData{SceneMappingType::UnsignedByte, 32, nullptr, {}}; /* This scene should get skipped when querying names as it has too little objects */ if(id == 1) - return SceneData{SceneObjectType::UnsignedByte, 32, nullptr, { - SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Matrix4x4, nullptr} + return SceneData{SceneMappingType::UnsignedByte, 32, nullptr, { + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Matrix4x4, nullptr} }}; if(id == 2) - return SceneData{SceneObjectType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 32, {}, _data.data(), sceneFieldDataNonOwningArray(_data.fieldData())}; /* A secondary scene, which should have non-overlapping IDs for the newly added objects */ if(id == 3) - return SceneData{SceneObjectType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; + return SceneData{SceneMappingType::UnsignedInt, 31, {}, _dataSecondary.data(), sceneFieldDataNonOwningArray(_dataSecondary.fieldData())}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } @@ -3282,13 +3282,13 @@ void AbstractImporterTest::sceneDeprecatedFallbackBoth2DAnd3DScene() { return {}; } Containers::Optional doScene(UnsignedInt id) override { - if(id == 0) return SceneData{SceneObjectType::UnsignedInt, 7, nullptr, { - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr}, + if(id == 0) return SceneData{SceneMappingType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr}, }}; - if(id == 1) return SceneData{SceneObjectType::UnsignedInt, 7, nullptr, { - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + if(id == 1) return SceneData{SceneMappingType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, }}; CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } @@ -3432,14 +3432,14 @@ void AbstractImporterTest::sceneNonOwningDeleters() { UnsignedInt doSceneCount() const override { return 1; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 0, + return SceneData{SceneMappingType::UnsignedInt, 0, Containers::Array{data, 1, Implementation::nonOwnedArrayDeleter}, sceneFieldDataNonOwningArray(fields)}; } char data[1]; SceneFieldData fields[1]{ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr} + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr} }; } importer; @@ -3462,7 +3462,7 @@ void AbstractImporterTest::sceneCustomDataDeleter() { UnsignedInt doSceneCount() const override { return 1; } Int doSceneForName(const std::string&) override { return 0; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 0, + return SceneData{SceneMappingType::UnsignedInt, 0, Containers::Array{data, 1, [](char*, std::size_t) {}}, {}}; } @@ -3493,10 +3493,10 @@ void AbstractImporterTest::sceneCustomFieldDataDeleter() { UnsignedInt doSceneCount() const override { return 1; } Int doSceneForName(const std::string&) override { return 0; } Containers::Optional doScene(UnsignedInt) override { - return SceneData{SceneObjectType::UnsignedInt, 0, nullptr, Containers::Array{&parents, 1, [](SceneFieldData*, std::size_t) {}}}; + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, Containers::Array{&parents, 1, [](SceneFieldData*, std::size_t) {}}}; } - SceneFieldData parents{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; + SceneFieldData parents{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; } importer; std::ostringstream out; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 20c8d190de..c3c9870027 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -50,9 +50,9 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct SceneDataTest: TestSuite::Tester { explicit SceneDataTest(); - void objectTypeSizeAlignment(); - void objectTypeSizeAlignmentInvalid(); - void debugObjectType(); + void mappingTypeSizeAlignment(); + void mappingTypeSizeAlignmentInvalid(); + void debugMappingType(); void customFieldName(); void customFieldNameTooLarge(); @@ -77,7 +77,7 @@ struct SceneDataTest: TestSuite::Tester { void constructFieldWrongType(); void constructFieldInconsistentViewSize(); - void constructFieldTooLargeObjectStride(); + void constructFieldTooLargeMappingStride(); void constructFieldTooLargeFieldStride(); void constructFieldWrongDataAccess(); void constructField2DWrongSize(); @@ -99,10 +99,10 @@ struct SceneDataTest: TestSuite::Tester { void constructDuplicateField(); void constructDuplicateCustomField(); - void constructInconsistentObjectType(); - void constructObjectDataNotContained(); + void constructInconsistentMappingType(); + void constructMappingDataNotContained(); void constructFieldDataNotContained(); - void constructObjectTypeTooSmall(); + void constructMappingTypeTooSmall(); void constructNotOwnedFlagOwned(); void constructMismatchedTRSViews(); template void constructMismatchedTRSDimensionality(); @@ -117,11 +117,11 @@ struct SceneDataTest: TestSuite::Tester { void findFieldObjectOffsetInvalidOffset(); void fieldObjectOffsetNotFound(); - template void objectsAsArrayByIndex(); - template void objectsAsArrayByName(); - void objectsIntoArrayByIndex(); - void objectsIntoArrayByName(); - void objectsIntoArrayInvalidSizeOrOffset(); + template void mappingAsArrayByIndex(); + template void mappingAsArrayByName(); + void mappingIntoArrayByIndex(); + void mappingIntoArrayByName(); + void mappingIntoArrayInvalidSizeOrOffset(); template void parentsAsArray(); void parentsIntoArray(); @@ -160,8 +160,8 @@ struct SceneDataTest: TestSuite::Tester { void mutableAccessNotAllowed(); - void objectsNotFound(); - void objectsWrongType(); + void mappingNotFound(); + void mappingWrongType(); void fieldNotFound(); void fieldWrongType(); @@ -220,13 +220,13 @@ const struct { std::size_t offset; std::size_t size; std::size_t expectedSize; - bool objects, field; + bool mapping, 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 mapping", 0, 3, 3, true, false}, {"only field", 0, 3, 3, false, true}, {"neither", 0, 3, 0, false, false} }; @@ -236,13 +236,13 @@ const struct { std::size_t offset; std::size_t size; std::size_t expectedSize; - bool objects, field1, field2; + bool mapping, 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 mapping", 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}, @@ -254,13 +254,13 @@ const struct { std::size_t offset; std::size_t size; std::size_t expectedSize; - bool objects, field1, field2, field3; + bool mapping, 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 mapping", 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}, @@ -285,9 +285,9 @@ const struct { #endif SceneDataTest::SceneDataTest() { - addTests({&SceneDataTest::objectTypeSizeAlignment, - &SceneDataTest::objectTypeSizeAlignmentInvalid, - &SceneDataTest::debugObjectType, + addTests({&SceneDataTest::mappingTypeSizeAlignment, + &SceneDataTest::mappingTypeSizeAlignmentInvalid, + &SceneDataTest::debugMappingType, &SceneDataTest::customFieldName, &SceneDataTest::customFieldNameTooLarge, @@ -312,7 +312,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructFieldWrongType, &SceneDataTest::constructFieldInconsistentViewSize, - &SceneDataTest::constructFieldTooLargeObjectStride, + &SceneDataTest::constructFieldTooLargeMappingStride, &SceneDataTest::constructFieldTooLargeFieldStride, &SceneDataTest::constructFieldWrongDataAccess, &SceneDataTest::constructField2DWrongSize, @@ -339,10 +339,10 @@ SceneDataTest::SceneDataTest() { addTests({&SceneDataTest::constructDuplicateField, &SceneDataTest::constructDuplicateCustomField, - &SceneDataTest::constructInconsistentObjectType, - &SceneDataTest::constructObjectDataNotContained, + &SceneDataTest::constructInconsistentMappingType, + &SceneDataTest::constructMappingDataNotContained, &SceneDataTest::constructFieldDataNotContained, - &SceneDataTest::constructObjectTypeTooSmall, + &SceneDataTest::constructMappingTypeTooSmall, &SceneDataTest::constructNotOwnedFlagOwned, &SceneDataTest::constructMismatchedTRSViews, &SceneDataTest::constructMismatchedTRSDimensionality, @@ -361,20 +361,20 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::findFieldObjectOffsetInvalidOffset, &SceneDataTest::fieldObjectOffsetNotFound, - &SceneDataTest::objectsAsArrayByIndex, - &SceneDataTest::objectsAsArrayByIndex, - &SceneDataTest::objectsAsArrayByIndex, - &SceneDataTest::objectsAsArrayByIndex, - &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayByName, - &SceneDataTest::objectsAsArrayByName}); - - addInstancedTests({&SceneDataTest::objectsIntoArrayByIndex, - &SceneDataTest::objectsIntoArrayByName}, + &SceneDataTest::mappingAsArrayByIndex, + &SceneDataTest::mappingAsArrayByIndex, + &SceneDataTest::mappingAsArrayByIndex, + &SceneDataTest::mappingAsArrayByIndex, + &SceneDataTest::mappingAsArrayByName, + &SceneDataTest::mappingAsArrayByName, + &SceneDataTest::mappingAsArrayByName, + &SceneDataTest::mappingAsArrayByName}); + + addInstancedTests({&SceneDataTest::mappingIntoArrayByIndex, + &SceneDataTest::mappingIntoArrayByName}, Containers::arraySize(IntoArrayOffsetData)); - addTests({&SceneDataTest::objectsIntoArrayInvalidSizeOrOffset, + addTests({&SceneDataTest::mappingIntoArrayInvalidSizeOrOffset, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, @@ -465,8 +465,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::mutableAccessNotAllowed, - &SceneDataTest::objectsNotFound, - &SceneDataTest::objectsWrongType, + &SceneDataTest::mappingNotFound, + &SceneDataTest::mappingWrongType, &SceneDataTest::fieldNotFound, &SceneDataTest::fieldWrongType, @@ -503,18 +503,18 @@ SceneDataTest::SceneDataTest() { using namespace Math::Literals; -void SceneDataTest::objectTypeSizeAlignment() { - CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedByte), 1); - CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedByte), 1); - CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedShort), 2); - CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedShort), 2); - CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedInt), 4); - CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedInt), 4); - CORRADE_COMPARE(sceneObjectTypeSize(SceneObjectType::UnsignedLong), 8); - CORRADE_COMPARE(sceneObjectTypeAlignment(SceneObjectType::UnsignedLong), 8); +void SceneDataTest::mappingTypeSizeAlignment() { + CORRADE_COMPARE(sceneMappingTypeSize(SceneMappingType::UnsignedByte), 1); + CORRADE_COMPARE(sceneMappingTypeAlignment(SceneMappingType::UnsignedByte), 1); + CORRADE_COMPARE(sceneMappingTypeSize(SceneMappingType::UnsignedShort), 2); + CORRADE_COMPARE(sceneMappingTypeAlignment(SceneMappingType::UnsignedShort), 2); + CORRADE_COMPARE(sceneMappingTypeSize(SceneMappingType::UnsignedInt), 4); + CORRADE_COMPARE(sceneMappingTypeAlignment(SceneMappingType::UnsignedInt), 4); + CORRADE_COMPARE(sceneMappingTypeSize(SceneMappingType::UnsignedLong), 8); + CORRADE_COMPARE(sceneMappingTypeAlignment(SceneMappingType::UnsignedLong), 8); } -void SceneDataTest::objectTypeSizeAlignmentInvalid() { +void SceneDataTest::mappingTypeSizeAlignmentInvalid() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -522,22 +522,22 @@ void SceneDataTest::objectTypeSizeAlignmentInvalid() { std::ostringstream out; Error redirectError{&out}; - sceneObjectTypeSize(SceneObjectType{}); - sceneObjectTypeAlignment(SceneObjectType{}); - sceneObjectTypeSize(SceneObjectType(0x73)); - sceneObjectTypeAlignment(SceneObjectType(0x73)); + sceneMappingTypeSize(SceneMappingType{}); + sceneMappingTypeAlignment(SceneMappingType{}); + sceneMappingTypeSize(SceneMappingType(0x73)); + sceneMappingTypeAlignment(SceneMappingType(0x73)); CORRADE_COMPARE(out.str(), - "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x0)\n" - "Trade::sceneObjectTypeAlignment(): invalid type Trade::SceneObjectType(0x0)\n" - "Trade::sceneObjectTypeSize(): invalid type Trade::SceneObjectType(0x73)\n" - "Trade::sceneObjectTypeAlignment(): invalid type Trade::SceneObjectType(0x73)\n"); + "Trade::sceneMappingTypeSize(): invalid type Trade::SceneMappingType(0x0)\n" + "Trade::sceneMappingTypeAlignment(): invalid type Trade::SceneMappingType(0x0)\n" + "Trade::sceneMappingTypeSize(): invalid type Trade::SceneMappingType(0x73)\n" + "Trade::sceneMappingTypeAlignment(): invalid type Trade::SceneMappingType(0x73)\n"); } -void SceneDataTest::debugObjectType() { +void SceneDataTest::debugMappingType() { std::ostringstream out; - Debug{&out} << SceneObjectType::UnsignedLong << SceneObjectType(0x73); - CORRADE_COMPARE(out.str(), "Trade::SceneObjectType::UnsignedLong Trade::SceneObjectType(0x73)\n"); + Debug{&out} << SceneMappingType::UnsignedLong << SceneMappingType(0x73); + CORRADE_COMPARE(out.str(), "Trade::SceneMappingType::UnsignedLong Trade::SceneMappingType(0x73)\n"); } void SceneDataTest::customFieldName() { @@ -645,29 +645,29 @@ void SceneDataTest::debugFieldType() { CORRADE_COMPARE(out.str(), "Trade::SceneFieldType::Matrix3x4h Trade::SceneFieldType(0xdead)\n"); } -constexpr Complexd Rotations2D[3] { - Complexd{Constantsd::sqrtHalf(), Constantsd::sqrtHalf()}, /* 45° */ - Complexd{1.0, 0.0}, /* 0° */ - Complexd{0.0, 1.0}, /* 90° */ -}; -const UnsignedShort RotationObjects2D[3] { +const UnsignedShort RotationMapping2D[3] { 17, 35, 98 }; +constexpr Complexd RotationField2D[3] { + Complexd{Constantsd::sqrtHalf(), Constantsd::sqrtHalf()}, /* 45° */ + Complexd{1.0, 0.0}, /* 0° */ + Complexd{0.0, 1.0}, /* 90° */ +}; void SceneDataTest::constructField() { - const UnsignedShort rotationObjectData[3]{}; + const UnsignedShort rotationMappingData[3]{}; const Complexd rotationFieldData[3]; - SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; + SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; CORRADE_VERIFY(!rotations.isOffsetOnly()); CORRADE_COMPARE(rotations.name(), SceneField::Rotation); CORRADE_COMPARE(rotations.size(), 3); - CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(rotations.objectData().size(), 3); - CORRADE_COMPARE(rotations.objectData().stride(), sizeof(UnsignedShort)); - CORRADE_VERIFY(rotations.objectData().data() == rotationObjectData); + CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(rotations.mappingData().size(), 3); + CORRADE_COMPARE(rotations.mappingData().stride(), sizeof(UnsignedShort)); + CORRADE_VERIFY(rotations.mappingData().data() == rotationMappingData); CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); CORRADE_COMPARE(rotations.fieldArraySize(), 0); CORRADE_COMPARE(rotations.fieldData().size(), 3); @@ -680,30 +680,30 @@ void SceneDataTest::constructField() { CORRADE_COMPARE(rotations.fieldData(someArray).size(), 3); CORRADE_COMPARE(rotations.fieldData(someArray).stride(), sizeof(Complexd)); CORRADE_VERIFY(rotations.fieldData(someArray).data() == rotationFieldData); - CORRADE_COMPARE(rotations.objectData(someArray).size(), 3); - CORRADE_COMPARE(rotations.objectData(someArray).stride(), sizeof(UnsignedShort)); - CORRADE_VERIFY(rotations.objectData(someArray).data() == rotationObjectData); + CORRADE_COMPARE(rotations.mappingData(someArray).size(), 3); + CORRADE_COMPARE(rotations.mappingData(someArray).stride(), sizeof(UnsignedShort)); + CORRADE_VERIFY(rotations.mappingData(someArray).data() == rotationMappingData); #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Won't bother anymore */ - constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationObjects2D), Containers::arrayView(Rotations2D)}; + constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationMapping2D), Containers::arrayView(RotationField2D)}; constexpr bool isOffsetOnly = crotations.isOffsetOnly(); constexpr SceneField name = crotations.name(); - constexpr SceneObjectType objectType = crotations.objectType(); - constexpr Containers::StridedArrayView1D objectData = crotations.objectData(); + constexpr SceneMappingType mappingType = crotations.mappingType(); + constexpr Containers::StridedArrayView1D mappingData = crotations.mappingData(); constexpr SceneFieldType fieldType = crotations.fieldType(); constexpr UnsignedShort fieldArraySize = crotations.fieldArraySize(); constexpr Containers::StridedArrayView1D fieldData = crotations.fieldData(); CORRADE_VERIFY(!isOffsetOnly); CORRADE_COMPARE(name, SceneField::Rotation); - CORRADE_COMPARE(objectType, SceneObjectType::UnsignedShort); - CORRADE_COMPARE(objectData.size(), 3); - CORRADE_COMPARE(objectData.stride(), sizeof(UnsignedShort)); - CORRADE_COMPARE(objectData.data(), RotationObjects2D); + CORRADE_COMPARE(mappingType, SceneMappingType::UnsignedShort); + CORRADE_COMPARE(mappingData.size(), 3); + CORRADE_COMPARE(mappingData.stride(), sizeof(UnsignedShort)); + CORRADE_COMPARE(mappingData.data(), RotationMapping2D); CORRADE_COMPARE(fieldType, SceneFieldType::Complexd); CORRADE_COMPARE(fieldArraySize, 0); CORRADE_COMPARE(fieldData.size(), 3); CORRADE_COMPARE(fieldData.stride(), sizeof(Complexd)); - CORRADE_COMPARE(fieldData.data(), Rotations2D); + CORRADE_COMPARE(fieldData.data(), RotationField2D); #endif } @@ -711,42 +711,42 @@ void SceneDataTest::constructFieldDefault() { SceneFieldData data; CORRADE_COMPARE(data.name(), SceneField{}); CORRADE_COMPARE(data.fieldType(), SceneFieldType{}); - CORRADE_COMPARE(data.objectType(), SceneObjectType{}); + CORRADE_COMPARE(data.mappingType(), SceneMappingType{}); constexpr SceneFieldData cdata; CORRADE_COMPARE(cdata.name(), SceneField{}); CORRADE_COMPARE(cdata.fieldType(), SceneFieldType{}); - CORRADE_COMPARE(cdata.objectType(), SceneObjectType{}); + CORRADE_COMPARE(cdata.mappingType(), SceneMappingType{}); } void SceneDataTest::constructFieldCustom() { /* Verifying it doesn't hit any assertion about disallowed type for given attribute */ - const UnsignedByte rangeObjectData[3]{}; + const UnsignedByte rangeMappingData[3]{}; const Range2Dh rangeFieldData[3]; - SceneFieldData ranges{sceneFieldCustom(13), Containers::arrayView(rangeObjectData), Containers::arrayView(rangeFieldData)}; + SceneFieldData ranges{sceneFieldCustom(13), Containers::arrayView(rangeMappingData), Containers::arrayView(rangeFieldData)}; CORRADE_COMPARE(ranges.name(), sceneFieldCustom(13)); - CORRADE_COMPARE(ranges.objectType(), SceneObjectType::UnsignedByte); - CORRADE_VERIFY(ranges.objectData().data() == rangeObjectData); + CORRADE_COMPARE(ranges.mappingType(), SceneMappingType::UnsignedByte); + CORRADE_VERIFY(ranges.mappingData().data() == rangeMappingData); CORRADE_COMPARE(ranges.fieldType(), SceneFieldType::Range2Dh); CORRADE_VERIFY(ranges.fieldData().data() == rangeFieldData); } void SceneDataTest::constructField2D() { - char rotationObjectData[6*sizeof(UnsignedShort)]; + char rotationMappingData[6*sizeof(UnsignedShort)]; char rotationFieldData[6*sizeof(Complexd)]; - auto rotationObjectView = Containers::StridedArrayView2D{rotationObjectData, {6, sizeof(UnsignedShort)}}.every(2); + auto rotationMappingView = Containers::StridedArrayView2D{rotationMappingData, {6, sizeof(UnsignedShort)}}.every(2); auto rotationFieldView = Containers::StridedArrayView2D{rotationFieldData, {6, sizeof(Complexd)}}.every(2); - SceneFieldData rotations{SceneField::Rotation, rotationObjectView, SceneFieldType::Complexd, rotationFieldView}; + SceneFieldData rotations{SceneField::Rotation, rotationMappingView, SceneFieldType::Complexd, rotationFieldView}; CORRADE_VERIFY(!rotations.isOffsetOnly()); CORRADE_COMPARE(rotations.name(), SceneField::Rotation); CORRADE_COMPARE(rotations.size(), 3); - CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(rotations.objectData().size(), 3); - CORRADE_COMPARE(rotations.objectData().stride(), 2*sizeof(UnsignedShort)); - CORRADE_COMPARE(rotations.objectData().data(), rotationObjectView.data()); + CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(rotations.mappingData().size(), 3); + CORRADE_COMPARE(rotations.mappingData().stride(), 2*sizeof(UnsignedShort)); + CORRADE_COMPARE(rotations.mappingData().data(), rotationMappingView.data()); CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); CORRADE_COMPARE(rotations.fieldArraySize(), 0); CORRADE_COMPARE(rotations.fieldData().size(), 3); @@ -755,16 +755,16 @@ void SceneDataTest::constructField2D() { } void SceneDataTest::constructFieldTypeErased() { - const UnsignedLong scalingObjectData[3]{}; + const UnsignedLong scalingMappingData[3]{}; const Vector3 scalingFieldData[3]; - SceneFieldData scalings{SceneField::Scaling, SceneObjectType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingObjectData)), SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingFieldData))}; + SceneFieldData scalings{SceneField::Scaling, SceneMappingType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingMappingData)), SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingFieldData))}; CORRADE_VERIFY(!scalings.isOffsetOnly()); CORRADE_COMPARE(scalings.name(), SceneField::Scaling); CORRADE_COMPARE(scalings.size(), 3); - CORRADE_COMPARE(scalings.objectType(), SceneObjectType::UnsignedLong); - CORRADE_COMPARE(scalings.objectData().size(), 3); - CORRADE_COMPARE(scalings.objectData().stride(), sizeof(UnsignedLong)); - CORRADE_COMPARE(scalings.objectData().data(), scalingObjectData); + CORRADE_COMPARE(scalings.mappingType(), SceneMappingType::UnsignedLong); + CORRADE_COMPARE(scalings.mappingData().size(), 3); + CORRADE_COMPARE(scalings.mappingData().stride(), sizeof(UnsignedLong)); + CORRADE_COMPARE(scalings.mappingData().data(), scalingMappingData); CORRADE_COMPARE(scalings.fieldType(), SceneFieldType::Vector3); CORRADE_COMPARE(scalings.fieldArraySize(), 0); CORRADE_COMPARE(scalings.fieldData().size(), 3); @@ -789,14 +789,14 @@ void SceneDataTest::constructFieldOffsetOnly() { {0, 15, {67.0f, -1.1f}} }; - SceneFieldData a{SceneField::Translation, 2, SceneObjectType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data)}; + SceneFieldData a{SceneField::Translation, 2, SceneMappingType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data)}; CORRADE_VERIFY(a.isOffsetOnly()); CORRADE_COMPARE(a.name(), SceneField::Translation); CORRADE_COMPARE(a.size(), 2); - CORRADE_COMPARE(a.objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(a.objectData(data).size(), 2); - CORRADE_COMPARE(a.objectData(data).stride(), sizeof(Data)); - CORRADE_COMPARE_AS(Containers::arrayCast(a.objectData(data)), + CORRADE_COMPARE(a.mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(a.mappingData(data).size(), 2); + CORRADE_COMPARE(a.mappingData(data).stride(), sizeof(Data)); + CORRADE_COMPARE_AS(Containers::arrayCast(a.mappingData(data)), Containers::arrayView({2, 15}), TestSuite::Compare::Container); CORRADE_COMPARE(a.fieldType(), SceneFieldType::Vector2); @@ -808,34 +808,34 @@ void SceneDataTest::constructFieldOffsetOnly() { TestSuite::Compare::Container); } -constexpr UnsignedByte ArrayOffsetObjectData[3]{}; +constexpr UnsignedByte ArrayOffsetMappingData[3]{}; constexpr Int ArrayOffsetFieldData[3*4]{}; void SceneDataTest::constructFieldArray() { - UnsignedByte offsetObjectData[3]; + UnsignedByte offsetMappingData[3]; Int offsetFieldData[3*4]; - SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetObjectData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}}; + SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); - CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); - CORRADE_COMPARE(data.objectData().size(), 3); - CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); - CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); + CORRADE_COMPARE(data.mappingData().size(), 3); + CORRADE_COMPARE(data.mappingData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.mappingData().data() == offsetMappingData); CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); CORRADE_COMPARE(data.fieldArraySize(), 4); CORRADE_COMPARE(data.fieldData().size(), 3); CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); CORRADE_VERIFY(data.fieldData().data() == offsetFieldData); - constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetObjectData), Containers::StridedArrayView2D{ArrayOffsetFieldData, {3, 4}}}; + constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetMappingData), Containers::StridedArrayView2D{ArrayOffsetFieldData, {3, 4}}}; CORRADE_VERIFY(!cdata.isOffsetOnly()); CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); CORRADE_COMPARE(cdata.size(), 3); - CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); - CORRADE_COMPARE(cdata.objectData().size(), 3); - CORRADE_COMPARE(cdata.objectData().stride(), sizeof(UnsignedByte)); - CORRADE_VERIFY(cdata.objectData().data() == ArrayOffsetObjectData); + CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte); + CORRADE_COMPARE(cdata.mappingData().size(), 3); + CORRADE_COMPARE(cdata.mappingData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(cdata.mappingData().data() == ArrayOffsetMappingData); CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); CORRADE_COMPARE(cdata.fieldArraySize(), 4); CORRADE_COMPARE(cdata.fieldData().size(), 3); @@ -844,16 +844,16 @@ void SceneDataTest::constructFieldArray() { } void SceneDataTest::constructFieldArray2D() { - char offsetObjectData[3*sizeof(UnsignedByte)]; + char offsetMappingData[3*sizeof(UnsignedByte)]; char offsetFieldData[3*4*sizeof(Int)]; - SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 4*sizeof(Int)}}, 4}; + SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetMappingData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 4*sizeof(Int)}}, 4}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); - CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); - CORRADE_COMPARE(data.objectData().size(), 3); - CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); - CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); + CORRADE_COMPARE(data.mappingData().size(), 3); + CORRADE_COMPARE(data.mappingData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.mappingData().data() == offsetMappingData); CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); CORRADE_COMPARE(data.fieldArraySize(), 4); CORRADE_COMPARE(data.fieldData().size(), 3); @@ -862,22 +862,22 @@ void SceneDataTest::constructFieldArray2D() { } void SceneDataTest::constructFieldArrayTypeErased() { - Int offsetData[3*4]; - Containers::StridedArrayView1D offset{offsetData, 3, 4*sizeof(Int)}; - UnsignedByte offsetObjectData[3]; - SceneFieldData data{sceneFieldCustom(34), SceneObjectType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetObjectData)), SceneFieldType::Int, Containers::arrayCast(offset), 4}; + UnsignedByte offsetMappingData[3]; + Int offsetFieldData[3*4]; + Containers::StridedArrayView1D offset{offsetFieldData, 3, 4*sizeof(Int)}; + SceneFieldData data{sceneFieldCustom(34), SceneMappingType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetMappingData)), SceneFieldType::Int, Containers::arrayCast(offset), 4}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); - CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); - CORRADE_COMPARE(data.objectData().size(), 3); - CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); - CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); + CORRADE_COMPARE(data.mappingData().size(), 3); + CORRADE_COMPARE(data.mappingData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.mappingData().data() == offsetMappingData); CORRADE_COMPARE(data.fieldArraySize(), 4); CORRADE_COMPARE(data.fieldData().size(), 3); CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); - CORRADE_VERIFY(data.fieldData().data() == offsetData); + CORRADE_VERIFY(data.fieldData().data() == offsetFieldData); } void SceneDataTest::constructFieldArrayOffsetOnly() { @@ -887,11 +887,11 @@ void SceneDataTest::constructFieldArrayOffsetOnly() { Int offset[4]; }; - SceneFieldData data{sceneFieldCustom(34), 3, SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; + SceneFieldData data{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; CORRADE_VERIFY(data.isOffsetOnly()); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); - CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); CORRADE_COMPARE(data.fieldArraySize(), 4); @@ -899,15 +899,15 @@ void SceneDataTest::constructFieldArrayOffsetOnly() { CORRADE_COMPARE(data.fieldData(actual).size(), 3); CORRADE_COMPARE(data.fieldData(actual).stride(), sizeof(Data)); CORRADE_VERIFY(data.fieldData(actual).data() == &actual[0].offset); - CORRADE_COMPARE(data.objectData(actual).size(), 3); - CORRADE_COMPARE(data.objectData(actual).stride(), sizeof(Data)); - CORRADE_VERIFY(data.objectData(actual).data() == &actual[0].object); + CORRADE_COMPARE(data.mappingData(actual).size(), 3); + CORRADE_COMPARE(data.mappingData(actual).stride(), sizeof(Data)); + CORRADE_VERIFY(data.mappingData(actual).data() == &actual[0].object); - constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; + constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; CORRADE_VERIFY(cdata.isOffsetOnly()); CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); CORRADE_COMPARE(cdata.size(), 3); - CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte); CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); CORRADE_COMPARE(cdata.fieldArraySize(), 4); } @@ -917,13 +917,13 @@ void SceneDataTest::constructFieldInconsistentViewSize() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - const UnsignedShort rotationObjectData[3]{}; + const UnsignedShort rotationMappingData[3]{}; const Complexd rotationFieldData[2]; std::ostringstream out; Error redirectError{&out}; - SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; - CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: expected object and field view to have the same size but got 3 and 2\n"); + SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: expected mapping and field view to have the same size but got 3 and 2\n"); } void SceneDataTest::constructFieldWrongType() { @@ -931,19 +931,19 @@ void SceneDataTest::constructFieldWrongType() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - const UnsignedShort rotationObjectData[3]{}; + const UnsignedShort rotationMappingData[3]{}; const Quaternion rotationFieldData[3]; std::ostringstream out; Error redirectError{&out}; - SceneFieldData{SceneField::Transformation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; - SceneFieldData{SceneField::Transformation, 3, SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; + SceneFieldData{SceneField::Transformation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; + SceneFieldData{SceneField::Transformation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: Trade::SceneFieldType::Quaternion is not a valid type for Trade::SceneField::Transformation\n" "Trade::SceneFieldData: Trade::SceneFieldType::Quaternion is not a valid type for Trade::SceneField::Transformation\n"); } -void SceneDataTest::constructFieldTooLargeObjectStride() { +void SceneDataTest::constructFieldTooLargeMappingStride() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -952,22 +952,22 @@ void SceneDataTest::constructFieldTooLargeObjectStride() { char toomuch[2*(32768 + sizeof(UnsignedInt))]; /* These should be fine */ - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}, SceneFieldType::UnsignedInt, enough}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 32767, SceneFieldType::UnsignedInt, 0, 4}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 65536, -32768, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}, SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 32767, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 65536, -32768, SceneFieldType::UnsignedInt, 0, 4}; std::ostringstream out; Error redirectError{&out}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}, SceneFieldType::UnsignedInt, enough}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 32768, SceneFieldType::UnsignedInt, 0, 4}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 65538, -32769, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}, SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>(), SceneFieldType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 32768, SceneFieldType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 65538, -32769, SceneFieldType::UnsignedInt, 0, 4}; CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got 32768\n" - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got -32769\n" - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got 32768\n" - "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got -32769\n"); + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got -32769\n" + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got 32768\n" + "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got -32769\n"); } void SceneDataTest::constructFieldTooLargeFieldStride() { @@ -979,17 +979,17 @@ void SceneDataTest::constructFieldTooLargeFieldStride() { char toomuch[2*(32768 + sizeof(UnsignedInt))]; /* These should be fine */ - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32767}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65536, -32768}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32767}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65536, -32768}; std::ostringstream out; Error redirectError{&out}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>()}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32768}; - SceneFieldData{SceneField::Mesh, 2, SceneObjectType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65538, -32769}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, enough, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 0, 32768}; + SceneFieldData{SceneField::Mesh, 2, SceneMappingType::UnsignedInt, 0, 4, SceneFieldType::UnsignedInt, 65538, -32769}; CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got 32768\n" "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got -32769\n" @@ -1002,22 +1002,22 @@ void SceneDataTest::constructFieldWrongDataAccess() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - const UnsignedShort rotationObjectData[3]{}; + const UnsignedShort rotationMappingData[3]{}; const Quaternion rotationFieldData[3]; - SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationObjectData), Containers::arrayView(rotationFieldData)}; - SceneFieldData b{SceneField::Rotation, 3, SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; + SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; + SceneFieldData b{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; CORRADE_VERIFY(!a.isOffsetOnly()); CORRADE_VERIFY(b.isOffsetOnly()); - a.objectData(rotationObjectData); /* This is fine, no asserts */ + a.mappingData(rotationMappingData); /* This is fine, no asserts */ a.fieldData(rotationFieldData); std::ostringstream out; Error redirectError{&out}; - b.objectData(); + b.mappingData(); b.fieldData(); CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData::objectData(): the field is offset-only, supply a data array\n" + "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array\n" "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array\n"); } @@ -1026,21 +1026,21 @@ void SceneDataTest::constructField2DWrongSize() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif + char rotationMappingData[5*sizeof(UnsignedInt)]; char rotationFieldData[5*sizeof(Complex)]; - char rotationObjectData[5*sizeof(UnsignedInt)]; std::ostringstream out; Error redirectError{&out}; SceneFieldData{SceneField::Rotation, - Containers::StridedArrayView2D{rotationObjectData, {4, 5}}.every(2), + Containers::StridedArrayView2D{rotationMappingData, {4, 5}}.every(2), SceneFieldType::Complex, Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2)}; SceneFieldData{SceneField::Translation, - Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2), + Containers::StridedArrayView2D{rotationMappingData, {4, sizeof(UnsignedInt)}}.every(2), SceneFieldType::Vector3, Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2)}; CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got 5\n" + "Trade::SceneFieldData: expected second mapping view dimension size 1, 2, 4 or 8 but got 5\n" "Trade::SceneFieldData: second field view dimension size 8 doesn't match Trade::SceneFieldType::Vector3\n"); } @@ -1049,21 +1049,21 @@ void SceneDataTest::constructField2DNonContiguous() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - char rotationObjectData[8*sizeof(UnsignedInt)]; + char rotationMappingData[8*sizeof(UnsignedInt)]; char rotationFieldData[8*sizeof(Complex)]; std::ostringstream out; Error redirectError{&out}; SceneFieldData{SceneField::Rotation, - Containers::StridedArrayView2D{rotationObjectData, {4, 2*sizeof(UnsignedInt)}}.every({1, 2}), + Containers::StridedArrayView2D{rotationMappingData, {4, 2*sizeof(UnsignedInt)}}.every({1, 2}), SceneFieldType::Complex, Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}}; SceneFieldData{SceneField::Rotation, - Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}, + Containers::StridedArrayView2D{rotationMappingData, {4, sizeof(UnsignedInt)}}, SceneFieldType::Complex, Containers::StridedArrayView2D{rotationFieldData, {4, 2*sizeof(Complex)}}.every({1, 2})}; CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData: second object view dimension is not contiguous\n" + "Trade::SceneFieldData: second mapping view dimension is not contiguous\n" "Trade::SceneFieldData: second field view dimension is not contiguous\n"); } @@ -1072,12 +1072,12 @@ void SceneDataTest::constructFieldArrayNonContiguous() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - UnsignedByte offsetObjectData[3]; + UnsignedByte offsetMappingData[3]; Int offsetFieldData[3*4]; std::ostringstream out; Error redirectError{&out}; - SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetObjectData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}.every({1, 2})}; + SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}.every({1, 2})}; CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: second field view dimension is not contiguous\n"); } @@ -1086,46 +1086,46 @@ void SceneDataTest::constructFieldArrayNotAllowed() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - UnsignedShort rotationObjectData[3]{}; + UnsignedShort rotationMappingData[3]{}; Quaternion rotationFieldData[3]; - Containers::ArrayView rotationObjects = rotationObjectData; - Containers::ArrayView rotationFields = rotationFieldData; + Containers::ArrayView rotationMapping = rotationMappingData; + Containers::ArrayView rotationField = rotationFieldData; Containers::StridedArrayView2D rotationFields2D{rotationFieldData, {3, 3}, {0, sizeof(Quaternion)}}; - auto rotationFields2DChar = Containers::arrayCast<2, const char>(rotationFields2D); - auto rotationObjectsChar = Containers::arrayCast<2, const char>(rotationObjects); + auto rotationMappingChar = Containers::arrayCast<2, const char>(rotationMapping); + auto rotationField2DChar = Containers::arrayCast<2, const char>(rotationFields2D); /* This is all fine */ SceneFieldData{SceneField::Rotation, - SceneObjectType::UnsignedShort, rotationObjects, - SceneFieldType::Quaternion, rotationFields, 0}; + SceneMappingType::UnsignedShort, rotationMapping, + SceneFieldType::Quaternion, rotationField, 0}; SceneFieldData{SceneField::Rotation, 3, - SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), 0}; SceneFieldData{sceneFieldCustom(37), - rotationObjects, + rotationMapping, rotationFields2D}; SceneFieldData{sceneFieldCustom(37), - rotationObjectsChar, - SceneFieldType::Quaternion, rotationFields2DChar, 3}; + rotationMappingChar, + SceneFieldType::Quaternion, rotationField2DChar, 3}; SceneFieldData{sceneFieldCustom(37), 3, - SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), 3}; /* This is not */ std::ostringstream out; Error redirectError{&out}; SceneFieldData{SceneField::Rotation, - SceneObjectType::UnsignedShort, rotationObjects, - SceneFieldType::Quaternion, rotationFields, 3}; + SceneMappingType::UnsignedShort, rotationMapping, + SceneFieldType::Quaternion, rotationField, 3}; SceneFieldData{SceneField::Rotation, 3, - SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), + SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), 3}; SceneFieldData{SceneField::Rotation, - rotationObjects, + rotationMapping, rotationFields2D}; SceneFieldData{SceneField::Rotation, - rotationObjectsChar, - SceneFieldType::Quaternion, rotationFields2DChar, 3}; + rotationMappingChar, + SceneFieldType::Quaternion, rotationField2DChar, 3}; CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n" "Trade::SceneFieldData: Trade::SceneField::Rotation can't be an array field\n" @@ -1138,13 +1138,13 @@ void SceneDataTest::constructFieldArray2DWrongSize() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - char rotationObjectData[4*sizeof(UnsignedInt)]; + char rotationMappingData[4*sizeof(UnsignedInt)]; char rotationFieldData[4*sizeof(Complex)]; std::ostringstream out; Error redirectError{&out}; SceneFieldData{sceneFieldCustom(37), - Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2), + Containers::StridedArrayView2D{rotationMappingData, {4, sizeof(UnsignedInt)}}.every(2), SceneFieldType::Int, Containers::StridedArrayView2D{rotationFieldData, {4, sizeof(Complex)}}.every(2), 3}; CORRADE_COMPARE(out.str(), @@ -1156,21 +1156,21 @@ void SceneDataTest::constructFieldArray2DNonContiguous() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - char offsetObjectData[18*sizeof(UnsignedInt)]; + char offsetMappingData[18*sizeof(UnsignedInt)]; char offsetFieldData[18*sizeof(Int)]; std::ostringstream out; Error redirectError{&out}; SceneFieldData{sceneFieldCustom(37), - Containers::StridedArrayView2D{offsetObjectData, {3, 2*sizeof(UnsignedInt)}}.every({1, 2}), + Containers::StridedArrayView2D{offsetMappingData, {3, 2*sizeof(UnsignedInt)}}.every({1, 2}), SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 3*sizeof(Int)}}, 3}; SceneFieldData{sceneFieldCustom(37), - Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedInt)}}, + Containers::StridedArrayView2D{offsetMappingData, {3, sizeof(UnsignedInt)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 6*sizeof(Int)}}.every({1, 2}), 3}; CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData: second object view dimension is not contiguous\n" + "Trade::SceneFieldData: second mapping view dimension is not contiguous\n" "Trade::SceneFieldData: second field view dimension is not contiguous\n"); } @@ -1181,70 +1181,70 @@ void SceneDataTest::construct() { Int parent; }; - Containers::StridedArrayView1D transformsParentFieldObjectData; + Containers::StridedArrayView1D transformsParentFieldMappingData; Containers::StridedArrayView1D meshFieldData; Containers::StridedArrayView1D radiusFieldData; - Containers::StridedArrayView1D materialMeshRadiusObjectData; + Containers::StridedArrayView1D materialMeshRadiusMappingData; Containers::ArrayTuple data{ - {NoInit, 5, transformsParentFieldObjectData}, + {NoInit, 5, transformsParentFieldMappingData}, {NoInit, 2, meshFieldData}, {NoInit, 2, radiusFieldData}, - {NoInit, 2, materialMeshRadiusObjectData}, + {NoInit, 2, materialMeshRadiusMappingData}, }; - transformsParentFieldObjectData[0].object = 4; - transformsParentFieldObjectData[0].transformation = Matrix4::translation(Vector3::xAxis(5.0f)); - transformsParentFieldObjectData[0].parent = -1; + transformsParentFieldMappingData[0].object = 4; + transformsParentFieldMappingData[0].transformation = Matrix4::translation(Vector3::xAxis(5.0f)); + transformsParentFieldMappingData[0].parent = -1; - transformsParentFieldObjectData[1].object = 2; - transformsParentFieldObjectData[1].transformation = Matrix4::translation(Vector3::yAxis(5.0f)); - transformsParentFieldObjectData[1].parent = 0; + transformsParentFieldMappingData[1].object = 2; + transformsParentFieldMappingData[1].transformation = Matrix4::translation(Vector3::yAxis(5.0f)); + transformsParentFieldMappingData[1].parent = 0; - transformsParentFieldObjectData[2].object = 3; - transformsParentFieldObjectData[2].transformation = Matrix4::translation(Vector3::zAxis(5.0f)); - transformsParentFieldObjectData[2].parent = 2; + transformsParentFieldMappingData[2].object = 3; + transformsParentFieldMappingData[2].transformation = Matrix4::translation(Vector3::zAxis(5.0f)); + transformsParentFieldMappingData[2].parent = 2; - transformsParentFieldObjectData[3].object = 0; - transformsParentFieldObjectData[3].transformation = Matrix4::translation(Vector3::yScale(5.0f)); - transformsParentFieldObjectData[3].parent = 1; + transformsParentFieldMappingData[3].object = 0; + transformsParentFieldMappingData[3].transformation = Matrix4::translation(Vector3::yScale(5.0f)); + transformsParentFieldMappingData[3].parent = 1; - transformsParentFieldObjectData[4].object = 1; - transformsParentFieldObjectData[4].transformation = Matrix4::translation(Vector3::zScale(5.0f)); - transformsParentFieldObjectData[4].parent = -1; + transformsParentFieldMappingData[4].object = 1; + transformsParentFieldMappingData[4].transformation = Matrix4::translation(Vector3::zScale(5.0f)); + transformsParentFieldMappingData[4].parent = -1; meshFieldData[0] = 5; radiusFieldData[0] = {37.5f, 1.5f}; - materialMeshRadiusObjectData[0] = 2; + materialMeshRadiusMappingData[0] = 2; meshFieldData[1] = 7; radiusFieldData[1] = {22.5f, 0.5f}; - materialMeshRadiusObjectData[1] = 6; + materialMeshRadiusMappingData[1] = 6; int importerState; SceneFieldData transformations{SceneField::Transformation, - transformsParentFieldObjectData.slice(&TransformParent::object), - transformsParentFieldObjectData.slice(&TransformParent::transformation)}; + transformsParentFieldMappingData.slice(&TransformParent::object), + transformsParentFieldMappingData.slice(&TransformParent::transformation)}; /* Offset-only */ SceneFieldData parents{SceneField::Parent, 5, - SceneObjectType::UnsignedShort, offsetof(TransformParent, object), sizeof(TransformParent), + SceneMappingType::UnsignedShort, offsetof(TransformParent, object), sizeof(TransformParent), SceneFieldType::Int, offsetof(TransformParent, parent), sizeof(TransformParent)}; SceneFieldData meshes{SceneField::Mesh, - materialMeshRadiusObjectData, + materialMeshRadiusMappingData, meshFieldData}; /* Custom & array */ SceneFieldData radiuses{sceneFieldCustom(37), - materialMeshRadiusObjectData, + materialMeshRadiusMappingData, Containers::arrayCast<2, Float>(radiusFieldData)}; - SceneData scene{SceneObjectType::UnsignedShort, 8, std::move(data), { + SceneData scene{SceneMappingType::UnsignedShort, 8, std::move(data), { transformations, parents, meshes, radiuses }, &importerState}; /* Basics */ CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(!scene.fieldData().empty()); - CORRADE_COMPARE(static_cast(scene.data()), transformsParentFieldObjectData.data()); - CORRADE_COMPARE(static_cast(scene.mutableData()), transformsParentFieldObjectData.data()); - CORRADE_COMPARE(scene.objectCount(), 8); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(static_cast(scene.data()), transformsParentFieldMappingData.data()); + CORRADE_COMPARE(static_cast(scene.mutableData()), transformsParentFieldMappingData.data()); + CORRADE_COMPARE(scene.mappingBound(), 8); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(scene.fieldCount(), 4); CORRADE_COMPARE(scene.importerState(), &importerState); @@ -1272,8 +1272,8 @@ void SceneDataTest::construct() { /* Raw field data access by ID */ CORRADE_COMPARE(scene.fieldData(2).name(), SceneField::Mesh); CORRADE_COMPARE(scene.fieldData(2).size(), 2); - CORRADE_COMPARE(scene.fieldData(2).objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).objectData())[1], 6); + CORRADE_COMPARE(scene.fieldData(2).mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).mappingData())[1], 6); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).fieldData())[1], 7); CORRADE_VERIFY(!scene.fieldData(2).isOffsetOnly()); CORRADE_COMPARE(scene.fieldData(2).fieldType(), SceneFieldType::UnsignedByte); @@ -1281,39 +1281,39 @@ void SceneDataTest::construct() { /* Offset-only */ CORRADE_COMPARE(scene.fieldData(1).name(), SceneField::Parent); CORRADE_COMPARE(scene.fieldData(1).size(), 5); - CORRADE_COMPARE(scene.fieldData(1).objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.fieldData(1).mappingType(), SceneMappingType::UnsignedShort); CORRADE_VERIFY(!scene.fieldData(1).isOffsetOnly()); CORRADE_COMPARE(scene.fieldData(1).fieldType(), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldData(1).fieldArraySize(), 0); - CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).objectData())[4], 1); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).mappingData())[4], 1); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).fieldData())[4], -1); /* Array */ CORRADE_COMPARE(scene.fieldData(3).name(), sceneFieldCustom(37)); CORRADE_COMPARE(scene.fieldData(3).size(), 2); - CORRADE_COMPARE(scene.fieldData(3).objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.fieldData(3).mappingType(), SceneMappingType::UnsignedShort); CORRADE_VERIFY(!scene.fieldData(3).isOffsetOnly()); CORRADE_COMPARE(scene.fieldData(3).fieldType(), SceneFieldType::Float); CORRADE_COMPARE(scene.fieldData(3).fieldArraySize(), 2); - CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).objectData())[0], 2); + CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).mappingData())[0], 2); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).fieldData())[0], (Vector2{37.5f, 1.5f})); - /* Typeless object access by ID with a cast later */ - CORRADE_COMPARE(scene.objects(0).size()[0], 5); - CORRADE_COMPARE(scene.objects(1).size()[0], 5); - CORRADE_COMPARE(scene.objects(2).size()[0], 2); - CORRADE_COMPARE(scene.objects(3).size()[0], 2); - CORRADE_COMPARE(scene.mutableObjects(0).size()[0], 5); - CORRADE_COMPARE(scene.mutableObjects(1).size()[0], 5); - CORRADE_COMPARE(scene.mutableObjects(2).size()[0], 2); - CORRADE_COMPARE(scene.mutableObjects(3).size()[0], 2); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(0))[2]), 3); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(1))[4]), 1); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(2))[1]), 6); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(3))[0]), 2); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(0))[2]), 3); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(1))[4]), 1); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(2))[1]), 6); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(3))[0]), 2); + /* Typeless mapping access by ID with a cast later */ + CORRADE_COMPARE(scene.mapping(0).size()[0], 5); + CORRADE_COMPARE(scene.mapping(1).size()[0], 5); + CORRADE_COMPARE(scene.mapping(2).size()[0], 2); + CORRADE_COMPARE(scene.mapping(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableMapping(0).size()[0], 5); + CORRADE_COMPARE(scene.mutableMapping(1).size()[0], 5); + CORRADE_COMPARE(scene.mutableMapping(2).size()[0], 2); + CORRADE_COMPARE(scene.mutableMapping(3).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(0))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(1))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(3))[0]), 2); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(0))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(1))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(3))[0]), 2); /* Typeless field access by ID with a cast later */ CORRADE_COMPARE(scene.field(0).size()[0], 5); @@ -1333,23 +1333,23 @@ void SceneDataTest::construct() { CORRADE_COMPARE((Containers::arrayCast<1, UnsignedByte>(scene.mutableField(2))[1]), 7); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>(scene.mutableField(3))[0]), (Vector2{37.5f, 1.5f})); - /* Typed object access by ID */ - CORRADE_COMPARE(scene.objects(0).size(), 5); - CORRADE_COMPARE(scene.objects(1).size(), 5); - CORRADE_COMPARE(scene.objects(2).size(), 2); - CORRADE_COMPARE(scene.objects(3).size(), 2); - CORRADE_COMPARE(scene.mutableObjects(0).size(), 5); - CORRADE_COMPARE(scene.mutableObjects(1).size(), 5); - CORRADE_COMPARE(scene.mutableObjects(2).size(), 2); - CORRADE_COMPARE(scene.mutableObjects(3).size(), 2); - CORRADE_COMPARE(scene.objects(0)[2], 3); - CORRADE_COMPARE(scene.objects(1)[4], 1); - CORRADE_COMPARE(scene.objects(2)[1], 6); - CORRADE_COMPARE(scene.objects(3)[0], 2); - CORRADE_COMPARE(scene.mutableObjects(0)[2], 3); - CORRADE_COMPARE(scene.mutableObjects(1)[4], 1); - CORRADE_COMPARE(scene.mutableObjects(2)[1], 6); - CORRADE_COMPARE(scene.mutableObjects(3)[0], 2); + /* Typed mapping access by ID */ + CORRADE_COMPARE(scene.mapping(0).size(), 5); + CORRADE_COMPARE(scene.mapping(1).size(), 5); + CORRADE_COMPARE(scene.mapping(2).size(), 2); + CORRADE_COMPARE(scene.mapping(3).size(), 2); + CORRADE_COMPARE(scene.mutableMapping(0).size(), 5); + CORRADE_COMPARE(scene.mutableMapping(1).size(), 5); + CORRADE_COMPARE(scene.mutableMapping(2).size(), 2); + CORRADE_COMPARE(scene.mutableMapping(3).size(), 2); + CORRADE_COMPARE(scene.mapping(0)[2], 3); + CORRADE_COMPARE(scene.mapping(1)[4], 1); + CORRADE_COMPARE(scene.mapping(2)[1], 6); + CORRADE_COMPARE(scene.mapping(3)[0], 2); + CORRADE_COMPARE(scene.mutableMapping(0)[2], 3); + CORRADE_COMPARE(scene.mutableMapping(1)[4], 1); + CORRADE_COMPARE(scene.mutableMapping(2)[1], 6); + CORRADE_COMPARE(scene.mutableMapping(3)[0], 2); /* Typed field access by ID */ CORRADE_COMPARE(scene.field(0).size(), 5); @@ -1387,23 +1387,23 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); CORRADE_COMPARE(scene.fieldArraySize(sceneFieldCustom(37)), 2); - /* Typeless object access by name with a cast later */ - CORRADE_COMPARE(scene.objects(SceneField::Transformation).size()[0], 5); - CORRADE_COMPARE(scene.objects(SceneField::Parent).size()[0], 5); - CORRADE_COMPARE(scene.objects(2).size()[0], 2); - CORRADE_COMPARE(scene.objects(3).size()[0], 2); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation).size()[0], 5); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent).size()[0], 5); - CORRADE_COMPARE(scene.mutableObjects(2).size()[0], 2); - CORRADE_COMPARE(scene.mutableObjects(3).size()[0], 2); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(SceneField::Transformation))[2]), 3); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(SceneField::Parent))[4]), 1); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(2))[1]), 6); - CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.objects(3))[0]), 2); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(SceneField::Transformation))[2]), 3); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(SceneField::Parent))[4]), 1); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(2))[1]), 6); - CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableObjects(3))[0]), 2); + /* Typeless mapping access by name with a cast later */ + CORRADE_COMPARE(scene.mapping(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.mapping(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.mapping(2).size()[0], 2); + CORRADE_COMPARE(scene.mapping(3).size()[0], 2); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Transformation).size()[0], 5); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Parent).size()[0], 5); + CORRADE_COMPARE(scene.mutableMapping(2).size()[0], 2); + CORRADE_COMPARE(scene.mutableMapping(3).size()[0], 2); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(SceneField::Transformation))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(SceneField::Parent))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(scene.mapping(3))[0]), 2); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(SceneField::Transformation))[2]), 3); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(SceneField::Parent))[4]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(2))[1]), 6); + CORRADE_COMPARE((Containers::arrayCast<1, UnsignedShort>(scene.mutableMapping(3))[0]), 2); /* Typeless field access by name with a cast later */ CORRADE_COMPARE(scene.field(SceneField::Transformation).size()[0], 5); @@ -1423,23 +1423,23 @@ void SceneDataTest::construct() { CORRADE_COMPARE((Containers::arrayCast<1, UnsignedByte>(scene.mutableField(SceneField::Mesh))[1]), 7); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>(scene.mutableField(sceneFieldCustom(37)))[0]), (Vector2{37.5f, 1.5f})); - /* Typed object access by name */ - CORRADE_COMPARE(scene.objects(SceneField::Transformation).size(), 5); - CORRADE_COMPARE(scene.objects(SceneField::Parent).size(), 5); - CORRADE_COMPARE(scene.objects(SceneField::Mesh).size(), 2); - CORRADE_COMPARE(scene.objects(sceneFieldCustom(37)).size(), 2); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation).size(), 5); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent).size(), 5); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Mesh).size(), 2); - CORRADE_COMPARE(scene.mutableObjects(sceneFieldCustom(37)).size(), 2); - CORRADE_COMPARE(scene.objects(SceneField::Transformation)[2], 3); - CORRADE_COMPARE(scene.objects(SceneField::Parent)[4], 1); - CORRADE_COMPARE(scene.objects(SceneField::Mesh)[1], 6); - CORRADE_COMPARE(scene.objects(sceneFieldCustom(37))[0], 2); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Transformation)[2], 3); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Parent)[4], 1); - CORRADE_COMPARE(scene.mutableObjects(SceneField::Mesh)[1], 6); - CORRADE_COMPARE(scene.mutableObjects(sceneFieldCustom(37))[0], 2); + /* Typed mapping access by name */ + CORRADE_COMPARE(scene.mapping(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.mapping(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.mapping(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.mapping(sceneFieldCustom(37)).size(), 2); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Transformation).size(), 5); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Parent).size(), 5); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Mesh).size(), 2); + CORRADE_COMPARE(scene.mutableMapping(sceneFieldCustom(37)).size(), 2); + CORRADE_COMPARE(scene.mapping(SceneField::Transformation)[2], 3); + CORRADE_COMPARE(scene.mapping(SceneField::Parent)[4], 1); + CORRADE_COMPARE(scene.mapping(SceneField::Mesh)[1], 6); + CORRADE_COMPARE(scene.mapping(sceneFieldCustom(37))[0], 2); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Transformation)[2], 3); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Parent)[4], 1); + CORRADE_COMPARE(scene.mutableMapping(SceneField::Mesh)[1], 6); + CORRADE_COMPARE(scene.mutableMapping(sceneFieldCustom(37))[0], 2); /* Typed field access by name */ CORRADE_COMPARE(scene.field(SceneField::Transformation).size(), 5); @@ -1466,14 +1466,14 @@ void SceneDataTest::construct() { void SceneDataTest::constructZeroFields() { int importerState; - SceneData scene{SceneObjectType::UnsignedShort, 37563, nullptr, {}, &importerState}; + SceneData scene{SceneMappingType::UnsignedShort, 37563, nullptr, {}, &importerState}; CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(scene.fieldData().empty()); CORRADE_COMPARE(static_cast(scene.data()), nullptr); CORRADE_COMPARE(static_cast(scene.mutableData()), nullptr); CORRADE_COMPARE(scene.importerState(), &importerState); - CORRADE_COMPARE(scene.objectCount(), 37563); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.mappingBound(), 37563); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(scene.fieldCount(), 0); CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(!scene.is3D()); @@ -1481,16 +1481,16 @@ void SceneDataTest::constructZeroFields() { void SceneDataTest::constructZeroObjects() { int importerState; - SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials}, &importerState}; + SceneFieldData meshes{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, {meshes, materials}, &importerState}; CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(!scene.fieldData().empty()); CORRADE_COMPARE(static_cast(scene.data()), nullptr); CORRADE_COMPARE(static_cast(scene.mutableData()), nullptr); CORRADE_COMPARE(scene.importerState(), &importerState); - CORRADE_COMPARE(scene.objectCount(), 0); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + CORRADE_COMPARE(scene.mappingBound(), 0); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt); CORRADE_COMPARE(scene.fieldCount(), 2); /* Field property access by name */ @@ -1498,8 +1498,8 @@ void SceneDataTest::constructZeroObjects() { CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 0); CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 0); - CORRADE_COMPARE(scene.objects(SceneField::Mesh).data(), nullptr); - CORRADE_COMPARE(scene.objects(SceneField::MeshMaterial).data(), nullptr); + CORRADE_COMPARE(scene.mapping(SceneField::Mesh).data(), nullptr); + CORRADE_COMPARE(scene.mapping(SceneField::MeshMaterial).data(), nullptr); } void SceneDataTest::constructSpecialStrides() { @@ -1516,31 +1516,31 @@ void SceneDataTest::constructSpecialStrides() { nonBroadcastedData[2] = 3; nonBroadcastedData[3] = 4; - SceneFieldData broadcastedObjects{sceneFieldCustom(38), + SceneFieldData broadcastedMapping{sceneFieldCustom(38), broadcastedData.broadcasted<0>(4), nonBroadcastedData}; - SceneFieldData broadcastedFields{sceneFieldCustom(39), + SceneFieldData broadcastedField{sceneFieldCustom(39), nonBroadcastedData, broadcastedData.broadcasted<0>(4)}; - SceneFieldData flippedFields{sceneFieldCustom(40), + SceneFieldData flippedField{sceneFieldCustom(40), nonBroadcastedData.flipped<0>(), nonBroadcastedData.flipped<0>()}; - SceneData scene{SceneObjectType::UnsignedShort, 8, std::move(data), { - broadcastedObjects, broadcastedFields, flippedFields + SceneData scene{SceneMappingType::UnsignedShort, 8, std::move(data), { + broadcastedMapping, broadcastedField, flippedField }}; - CORRADE_COMPARE_AS(scene.objects(0), + CORRADE_COMPARE_AS(scene.mapping(0), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(0), Containers::arrayView({1, 2, 3, 4}), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.objects(1), + CORRADE_COMPARE_AS(scene.mapping(1), Containers::arrayView({1, 2, 3, 4}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(1), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.objects(2), + CORRADE_COMPARE_AS(scene.mapping(2), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(2), @@ -1565,21 +1565,21 @@ void SceneDataTest::constructNotOwned() { SceneFieldData mesh{SceneField::Mesh, Containers::stridedArrayView(data).slice(&Data::object), Containers::stridedArrayView(data).slice(&Data::mesh)}; - SceneData scene{SceneObjectType::UnsignedShort, 7, instanceData.dataFlags, Containers::arrayView(data), {mesh}, &importerState}; + SceneData scene{SceneMappingType::UnsignedShort, 7, instanceData.dataFlags, Containers::arrayView(data), {mesh}, &importerState}; CORRADE_COMPARE(scene.dataFlags(), instanceData.dataFlags); CORRADE_COMPARE(static_cast(scene.data()), +data); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(static_cast(scene.mutableData()), static_cast(data)); - CORRADE_COMPARE(scene.objectCount(), 7); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(scene.mappingBound(), 7); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(scene.fieldCount(), 1); CORRADE_COMPARE(scene.importerState(), &importerState); - CORRADE_COMPARE(scene.objects(0).size(), 3); - CORRADE_COMPARE(scene.objects(0)[2], 2); + CORRADE_COMPARE(scene.mapping(0).size(), 3); + CORRADE_COMPARE(scene.mapping(0)[2], 2); if(instanceData.dataFlags & DataFlag::Mutable) - CORRADE_COMPARE(scene.mutableObjects(0)[2], 2); + CORRADE_COMPARE(scene.mutableMapping(0)[2], 2); CORRADE_COMPARE(scene.field(0).size(), 3); CORRADE_COMPARE(scene.field(0)[2], 0); @@ -1599,18 +1599,18 @@ void SceneDataTest::constructDeprecated() { data.is3D ? std::vector{5, 17, 36, 22} : std::vector{}, &a}; CORRADE_IGNORE_DEPRECATED_POP - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt); if(data.is2D || data.is3D) - CORRADE_COMPARE(scene.objectCount(), 37); + CORRADE_COMPARE(scene.mappingBound(), 37); else - CORRADE_COMPARE(scene.objectCount(), 0); + CORRADE_COMPARE(scene.mappingBound(), 0); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Mutable|DataFlag::Owned); CORRADE_COMPARE(scene.importerState(), &a); CORRADE_COMPARE(scene.fieldCount(), 1); CORRADE_COMPARE(scene.fieldName(0), SceneField::Parent); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Int); if(data.is2D || data.is3D) { - CORRADE_COMPARE_AS(scene.objects(0), + CORRADE_COMPARE_AS(scene.mapping(0), Containers::arrayView({5, 17, 36, 22}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(0), @@ -1649,13 +1649,13 @@ void SceneDataTest::constructDuplicateField() { /* Builtin fields are checked using a bitfield, as they have monotonic numbering */ - SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; - SceneFieldData meshesAgain{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData meshes{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}; + SceneFieldData meshesAgain{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; std::ostringstream out; Error redirectError{&out}; - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials, meshesAgain}}; + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, {meshes, materials, meshesAgain}}; CORRADE_COMPARE(out.str(), "Trade::SceneData: duplicate field Trade::SceneField::Mesh\n"); } @@ -1666,31 +1666,31 @@ void SceneDataTest::constructDuplicateCustomField() { /* These are checked in an O(n^2) way, separately from builtin fields. Can't use a bitfield since the field index can be anything. */ - SceneFieldData customA{sceneFieldCustom(37), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData customB{sceneFieldCustom(1038576154), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; - SceneFieldData customAAgain{sceneFieldCustom(37), SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData customA{sceneFieldCustom(37), SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData customB{sceneFieldCustom(1038576154), SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; + SceneFieldData customAAgain{sceneFieldCustom(37), SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}; std::ostringstream out; Error redirectError{&out}; - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {customA, customB, customAAgain}}; + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, {customA, customB, customAAgain}}; CORRADE_COMPARE(out.str(), "Trade::SceneData: duplicate field Trade::SceneField::Custom(37)\n"); } -void SceneDataTest::constructInconsistentObjectType() { +void SceneDataTest::constructInconsistentMappingType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneFieldData meshes{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; - SceneFieldData materials{SceneField::MeshMaterial, SceneObjectType::UnsignedShort, nullptr, SceneFieldType::Int, nullptr}; + SceneFieldData meshes{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedShort, nullptr}; + SceneFieldData materials{SceneField::MeshMaterial, SceneMappingType::UnsignedShort, nullptr, SceneFieldType::Int, nullptr}; std::ostringstream out; Error redirectError{&out}; - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, {meshes, materials}}; - CORRADE_COMPARE(out.str(), "Trade::SceneData: inconsistent object type, got Trade::SceneObjectType::UnsignedShort for field 1 but expected Trade::SceneObjectType::UnsignedInt\n"); + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, {meshes, materials}}; + CORRADE_COMPARE(out.str(), "Trade::SceneData: inconsistent mapping type, got Trade::SceneMappingType::UnsignedShort for field 1 but expected Trade::SceneMappingType::UnsignedInt\n"); } -void SceneDataTest::constructObjectDataNotContained() { +void SceneDataTest::constructMappingDataNotContained() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -1703,35 +1703,35 @@ void SceneDataTest::constructObjectDataNotContained() { std::ostringstream out; Error redirectError{&out}; /* First a "slightly off" view that exceeds the original by one byte */ - SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneData{SceneMappingType::UnsignedShort, 5, {}, data, { SceneFieldData{SceneField::Mesh, dataSlightlyOut, dataIn} }}; /* Second a view that's in a completely different location */ - SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneData{SceneMappingType::UnsignedShort, 5, {}, data, { SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataOut, dataIn} }}; /* Verify the owning constructor does the checks as well */ - SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { + SceneData{SceneMappingType::UnsignedShort, 5, std::move(data), { SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataOut, dataIn} }}; /* And if we have no data at all, it doesn't try to dereference them but still checks properly */ - SceneData{SceneObjectType::UnsignedShort, 5, nullptr, { + SceneData{SceneMappingType::UnsignedShort, 5, nullptr, { SceneFieldData{SceneField::Mesh, dataOut, dataIn} }}; /* Finally, offset-only fields with a different message */ - SceneData{SceneObjectType::UnsignedByte, 6, Containers::Array{24}, { - SceneFieldData{SceneField::Mesh, 6, SceneObjectType::UnsignedByte, 4, 4, SceneFieldType::UnsignedByte, 0, 4} + SceneData{SceneMappingType::UnsignedByte, 6, Containers::Array{24}, { + SceneFieldData{SceneField::Mesh, 6, SceneMappingType::UnsignedByte, 4, 4, SceneFieldType::UnsignedByte, 0, 4} }}; CORRADE_COMPARE(out.str(), - "Trade::SceneData: object data [0xbaddaa:0xbaddb4] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" - "Trade::SceneData: object data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" - "Trade::SceneData: object data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" - "Trade::SceneData: object data [0xdead:0xdeb7] of field 0 are not contained in passed data array [0x0:0x0]\n" + "Trade::SceneData: mapping data [0xbaddaa:0xbaddb4] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: mapping data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: mapping data [0xdead:0xdeb7] of field 1 are not contained in passed data array [0xbadda9:0xbaddb3]\n" + "Trade::SceneData: mapping data [0xdead:0xdeb7] of field 0 are not contained in passed data array [0x0:0x0]\n" - "Trade::SceneData: offset-only object data of field 0 span 25 bytes but passed data array has only 24\n"); + "Trade::SceneData: offset-only mapping data of field 0 span 25 bytes but passed data array has only 24\n"); } void SceneDataTest::constructFieldDataNotContained() { @@ -1739,7 +1739,7 @@ void SceneDataTest::constructFieldDataNotContained() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - /* Mostly the same as constructObjectDataNotContained() with object and + /* Mostly the same as constructMappingDataNotContained() with mapping and field views swapped, and added checks for array fields */ Containers::Array data{reinterpret_cast(0xbadda9), 10, [](char*, std::size_t){}}; @@ -1750,34 +1750,34 @@ void SceneDataTest::constructFieldDataNotContained() { std::ostringstream out; Error redirectError{&out}; /* First a "slightly off" view that exceeds the original by one byte */ - SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneData{SceneMappingType::UnsignedShort, 5, {}, data, { SceneFieldData{SceneField::Mesh, dataIn, dataSlightlyOut} }}; /* Second a view that's in a completely different location */ - SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneData{SceneMappingType::UnsignedShort, 5, {}, data, { SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataIn, dataOut} }}; /* Verify array size is taken into account as well. If not, the data would span only 7 bytes out of 10 (instead of 12), which is fine. */ - SceneData{SceneObjectType::UnsignedShort, 5, {}, data, { + SceneData{SceneMappingType::UnsignedShort, 5, {}, data, { SceneFieldData{sceneFieldCustom(37), dataIn.prefix(2), Containers::StridedArrayView2D{Containers::ArrayView{reinterpret_cast(0xbadda9), 12}, {2, 6}}} }}; /* Verify the owning constructor does the checks as well */ - SceneData{SceneObjectType::UnsignedShort, 5, std::move(data), { + SceneData{SceneMappingType::UnsignedShort, 5, std::move(data), { SceneFieldData{SceneField::Light, dataIn, dataIn}, SceneFieldData{SceneField::Mesh, dataIn, dataOut} }}; - /* Not checking for nullptr data, since that got checked for object view + /* Not checking for nullptr data, since that got checked for mapping view already and there's no way to trigger it for fields */ /* Finally, offset-only fields with a different message */ - SceneData{SceneObjectType::UnsignedShort, 6, Containers::Array{24}, { - SceneFieldData{SceneField::Mesh, 6, SceneObjectType::UnsignedShort, 0, 4, SceneFieldType::UnsignedByte, 4, 4} + SceneData{SceneMappingType::UnsignedShort, 6, Containers::Array{24}, { + SceneFieldData{SceneField::Mesh, 6, SceneMappingType::UnsignedShort, 0, 4, SceneFieldType::UnsignedByte, 4, 4} }}; /* This again spans 21 bytes if array size isn't taken into account, and 25 if it is */ - SceneData{SceneObjectType::UnsignedShort, 5, Containers::Array{24}, { - SceneFieldData{sceneFieldCustom(37), 5, SceneObjectType::UnsignedShort, 0, 5, SceneFieldType::UnsignedByte, 0, 5, 5} + SceneData{SceneMappingType::UnsignedShort, 5, Containers::Array{24}, { + SceneFieldData{sceneFieldCustom(37), 5, SceneMappingType::UnsignedShort, 0, 5, SceneFieldType::UnsignedByte, 0, 5, 5} }}; CORRADE_COMPARE(out.str(), "Trade::SceneData: field data [0xbaddaa:0xbaddb4] of field 0 are not contained in passed data array [0xbadda9:0xbaddb3]\n" @@ -1789,25 +1789,25 @@ void SceneDataTest::constructFieldDataNotContained() { "Trade::SceneData: offset-only field data of field 0 span 25 bytes but passed data array has only 24\n"); } -void SceneDataTest::constructObjectTypeTooSmall() { +void SceneDataTest::constructMappingTypeTooSmall() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif /* This is fine */ - SceneData{SceneObjectType::UnsignedByte, 0xff, nullptr, {}}; - SceneData{SceneObjectType::UnsignedShort, 0xffff, nullptr, {}}; - SceneData{SceneObjectType::UnsignedInt, 0xffffffffu, nullptr, {}}; + SceneData{SceneMappingType::UnsignedByte, 0xff, nullptr, {}}; + SceneData{SceneMappingType::UnsignedShort, 0xffff, nullptr, {}}; + SceneData{SceneMappingType::UnsignedInt, 0xffffffffu, nullptr, {}}; std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedByte, 0x100, nullptr, {}}; - SceneData{SceneObjectType::UnsignedShort, 0x10000, nullptr, {}}; - SceneData{SceneObjectType::UnsignedInt, 0x100000000ull, nullptr, {}}; + SceneData{SceneMappingType::UnsignedByte, 0x100, nullptr, {}}; + SceneData{SceneMappingType::UnsignedShort, 0x10000, nullptr, {}}; + SceneData{SceneMappingType::UnsignedInt, 0x100000000ull, nullptr, {}}; CORRADE_COMPARE(out.str(), - "Trade::SceneData: Trade::SceneObjectType::UnsignedByte is too small for 256 objects\n" - "Trade::SceneData: Trade::SceneObjectType::UnsignedShort is too small for 65536 objects\n" - "Trade::SceneData: Trade::SceneObjectType::UnsignedInt is too small for 4294967296 objects\n"); + "Trade::SceneData: Trade::SceneMappingType::UnsignedByte is too small for 256 objects\n" + "Trade::SceneData: Trade::SceneMappingType::UnsignedShort is too small for 65536 objects\n" + "Trade::SceneData: Trade::SceneMappingType::UnsignedInt is too small for 4294967296 objects\n"); } void SceneDataTest::constructNotOwnedFlagOwned() { @@ -1819,7 +1819,7 @@ void SceneDataTest::constructNotOwnedFlagOwned() { std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedByte, 5, DataFlag::Owned, Containers::arrayView(data), {}}; + SceneData{SceneMappingType::UnsignedByte, 5, DataFlag::Owned, Containers::arrayView(data), {}}; CORRADE_COMPARE(out.str(), "Trade::SceneData: can't construct with non-owned data but Trade::DataFlag::Owned\n"); } @@ -1832,41 +1832,41 @@ void SceneDataTest::constructMismatchedTRSViews() { Containers::ArrayView data{reinterpret_cast(0xcafe0000), /* Three entries, each having a 2D TRS and 3 object IDs */ 3*(24 + 12)}; - Containers::ArrayView translationObjectData{ + Containers::ArrayView translationMappingData{ reinterpret_cast(data.data()), 3}; Containers::ArrayView translationFieldData{ reinterpret_cast(data.data() + 0x0c), 3}; - Containers::ArrayView rotationObjectData{ + Containers::ArrayView rotationMappingData{ reinterpret_cast(data.data() + 0x24), 3}; Containers::ArrayView rotationFieldData{ reinterpret_cast(data.data() + 0x30), 3}; - Containers::ArrayView scalingObjectData{ + Containers::ArrayView scalingMappingData{ reinterpret_cast(data.data() + 0x48), 3}; Containers::ArrayView scalingFieldData{ reinterpret_cast(data.data() + 0x54), 3}; - SceneFieldData translations{SceneField::Translation, translationObjectData, translationFieldData}; - SceneFieldData rotationsDifferent{SceneField::Rotation, rotationObjectData, rotationFieldData}; - SceneFieldData scalingsDifferent{SceneField::Scaling, scalingObjectData, scalingFieldData}; - SceneFieldData rotationsSameButLess{SceneField::Rotation, translationObjectData.except(1), rotationFieldData.except(1)}; - SceneFieldData scalingsSameButLess{SceneField::Scaling, translationObjectData.except(2), scalingFieldData.except(2)}; + SceneFieldData translations{SceneField::Translation, translationMappingData, translationFieldData}; + SceneFieldData rotationsDifferent{SceneField::Rotation, rotationMappingData, rotationFieldData}; + SceneFieldData scalingsDifferent{SceneField::Scaling, scalingMappingData, scalingFieldData}; + SceneFieldData rotationsSameButLess{SceneField::Rotation, translationMappingData.except(1), rotationFieldData.except(1)}; + SceneFieldData scalingsSameButLess{SceneField::Scaling, translationMappingData.except(2), scalingFieldData.except(2)}; /* Test that all pairs get checked */ std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, rotationsDifferent}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, scalingsDifferent}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {rotationsDifferent, scalingsDifferent}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, rotationsSameButLess}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {translations, scalingsSameButLess}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {rotationsSameButLess, scalingsSameButLess}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {translations, rotationsDifferent}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {translations, scalingsDifferent}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {rotationsDifferent, scalingsDifferent}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {translations, rotationsSameButLess}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {translations, scalingsSameButLess}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {rotationsSameButLess, scalingsSameButLess}}; CORRADE_COMPARE(out.str(), - "Trade::SceneData: Trade::SceneField::Rotation object data [0xcafe0024:0xcafe0030] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" - "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" - "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Rotation object data [0xcafe0024:0xcafe0030]\n" - "Trade::SceneData: Trade::SceneField::Rotation object data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" - "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Translation object data [0xcafe0000:0xcafe000c]\n" - "Trade::SceneData: Trade::SceneField::Scaling object data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Rotation object data [0xcafe0000:0xcafe0008]\n"); + "Trade::SceneData: Trade::SceneField::Rotation mapping data [0xcafe0024:0xcafe0030] is different from Trade::SceneField::Translation mapping data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling mapping data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Translation mapping data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling mapping data [0xcafe0048:0xcafe0054] is different from Trade::SceneField::Rotation mapping data [0xcafe0024:0xcafe0030]\n" + "Trade::SceneData: Trade::SceneField::Rotation mapping data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Translation mapping data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling mapping data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Translation mapping data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::Scaling mapping data [0xcafe0000:0xcafe0004] is different from Trade::SceneField::Rotation mapping data [0xcafe0000:0xcafe0008]\n"); } template struct NameTraits; @@ -1918,49 +1918,49 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData transformationRectangularMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData transformations2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData transformationRectangularMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData transformations3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData translations2D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData translations3D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData rotations2D{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData rotations3D{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData scalings2D{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; - SceneFieldData scalings3D{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices2D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformations2D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices3D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformations3D{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData translations2D{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData translations3D{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData rotations2D{SceneField::Rotation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData rotations3D{SceneField::Rotation, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData scalings2D{SceneField::Scaling, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData scalings3D{SceneField::Scaling, SceneMappingType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; /* Test that all pairs get checked */ std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, translations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, rotations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, scalings3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, translations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, rotations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, scalings3D}}; - - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, translations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, rotations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, scalings3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations2D, rotations3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations2D, scalings3D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {rotations2D, scalings3D}}; - - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, translations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, rotations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, scalings2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, translations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, rotations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, scalings2D}}; - - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, translations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, rotations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, scalings2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations3D, rotations2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {translations3D, scalings2D}}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {rotations3D, scalings2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices2D, translations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices2D, rotations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices2D, scalings3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, translations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, rotations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, scalings3D}}; + + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations2D, translations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations2D, rotations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations2D, scalings3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {translations2D, rotations3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {translations2D, scalings3D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {rotations2D, scalings3D}}; + + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices3D, translations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices3D, rotations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationMatrices3D, scalings2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, translations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, rotations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, scalings2D}}; + + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations3D, translations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations3D, rotations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {transformations3D, scalings2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {translations3D, rotations2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {translations3D, scalings2D}}; + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {rotations3D, scalings2D}}; CORRADE_COMPARE(out.str(), Utility::formatString( "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" @@ -2003,26 +2003,26 @@ void SceneDataTest::constructMismatchedMeshMaterialView() { Containers::ArrayView data{reinterpret_cast(0xcafe0000), /* Three entries, each having mesh/material ID and 2 object IDs */ 3*(8 + 8)}; - Containers::ArrayView meshObjectData{ + Containers::ArrayView meshMappingData{ reinterpret_cast(data.data()), 3}; Containers::ArrayView meshFieldData{ reinterpret_cast(data.data() + 0x0c), 3}; - Containers::ArrayView meshMaterialObjectData{ + Containers::ArrayView meshMaterialMappingData{ reinterpret_cast(data.data() + 0x18), 3}; Containers::ArrayView meshMaterialFieldData{ reinterpret_cast(data.data() + 0x24), 3}; - SceneFieldData meshes{SceneField::Mesh, meshObjectData, meshFieldData}; - SceneFieldData meshMaterialsDifferent{SceneField::MeshMaterial, meshMaterialObjectData, meshMaterialFieldData}; - SceneFieldData meshMaterialsSameButLess{SceneField::MeshMaterial, meshObjectData.except(1), meshMaterialFieldData.except(1)}; + SceneFieldData meshes{SceneField::Mesh, meshMappingData, meshFieldData}; + SceneFieldData meshMaterialsDifferent{SceneField::MeshMaterial, meshMaterialMappingData, meshMaterialFieldData}; + SceneFieldData meshMaterialsSameButLess{SceneField::MeshMaterial, meshMappingData.except(1), meshMaterialFieldData.except(1)}; std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsDifferent}}; - SceneData{SceneObjectType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsSameButLess}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsDifferent}}; + SceneData{SceneMappingType::UnsignedInt, 3, {}, data, {meshes, meshMaterialsSameButLess}}; CORRADE_COMPARE(out.str(), - "Trade::SceneData: Trade::SceneField::MeshMaterial object data [0xcafe0018:0xcafe0024] is different from Trade::SceneField::Mesh object data [0xcafe0000:0xcafe000c]\n" - "Trade::SceneData: Trade::SceneField::MeshMaterial object data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Mesh object data [0xcafe0000:0xcafe000c]\n"); + "Trade::SceneData: Trade::SceneField::MeshMaterial mapping data [0xcafe0018:0xcafe0024] is different from Trade::SceneField::Mesh mapping data [0xcafe0000:0xcafe000c]\n" + "Trade::SceneData: Trade::SceneField::MeshMaterial mapping data [0xcafe0000:0xcafe0008] is different from Trade::SceneField::Mesh mapping data [0xcafe0000:0xcafe000c]\n"); } void SceneDataTest::constructAmbiguousSkinDimensions() { @@ -2032,8 +2032,8 @@ void SceneDataTest::constructAmbiguousSkinDimensions() { std::ostringstream out; Error redirectError{&out}; - SceneData{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Skin, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr} + SceneData{SceneMappingType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Skin, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr} }}; CORRADE_COMPARE(out.str(), "Trade::SceneData: a skin field requires some transformation field to be present in order to disambiguate between 2D and 3D\n"); } @@ -2057,12 +2057,12 @@ void SceneDataTest::constructMove() { int importerState; SceneFieldData meshes{SceneField::Mesh, stridedArrayView(meshData).slice(&Mesh::object), stridedArrayView(meshData).slice(&Mesh::mesh)}; - SceneData a{SceneObjectType::UnsignedShort, 15, std::move(data), {meshes}, &importerState}; + SceneData a{SceneMappingType::UnsignedShort, 15, std::move(data), {meshes}, &importerState}; SceneData b{std::move(a)}; CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(b.objectCount(), 15); - CORRADE_COMPARE(b.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(b.mappingBound(), 15); + CORRADE_COMPARE(b.mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(b.fieldCount(), 1); CORRADE_COMPARE(b.importerState(), &importerState); CORRADE_COMPARE(static_cast(b.data()), meshData.data()); @@ -2070,14 +2070,14 @@ void SceneDataTest::constructMove() { CORRADE_COMPARE(b.fieldType(0), SceneFieldType::UnsignedInt); CORRADE_COMPARE(b.fieldSize(0), 3); CORRADE_COMPARE(b.fieldArraySize(0), 0); - CORRADE_COMPARE(b.objects(0)[2], 122); + CORRADE_COMPARE(b.mapping(0)[2], 122); CORRADE_COMPARE(b.field(0)[2], 2); - SceneData c{SceneObjectType::UnsignedByte, 76, nullptr, {}}; + SceneData c{SceneMappingType::UnsignedByte, 76, nullptr, {}}; c = std::move(b); CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(c.objectCount(), 15); - CORRADE_COMPARE(c.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(c.mappingBound(), 15); + CORRADE_COMPARE(c.mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(c.fieldCount(), 1); CORRADE_COMPARE(c.importerState(), &importerState); CORRADE_COMPARE(static_cast(c.data()), meshData.data()); @@ -2085,7 +2085,7 @@ void SceneDataTest::constructMove() { CORRADE_COMPARE(c.fieldType(0), SceneFieldType::UnsignedInt); CORRADE_COMPARE(c.fieldSize(0), 3); CORRADE_COMPARE(c.fieldArraySize(0), 0); - CORRADE_COMPARE(c.objects(0)[2], 122); + CORRADE_COMPARE(c.mapping(0)[2], 122); CORRADE_COMPARE(c.field(0)[2], 2); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); @@ -2093,9 +2093,9 @@ void SceneDataTest::constructMove() { } void SceneDataTest::findFieldId() { - SceneData scene{SceneObjectType::UnsignedInt, 0, {}, nullptr, { - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedByte, nullptr} + SceneData scene{SceneMappingType::UnsignedInt, 0, {}, nullptr, { + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedByte, nullptr} }}; CORRADE_COMPARE(scene.findFieldId(SceneField::Parent), 0); @@ -2127,9 +2127,9 @@ template void SceneDataTest::findFieldObjectOffset() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{Implementation::sceneObjectTypeFor(), 7, {}, fields, { + SceneData scene{Implementation::sceneMappingTypeFor(), 7, {}, fields, { /* Test also with a completely empty field */ - SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, Implementation::sceneMappingTypeFor(), nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; @@ -2184,7 +2184,7 @@ void SceneDataTest::findFieldObjectOffsetInvalidOffset() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; @@ -2218,9 +2218,9 @@ void SceneDataTest::fieldObjectOffsetNotFound() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { /* Test also with a completely empty field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; @@ -2237,7 +2237,7 @@ void SceneDataTest::fieldObjectOffsetNotFound() { "Trade::SceneData::fieldObjectOffset(): object 1 not found in field Trade::SceneField::Mesh starting at offset 2\n"); } -template void SceneDataTest::objectsAsArrayByIndex() { +template void SceneDataTest::mappingAsArrayByIndex() { setTestCaseTemplateName(NameTraits::name()); struct Field { @@ -2251,18 +2251,18 @@ template void SceneDataTest::objectsAsArrayByIndex() { Containers::StridedArrayView1D view = fields; - SceneData scene{Implementation::sceneObjectTypeFor(), 50, {}, fields, { + SceneData scene{Implementation::sceneMappingTypeFor(), 50, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, Implementation::sceneMappingTypeFor(), nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; - CORRADE_COMPARE_AS(scene.objectsAsArray(1), + CORRADE_COMPARE_AS(scene.mappingAsArray(1), Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); } -template void SceneDataTest::objectsAsArrayByName() { +template void SceneDataTest::mappingAsArrayByName() { setTestCaseTemplateName(NameTraits::name()); struct Field { @@ -2276,18 +2276,18 @@ template void SceneDataTest::objectsAsArrayByName() { Containers::StridedArrayView1D view = fields; - SceneData scene{Implementation::sceneObjectTypeFor(), 50, {}, fields, { + SceneData scene{Implementation::sceneMappingTypeFor(), 50, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor(), nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, Implementation::sceneMappingTypeFor(), nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; - CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::Mesh), + CORRADE_COMPARE_AS(scene.mappingAsArray(SceneField::Mesh), Containers::arrayView({15, 37, 44}), TestSuite::Compare::Container); } -void SceneDataTest::objectsIntoArrayByIndex() { +void SceneDataTest::mappingIntoArrayByIndex() { auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -2306,9 +2306,9 @@ void SceneDataTest::objectsIntoArrayByIndex() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 50, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 50, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, @@ -2317,7 +2317,7 @@ void SceneDataTest::objectsIntoArrayByIndex() { /* The offset-less overload should give back all data */ { UnsignedInt out[3]; - scene.objectsInto(1, out); + scene.mappingInto(1, out); CORRADE_COMPARE_AS(Containers::stridedArrayView(out), view.slice(&Field::object), TestSuite::Compare::Container); @@ -2325,7 +2325,7 @@ void SceneDataTest::objectsIntoArrayByIndex() { /* The offset variant only a subset */ } { Containers::Array out{data.size}; - CORRADE_COMPARE(scene.objectsInto(1, data.offset, out), data.expectedSize); + CORRADE_COMPARE(scene.mappingInto(1, data.offset, out), data.expectedSize); CORRADE_COMPARE_AS(out.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), @@ -2333,7 +2333,7 @@ void SceneDataTest::objectsIntoArrayByIndex() { } } -void SceneDataTest::objectsIntoArrayByName() { +void SceneDataTest::mappingIntoArrayByName() { auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -2352,9 +2352,9 @@ void SceneDataTest::objectsIntoArrayByName() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 50, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 50, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, @@ -2363,7 +2363,7 @@ void SceneDataTest::objectsIntoArrayByName() { /* The offset-less overload should give back all data */ { UnsignedInt out[3]; - scene.objectsInto(SceneField::Mesh, out); + scene.mappingInto(SceneField::Mesh, out); CORRADE_COMPARE_AS(Containers::stridedArrayView(out), view.slice(&Field::object), TestSuite::Compare::Container); @@ -2371,7 +2371,7 @@ void SceneDataTest::objectsIntoArrayByName() { /* The offset variant only a subset */ } { Containers::Array out{data.size}; - CORRADE_COMPARE(scene.objectsInto(SceneField::Mesh, data.offset, out), data.expectedSize); + CORRADE_COMPARE(scene.mappingInto(SceneField::Mesh, data.offset, out), data.expectedSize); CORRADE_COMPARE_AS(out.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), @@ -2379,7 +2379,7 @@ void SceneDataTest::objectsIntoArrayByName() { } } -void SceneDataTest::objectsIntoArrayInvalidSizeOrOffset() { +void SceneDataTest::mappingIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -2391,22 +2391,22 @@ void SceneDataTest::objectsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; std::ostringstream out; Error redirectError{&out}; UnsignedInt destination[2]; - scene.objectsInto(0, destination); - scene.objectsInto(SceneField::Mesh, destination); - scene.objectsInto(0, 4, destination); - scene.objectsInto(SceneField::Mesh, 4, destination); + scene.mappingInto(0, destination); + scene.mappingInto(SceneField::Mesh, destination); + scene.mappingInto(0, 4, destination); + scene.mappingInto(SceneField::Mesh, 4, destination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::objectsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::objectsInto(): offset 4 out of bounds for a field of size 3\n" - "Trade::SceneData::objectsInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::mappingInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::mappingInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::mappingInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::mappingInto(): offset 4 out of bounds for a field of size 3\n"); } template void SceneDataTest::parentsAsArray() { @@ -2423,9 +2423,9 @@ template void SceneDataTest::parentsAsArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::UnsignedByte, 50, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::UnsignedInt, nullptr}, + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::UnsignedInt, nullptr}, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; @@ -2455,9 +2455,9 @@ void SceneDataTest::parentsIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}, + SceneFieldData{SceneField::Mesh, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::UnsignedInt, nullptr}, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}, @@ -2465,13 +2465,13 @@ void SceneDataTest::parentsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Int field[3]; scene.parentsInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -2480,13 +2480,13 @@ void SceneDataTest::parentsIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.parentsInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -2509,25 +2509,25 @@ void SceneDataTest::parentsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.parentsInto(mappingDestination, fieldDestinationCorrect); + scene.parentsInto(mappingDestinationCorrect, fieldDestination); + scene.parentsInto(4, mappingDestination, fieldDestination); + scene.parentsInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::parentsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::parentsInto(): expected mapping 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"); + "Trade::SceneData::parentsInto(): mapping and field destination views have different size, 3 vs 2\n"); } template struct TransformationTypeFor { @@ -2582,9 +2582,9 @@ template void SceneDataTest::transformations2DAsArray() { for those and error/warn on those, they get just ignored. */ components[1] = {2, {3.5f, -1.0f}, {1.0f, 1.5f}}; - SceneData scene{SceneObjectType::UnsignedInt, 6, std::move(data), { + SceneData scene{SceneMappingType::UnsignedInt, 6, std::move(data), { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Transformation, transformations.slice(&Transformation::object), transformations.slice(&Transformation::transformation)}, @@ -2646,7 +2646,7 @@ template void SceneDataTest::transformations2DAsArray /* Just one of translation / rotation / scaling */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation }}; CORRADE_VERIFY(scene.is2D()); @@ -2666,7 +2666,7 @@ template void SceneDataTest::transformations2DAsArray {7, {{1.5f, 2.5f}, {}, Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { rotation }}; CORRADE_VERIFY(scene.is2D()); @@ -2686,7 +2686,7 @@ template void SceneDataTest::transformations2DAsArray {7, {{}, Complex::rotation(-15.0_degf), Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { scaling }}; CORRADE_VERIFY(scene.is2D()); @@ -2709,7 +2709,7 @@ template void SceneDataTest::transformations2DAsArray /* Pairs */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, rotation }}; @@ -2730,7 +2730,7 @@ template void SceneDataTest::transformations2DAsArray {7, {{1.5f, 2.5f}, Complex::rotation(-15.0_degf), Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, scaling }}; @@ -2751,7 +2751,7 @@ template void SceneDataTest::transformations2DAsArray {7, {{1.5f, 2.5f}, {}, {-0.5f, 4.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { rotation, scaling }}; @@ -2775,7 +2775,7 @@ template void SceneDataTest::transformations2DAsArray /* All */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, rotation, scaling @@ -2809,8 +2809,8 @@ void SceneDataTest::transformations2DAsArrayBut3DType() { assertion for translations, as those are at offset 0, which would be interpreted as an empty view if there were no elements. Thus using rotations instead. */ - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Quaternion, nullptr} + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Quaternion, nullptr} }}; std::ostringstream out; @@ -2841,9 +2841,9 @@ void SceneDataTest::transformations2DIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)}, @@ -2851,13 +2851,13 @@ void SceneDataTest::transformations2DIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Matrix3 field[3]; scene.transformations2DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -2866,13 +2866,13 @@ void SceneDataTest::transformations2DIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.transformations2DInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -2903,9 +2903,9 @@ void SceneDataTest::transformations2DTRSIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)}, @@ -2925,13 +2925,13 @@ void SceneDataTest::transformations2DTRSIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Matrix3 field[3]; scene.transformations2DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -2940,13 +2940,13 @@ void SceneDataTest::transformations2DTRSIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.transformations2DInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -2977,9 +2977,9 @@ void SceneDataTest::transformations2DIntoArrayTRS() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)}, @@ -2993,17 +2993,17 @@ void SceneDataTest::transformations2DIntoArrayTRS() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Vector2 translations[3]; Complex rotations[3]; Vector2 scalings[3]; scene.translationsRotationsScalings2DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : 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), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(translations), @@ -3018,17 +3018,17 @@ void SceneDataTest::transformations2DIntoArrayTRS() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{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.mapping ? arrayView(mapping) : 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), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3059,25 +3059,25 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.transformations2DInto(mappingDestination, fieldDestinationCorrect); + scene.transformations2DInto(mappingDestinationCorrect, fieldDestination); + scene.transformations2DInto(4, mappingDestination, fieldDestination); + scene.transformations2DInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations2DInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations2DInto(): expected mapping 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"); + "Trade::SceneData::transformations2DInto(): mapping and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { @@ -3092,40 +3092,40 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[2]; Vector2 translationDestinationCorrect[3]; Vector2 translationDestination[2]; Complex rotationDestinationCorrect[3]; Complex rotationDestination[2]; Vector2 scalingDestinationCorrect[3]; Vector2 scalingDestination[2]; - 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(mappingDestination, translationDestinationCorrect, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(mappingDestinationCorrect, translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(mappingDestinationCorrect, translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(mappingDestinationCorrect, translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings2DInto(4, mappingDestination, translationDestination, rotationDestination, scalingDestination); + scene.translationsRotationsScalings2DInto(0, mappingDestinationCorrect, translationDestination, nullptr, nullptr); + scene.translationsRotationsScalings2DInto(0, mappingDestinationCorrect, nullptr, rotationDestination, nullptr); + scene.translationsRotationsScalings2DInto(0, mappingDestinationCorrect, 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 mapping 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(): mapping and translation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): mapping and rotation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): mapping 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"); @@ -3176,9 +3176,9 @@ template void SceneDataTest::transformations3DAsArray() { for those and error/warn on those, they get just ignored. */ components[1] = {2, {3.5f, -1.0f, 2.2f}, {1.0f, 1.5f, 1.0f}}; - SceneData scene{SceneObjectType::UnsignedInt, 6, std::move(data), { + SceneData scene{SceneMappingType::UnsignedInt, 6, std::move(data), { /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Transformation, transformations.slice(&Transformation::object), transformations.slice(&Transformation::transformation)}, @@ -3240,7 +3240,7 @@ template void SceneDataTest::transformations3DAsArray /* Just one of translation / rotation / scaling */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation }}; CORRADE_VERIFY(!scene.is2D()); @@ -3260,7 +3260,7 @@ template void SceneDataTest::transformations3DAsArray {7, {{1.5f, 2.5f, 3.5f}, {}, Vector3{1.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { rotation }}; CORRADE_VERIFY(!scene.is2D()); @@ -3280,7 +3280,7 @@ template void SceneDataTest::transformations3DAsArray {7, {{}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), Vector3{1.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { scaling }}; CORRADE_VERIFY(!scene.is2D()); @@ -3303,7 +3303,7 @@ template void SceneDataTest::transformations3DAsArray /* Pairs */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, rotation }}; @@ -3324,7 +3324,7 @@ template void SceneDataTest::transformations3DAsArray {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, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, scaling }}; @@ -3345,7 +3345,7 @@ template void SceneDataTest::transformations3DAsArray {7, {{1.5f, 2.5f, 3.5f}, {}, {-0.5f, 4.0f, -16.0f}}}, })), TestSuite::Compare::Container); } { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { rotation, scaling }}; @@ -3369,7 +3369,7 @@ template void SceneDataTest::transformations3DAsArray /* All */ { - SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 8, {}, fields, { translation, rotation, scaling @@ -3403,8 +3403,8 @@ void SceneDataTest::transformations3DAsArrayBut2DType() { assertion for translations, as those are at offset 0, which would be interpreted as an empty view if there were no elements. Thus using rotations instead. */ - SceneData scene{SceneObjectType::UnsignedInt, 0, nullptr, { - SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Complex, nullptr} + SceneData scene{SceneMappingType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Rotation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Complex, nullptr} }}; std::ostringstream out; @@ -3435,9 +3435,9 @@ void SceneDataTest::transformations3DIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)}, @@ -3445,13 +3445,13 @@ void SceneDataTest::transformations3DIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Matrix4 field[3]; scene.transformations3DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -3460,13 +3460,13 @@ void SceneDataTest::transformations3DIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.transformations3DInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3497,9 +3497,9 @@ void SceneDataTest::transformations3DTRSIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)}, @@ -3519,13 +3519,13 @@ void SceneDataTest::transformations3DTRSIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Matrix4 field[3]; scene.transformations3DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -3534,13 +3534,13 @@ void SceneDataTest::transformations3DTRSIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.transformations3DInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3571,9 +3571,9 @@ void SceneDataTest::transformations3DIntoArrayTRS() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)}, @@ -3587,17 +3587,17 @@ void SceneDataTest::transformations3DIntoArrayTRS() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; Vector3 translations[3]; Quaternion rotations[3]; Vector3 scalings[3]; scene.translationsRotationsScalings3DInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : 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), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(translations), @@ -3612,17 +3612,17 @@ void SceneDataTest::transformations3DIntoArrayTRS() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{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.mapping ? arrayView(mapping) : 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), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3653,25 +3653,25 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.transformations3DInto(mappingDestination, fieldDestinationCorrect); + scene.transformations3DInto(mappingDestinationCorrect, fieldDestination); + scene.transformations3DInto(4, mappingDestination, fieldDestination); + scene.transformations3DInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations3DInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations3DInto(): expected mapping 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"); + "Trade::SceneData::transformations3DInto(): mapping and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { @@ -3686,40 +3686,40 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[2]; Vector3 translationDestinationCorrect[3]; Vector3 translationDestination[2]; Quaternion rotationDestinationCorrect[3]; Quaternion rotationDestination[2]; Vector3 scalingDestinationCorrect[3]; Vector3 scalingDestination[2]; - 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(mappingDestination, translationDestinationCorrect, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(mappingDestinationCorrect, translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(mappingDestinationCorrect, translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(mappingDestinationCorrect, translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings3DInto(4, mappingDestination, translationDestination, rotationDestination, scalingDestination); + scene.translationsRotationsScalings3DInto(0, mappingDestinationCorrect, translationDestination, nullptr, nullptr); + scene.translationsRotationsScalings3DInto(0, mappingDestinationCorrect, nullptr, rotationDestination, nullptr); + scene.translationsRotationsScalings3DInto(0, mappingDestinationCorrect, 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 mapping 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(): mapping and translation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): mapping and rotation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): mapping 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"); @@ -3749,9 +3749,9 @@ template void SceneDataTest::meshesMaterialsAsArray() { /* Both meshes and materials */ { - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, meshes, meshMaterials }}; @@ -3764,7 +3764,7 @@ template void SceneDataTest::meshesMaterialsAsArray() { /* Only meshes */ } { - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::UnsignedByte, 50, {}, fields, { meshes }}; @@ -3796,9 +3796,9 @@ void SceneDataTest::meshesMaterialsIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, @@ -3809,15 +3809,15 @@ void SceneDataTest::meshesMaterialsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; UnsignedInt meshes[3]; Int meshMaterials[3]; scene.meshesMaterialsInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field1 ? Containers::arrayView(meshes) : nullptr, data.field2 ? Containers::arrayView(meshMaterials) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(meshes), @@ -3829,15 +3829,15 @@ void SceneDataTest::meshesMaterialsIntoArray() { /* The offset variant should give back only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array meshes{data.size}; Containers::Array meshMaterials{data.size}; CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field1 ? arrayView(meshes) : nullptr, data.field2 ? arrayView(meshMaterials) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3864,32 +3864,32 @@ void SceneDataTest::meshesMaterialsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[2]; UnsignedInt meshDestinationCorrect[3]; UnsignedInt meshDestination[2]; Int meshMaterialDestinationCorrect[3]; Int meshMaterialDestination[2]; - 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(mappingDestination, meshDestinationCorrect, meshMaterialDestinationCorrect); + scene.meshesMaterialsInto(mappingDestinationCorrect, meshDestination, meshMaterialDestinationCorrect); + scene.meshesMaterialsInto(mappingDestinationCorrect, meshDestinationCorrect, meshMaterialDestination); + scene.meshesMaterialsInto(4, mappingDestination, meshDestination, meshMaterialDestination); + scene.meshesMaterialsInto(0, mappingDestinationCorrect, meshDestination, nullptr); + scene.meshesMaterialsInto(0, mappingDestinationCorrect, 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 mapping 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(): mapping and mesh destination views have different size, 3 vs 2\n" + "Trade::SceneData::meshesMaterialsInto(): mapping 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"); } @@ -3907,9 +3907,9 @@ template void SceneDataTest::lightsAsArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} }}; @@ -3939,9 +3939,9 @@ void SceneDataTest::lightsIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)}, @@ -3949,13 +3949,13 @@ void SceneDataTest::lightsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; UnsignedInt field[3]; scene.lightsInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -3964,13 +3964,13 @@ void SceneDataTest::lightsIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.lightsInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3993,25 +3993,25 @@ void SceneDataTest::lightsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.lightsInto(mappingDestination, fieldDestinationCorrect); + scene.lightsInto(mappingDestinationCorrect, fieldDestination); + scene.lightsInto(4, mappingDestination, fieldDestination); + scene.lightsInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::lightsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::lightsInto(): expected mapping 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"); + "Trade::SceneData::lightsInto(): mapping and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::camerasAsArray() { @@ -4028,9 +4028,9 @@ template void SceneDataTest::camerasAsArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} }}; @@ -4060,9 +4060,9 @@ void SceneDataTest::camerasIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)}, @@ -4070,13 +4070,13 @@ void SceneDataTest::camerasIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; UnsignedInt field[3]; scene.camerasInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -4085,13 +4085,13 @@ void SceneDataTest::camerasIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.camerasInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -4114,25 +4114,25 @@ void SceneDataTest::camerasIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.camerasInto(mappingDestination, fieldDestinationCorrect); + scene.camerasInto(mappingDestinationCorrect, fieldDestination); + scene.camerasInto(4, mappingDestination, fieldDestination); + scene.camerasInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::camerasInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::camerasInto(): expected mapping 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"); + "Trade::SceneData::camerasInto(): mapping and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::skinsAsArray() { @@ -4149,11 +4149,11 @@ template void SceneDataTest::skinsAsArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::UnsignedByte, 50, {}, fields, { /* To verify it isn't just picking the first ever field; also to satisfy the requirement of having a transformation field to disambiguate the dimensionality */ - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; @@ -4183,11 +4183,11 @@ void SceneDataTest::skinsIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { /* To verify it isn't just picking the first ever field; also to satisfy the requirement of having a transformation field to disambiguate the dimensionality */ - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)}, @@ -4195,13 +4195,13 @@ void SceneDataTest::skinsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; UnsignedInt field[3]; scene.skinsInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -4210,13 +4210,13 @@ void SceneDataTest::skinsIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.skinsInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -4239,28 +4239,28 @@ void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { /* To satisfy the requirement of having a transformation field to disambiguate the dimensionality */ - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.skinsInto(mappingDestination, fieldDestinationCorrect); + scene.skinsInto(mappingDestinationCorrect, fieldDestination); + scene.skinsInto(4, mappingDestination, fieldDestination); + scene.skinsInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::skinsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::skinsInto(): expected mapping 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"); + "Trade::SceneData::skinsInto(): mapping and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::importerStateAsArray() { @@ -4279,9 +4279,9 @@ template void SceneDataTest::importerStateAsArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} }}; @@ -4313,9 +4313,9 @@ void SceneDataTest::importerStateIntoArray() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::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::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)}, @@ -4323,13 +4323,13 @@ void SceneDataTest::importerStateIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt objects[3]; + UnsignedInt mapping[3]; const void* field[3]; scene.importerStateInto( - data.objects ? Containers::arrayView(objects) : nullptr, + data.mapping ? Containers::arrayView(mapping) : nullptr, data.field ? Containers::arrayView(field) : nullptr ); - if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + if(data.mapping) CORRADE_COMPARE_AS(Containers::stridedArrayView(mapping), view.slice(&Field::object), TestSuite::Compare::Container); if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), @@ -4338,13 +4338,13 @@ void SceneDataTest::importerStateIntoArray() { /* The offset variant only a subset */ } { - Containers::Array objects{data.size}; + Containers::Array mapping{data.size}; Containers::Array field{data.size}; CORRADE_COMPARE(scene.importerStateInto(data.offset, - data.objects ? arrayView(objects) : nullptr, + data.mapping ? arrayView(mapping) : nullptr, data.field ? arrayView(field) : nullptr ), data.expectedSize); - if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + if(data.mapping) CORRADE_COMPARE_AS(mapping.prefix(data.expectedSize), view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -4367,25 +4367,25 @@ void SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} }}; std::ostringstream out; Error redirectError{&out}; - UnsignedInt objectDestinationCorrect[3]; - UnsignedInt objectDestination[2]; + UnsignedInt mappingDestinationCorrect[3]; + UnsignedInt mappingDestination[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); + scene.importerStateInto(mappingDestination, fieldDestinationCorrect); + scene.importerStateInto(mappingDestinationCorrect, fieldDestination); + scene.importerStateInto(4, mappingDestination, fieldDestination); + scene.importerStateInto(0, mappingDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::importerStateInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::importerStateInto(): expected mapping 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"); + "Trade::SceneData::importerStateInto(): mapping and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::mutableAccessNotAllowed() { @@ -4401,7 +4401,7 @@ void SceneDataTest::mutableAccessNotAllowed() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, {}, fields, { SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, @@ -4413,10 +4413,10 @@ void SceneDataTest::mutableAccessNotAllowed() { std::ostringstream out; Error redirectError{&out}; scene.mutableData(); - scene.mutableObjects(0); - scene.mutableObjects(0); - scene.mutableObjects(SceneField::Mesh); - scene.mutableObjects(SceneField::Mesh); + scene.mutableMapping(0); + scene.mutableMapping(0); + scene.mutableMapping(SceneField::Mesh); + scene.mutableMapping(SceneField::Mesh); scene.mutableField(0); scene.mutableField(0); scene.mutableField(1); @@ -4425,10 +4425,10 @@ void SceneDataTest::mutableAccessNotAllowed() { scene.mutableField(sceneFieldCustom(35)); CORRADE_COMPARE(out.str(), "Trade::SceneData::mutableData(): data not mutable\n" - "Trade::SceneData::mutableObjects(): data not mutable\n" - "Trade::SceneData::mutableObjects(): data not mutable\n" - "Trade::SceneData::mutableObjects(): data not mutable\n" - "Trade::SceneData::mutableObjects(): data not mutable\n" + "Trade::SceneData::mutableMapping(): data not mutable\n" + "Trade::SceneData::mutableMapping(): data not mutable\n" + "Trade::SceneData::mutableMapping(): data not mutable\n" + "Trade::SceneData::mutableMapping(): data not mutable\n" "Trade::SceneData::mutableField(): data not mutable\n" "Trade::SceneData::mutableField(): data not mutable\n" "Trade::SceneData::mutableField(): data not mutable\n" @@ -4437,7 +4437,7 @@ void SceneDataTest::mutableAccessNotAllowed() { "Trade::SceneData::mutableField(): data not mutable\n"); } -void SceneDataTest::objectsNotFound() { +void SceneDataTest::mappingNotFound() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -4450,39 +4450,39 @@ void SceneDataTest::objectsNotFound() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, DataFlag::Mutable, fields, { SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, }}; std::ostringstream out; Error redirectError{&out}; - scene.objects(2); - scene.objects(2); - scene.mutableObjects(2); - scene.mutableObjects(2); - scene.objects(sceneFieldCustom(666)); - scene.objects(sceneFieldCustom(666)); - scene.mutableObjects(sceneFieldCustom(666)); - scene.mutableObjects(sceneFieldCustom(666)); - - scene.objectsAsArray(2); - scene.objectsAsArray(sceneFieldCustom(666)); + scene.mapping(2); + scene.mapping(2); + scene.mutableMapping(2); + scene.mutableMapping(2); + scene.mapping(sceneFieldCustom(666)); + scene.mapping(sceneFieldCustom(666)); + scene.mutableMapping(sceneFieldCustom(666)); + scene.mutableMapping(sceneFieldCustom(666)); + + scene.mappingAsArray(2); + scene.mappingAsArray(sceneFieldCustom(666)); CORRADE_COMPARE(out.str(), - "Trade::SceneData::objects(): index 2 out of range for 2 fields\n" - "Trade::SceneData::objects(): index 2 out of range for 2 fields\n" - "Trade::SceneData::mutableObjects(): index 2 out of range for 2 fields\n" - "Trade::SceneData::mutableObjects(): index 2 out of range for 2 fields\n" - "Trade::SceneData::objects(): field Trade::SceneField::Custom(666) not found\n" - "Trade::SceneData::objects(): field Trade::SceneField::Custom(666) not found\n" - "Trade::SceneData::mutableObjects(): field Trade::SceneField::Custom(666) not found\n" - "Trade::SceneData::mutableObjects(): field Trade::SceneField::Custom(666) not found\n" - - "Trade::SceneData::objectsInto(): index 2 out of range for 2 fields\n" - "Trade::SceneData::objectsInto(): field Trade::SceneField::Custom(666) not found\n"); + "Trade::SceneData::mapping(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mapping(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableMapping(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mutableMapping(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mapping(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mapping(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableMapping(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::mutableMapping(): field Trade::SceneField::Custom(666) not found\n" + + "Trade::SceneData::mappingInto(): index 2 out of range for 2 fields\n" + "Trade::SceneData::mappingInto(): field Trade::SceneField::Custom(666) not found\n"); } -void SceneDataTest::objectsWrongType() { +void SceneDataTest::mappingWrongType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -4495,22 +4495,22 @@ void SceneDataTest::objectsWrongType() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedShort, 5, DataFlag::Mutable, fields, { + SceneData scene{SceneMappingType::UnsignedShort, 5, DataFlag::Mutable, fields, { SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, }}; std::ostringstream out; Error redirectError{&out}; - scene.objects(1); - scene.mutableObjects(1); - scene.objects(SceneField::Mesh); - scene.mutableObjects(SceneField::Mesh); + scene.mapping(1); + scene.mutableMapping(1); + scene.mapping(SceneField::Mesh); + scene.mutableMapping(SceneField::Mesh); CORRADE_COMPARE(out.str(), - "Trade::SceneData::objects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" - "Trade::SceneData::mutableObjects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" - "Trade::SceneData::objects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n" - "Trade::SceneData::mutableObjects(): objects are Trade::SceneObjectType::UnsignedShort but requested Trade::SceneObjectType::UnsignedByte\n"); + "Trade::SceneData::mapping(): mapping is Trade::SceneMappingType::UnsignedShort but requested Trade::SceneMappingType::UnsignedByte\n" + "Trade::SceneData::mutableMapping(): mapping is Trade::SceneMappingType::UnsignedShort but requested Trade::SceneMappingType::UnsignedByte\n" + "Trade::SceneData::mapping(): mapping is Trade::SceneMappingType::UnsignedShort but requested Trade::SceneMappingType::UnsignedByte\n" + "Trade::SceneData::mutableMapping(): mapping is Trade::SceneMappingType::UnsignedShort but requested Trade::SceneMappingType::UnsignedByte\n"); } void SceneDataTest::fieldNotFound() { @@ -4525,7 +4525,7 @@ void SceneDataTest::fieldNotFound() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, DataFlag::Mutable, fields, { SceneFieldData{sceneFieldCustom(34), view.slice(&Field::object), view.slice(&Field::foo)}, SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::bar)}, }}; @@ -4669,7 +4669,7 @@ void SceneDataTest::fieldWrongType() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, DataFlag::Mutable, fields, { SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), view.slice(&Field::foobar)}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, }}; @@ -4707,7 +4707,7 @@ void SceneDataTest::fieldWrongPointerType() { } things[2]; Containers::StridedArrayView1D view = things; - SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, things, { + SceneData scene{SceneMappingType::UnsignedInt, 5, DataFlag::Mutable, things, { SceneFieldData{sceneFieldCustom(35), view.slice(&Thing::object), Containers::arrayCast<2, Int*>(view.slice(&Thing::foobar))}, SceneFieldData{SceneField::ImporterState, view.slice(&Thing::object), view.slice(&Thing::importerState)}, }}; @@ -4780,7 +4780,7 @@ void SceneDataTest::fieldWrongArrayAccess() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 5, DataFlag::Mutable, fields, { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, SceneFieldData{sceneFieldCustom(35), view.slice(&Field::object), Containers::arrayCast<2, UnsignedInt>(view.slice(&Field::foobar))}, }}; @@ -4818,7 +4818,7 @@ void SceneDataTest::parentFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; @@ -4843,7 +4843,7 @@ void SceneDataTest::parentForTrivialParent() { {3, 4, 2, 4 /* duplicate, ignored */}, {-1} }}; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, Containers::stridedArrayView(fields->object), Containers::stridedArrayView(fields->parent).broadcasted<0>(4)} }}; @@ -4873,7 +4873,7 @@ void SceneDataTest::childrenFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; @@ -4905,15 +4905,15 @@ void SceneDataTest::childrenForTrivialParent() { /* Going a bit overboard with the arrays, it makes the view creation below easier tho */ struct Field { - UnsignedInt object[4]; + UnsignedInt mapping[4]; Int parent[1]; } fields[]{{ {3, 4, 2, 4 /* duplicate, gets put to the output */}, {-1} }}; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Parent, - Containers::stridedArrayView(fields->object), Containers::stridedArrayView(fields->parent).broadcasted<0>(4)} + Containers::stridedArrayView(fields->mapping), Containers::stridedArrayView(fields->parent).broadcasted<0>(4)} }}; /* Trivial children */ @@ -4944,7 +4944,7 @@ void SceneDataTest::transformation2DFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} }}; @@ -4977,7 +4977,7 @@ void SceneDataTest::transformation2DForTRS() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { 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)} @@ -5011,8 +5011,8 @@ void SceneDataTest::transformation2DForBut3DType() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr} + SceneData scene{SceneMappingType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr} }}; std::ostringstream out; @@ -5036,7 +5036,7 @@ void SceneDataTest::transformation3DFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)} }}; @@ -5069,7 +5069,7 @@ void SceneDataTest::transformation3DForTRS() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { 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)} @@ -5103,8 +5103,8 @@ void SceneDataTest::transformation3DForBut2DType() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, { - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr} + SceneData scene{SceneMappingType::UnsignedInt, 1, nullptr, { + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector2, nullptr} }}; std::ostringstream out; @@ -5130,7 +5130,7 @@ void SceneDataTest::meshesMaterialsFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} }}; @@ -5167,7 +5167,7 @@ void SceneDataTest::lightsFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} }}; @@ -5203,7 +5203,7 @@ void SceneDataTest::camerasFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} }}; @@ -5239,10 +5239,10 @@ void SceneDataTest::skinsFor() { }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { /* To satisfy the requirement of having a transformation field to disambiguate the dimensionality */ - SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; @@ -5281,7 +5281,7 @@ void SceneDataTest::importerStateFor() { Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneData scene{SceneMappingType::UnsignedInt, 7, {}, fields, { SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} }}; @@ -5318,11 +5318,11 @@ void SceneDataTest::childrenDeprecated() { if(!data.skipParent) arrayAppend(fieldData, SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}); if(data.is2D) - arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector2, nullptr}); + arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Vector2, nullptr}); if(data.is3D) - arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}); + arrayAppend(fieldData, SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Vector3, nullptr}); - SceneData scene{SceneObjectType::UnsignedByte, 25, {}, fields, std::move(fieldData)}; + SceneData scene{SceneMappingType::UnsignedByte, 25, {}, fields, std::move(fieldData)}; if(!data.skipParent) { CORRADE_IGNORE_DEPRECATED_PUSH @@ -5351,7 +5351,7 @@ void SceneDataTest::childrenDeprecated() { #endif void SceneDataTest::fieldForFieldMissing() { - SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; + SceneData scene{SceneMappingType::UnsignedInt, 7, nullptr, {}}; CORRADE_COMPARE(scene.parentFor(6), Containers::NullOpt); CORRADE_COMPARE_AS(scene.childrenFor(6), @@ -5380,8 +5380,8 @@ void SceneDataTest::findFieldObjectOffsetInvalidObject() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, { - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneData scene{SceneMappingType::UnsignedInt, 7, nullptr, { + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, }}; std::ostringstream out; @@ -5433,12 +5433,12 @@ void SceneDataTest::releaseFieldData() { Containers::StridedArrayView1D view = Containers::arrayCast(data); auto fields = Containers::array({ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }); SceneFieldData* originalFields = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, std::move(data), std::move(fields)}; + SceneData scene{SceneMappingType::UnsignedByte, 50, std::move(data), std::move(fields)}; Containers::Array released = scene.releaseFieldData(); CORRADE_COMPARE(released.data(), originalFields); @@ -5451,8 +5451,8 @@ void SceneDataTest::releaseFieldData() { /* Data stays untouched, object count and type as well, as it con't result in any dangling data access */ CORRADE_COMPARE(scene.data(), view.data()); - CORRADE_COMPARE(scene.objectCount(), 50); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(scene.mappingBound(), 50); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedByte); } void SceneDataTest::releaseData() { @@ -5464,8 +5464,8 @@ void SceneDataTest::releaseData() { Containers::Array data{NoInit, 3*sizeof(Field)}; Containers::StridedArrayView1D view = Containers::arrayCast(data); - SceneData scene{SceneObjectType::UnsignedByte, 50, std::move(data), { - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneData scene{SceneMappingType::UnsignedByte, 50, std::move(data), { + SceneFieldData{SceneField::Parent, SceneMappingType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; @@ -5480,8 +5480,8 @@ void SceneDataTest::releaseData() { /* Object count and type stays untouched, as it con't result in any dangling data access */ - CORRADE_COMPARE(scene.objectCount(), 50); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(scene.mappingBound(), 50); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedByte); } }}}} diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index fabb93855e..2cb3ae373d 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -47,12 +47,12 @@ struct SceneToolsTest: TestSuite::Tester { struct { const char* name; - SceneObjectType objectType; + SceneMappingType objectType; } CombineData[]{ - {"UnsignedByte output", SceneObjectType::UnsignedByte}, - {"UnsignedShort output", SceneObjectType::UnsignedShort}, - {"UnsignedInt output", SceneObjectType::UnsignedInt}, - {"UnsignedLong output", SceneObjectType::UnsignedLong}, + {"UnsignedByte output", SceneMappingType::UnsignedByte}, + {"UnsignedShort output", SceneMappingType::UnsignedShort}, + {"UnsignedInt output", SceneMappingType::UnsignedInt}, + {"UnsignedLong output", SceneMappingType::UnsignedLong}, }; struct { @@ -86,76 +86,76 @@ void SceneToolsTest::combine() { /* Testing the four possible object types, it should be possible to combine them */ - const UnsignedInt meshObjects[]{45, 78, 23}; - const UnsignedByte meshes[]{3, 5, 17}; + const UnsignedInt meshMappingData[]{45, 78, 23}; + const UnsignedByte meshFieldData[]{3, 5, 17}; - const UnsignedShort parentObjects[]{33, 25}; - const Short parents[]{-1, 33}; + const UnsignedShort parentMappingData[]{33, 25}; + const Short parentData[]{-1, 33}; - const UnsignedByte translationObjects[]{16}; - const Vector2d translations[]{{1.5, -0.5}}; + const UnsignedByte translationMappingData[]{16}; + const Vector2d translationFieldData[]{{1.5, -0.5}}; - const UnsignedLong fooObjects[]{15, 23}; - const Int foos[]{0, 1, 2, 3}; + const UnsignedLong fooMappingData[]{15, 23}; + const Int fooFieldData[]{0, 1, 2, 3}; SceneData scene = Implementation::sceneCombine(data.objectType, 167, Containers::arrayView({ - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, - SceneFieldData{SceneField::Parent, Containers::arrayView(parentObjects), Containers::arrayView(parents)}, - SceneFieldData{SceneField::Translation, Containers::arrayView(translationObjects), Containers::arrayView(translations)}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, + SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentData)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)}, /* Array field */ - SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooObjects), Containers::StridedArrayView2D{foos, {2, 2}}}, + SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D{fooFieldData, {2, 2}}}, /* Empty field */ SceneFieldData{SceneField::Camera, Containers::ArrayView{}, Containers::ArrayView{}} })); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(scene.objectType(), data.objectType); - CORRADE_COMPARE(scene.objectCount(), 167); + CORRADE_COMPARE(scene.mappingType(), data.objectType); + CORRADE_COMPARE(scene.mappingBound(), 167); CORRADE_COMPARE(scene.fieldCount(), 5); CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldArraySize(0), 0); - CORRADE_COMPARE_AS(scene.objectsAsArray(0), Containers::arrayView({ + CORRADE_COMPARE_AS(scene.mappingAsArray(0), Containers::arrayView({ 45, 78, 23 }), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(0), - Containers::arrayView(meshes), + Containers::arrayView(meshFieldData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Short); CORRADE_COMPARE(scene.fieldArraySize(1), 0); - CORRADE_COMPARE_AS(scene.objectsAsArray(1), Containers::arrayView({ + CORRADE_COMPARE_AS(scene.mappingAsArray(1), Containers::arrayView({ 33, 25 }), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(1), - Containers::arrayView(parents), + Containers::arrayView(parentData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(2), SceneField::Translation); CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::Vector2d); CORRADE_COMPARE(scene.fieldArraySize(2), 0); - CORRADE_COMPARE_AS(scene.objectsAsArray(2), + CORRADE_COMPARE_AS(scene.mappingAsArray(2), Containers::arrayView({16}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(2), - Containers::arrayView(translations), + Containers::arrayView(translationFieldData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(15)); CORRADE_COMPARE(scene.fieldType(3), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldArraySize(3), 2); - CORRADE_COMPARE_AS(scene.objectsAsArray(3), + CORRADE_COMPARE_AS(scene.mappingAsArray(3), Containers::arrayView({15, 23}), TestSuite::Compare::Container); /** @todo clean up once it's possible to compare multidimensional containers */ CORRADE_COMPARE_AS(scene.field(3)[0], - (Containers::StridedArrayView2D{foos, {2, 2}})[0], + (Containers::StridedArrayView2D{fooFieldData, {2, 2}})[0], TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(3)[1], - (Containers::StridedArrayView2D{foos, {2, 2}})[1], + (Containers::StridedArrayView2D{fooFieldData, {2, 2}})[1], TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(4), SceneField::Camera); @@ -165,33 +165,33 @@ void SceneToolsTest::combine() { } void SceneToolsTest::combineAlignment() { - const UnsignedShort meshObjects[]{15, 23, 47}; - const UnsignedByte meshes[]{0, 1, 2}; - const UnsignedShort translationObjects[]{5}; /* 1 byte padding before */ - const Vector2d translations[]{{1.5, 3.0}}; /* 4 byte padding before */ - - SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedShort, 167, Containers::arrayView({ - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, - SceneFieldData{SceneField::Translation, Containers::arrayView(translationObjects), Containers::arrayView(translations)} + const UnsignedShort meshMappingData[]{15, 23, 47}; + const UnsignedByte meshFieldData[]{0, 1, 2}; + const UnsignedShort translationMappingData[]{5}; /* 1 byte padding before */ + const Vector2d translationFieldData[]{{1.5, 3.0}}; /* 4 byte padding before */ + + SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 167, Containers::arrayView({ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)} })); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(scene.objectCount(), 167); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(scene.mappingBound(), 167); CORRADE_COMPARE(scene.fieldCount(), 2); CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldArraySize(0), 0); - CORRADE_COMPARE_AS(scene.objects(0), - Containers::arrayView(meshObjects), + CORRADE_COMPARE_AS(scene.mapping(0), + Containers::arrayView(meshMappingData), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(0), - Containers::arrayView(meshes), + Containers::arrayView(meshFieldData), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(reinterpret_cast(scene.objects(0).data()), 2, TestSuite::Compare::Divisible); - CORRADE_COMPARE(scene.objects(0).data(), scene.data()); - CORRADE_COMPARE(scene.objects(0).stride()[0], 2); + CORRADE_COMPARE_AS(reinterpret_cast(scene.mapping(0).data()), 2, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.mapping(0).data(), scene.data()); + CORRADE_COMPARE(scene.mapping(0).stride()[0], 2); CORRADE_COMPARE_AS(reinterpret_cast(scene.field(0).data()), 1, TestSuite::Compare::Divisible); CORRADE_COMPARE(scene.field(0).data(), scene.data() + 3*2); CORRADE_COMPARE(scene.field(0).stride()[0], 1); @@ -199,60 +199,60 @@ void SceneToolsTest::combineAlignment() { CORRADE_COMPARE(scene.fieldName(1), SceneField::Translation); CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Vector2d); CORRADE_COMPARE(scene.fieldArraySize(1), 0); - CORRADE_COMPARE_AS(scene.objects(1), - Containers::arrayView(translationObjects), + CORRADE_COMPARE_AS(scene.mapping(1), + Containers::arrayView(translationMappingData), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(1), - Containers::arrayView(translations), + Containers::arrayView(translationFieldData), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(reinterpret_cast(scene.objects(1).data()), 2, TestSuite::Compare::Divisible); - CORRADE_COMPARE(scene.objects(1).data(), scene.data() + 3*2 + 3 + 1); - CORRADE_COMPARE(scene.objects(1).stride()[0], 2); + CORRADE_COMPARE_AS(reinterpret_cast(scene.mapping(1).data()), 2, TestSuite::Compare::Divisible); + CORRADE_COMPARE(scene.mapping(1).data(), scene.data() + 3*2 + 3 + 1); + CORRADE_COMPARE(scene.mapping(1).stride()[0], 2); CORRADE_COMPARE_AS(reinterpret_cast(scene.field(1).data()), 8, TestSuite::Compare::Divisible); CORRADE_COMPARE(scene.field(1).data(), scene.data() + 3*2 + 3 + 1 + 2 + 4); CORRADE_COMPARE(scene.field(1).stride()[0], 16); } void SceneToolsTest::combineObjectsShared() { - const UnsignedShort meshObjects[]{15, 23, 47}; - const UnsignedByte meshes[]{0, 1, 2}; - const Int meshMaterials[]{72, -1, 23}; + const UnsignedShort meshMappingData[]{15, 23, 47}; + const UnsignedByte meshFieldData[]{0, 1, 2}; + const Int meshMaterialFieldData[]{72, -1, 23}; - const UnsignedShort translationRotationObjects[]{14, 22}; - const Vector2 translations[]{{-1.0f, 25.3f}, {2.2f, 2.1f}}; - const Complex rotations[]{Complex::rotation(35.0_degf), Complex::rotation(22.5_degf)}; + const UnsignedShort translationRotationMappingData[]{14, 22}; + const Vector2 translationFieldData[]{{-1.0f, 25.3f}, {2.2f, 2.1f}}; + const Complex rotationFieldData[]{Complex::rotation(35.0_degf), Complex::rotation(22.5_degf)}; - SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedInt, 173, Containers::arrayView({ + SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedInt, 173, Containers::arrayView({ /* Deliberately in an arbitrary order to avoid false assumptions like fields sharing the same object mapping always being after each other */ - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, - SceneFieldData{SceneField::Translation, Containers::arrayView(translationRotationObjects), Containers::arrayView(translations)}, - SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::arrayView(meshMaterials)}, - SceneFieldData{SceneField::Rotation, Containers::arrayView(translationRotationObjects), Containers::arrayView(rotations)} + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, + SceneFieldData{SceneField::Translation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(translationFieldData)}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::arrayView(meshMaterialFieldData)}, + SceneFieldData{SceneField::Rotation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(rotationFieldData)} })); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); - CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt); + CORRADE_COMPARE(scene.mappingBound(), 173); CORRADE_COMPARE(scene.fieldCount(), 4); CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 3); CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3); - CORRADE_COMPARE(scene.objects(SceneField::Mesh).data(), scene.objects(SceneField::MeshMaterial).data()); + CORRADE_COMPARE(scene.mapping(SceneField::Mesh).data(), scene.mapping(SceneField::MeshMaterial).data()); CORRADE_COMPARE(scene.fieldSize(SceneField::Translation), 2); CORRADE_COMPARE(scene.fieldSize(SceneField::Rotation), 2); - CORRADE_COMPARE(scene.objects(SceneField::Translation).data(), scene.objects(SceneField::Rotation).data()); + CORRADE_COMPARE(scene.mapping(SceneField::Translation).data(), scene.mapping(SceneField::Rotation).data()); } void SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder() { - const UnsignedShort meshObjects[]{15, 23, 47}; - const UnsignedByte meshes[]{0, 1, 2}; + const UnsignedShort meshMappingData[]{15, 23, 47}; + const UnsignedByte meshFieldData[]{0, 1, 2}; - SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedShort, 173, Containers::arrayView({ + SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 173, Containers::arrayView({ SceneFieldData{SceneField::Camera, Containers::ArrayView{nullptr, 1}, Containers::ArrayView{nullptr, 1}}, - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, /* Looks like sharing object mapping with the Camera field, but actually both are placeholders */ SceneFieldData{SceneField::Light, Containers::ArrayView{nullptr, 2}, Containers::ArrayView{nullptr, 2}}, @@ -261,73 +261,73 @@ void SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder() { })); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedShort); - CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort); + CORRADE_COMPARE(scene.mappingBound(), 173); CORRADE_COMPARE(scene.fieldCount(), 4); CORRADE_COMPARE(scene.fieldType(SceneField::Camera), SceneFieldType::UnsignedShort); CORRADE_COMPARE(scene.fieldSize(SceneField::Camera), 1); CORRADE_COMPARE(scene.fieldArraySize(SceneField::Camera), 0); - CORRADE_COMPARE(scene.objects(SceneField::Camera).data(), scene.data()); - CORRADE_COMPARE(scene.objects(SceneField::Camera).stride()[0], 2); + CORRADE_COMPARE(scene.mapping(SceneField::Camera).data(), scene.data()); + CORRADE_COMPARE(scene.mapping(SceneField::Camera).stride()[0], 2); CORRADE_COMPARE(scene.field(SceneField::Camera).data(), scene.data() + 2); CORRADE_COMPARE(scene.field(SceneField::Camera).stride()[0], 2); CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); - CORRADE_COMPARE_AS(scene.objects(SceneField::Mesh), - Containers::arrayView(meshObjects), + CORRADE_COMPARE_AS(scene.mapping(SceneField::Mesh), + Containers::arrayView(meshMappingData), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(SceneField::Mesh), - Containers::arrayView(meshes), + Containers::arrayView(meshFieldData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldType(SceneField::Light), SceneFieldType::UnsignedInt); CORRADE_COMPARE(scene.fieldSize(SceneField::Light), 2); CORRADE_COMPARE(scene.fieldArraySize(SceneField::Light), 0); - CORRADE_COMPARE(scene.objects(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1); - CORRADE_COMPARE(scene.objects(SceneField::Light).stride()[0], 2); + CORRADE_COMPARE(scene.mapping(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1); + CORRADE_COMPARE(scene.mapping(SceneField::Light).stride()[0], 2); CORRADE_COMPARE(scene.field(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2); CORRADE_COMPARE(scene.field(SceneField::Light).stride()[0], 4); CORRADE_COMPARE(scene.fieldType(sceneFieldCustom(15)), SceneFieldType::Short); CORRADE_COMPARE(scene.fieldSize(sceneFieldCustom(15)), 2); CORRADE_COMPARE(scene.fieldArraySize(sceneFieldCustom(15)), 4); - CORRADE_COMPARE(scene.objects(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4); - CORRADE_COMPARE(scene.objects(sceneFieldCustom(15)).stride()[0], 2); + CORRADE_COMPARE(scene.mapping(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4); + CORRADE_COMPARE(scene.mapping(sceneFieldCustom(15)).stride()[0], 2); CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4 + 2*2); CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).stride()[0], 4*2); } void SceneToolsTest::combineObjectSharedFieldPlaceholder() { - const UnsignedInt meshObjects[]{15, 23, 47}; - const UnsignedByte meshes[]{0, 1, 2}; + const UnsignedInt meshMappingData[]{15, 23, 47}; + const UnsignedByte meshFieldData[]{0, 1, 2}; - SceneData scene = Implementation::sceneCombine(SceneObjectType::UnsignedInt, 173, Containers::arrayView({ - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::arrayView(meshes)}, - SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::ArrayView{nullptr, 3}}, + SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedInt, 173, Containers::arrayView({ + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::ArrayView{nullptr, 3}}, })); CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable); - CORRADE_COMPARE(scene.objectType(), SceneObjectType::UnsignedInt); - CORRADE_COMPARE(scene.objectCount(), 173); + CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt); + CORRADE_COMPARE(scene.mappingBound(), 173); CORRADE_COMPARE(scene.fieldCount(), 2); CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0); - CORRADE_COMPARE_AS(scene.objects(0), - Containers::arrayView(meshObjects), + CORRADE_COMPARE_AS(scene.mapping(0), + Containers::arrayView(meshMappingData), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(0), - Containers::arrayView(meshes), + Containers::arrayView(meshFieldData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3); CORRADE_COMPARE(scene.fieldArraySize(SceneField::MeshMaterial), 0); - CORRADE_COMPARE(scene.objects(SceneField::MeshMaterial).data(), scene.objects(SceneField::Mesh).data()); - CORRADE_COMPARE_AS(scene.objects(SceneField::MeshMaterial), - Containers::arrayView(meshObjects), + CORRADE_COMPARE(scene.mapping(SceneField::MeshMaterial).data(), scene.mapping(SceneField::Mesh).data()); + CORRADE_COMPARE_AS(scene.mapping(SceneField::MeshMaterial), + Containers::arrayView(meshMappingData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).data(), scene.data() + 3*4 + 3 + 1); CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).stride()[0], 4); @@ -340,13 +340,13 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Haha now I can use sceneCombine() to conveniently prepare the initial state here, without having to mess with an ArrayTuple */ - const UnsignedShort parentObjects[]{15, 21, 22, 23, 1}; - const Byte parents[]{-1, -1, 21, 22, -1}; + const UnsignedShort parentMappingData[]{15, 21, 22, 23, 1}; + const Byte parentFieldData[]{-1, -1, 21, 22, -1}; /* Two objects have two and three mesh assignments respectively, meaning we need three extra */ - const UnsignedShort meshObjects[]{15, 23, 23, 23, 1, 15, 21}; - const Containers::Pair meshesMaterials[]{ + const UnsignedShort meshMappingData[]{15, 23, 23, 23, 1, 15, 21}; + const Containers::Pair meshMaterialFieldData[]{ {6, 4}, {1, 0}, {2, 3}, @@ -358,13 +358,13 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* One camera is attached to an object that already has a mesh, meaning we need a third extra object */ - const UnsignedShort cameraObjects[]{22, 1}; - const UnsignedInt cameras[]{1, 5}; - SceneData original = Implementation::sceneCombine(SceneObjectType::UnsignedShort, data.originalObjectCount, Containers::arrayView({ - SceneFieldData{SceneField::Parent, Containers::arrayView(parentObjects), Containers::arrayView(parents)}, - SceneFieldData{SceneField::Mesh, Containers::arrayView(meshObjects), Containers::StridedArrayView1D{meshesMaterials, &meshesMaterials[0].first(), Containers::arraySize(meshesMaterials), sizeof(meshesMaterials[0])}}, - SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshObjects), Containers::StridedArrayView1D{meshesMaterials, &meshesMaterials[0].second(), Containers::arraySize(meshesMaterials), sizeof(meshesMaterials[0])}}, - SceneFieldData{SceneField::Camera, Containers::arrayView(cameraObjects), Containers::arrayView(cameras)}, + const UnsignedShort cameraMappingData[]{22, 1}; + const UnsignedInt cameraFieldData[]{1, 5}; + SceneData original = Implementation::sceneCombine(SceneMappingType::UnsignedShort, data.originalObjectCount, Containers::arrayView({ + SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData)}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D{meshMaterialFieldData, &meshMaterialFieldData[0].first(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}}, + SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D{meshMaterialFieldData, &meshMaterialFieldData[0].second(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}}, + SceneFieldData{SceneField::Camera, Containers::arrayView(cameraMappingData), Containers::arrayView(cameraFieldData)}, })); SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original, Containers::arrayView({ @@ -379,7 +379,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* There should be three more objects, or the original count preserved if it's large enough */ - CORRADE_COMPARE(scene.objectCount(), data.expectedObjectCount); + CORRADE_COMPARE(scene.mappingBound(), data.expectedObjectCount); /* Object 1 should have a new child that has the camera, as it has a mesh */ CORRADE_COMPARE_AS(scene.childrenFor(1), diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index fdd47b9c78..af0411e4a7 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -100,7 +100,7 @@ class PbrSpecularGlossinessMaterialData; class PhongMaterialData; class TextureData; -enum class SceneObjectType: UnsignedByte; +enum class SceneMappingType: UnsignedByte; enum class SceneField: UnsignedInt; enum class SceneFieldType: UnsignedShort; class SceneFieldData; From 1fbb87e732a02e099145cef1ed4fedf0729e8b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 2 Sep 2021 18:53:10 +0200 Subject: [PATCH 42/49] Trade: high-level documentation for SceneData. --- doc/artwork/scenedata-dod.svg | 1610 +++++++++++++++++++++++++++++++ doc/artwork/scenedata-tree.svg | 1290 +++++++++++++++++++++++++ doc/snippets/MagnumTrade.cpp | 229 +++++ doc/snippets/README.md | 7 +- doc/snippets/scenedata-dod.svg | 148 +++ doc/snippets/scenedata-tree.svg | 255 +++++ src/Magnum/Trade/SceneData.h | 273 +++++- 7 files changed, 3807 insertions(+), 5 deletions(-) create mode 100644 doc/artwork/scenedata-dod.svg create mode 100644 doc/artwork/scenedata-tree.svg create mode 100644 doc/snippets/scenedata-dod.svg create mode 100644 doc/snippets/scenedata-tree.svg diff --git a/doc/artwork/scenedata-dod.svg b/doc/artwork/scenedata-dod.svg new file mode 100644 index 0000000000..182e608aef --- /dev/null +++ b/doc/artwork/scenedata-dod.svg @@ -0,0 +1,1610 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + materials + + + + skins + + + + meshes + + + + transforms + + + + parents + + + + lights + + + + cameras + + + + 7 + + + + 1 + + + + 0 + + + + 5 + + + + 3 + + + + 6 + + + + 2 + + + + 4 + + + + 1 + + + + 0 + + + + 5 + + + + 6 + + + + 2 + + + + 1 + + + + 1 + + + + 5 + + + + 0 + + + + 4 + + + + 5 + + + + 6 + + + + 3 + + + + 2 + + + + 5 + + + + 6 + + + + 3 + + + + 2 + + + + 2 + + + + 6 + + + + 6 + + + diff --git a/doc/artwork/scenedata-tree.svg b/doc/artwork/scenedata-tree.svg new file mode 100644 index 0000000000..db891c0fa0 --- /dev/null +++ b/doc/artwork/scenedata-tree.svg @@ -0,0 +1,1290 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + material + + + + mesh + + + + skin + + + + material + + + + mesh + + + + light + + + + material + + + + mesh + + + + skin + + + + light + + + + camera + + + + light + + + + transform + + + + transform + + + + transform + + + + transform + + + + + transform + + + + + transform + + + + mesh + + + + transform + + + + transform + + + + material + + + + 3 + + + + 5 + + + + 7 + + + + 0 + + + + 6 + + + + 2 + + + + 1 + + + + 4 + + + + material #2 + + + + mesh #2 + + + diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 6862caa3cb..75ac34c709 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -24,8 +24,11 @@ */ #include +#include #include +#include #include +#include #include #include @@ -51,6 +54,11 @@ #include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" #include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" +#include "Magnum/Trade/SceneData.h" +#include "Magnum/SceneGraph/Drawable.h" +#include "Magnum/SceneGraph/Scene.h" +#include "Magnum/SceneGraph/Object.h" +#include "Magnum/SceneGraph/MatrixTransformation3D.h" #ifdef MAGNUM_TARGET_GL #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" @@ -73,6 +81,7 @@ #endif #define DOXYGEN_ELLIPSIS(...) __VA_ARGS__ +#define DOXYGEN_IGNORE(...) __VA_ARGS__ using namespace Magnum; using namespace Magnum::Math::Literals; @@ -872,4 +881,224 @@ CORRADE_IGNORE_DEPRECATED_POP } #endif +{ +/* [SceneFieldData-usage] */ +Containers::StridedArrayView1D transformationMapping = DOXYGEN_ELLIPSIS({}); +Containers::StridedArrayView1D transformations = DOXYGEN_ELLIPSIS({}); + +Trade::SceneFieldData field{Trade::SceneField::Transformation, + transformationMapping, transformations}; +/* [SceneFieldData-usage] */ +} + +{ +/* [SceneFieldData-usage-offset-only] */ +struct Node { + UnsignedInt object; + Int parent; + Matrix4 transform; +}; + +/* Layout defined statically, 120 objects in total */ +constexpr Trade::SceneFieldData parents{Trade::SceneField::Parent, 120, + Trade::SceneMappingType::UnsignedInt, offsetof(Node, object), sizeof(Node), + Trade::SceneFieldType::Int, offsetof(Node, parent), sizeof(Node)}; +constexpr Trade::SceneFieldData transforms{Trade::SceneField::Transformation, 120, + Trade::SceneMappingType::UnsignedInt, offsetof(Node, object), sizeof(Node), + Trade::SceneFieldType::Matrix4x4, offsetof(Node, transform), sizeof(Node)}; + +/* Actual data populated later */ +Containers::Array data{120*sizeof(Node)}; +DOXYGEN_ELLIPSIS() +Trade::SceneData{Trade::SceneMappingType::UnsignedInt, 120, std::move(data), + {parents, transforms}}; +/* [SceneFieldData-usage-offset-only] */ +} + +{ +typedef SceneGraph::Scene Scene3D; +typedef SceneGraph::Object Object3D; +/* [SceneData-usage1] */ +Trade::SceneData data = DOXYGEN_ELLIPSIS(Trade::SceneData{{}, 0, nullptr, nullptr}); +if(!data.is3D() || + !data.hasField(Trade::SceneField::Parent) || + !data.hasField(Trade::SceneField::Mesh)) + Fatal{} << "Oh noes!"; + +Scene3D scene; +Containers::Array objects{std::size_t(data.mappingBound())}; +/* [SceneData-usage1] */ + +/* [SceneData-usage2] */ +auto parents = data.parentsAsArray(); +for(Containers::Pair& parent: parents) + objects[parent.first()] = new Object3D{}; +/* [SceneData-usage2] */ + +/* [SceneData-usage3] */ +for(Containers::Pair& parent: parents) + objects[parent.first()]->setParent( + parent.second() == -1 ? &scene : objects[parent.second()] + ); +/* [SceneData-usage3] */ + +/* [SceneData-usage4] */ +for(Containers::Pair& transformation: + data.transformations3DAsArray()) +{ + if(Object3D* const object = objects[transformation.first()]) + object->setTransformation(transformation.second()); +} +/* [SceneData-usage4] */ + +/* [SceneData-usage5] */ +class Drawable: public SceneGraph::Drawable3D { + public: + explicit Drawable(Object3D& object, UnsignedInt mesh, Int material, DOXYGEN_ELLIPSIS(int))DOXYGEN_IGNORE(: SceneGraph::Drawable3D{object} { + static_cast(mesh); + static_cast(material); + } int foo); + + DOXYGEN_ELLIPSIS(void draw(const Matrix4&, SceneGraph::Camera3D&) override {}) +}; + +for(const Containers::Pair>& + meshMaterial: data.meshesMaterialsAsArray()) +{ + if(Object3D* const object = objects[meshMaterial.first()]) + new Drawable{*object, meshMaterial.second().first(), + meshMaterial.second().second(), DOXYGEN_ELLIPSIS(0)}; +} +/* [SceneData-usage5] */ + +/* [SceneData-usage-advanced] */ +Containers::StridedArrayView1D transformationMapping = + data.mapping(Trade::SceneField::Transformation); +Containers::StridedArrayView1D transformations = + data.field(Trade::SceneField::Transformation); +for(std::size_t i = 0; i != transformationMapping.size(); ++i) { + if(Object3D* const object = objects[transformationMapping[i]]) + object->setTransformation(transformations[i]); +} +/* [SceneData-usage-advanced] */ +} + +{ +Trade::SceneData data{{}, 0, nullptr, nullptr}; +/* [SceneData-per-object] */ +Containers::Pointer importer = DOXYGEN_ELLIPSIS({}); + +for(const Containers::Pair& meshMaterial: + data.meshesMaterialsFor(importer->objectForName("Chair"))) +{ + Debug{} << "Mesh:" << importer->meshName(meshMaterial.first()); + if(meshMaterial.second() != -1) + Debug{} << "With a material:" << importer->materialName(meshMaterial.second()); +} +/* [SceneData-per-object] */ +} + +{ +Trade::SceneData data{{}, 0, nullptr, nullptr}; +typedef SceneGraph::Object Object3D; +Containers::Array objects; +/* [SceneData-usage-mutable] */ +Containers::StridedArrayView1D transformationMapping = + data.mapping(Trade::SceneField::Transformation); +Containers::StridedArrayView1D mutableTransformations = + data.mutableField(Trade::SceneField::Transformation); +for(std::size_t i = 0; i != transformationMapping.size(); ++i) { + if(Object3D* const object = objects[transformationMapping[i]]) + mutableTransformations[i] = object->transformation(); +} +/* [SceneData-usage-mutable] */ +} + +{ +const std::size_t nodeCount{}, meshAssignmentCount{}; +/* [SceneData-populating] */ +struct Common { + UnsignedShort object; + Short parent; + Matrix4 transformation; +}; + +Containers::StridedArrayView1D common; +Containers::ArrayView meshMaterialMapping; +Containers::ArrayView meshes; +Containers::ArrayView meshMaterials; +Containers::Array data = Containers::ArrayTuple{ + {nodeCount, common}, + {meshAssignmentCount, meshMaterialMapping}, + {meshAssignmentCount, meshes}, + {meshAssignmentCount, meshMaterials} +}; + +// populate the views ... + +Trade::SceneData scene{ + Trade::SceneMappingType::UnsignedShort, nodeCount, + std::move(data), { + Trade::SceneFieldData{Trade::SceneField::Parent, + common.slice(&Common::object), common.slice(&Common::parent)}, + Trade::SceneFieldData{Trade::SceneField::Transformation, + common.slice(&Common::object), common.slice(&Common::transformation)}, + Trade::SceneFieldData{Trade::SceneField::Mesh, + meshMaterialMapping, meshes}, + Trade::SceneFieldData{Trade::SceneField::MeshMaterial, + meshMaterialMapping, meshMaterials} + }}; +/* [SceneData-populating] */ +} + +{ +std::size_t nodeCount{}; +/* [SceneData-populating-custom1] */ +DOXYGEN_ELLIPSIS() +Containers::ArrayView cellMapping; +Containers::ArrayView cellFrustums; +Containers::StridedArrayView2D cellLights; +Containers::Array data = Containers::ArrayTuple{ + DOXYGEN_ELLIPSIS() + {32*24, cellMapping}, + {32*24, cellFrustums}, + {{32*24, 8}, cellLights}, +}; + +for(std::size_t i = 0; i != cellMapping.size(); ++i) { + cellMapping[i] = nodeCount + i; + cellFrustums[i] = DOXYGEN_ELLIPSIS({}); + for(std::size_t j = 0; j != cellLights[i].size(); ++j) + cellLights[i][j] = DOXYGEN_ELLIPSIS({}); +} +/* [SceneData-populating-custom1] */ + +/* [SceneData-populating-custom2] */ +constexpr Trade::SceneField CellFrustum = Trade::sceneFieldCustom(0x00); +constexpr Trade::SceneField CellLights = Trade::sceneFieldCustom(0x01); + +Trade::SceneData scene{ + Trade::SceneMappingType::UnsignedShort, nodeCount + cellMapping.size(), + std::move(data), { + DOXYGEN_ELLIPSIS() + Trade::SceneFieldData{CellFrustum, cellMapping, cellFrustums}, + Trade::SceneFieldData{CellLights, cellMapping, cellLights}, + }}; +/* [SceneData-populating-custom2] */ +} + +{ +constexpr Trade::SceneField CellFrustum = Trade::sceneFieldCustom(0); +constexpr Trade::SceneField CellLights = Trade::sceneFieldCustom(1); +Trade::SceneData scene{{}, 0, nullptr, nullptr}; +/* [SceneData-populating-custom-retrieve] */ +Containers::StridedArrayView1D cellFrustums = + scene.field(CellFrustum); +Containers::StridedArrayView2D cellLights = + scene.field(CellLights); +/* [SceneData-populating-custom-retrieve] */ +static_cast(cellFrustums); +static_cast(cellLights); +} + } diff --git a/doc/snippets/README.md b/doc/snippets/README.md index d391a6da2b..64477e04d2 100644 --- a/doc/snippets/README.md +++ b/doc/snippets/README.md @@ -14,12 +14,13 @@ smaller file sizes: The output printed by the application can be used to update the example output in `doc/getting-started.dox`. -### triangulate.svg +### triangulate.svg, scenedata-tree.svg, scenedata-dod.svg Created by Inkscape from `doc/artwork/triangulate.svg` by saving as Optimized SVG and: -- cleaning up the `` header +- cleaning up the `` header (removing `version`, `xmlns`) - converting to a `style=""`, *keeping* `viewBox` - adding `class="m-image"` -- removing metadata and the background layer +- removing metadata, the background layer and all layers that have + `display: none` diff --git a/doc/snippets/scenedata-dod.svg b/doc/snippets/scenedata-dod.svg new file mode 100644 index 0000000000..826bd3b7f2 --- /dev/null +++ b/doc/snippets/scenedata-dod.svg @@ -0,0 +1,148 @@ + + + + + materials + + + + skins + + + + meshes + + + + transforms + + + + parents + + + + lights + + + + cameras + + + + 7 + + + + 1 + + + + 0 + + + + 5 + + + + 3 + + + + 6 + + + + 2 + + + + 4 + + + + 1 + + + + 0 + + + + 5 + + + + 6 + + + + 2 + + + + 1 + + + + 1 + + + + 5 + + + + 0 + + + + 4 + + + + 5 + + + + 6 + + + + 3 + + + + 2 + + + + 5 + + + + 6 + + + + 3 + + + + 2 + + + + 2 + + + + 6 + + + + 6 + + + diff --git a/doc/snippets/scenedata-tree.svg b/doc/snippets/scenedata-tree.svg new file mode 100644 index 0000000000..c64e2ac77f --- /dev/null +++ b/doc/snippets/scenedata-tree.svg @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + materials + + + + + textures + + + + meshes + + + + + transforms + + + + + parents + + + + + physics + + + + camera + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + material + + + + mesh + + + + skin + + + + material + + + + mesh + + + + light + + + + material + + + + mesh + + + + skin + + + + light + + + + camera + + + + light + + + + transform + + + + transform + + + + transform + + + + transform + + + + + transform + + + + + transform + + + + mesh + + + + transform + + + + transform + + + + material + + + + 3 + + + + 5 + + + + 7 + + + + 0 + + + + 6 + + + + 2 + + + + 1 + + + + 4 + + + + material #2 + + + + mesh #2 + + diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index a384f3acf2..5959182e17 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -533,6 +533,28 @@ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type); Convenience type for populating @ref SceneData, see its documentation for an introduction. + +@section Trade-SceneFieldData-usage Usage + +The most straightforward usage is constructing an instance from a +@ref SceneField and a strided view for the field data and object mapping. The +@ref SceneMappingType and @ref SceneFieldType get inferred from the view types: + +@snippet MagnumTrade.cpp SceneFieldData-usage + +Alternatively, you can pass typeless @cpp const void @ce or 2D views and supply +@ref SceneMappingType and @ref SceneFieldType explicitly. + +@subsection Trade-SceneFieldData-usage-offset-only Offset-only field data + +If the actual field / object data location is not known yet, the instance can +be created as "offset-only", meaning the actual view gets created only later +when passed to a @ref SceneData instance with a concrete data array. This is +useful mainly to avoid pointer patching during data serialization, less so when +the data layout is static (and thus can be defined at compile time), but the +actual data is allocated / populated at runtime: + +@snippet MagnumTrade.cpp SceneFieldData-usage-offset-only */ class MAGNUM_TRADE_EXPORT SceneFieldData { public: @@ -779,8 +801,255 @@ Containers::Array MAGNUM_TRADE_EXPORT sceneFieldDataNonOwningArr /** @brief Scene data -Contains scene hierarchy, object transformations and association of mesh, -material, camera, light and other resources with particular objects. +Contains scene node hierarchy, transformations, resource assignment as well as +any other data associated with the scene. Populated instances of this class are +returned from @ref AbstractImporter::scene(). + +@section Trade-SceneData-representation Data representation and terminology + +@m_div{m-container-inflate m-col-l-9 m-left-l} +@htmlinclude scenedata-tree.svg +@m_enddiv + +The usual mental image of a scene is a tree hierarchy with varying amount of +data attached to each node, like shown in the first diagram. The @ref SceneData +however decouples the hierarchy from the data and stores everything in linear +arrays, like in the second diagram. + +This allows for a more efficient storage, as only the actually needed +information is stored. For example, three nodes in the tree have an implicit +transformation, which we can simply omit, or because there might be way less +materials than meshes, their references can be in a smaller type. It's also +more flexible --- having multiple meshes per node is just about having multiple +entries associated with the same node. + +@m_div{m-clearfix-l} @m_enddiv + +@m_div{m-container-inflate m-col-l-8 m-right-l} +@htmlinclude scenedata-dod.svg +@m_enddiv + +From a high-level perspective, the scene data storage can thought of as a set +of *Fields*, with field entries mapped to *Objects*. Scene *Nodes* are a +special case of *Objects*. + +An *Object* is an arbitrary numeric identifier, not containing anything on its +own. All objects referenced by a particular scene are contained in a range from +@cpp 0 @ce up to @ref mappingBound() minus one. The range is allowed to be +sparse. + +A *Field* is a list of data --- for example transformations, mesh IDs, or +parent objects. The @ref SceneField enum lists all predefined fields together +with possible restrictions and the expected @ref SceneFieldType they're +expected to be in. Custom fields are supported as well. Field entries are +mapped to objects with the same 8-, 16-, 32- or 64-bit type for all fields, +indicated with @ref SceneMappingType. Generally there's a 1:N mapping between +objects and fields (not all objects need to have a transformation, a single +object can reference multiple meshes...), but certain field types expect +various restrictions (such as an object allowed to only have one parent or +transformation). + +Finally, scene *Nodes* are *Objects* that have the @ref SceneField::Parent +field associated. An *Object* thus doesn't have to represent just a node in the +hierarchy. For example, a scene can also contain an alternative representation +in the form of an octree, and thus some objects would be nodes and some octree +cells. + +@subsection Trade-SceneData-representation-multi-scene Object identifiers and multiple scenes + +For a standalone scene, a common case is that the object identifiers form a +contigous range of numbers, and each of the objects has at least one field +assigned. + +The @ref AbstractImporter supports files with multiple scenes. All imported +scenes share a single object range, from @cpp 0 @ce to +@ref AbstractImporter::objectCount(). A particular object can be part of any of +the scenes, causing the @ref SceneData::mappingBound() ranges to be sparse --- +a particular scene having certain object IDs that have no fields assigned. This +is something to be aware of when consuming the scene data, that not all objects +identifiers in the mapping range may actually exist. + +It's also possible for a single object identifier to be contained in multiple +scenes at the same time --- for example, when two scenes are variants of the +same model, with most data shared but certain textures or colors different. +Another theoretical use case is that an object could identify a building in a +3D scene and a corresponding area on a map in a 2D scene. There's no set of +rules the objects should follow, but such identifier reusal should not be +abused for completely unrelated objects. + +@todoc mention handles and how they would affect the basic use below (aaaa!) + +@section Trade-SceneData-usage Basic usage + +A simple goal could be to populate a @ref SceneGraph with a node hierarchy +and attach drawables for meshes where appropriate. First we check if the scene +is 3D with @ref is3D(), because if it's not, it could mean it's either 2D or +that it has no transformation field altogether, suggesting a need for +specialized handling. It's also of no use for this example if there's no node +hierarchy, or if there are no meshes we could draw. + +Then we create the scene instance and an array of pointers that will act as a +map from object identifiers to live objects. The @ref mappingBound() is an +upper bound to all object identifiers referenced by the scene, but as mentioned +above, not all of them may be actual nodes so we don't allocate actual scene +graph object instances for them yet. Alternatively, for very sparse ranges, a hashmap could be also used here. + +@snippet MagnumTrade.cpp SceneData-usage1 + + + +@m_class{m-noindent} + +Next we go through objects that have an associated parent using +@ref parentsAsArray(). Those are the actual nodes we want, so we allocate a +scene graph object for each ... + +@snippet MagnumTrade.cpp SceneData-usage2 + +@m_class{m-noindent} + + + +... and then we assign a proper parent, or add it directly to the scene if the +parent is @cpp -1 @ce. We do this in a separate pass to ensure the parent +object is already allocated by the time we pass it to +@ref SceneGraph::Object::setParent() --- generally there's no guarantee that a +parent appears in the field before its children. + +@snippet MagnumTrade.cpp SceneData-usage3 + +With the hierarchy done, we assign transformations. The transformation field +can be present for only a subset of the nodes, with the rest implicitly having +an indentity transformation, but it can also be present for objects that aren't +nodes, so we only set it for objects present in our hierarchy. The +@ref transformations3DAsArray() function also conveniently converts separate +transformation / rotation / scaling fields into a matrix for us, if the scene +contains only those. + +@snippet MagnumTrade.cpp SceneData-usage4 + +Finally, assuming there's a `Drawable` class derived from +@ref SceneGraph::Drawable that accepts a mesh and material ID (retrieving them +subsequently from @ref AbstractImporter::mesh() / +@relativeref{AbstractImporter,material()}, for example), the process of +assigning actual meshes to corresponding scene nodes is just another +@cpp for @ce loop over @ref meshesMaterialsAsArray(): + +@snippet MagnumTrade.cpp SceneData-usage5 + + + +@m_class{m-note m-success} + +@par + The full process of importing a scene including meshes, materials and + textures is shown in the @ref examples-viewer example. + +@section Trade-SceneData-usage-advanced Advanced usage + +The @ref parentsAsArray(), ... functions shown above always return a +newly-allocated @relativeref{Corrade,Containers::Array} instance in a +well-defined canonical type. While that's convenient and fine at a smaller +scale, it may prove problematic with huge scenes. Or maybe the internal +representation is already optimized for best processing efficiency and the +convenience functions would ruin that. The @ref SceneData class thus provides +access directly to the stored object mapping and field data using the +@ref mapping() and @ref field() accessors. + +However, since each @ref SceneField can be in a variety of types, you're +expected to either check that the type is indeed what you expect using +@ref fieldType(SceneField) const, or at least check with documentation of the +corresponding importer. For example, because glTF files represent the scene +in a textual form, @ref CgltfImporter will always parse the data into canonical +32-bit types. With that assumption, the above snippet that used +@ref transformations3DAsArray() can be rewritten to a zero-copy form like this: + +@snippet MagnumTrade.cpp SceneData-usage-advanced + +@section Trade-SceneData-usage-per-object Per-object access + +While the designated way to access scene data is by iterating through the field +and object arrays, it's also possible to directly look at fields for a +particular object without having to do a lookup on your own and with simplified +error handling. The @ref parentFor(), @ref childrenFor(), +@ref transformation3DFor(), @ref meshesMaterialsFor() and other functions +return either an @relativeref{Corrade,Containers::Optional} or an +@relativeref{Corrade,Containers::Array} depending on whether there's expected +just one occurence of the field or more, returning an empty optional or array +if the field is not present in the scene or if the object was not found in the +field array. + +For example, together with an @ref AbstractImporter instance the scene comes +from, the following snippet lists meshes and material names that are associated +with a "Chair" object, assuming such object exists: + +@snippet MagnumTrade.cpp SceneData-per-object + +Since these APIs perform a linear lookup through the field and object arrays, +these are suited mainly for introspection and debugging purposes. Retrieving +field data for many objects is better achieved by accessing the field data +directly. + +@section Trade-SceneData-usage-mutable Mutable data access + +The interfaces implicitly provide @cpp const @ce views on the contained object +and field data through the @ref data(), @ref mapping() and @ref field() +accessors. This is done because in general case the data can also refer to a +memory-mapped file or constant memory. In cases when it's desirable to modify +the data in-place, there's the @ref mutableData(), @ref mutableMapping() and +@ref mutableField() set of functions. To use these, you need to check that +the data are mutable using @ref dataFlags() first. The following snippet +updates all transformations with the live state of a scene imported earlier, +for example in order to bake in a certain animation state: + +@snippet MagnumTrade.cpp SceneData-usage-mutable + +@section Trade-SceneData-populating Populating an instance + +The actual data in a @ref SceneData instance are represented as a single block +of contiguous memory, which all object and field views point to. This is +easiest to achieve with an @relativeref{Corrade,Containers::ArrayTuple}. In the +example below, all objects have a parent and a transformation field, which are +stored together in a @cpp struct @ce, while a subset of them has a mesh and a +material assigned, which are stored in separate arrays. And because the scene +is small, we save space by using just 16-bit indices for everything. + +@snippet MagnumTrade.cpp SceneData-populating + +Note that the above layout is just an example, you're free to choose any +representation that matches your use case best, with fields interleaved +together or not. See also the @ref SceneFieldData class documentation for +additional ways how to specify and annotate the data. + +@subsection Trade-SceneData-populating-custom Custom scene fields and non-node objects + +Let's say that, in addition to the node hierarchy from above, our scene +contains also a precomputed [camera-space light culling grid](https://wickedengine.net/2018/01/10/optimizing-tile-based-light-culling/), +where each cell of the grid contains a list of lights that affect given area of +the screen. And we want to add it into the @ref SceneData for verification with +external tools. + +For simplicity let's assume we have a 32x24 grid and the shader we have can +work with up to 8 lights. So there will be a fixed-size array for each of those +cells, and we save calculated frustums for inspection as well. For the new data +we allocate object IDs from a range after `nodeCount`, and copy in the actual +data. + +@snippet MagnumTrade.cpp SceneData-populating-custom1 + +Then, similarly as with @ref MeshData, the scene can have custom fields as +well, created with @ref sceneFieldCustom(). We create one for the cell light +reference array and one for the cell frustum and then use them to annotate +the views allocated above. Note that we also increased the total object count +to include the light culling grid cells as well. + +@snippet MagnumTrade.cpp SceneData-populating-custom2 + +Later, the fields can be retrieved back using the same custom identifiers. +The light references are actually a 2D array (8 lights for each cell), so a +@cpp [] @ce needs to be used: + +@snippet MagnumTrade.cpp SceneData-populating-custom-retrieve @see @ref AbstractImporter::scene() */ From 3f38ab1952dab3a5496b4458ca41336ad3db3e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 4 Dec 2021 19:44:17 +0100 Subject: [PATCH 43/49] Trade: flags for annotating SceneData field object mappings. Currently used by the per-object access APIs to make the lookup constant- or logarithmic-time instead of linear, available for use by external data consumers as well. --- src/Magnum/Trade/Implementation/sceneTools.h | 2 +- src/Magnum/Trade/SceneData.cpp | 87 +++++- src/Magnum/Trade/SceneData.h | 274 ++++++++++++++----- src/Magnum/Trade/Test/SceneDataTest.cpp | 262 ++++++++++++------ src/Magnum/Trade/Trade.h | 2 + 5 files changed, 467 insertions(+), 160 deletions(-) diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index 6336dc43e0..a4242f6c3c 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -109,7 +109,7 @@ inline SceneData sceneCombine(const SceneMappingType mappingType, const Unsigned for(std::size_t i = 0; i != fields.size(); ++i) { const SceneFieldData& field = fields[i]; - CORRADE_INTERNAL_ASSERT(!field.isOffsetOnly()); + CORRADE_INTERNAL_ASSERT(!(field.flags() & SceneFieldFlag::OffsetOnly)); /* Mapping data. Allocate if the view is a placeholder of if it wasn't used by other fields yet. */ diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 44a19a77c7..a2ad3a3252 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -25,9 +25,12 @@ #include "SceneData.h" +#include +#include #include #include #include +#include #include #include @@ -473,7 +476,32 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) { CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeAlignment(): invalid type" << type, {}); } -SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} { +Debug& operator<<(Debug& debug, const SceneFieldFlag value) { + debug << "Trade::SceneFieldFlag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case SceneFieldFlag::value: return debug << "::" #value; + _c(OffsetOnly) + _c(ImplicitMapping) + _c(OrderedMapping) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const SceneFieldFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Trade::SceneFieldFlags{}", { + SceneFieldFlag::OffsetOnly, + SceneFieldFlag::ImplicitMapping, + /* This one is implied by ImplicitMapping, so has to be after */ + SceneFieldFlag::OrderedMapping + }); +} + +SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize, flags} { /* Yes, this calls into a constexpr function defined in the header -- because I feel that makes more sense than duplicating the full assert logic */ @@ -565,7 +593,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp if(field._size) { const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)* (field._fieldArraySize ? field._fieldArraySize : 1); - if(field._isOffsetOnly) { + if(field._flags & SceneFieldFlag::OffsetOnly) { const std::size_t mappingSize = field._mappingData.offset + (field._size - 1)*field._mappingStride + mappingTypeSize; const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize; CORRADE_ASSERT(mappingSize <= _data.size(), @@ -780,7 +808,7 @@ Containers::StridedArrayView1D SceneData::fieldDataMappingViewIntern CORRADE_INTERNAL_ASSERT(offset + size <= field._size); return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ - {static_cast(field._isOffsetOnly ? + {static_cast(field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._mappingData.offset : field._mappingData.pointer) + field._mappingStride*offset, ~std::size_t{}}, size, field._mappingStride @@ -795,7 +823,7 @@ Containers::StridedArrayView1D SceneData::fieldDataFieldViewInternal CORRADE_INTERNAL_ASSERT(offset + size <= field._size); return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ - {static_cast(field._isOffsetOnly ? + {static_cast(field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : field._fieldData.pointer) + field._fieldStride*offset, ~std::size_t{}}, size, field._fieldStride}; @@ -809,7 +837,7 @@ SceneFieldData SceneData::fieldData(const UnsignedInt id) const { CORRADE_ASSERT(id < _fields.size(), "Trade::SceneData::fieldData(): index" << id << "out of range for" << _fields.size() << "fields", SceneFieldData{}); const SceneFieldData& field = _fields[id]; - return SceneFieldData{field._name, field._mappingType, fieldDataMappingViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize}; + return SceneFieldData{field._name, field._mappingType, fieldDataMappingViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize, field._flags & ~SceneFieldFlag::OffsetOnly}; } SceneField SceneData::fieldName(const UnsignedInt id) const { @@ -818,6 +846,12 @@ SceneField SceneData::fieldName(const UnsignedInt id) const { return _fields[id]._name; } +SceneFieldFlags SceneData::fieldFlags(const UnsignedInt id) const { + CORRADE_ASSERT(id < _fields.size(), + "Trade::SceneData::fieldFlags(): index" << id << "out of range for" << _fields.size() << "fields", {}); + return _fields[id]._flags; +} + SceneFieldType SceneData::fieldType(const UnsignedInt id) const { CORRADE_ASSERT(id < _fields.size(), "Trade::SceneData::fieldType(): index" << id << "out of range for" << _fields.size() << "fields", {}); @@ -859,11 +893,32 @@ bool SceneData::hasField(const SceneField name) const { namespace { -template std::size_t findObject(const Containers::StridedArrayView1D& mapping, const UnsignedInt object) { +/* The `objects` view is already adjusted for `offset`, the offset is needed + only to return the correct value for ImplicitMapping */ +template std::size_t findObject(const SceneFieldFlags flags, const Containers::StridedArrayView1D& mapping, const std::size_t offset, const UnsignedInt object) { + const std::size_t max = mapping.size(); + + /* Implicit mapping, position equals object ID (if in bounds) and thus an + O(1)-complexity search. A superset of OrderedMapping so has to be + before. */ + if(flags >= SceneFieldFlag::ImplicitMapping) + return object >= offset && object - offset < mapping.size() ? + object - offset : max; + const Containers::StridedArrayView1D mappingT = Containers::arrayCast(mapping); - const std::size_t max = mappingT.size(); - /** @todo implement something faster than O(n) when field-specific flags - can annotate how the object mapping is done */ + + /* Ordered mapping, so an O(log n)-complexity search. It also has to be + noted that STL algorithms generally suck (this needs a + std::iterator_traits specialization for the StridedArrayView, FFS!), + std::binary_search() is useless because it returns just a bool (!!) and + std::lower_bound() is error prone beyond any reason. */ + if(flags >= SceneFieldFlag::OrderedMapping) { + const Containers::StridedIterator<1, const T> found = std::lower_bound(mappingT.begin(), mappingT.end(), T(object)); + if(found == mappingT.end() || *found != object) return max; + return found - mappingT.begin(); + } + + /* Generally unordered container, O(n)-complexity search. */ for(std::size_t i = 0; i != max; ++i) if(mappingT[i] == object) return i; return max; @@ -874,13 +929,13 @@ template std::size_t findObject(const Containers::StridedArrayView1D mapping = fieldDataMappingViewInternal(field, offset, field._size - offset); if(field._mappingType == SceneMappingType::UnsignedInt) - return offset + findObject(mapping, object); + return offset + findObject(field._flags, mapping, offset, object); else if(field._mappingType == SceneMappingType::UnsignedShort) - return offset + findObject(mapping, object); + return offset + findObject(field._flags, mapping, offset, object); else if(field._mappingType == SceneMappingType::UnsignedByte) - return offset + findObject(mapping, object); + return offset + findObject(field._flags, mapping, offset, object); else if(field._mappingType == SceneMappingType::UnsignedLong) - return offset + findObject(mapping, object); + return offset + findObject(field._flags, mapping, offset, object); else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -970,6 +1025,12 @@ bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt obj return findFieldObjectOffsetInternal(field, object, 0) != field._size; } +SceneFieldFlags SceneData::fieldFlags(const SceneField name) const { + const UnsignedInt fieldId = findFieldIdInternal(name); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldFlags(): field" << name << "not found", {}); + return _fields[fieldId]._flags; +} + SceneFieldType SceneData::fieldType(const SceneField name) const { const UnsignedInt fieldId = findFieldIdInternal(name); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {}); diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 5959182e17..53feab5688 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneMappingType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneMappingTypeSize(), @ref Magnum::sceneMappingTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() + * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneMappingType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, @ref Magnum::Trade::SceneFieldFlag, enum set @ref Magnum::Trade::SceneFieldFlags, function @ref Magnum::sceneMappingTypeSize(), @ref Magnum::sceneMappingTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom() */ #include @@ -527,6 +527,80 @@ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type); */ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type); +/** +@brief Scene field flag +@m_since_latest + +@see @ref SceneFieldFlags, @ref SceneFieldData, @ref SceneFieldData::flags(), + @ref SceneData::fieldFlags() +*/ +enum class SceneFieldFlag: UnsignedByte { + /** + * The field is offset-only, i.e. doesn't contain the data views directly + * but referes to unspecified external data. Set implicitly by + * the @ref SceneFieldData::SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort, SceneFieldFlags) + * constructor, can't be used for any other constructor. + * @see @ref SceneFieldData::mappingData(Containers::ArrayView) const, + * @ref SceneFieldData::fieldData(Containers::ArrayView) const + */ + OffsetOnly = 1 << 0, + + /** + * The field has an ordered object mapping, i.e. a monotonically increasing + * sequence. Object IDs in fields marked with this flag can be looked up + * with an @f$ \mathcal{O}(\log{} n) @f$ complexity, gaps and duplicates + * are possible. + * + * Note that validity of the object mapping data isn't checked in any way + * and if the data doesn't correspond to rules of the flag, queries such + * as @ref SceneData::findFieldObjectOffset() may return a wrong value. + * + * If a field has neither this nor the @ref SceneFieldFlag::ImplicitMapping + * flag, it's assumed to be unordered, with an + * @f$ \mathcal{O}(n) @f$ lookup complexity. + */ + OrderedMapping = 1 << 1, + + /** + * The field has an implicit object mapping, i.e. a contiguous sequence + * from 0 up to size of the field. A superset of + * @ref SceneFieldFlag::OrderedMapping. Object IDs in fields marked with + * this flag can be looked up with an @f$ \mathcal{O}(1) @f$ complexity, + * but the field is restricted to exactly one value for each object. + * + * Note that validity of the object mapping data isn't checked in any way + * and if the data doesn't correspond to rules of the flag, queries such + * as @ref SceneData::findFieldObjectOffset() may return a wrong value. + * + * If a field has neither this nor the @ref SceneFieldFlag::OrderedMapping + * flag, it's assumed to be unordered, with an + * @f$ \mathcal{O}(n) @f$ lookup complexity. + */ + ImplicitMapping = (1 << 2)|OrderedMapping, +}; + +/** +@debugoperatorenum{SceneFieldFlag} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldFlag value); + +/** +@brief Scene field flags +@m_since_latest + +@see @ref SceneFieldData::flags(), @ref SceneData::fieldFlags() +*/ +typedef Containers::EnumSet SceneFieldFlags; + +CORRADE_ENUMSET_OPERATORS(SceneFieldFlags) + +/** +@debugoperatorenum{SceneFieldFlags} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldFlags value); + /** @brief Scene field data @m_since_latest @@ -555,6 +629,25 @@ the data layout is static (and thus can be defined at compile time), but the actual data is allocated / populated at runtime: @snippet MagnumTrade.cpp SceneFieldData-usage-offset-only + +Offset-only fields are marked with @ref SceneFieldFlag::OffsetOnly in +@ref flags(). + +@subsection Trade-SceneFieldData-usage-object-mapping Ordered and implicit object mapping + +If you can guarantee the object mapping field is monotonically non-decreasing, +it's recommended to annotate it with @ref SceneFieldFlag::OrderedMapping. This +makes certain convenience APIs such as @ref SceneData::findFieldObjectOffset() +or e.g. @relativeref{SceneData,transformation3DFor()} perform the lookup in +@f$ \mathcal{O}(\log{} n) @f$ instead of @f$ \mathcal{O}(n) @f$. Data consuming +algorithms on the application side can then also adapt based on what flags are +present in @ref SceneData::fieldFlags(). + +In some cases the object mapping is even implicit, i.e. the first entry of the +field specifying data for object @cpp 0 @ce, second entry for object +@cpp 1 @ce, third for object @cpp 2 @ce and so on. You can annotate such fields +with @ref SceneFieldFlag::ImplicitMapping, which is a superset of +@relativeref{SceneFieldFlag,OrderedMapping}. */ class MAGNUM_TRADE_EXPORT SceneFieldData { public: @@ -565,7 +658,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * initialization of the field array for @ref SceneData, expected to be * replaced with concrete values later. */ - constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _isOffsetOnly{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} + constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _flags{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} /** * @brief Type-erased constructor @@ -576,12 +669,17 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @param fieldData Field data * @param fieldArraySize Field array size. Use @cpp 0 @ce for * non-array fields. + * @param flags Field flags. + * @ref SceneFieldFlag::OffsetOnly is not allowed here. * * Expects that @p mappingData and @p fieldData have the same size, * @p fieldType corresponds to @p name and @p fieldArraySize is zero * for builtin fields. */ - constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept; + + /** @overload */ + constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingType, mappingData, fieldType, fieldData, 0, flags} {} /** * @brief Constructor @@ -591,6 +689,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @param fieldData Field data * @param fieldArraySize Field array size. Use @cpp 0 @ce for * non-array fields. + * @param flags Field flags. + * @ref SceneFieldFlag::OffsetOnly is not allowed here. * * Expects that @p mappingData and @p fieldData have the same size in * the first dimension, that the second dimension of @p mappingData is @@ -600,51 +700,58 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @p fieldArraySize and that @p fieldType corresponds to @p name and * @p fieldArraySize is zero for builtin attributes. */ - explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, UnsignedShort fieldArraySize = 0) noexcept; + explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept; + + /** @overload */ + explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingData, fieldType, fieldData, 0, flags} {} /** * @brief Constructor * @param name Field name * @param mappingData Object mapping data * @param fieldData Field data + * @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is + * not allowed here. * * Detects @ref SceneMappingType based on @p T and @ref SceneFieldType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort). + * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort, SceneFieldFlags). * For all types known by Magnum, the detected @ref SceneFieldType is * of the same name as the type (so e.g. @relativeref{Magnum,Vector3ui} * gets recognized as @ref SceneFieldType::Vector3ui). */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, mappingData, Containers::stridedArrayView(fieldData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::ArrayView& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, mappingData, Containers::stridedArrayView(fieldData), flags} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::ArrayView& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), Containers::stridedArrayView(fieldData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::ArrayView& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), Containers::stridedArrayView(fieldData), flags} {} /** * @brief Construct an array field * @param name Field name * @param mappingData Object mapping data * @param fieldData Field data + * @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is + * not allowed here. * * Detects @ref SceneMappingType based on @p T and @ref SceneFieldType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort, SceneFieldFlags) * with the @p fieldData second dimension size passed to * @p fieldArraySize. Expects that the second dimension of @p fieldData * is contiguous. At the moment only custom fields can be arrays, which * means this function can't be used with a builtin @p name. See - * @ref SceneFieldData(SceneField, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) + * @ref SceneFieldData(SceneField, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, SceneFieldFlags) * for details about @ref SceneMappingType and @ref SceneFieldType * detection. */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& mappingData, const Containers::StridedArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {} /** * @brief Construct an offset-only field @@ -658,6 +765,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @param fieldStride Field data stride * @param fieldArraySize Field array size. Use @cpp 0 @ce for * non-array fields. + * @param flags Field flags. + * @ref SceneFieldFlag::OffsetOnly is set implicitly. * * Instances created this way refer to offsets in unspecified * external scene data instead of containing the data views directly. @@ -668,24 +777,19 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * Note that due to the @cpp constexpr @ce nature of this constructor, * no @p mappingType checks against @p mappingStride or * @p fieldType / @p fieldArraySize checks against @p fieldStride can - * be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort) + * be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D&, SceneFieldType, const Containers::StridedArrayView1D&, UnsignedShort, SceneFieldFlags) * constructor if you want additional safeguards. - * @see @ref isOffsetOnly(), @ref fieldArraySize(), + * @see @ref flags(), @ref fieldArraySize(), * @ref mappingData(Containers::ArrayView) const, * @ref fieldData(Containers::ArrayView) const */ - explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0) noexcept; + explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept; - /** - * @brief If the field is offset-only - * - * Returns @cpp true @ce if the field doesn't contain the data views - * directly, but instead refers to unspecified external data. - * @see @ref mappingData(Containers::ArrayView) const, - * @ref fieldData(Containers::ArrayView) const, - * @ref SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort) - */ - constexpr bool isOffsetOnly() const { return _isOffsetOnly; } + /** @overload */ + explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags) noexcept: SceneFieldData{name, size, mappingType, mappingOffset, mappingStride, fieldType, fieldOffset, fieldStride, 0, flags} {} + + /** @brief Field flags */ + constexpr SceneFieldFlags flags() const { return _flags; } /** @brief Field name */ constexpr SceneField name() const { return _name; } @@ -699,30 +803,31 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { /** * @brief Type-erased object mapping data * - * Expects that the field is not offset-only, in that case use the - * @ref mappingData(Containers::ArrayView) const overload - * instead. - * @see @ref isOffsetOnly() + * Expects that the field does not have @ref SceneFieldFlag::OffsetOnly + * set, in that case use the @ref mappingData(Containers::ArrayView) const + * overload instead. + * @see @ref flags() */ constexpr Containers::StridedArrayView1D mappingData() const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ {_mappingData.pointer, ~std::size_t{}}, _size, - (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array"), _mappingStride)}; + (CORRADE_CONSTEXPR_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly), "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array"), _mappingStride)}; } /** * @brief Type-erased object mapping data for an offset-only attribute * - * If the field is not offset-only, the @p data parameter is ignored. - * @see @ref isOffsetOnly(), @ref mappingData() const + * If the field does not have @ref SceneFieldFlag::OffsetOnly set, the + * @p data parameter is ignored. + * @see @ref flags(), @ref mappingData() const */ Containers::StridedArrayView1D mappingData(Containers::ArrayView data) const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ - data, _isOffsetOnly ? reinterpret_cast(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride}; + data, _flags & SceneFieldFlag::OffsetOnly ? reinterpret_cast(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride}; } /** @brief Field type */ @@ -734,30 +839,31 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { /** * @brief Type-erased field data * - * Expects that the field is not offset-only, in that case use the - * @ref fieldData(Containers::ArrayView) const overload - * instead. - * @see @ref isOffsetOnly() + * Expects that the field does not have @ref SceneFieldFlag::OffsetOnly + * set, in that case use the @ref fieldData(Containers::ArrayView) const + * overload instead. + * @see @ref flags() */ constexpr Containers::StridedArrayView1D fieldData() const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ {_fieldData.pointer, ~std::size_t{}}, _size, - (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array"), _fieldStride)}; + (CORRADE_CONSTEXPR_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly), "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array"), _fieldStride)}; } /** * @brief Type-erased field data for an offset-only attribute * - * If the field is not offset-only, the @p data parameter is ignored. - * @see @ref isOffsetOnly(), @ref fieldData() const + * If the field does not have @ref SceneFieldFlag::OffsetOnly set, the + * @p data parameter is ignored. + * @see @ref flags(), @ref fieldData() const */ Containers::StridedArrayView1D fieldData(Containers::ArrayView data) const { return Containers::StridedArrayView1D{ /* We're *sure* the view is correct, so faking the view size */ /** @todo better ideas for the StridedArrayView API? */ - data, _isOffsetOnly ? reinterpret_cast(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride}; + data, _flags & SceneFieldFlag::OffsetOnly ? reinterpret_cast(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride}; } private: @@ -776,7 +882,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { UnsignedLong _size; SceneField _name; - bool _isOffsetOnly; + SceneFieldFlags _flags; SceneMappingType _mappingType; Short _mappingStride; Data _mappingData; @@ -985,10 +1091,12 @@ with a "Chair" object, assuming such object exists: @snippet MagnumTrade.cpp SceneData-per-object -Since these APIs perform a linear lookup through the field and object arrays, -these are suited mainly for introspection and debugging purposes. Retrieving -field data for many objects is better achieved by accessing the field data -directly. +The actual object ID lookup is done by @ref findFieldObjectOffset() and +depending on what @ref SceneFieldFlags are present for given field, it can be +done in constant, logarithmic or, worst case, linear time. As such, for general +scene representations these are suited mainly for introspection and debugging +purposes and retrieving field data for many objects is better achieved by +accessing the field data directly. @section Trade-SceneData-usage-mutable Mutable data access @@ -1219,9 +1327,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref fieldSize() and @ref fieldArraySize() accessors. Compared to * those and to @ref fieldData(UnsignedInt) const, the * @ref SceneFieldData instances returned by this function may have - * different data pointers, and some of them might be offset-only --- - * use this function only if you *really* know what are you doing. - * @see @ref SceneFieldData::isOffsetOnly() + * different data pointers, and some of them might have + * @ref SceneFieldFlag::OffsetOnly set --- use this function only if + * you *really* know what are you doing. + * @see @ref SceneFieldData::flags() */ Containers::ArrayView fieldData() const & { return _fields; } @@ -1244,9 +1353,9 @@ class MAGNUM_TRADE_EXPORT SceneData { * * Unlike with @ref fieldData() and @ref releaseFieldData(), returned * instances are guaranteed to always have an absolute data pointer - * (i.e., @ref SceneFieldData::isOffsetOnly() always returning - * @cpp false @ce). The @p id is expected to be smaller than - * @ref fieldCount(). + * (i.e., @ref SceneFieldData::flags() never having + * @ref SceneFieldFlag::OffsetOnly set). The @p id is expected to be + * smaller than @ref fieldCount(). */ SceneFieldData fieldData(UnsignedInt id) const; @@ -1261,6 +1370,15 @@ class MAGNUM_TRADE_EXPORT SceneData { */ SceneField fieldName(UnsignedInt id) const; + /** + * @brief Field flags + * @m_since_latest + * + * The @p id is expected to be smaller than @ref fieldCount(). + * @see @ref findFieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const + */ + SceneFieldFlags fieldFlags(UnsignedInt id) const; + /** * @brief Field type * @m_since_latest @@ -1380,8 +1498,12 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref mappingBound() and @p offset not larger than * @ref fieldSize(UnsignedInt) const. * - * The lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with - * @f$ n @f$ being the size of the field. + * If the field has @ref SceneFieldFlag::ImplicitMapping, the lookup is + * done in an @f$ \mathcal{O}(1) @f$ complexity. Otherwise, if the + * field has @ref SceneFieldFlag::OrderedMapping, the lookup is done in + * an @f$ \mathcal{O}(\log{} n) @f$ complexity with @f$ n @f$ being the + * size of the field. Otherwise, the lookup is done in an + * @f$ \mathcal{O}(n) @f$ complexity. * * You can also use @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const * to directly find offset of an object in given named field. @@ -1399,8 +1521,13 @@ class MAGNUM_TRADE_EXPORT SceneData { * exist, @p object is expected to be smaller than @ref mappingBound() * and @p offset not be larger than @ref fieldSize(SceneField) const. * - * The lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with - * @f$ m @f$ being the field count and @f$ n @f$ the size of the field. + * If the field has @ref SceneFieldFlag::ImplicitMapping, the lookup is + * done in an @f$ \mathcal{O}(m) @f$ complexity with @f$ m @f$ being + * the * field count. Otherwise, if the field has + * @ref SceneFieldFlag::OrderedMapping, the lookup is done in an + * @f$ \mathcal{O}(m + \log{} n) @f$ complexity with @f$ m @f$ being + * the field count and @f$ n @f$ the size of the field. Otherwise, the + * lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity. * * @see @ref hasField(), @ref hasFieldObject(SceneField, UnsignedInt) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const @@ -1449,6 +1576,15 @@ class MAGNUM_TRADE_EXPORT SceneData { */ bool hasFieldObject(SceneField fieldName, UnsignedInt object) const; + /** + * @brief Field flags + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const + */ + SceneFieldFlags fieldFlags(SceneField name) const; + /** * @brief Type of a named field * @m_since_latest @@ -2632,10 +2768,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * the actual memory might be not. Additionally, the returned * @ref SceneFieldData instances may have different data pointers and * sizes than what's returned by the @ref field() and - * @ref fieldData(UnsignedInt) const accessors as some of them might be - * offset-only --- use this function only if you *really* know what are - * you doing. - * @see @ref fieldData(), @ref SceneFieldData::isOffsetOnly() + * @ref fieldData(UnsignedInt) const accessors as some of them might + * have @ref SceneFieldFlag::OffsetOnly --- use this function only if + * you *really* know what are you doing. + * @see @ref fieldData(), @ref SceneFieldData::flags() */ Containers::Array releaseFieldData(); @@ -2909,12 +3045,13 @@ namespace Implementation { } } -constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize) noexcept: +constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: _size{(CORRADE_CONSTEXPR_ASSERT(mappingData.size() == fieldData.size(), "Trade::SceneFieldData: expected mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldData.size()), mappingData.size())}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, - _isOffsetOnly{false}, + _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::OffsetOnly), + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view"), flags)}, _mappingType{mappingType}, _mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingData.stride() >= -32768 && mappingData.stride() <= 32767, "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingData.stride()), Short(mappingData.stride()))}, @@ -2926,23 +3063,24 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, _fieldData{fieldData.data()} {} -template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor::type>(), mappingData, Implementation::SceneFieldTypeFor::type>::type(), fieldData, 0} {} +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView1D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor::type>(), mappingData, Implementation::SceneFieldTypeFor::type>::type(), fieldData, 0, flags} {} -template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData) noexcept: SceneFieldData{ +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& mappingData, const Containers::StridedArrayView2D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{ name, Implementation::sceneMappingTypeFor::type>(), mappingData, Implementation::SceneFieldTypeFor::type>::type(), Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, /* Not using isContiguous<1>() as that's not constexpr */ - (CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])) + (CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])), + flags } {} -constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize) noexcept: +constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: _size{size}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, - _isOffsetOnly{true}, + _flags{flags|SceneFieldFlag::OffsetOnly}, _mappingType{mappingType}, _mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingStride >= -32768 && mappingStride <= 32767, "Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingStride), Short(mappingStride))}, diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index c3c9870027..19a7978a02 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -63,6 +63,10 @@ struct SceneDataTest: TestSuite::Tester { void fieldTypeSizeAlignmentInvalid(); void debugFieldType(); + void debugFieldFlag(); + void debugFieldFlags(); + void debugFieldFlagsSupersets(); + void constructField(); void constructFieldDefault(); void constructFieldCustom(); @@ -79,6 +83,7 @@ struct SceneDataTest: TestSuite::Tester { void constructFieldInconsistentViewSize(); void constructFieldTooLargeMappingStride(); void constructFieldTooLargeFieldStride(); + void constructFieldOffsetOnlyNotAllowed(); void constructFieldWrongDataAccess(); void constructField2DWrongSize(); void constructField2DNonContiguous(); @@ -203,6 +208,57 @@ const struct { {"mutable", DataFlag::Mutable} }; +const struct { + const char* name; + SceneFieldFlags flags; + UnsignedInt mapping[5]; + UnsignedInt object; + UnsignedInt offset; + Containers::Optional expected; +} FindFieldObjectOffsetData[]{ + {"", {}, + {4, 2, 1, 0, 2}, 2, 0, 1}, + {"not found", {}, + {4, 2, 1, 0, 2}, 3, 0, Containers::NullOpt}, + {"offset", {}, + {4, 2, 1, 0, 2}, 2, 2, 4}, + {"offset, not found", {}, + {4, 2, 1, 0, 2}, 2, 5, Containers::NullOpt}, + + {"ordered", SceneFieldFlag::OrderedMapping, + {1, 3, 4, 4, 5}, 4, 0, 2}, + {"ordered, not found", SceneFieldFlag::OrderedMapping, + /* It *is* there but the binary search expects an ordered range and thus + should not even see it */ + {1, 3, 4, 4, 2}, 2, 0, Containers::NullOpt}, + {"ordered, not found, too small", SceneFieldFlag::OrderedMapping, + {1, 3, 4, 4, 5}, 0, 0, Containers::NullOpt}, + {"ordered, not found, too large", SceneFieldFlag::OrderedMapping, + {1, 3, 4, 4, 5}, 6, 0, Containers::NullOpt}, + {"ordered, offset", SceneFieldFlag::OrderedMapping, + {1, 3, 4, 4, 5}, 4, 3, 3}, + {"ordered, offset, not found", SceneFieldFlag::OrderedMapping, + {1, 3, 4, 4, 5}, 4, 4, Containers::NullOpt}, + + {"implicit", SceneFieldFlag::ImplicitMapping, + /* Not there but the assumption is that the ID matches the offset */ + {5, 5, 5, 5, 5}, 3, 0, 3}, + {"implicit, not found", SceneFieldFlag::ImplicitMapping, + /* Is there but the assumption is that the ID matches the offset, which + is out of bounds */ + {5, 5, 5, 5, 5}, 5, 0, Containers::NullOpt}, + {"implicit, offset", SceneFieldFlag::ImplicitMapping, + /* Not there but the assumption is that the ID matches the offset; + verifying that the offset is properly accounted for */ + {5, 5, 5, 5, 5}, 3, 3, 3}, + {"implicit, offset, not found, less than offset", SceneFieldFlag::ImplicitMapping, + /* Cerifying that the offset is properly accounted for -- it's never + found if offset > id */ + {5, 5, 5, 5, 5}, 3, 4, Containers::NullOpt}, + {"implicit, offset, not found, out of bounds", SceneFieldFlag::ImplicitMapping, + {5, 5, 5, 5, 5}, 5, 4, Containers::NullOpt} +}; + const struct { const char* name; std::size_t offset; @@ -298,6 +354,10 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldTypeSizeAlignmentInvalid, &SceneDataTest::debugFieldType, + &SceneDataTest::debugFieldFlag, + &SceneDataTest::debugFieldFlags, + &SceneDataTest::debugFieldFlagsSupersets, + &SceneDataTest::constructField, &SceneDataTest::constructFieldDefault, &SceneDataTest::constructFieldCustom, @@ -314,6 +374,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructFieldInconsistentViewSize, &SceneDataTest::constructFieldTooLargeMappingStride, &SceneDataTest::constructFieldTooLargeFieldStride, + &SceneDataTest::constructFieldOffsetOnlyNotAllowed, &SceneDataTest::constructFieldWrongDataAccess, &SceneDataTest::constructField2DWrongSize, &SceneDataTest::constructField2DNonContiguous, @@ -353,12 +414,16 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::constructCopy, &SceneDataTest::constructMove, - &SceneDataTest::findFieldId, - &SceneDataTest::findFieldObjectOffset, - &SceneDataTest::findFieldObjectOffset, - &SceneDataTest::findFieldObjectOffset, - &SceneDataTest::findFieldObjectOffset, - &SceneDataTest::findFieldObjectOffsetInvalidOffset, + &SceneDataTest::findFieldId}); + + addInstancedTests({ + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset, + &SceneDataTest::findFieldObjectOffset + }, Containers::arraySize(FindFieldObjectOffsetData)); + + addTests({&SceneDataTest::findFieldObjectOffsetInvalidOffset, &SceneDataTest::fieldObjectOffsetNotFound, &SceneDataTest::mappingAsArrayByIndex, @@ -645,6 +710,30 @@ void SceneDataTest::debugFieldType() { CORRADE_COMPARE(out.str(), "Trade::SceneFieldType::Matrix3x4h Trade::SceneFieldType(0xdead)\n"); } +void SceneDataTest::debugFieldFlag() { + std::ostringstream out; + + Debug(&out) << SceneFieldFlag::OffsetOnly << SceneFieldFlag(0xbe); + CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::OffsetOnly Trade::SceneFieldFlag(0xbe)\n"); +} + +void SceneDataTest::debugFieldFlags() { + std::ostringstream out; + + Debug{&out} << (SceneFieldFlag::OffsetOnly|SceneFieldFlag(0xe0)) << SceneFieldFlags{}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag(0xe0) Trade::SceneFieldFlags{}\n"); +} + +void SceneDataTest::debugFieldFlagsSupersets() { + /* ImplicitMapping is a superset of OrderedMapping, so only one should be + printed */ + { + std::ostringstream out; + Debug{&out} << (SceneFieldFlag::ImplicitMapping|SceneFieldFlag::OrderedMapping); + CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::ImplicitMapping\n"); + } +} + const UnsignedShort RotationMapping2D[3] { 17, 35, @@ -660,8 +749,8 @@ void SceneDataTest::constructField() { const UnsignedShort rotationMappingData[3]{}; const Complexd rotationFieldData[3]; - SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; - CORRADE_VERIFY(!rotations.isOffsetOnly()); + SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OrderedMapping}; + CORRADE_COMPARE(rotations.flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(rotations.name(), SceneField::Rotation); CORRADE_COMPARE(rotations.size(), 3); CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort); @@ -685,16 +774,16 @@ void SceneDataTest::constructField() { CORRADE_VERIFY(rotations.mappingData(someArray).data() == rotationMappingData); #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Won't bother anymore */ - constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationMapping2D), Containers::arrayView(RotationField2D)}; - constexpr bool isOffsetOnly = crotations.isOffsetOnly(); + constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationMapping2D), Containers::arrayView(RotationField2D), SceneFieldFlag::ImplicitMapping}; constexpr SceneField name = crotations.name(); + constexpr SceneFieldFlags flags = crotations.flags(); constexpr SceneMappingType mappingType = crotations.mappingType(); constexpr Containers::StridedArrayView1D mappingData = crotations.mappingData(); constexpr SceneFieldType fieldType = crotations.fieldType(); constexpr UnsignedShort fieldArraySize = crotations.fieldArraySize(); constexpr Containers::StridedArrayView1D fieldData = crotations.fieldData(); - CORRADE_VERIFY(!isOffsetOnly); CORRADE_COMPARE(name, SceneField::Rotation); + CORRADE_COMPARE(flags, SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(mappingType, SceneMappingType::UnsignedShort); CORRADE_COMPARE(mappingData.size(), 3); CORRADE_COMPARE(mappingData.stride(), sizeof(UnsignedShort)); @@ -739,8 +828,8 @@ void SceneDataTest::constructField2D() { auto rotationMappingView = Containers::StridedArrayView2D{rotationMappingData, {6, sizeof(UnsignedShort)}}.every(2); auto rotationFieldView = Containers::StridedArrayView2D{rotationFieldData, {6, sizeof(Complexd)}}.every(2); - SceneFieldData rotations{SceneField::Rotation, rotationMappingView, SceneFieldType::Complexd, rotationFieldView}; - CORRADE_VERIFY(!rotations.isOffsetOnly()); + SceneFieldData rotations{SceneField::Rotation, rotationMappingView, SceneFieldType::Complexd, rotationFieldView, SceneFieldFlag::ImplicitMapping}; + CORRADE_COMPARE(rotations.flags(), SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(rotations.name(), SceneField::Rotation); CORRADE_COMPARE(rotations.size(), 3); CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort); @@ -757,8 +846,8 @@ void SceneDataTest::constructField2D() { void SceneDataTest::constructFieldTypeErased() { const UnsignedLong scalingMappingData[3]{}; const Vector3 scalingFieldData[3]; - SceneFieldData scalings{SceneField::Scaling, SceneMappingType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingMappingData)), SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingFieldData))}; - CORRADE_VERIFY(!scalings.isOffsetOnly()); + SceneFieldData scalings{SceneField::Scaling, SceneMappingType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingMappingData)), SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingFieldData)), SceneFieldFlag::OrderedMapping}; + CORRADE_COMPARE(scalings.flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scalings.name(), SceneField::Scaling); CORRADE_COMPARE(scalings.size(), 3); CORRADE_COMPARE(scalings.mappingType(), SceneMappingType::UnsignedLong); @@ -789,8 +878,8 @@ void SceneDataTest::constructFieldOffsetOnly() { {0, 15, {67.0f, -1.1f}} }; - SceneFieldData a{SceneField::Translation, 2, SceneMappingType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data)}; - CORRADE_VERIFY(a.isOffsetOnly()); + SceneFieldData a{SceneField::Translation, 2, SceneMappingType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data), SceneFieldFlag::ImplicitMapping}; + CORRADE_COMPARE(a.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(a.name(), SceneField::Translation); CORRADE_COMPARE(a.size(), 2); CORRADE_COMPARE(a.mappingType(), SceneMappingType::UnsignedShort); @@ -814,8 +903,8 @@ constexpr Int ArrayOffsetFieldData[3*4]{}; void SceneDataTest::constructFieldArray() { UnsignedByte offsetMappingData[3]; Int offsetFieldData[3*4]; - SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}}; - CORRADE_VERIFY(!data.isOffsetOnly()); + SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D{offsetFieldData, {3, 4}}, SceneFieldFlag::ImplicitMapping}; + CORRADE_COMPARE(data.flags(), SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); @@ -828,8 +917,8 @@ void SceneDataTest::constructFieldArray() { CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); CORRADE_VERIFY(data.fieldData().data() == offsetFieldData); - constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetMappingData), Containers::StridedArrayView2D{ArrayOffsetFieldData, {3, 4}}}; - CORRADE_VERIFY(!cdata.isOffsetOnly()); + constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetMappingData), Containers::StridedArrayView2D{ArrayOffsetFieldData, {3, 4}}, SceneFieldFlag::OrderedMapping}; + CORRADE_COMPARE(cdata.flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); CORRADE_COMPARE(cdata.size(), 3); CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte); @@ -846,8 +935,8 @@ void SceneDataTest::constructFieldArray() { void SceneDataTest::constructFieldArray2D() { char offsetMappingData[3*sizeof(UnsignedByte)]; char offsetFieldData[3*4*sizeof(Int)]; - SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetMappingData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 4*sizeof(Int)}}, 4}; - CORRADE_VERIFY(!data.isOffsetOnly()); + SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetMappingData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D{offsetFieldData, {3, 4*sizeof(Int)}}, 4, SceneFieldFlag::ImplicitMapping}; + CORRADE_COMPARE(data.flags(), SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); @@ -865,8 +954,8 @@ void SceneDataTest::constructFieldArrayTypeErased() { UnsignedByte offsetMappingData[3]; Int offsetFieldData[3*4]; Containers::StridedArrayView1D offset{offsetFieldData, 3, 4*sizeof(Int)}; - SceneFieldData data{sceneFieldCustom(34), SceneMappingType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetMappingData)), SceneFieldType::Int, Containers::arrayCast(offset), 4}; - CORRADE_VERIFY(!data.isOffsetOnly()); + SceneFieldData data{sceneFieldCustom(34), SceneMappingType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetMappingData)), SceneFieldType::Int, Containers::arrayCast(offset), 4, SceneFieldFlag::OrderedMapping}; + CORRADE_COMPARE(data.flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); @@ -887,8 +976,8 @@ void SceneDataTest::constructFieldArrayOffsetOnly() { Int offset[4]; }; - SceneFieldData data{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; - CORRADE_VERIFY(data.isOffsetOnly()); + SceneFieldData data{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4, SceneFieldFlag::ImplicitMapping}; + CORRADE_COMPARE(data.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte); @@ -903,8 +992,8 @@ void SceneDataTest::constructFieldArrayOffsetOnly() { CORRADE_COMPARE(data.mappingData(actual).stride(), sizeof(Data)); CORRADE_VERIFY(data.mappingData(actual).data() == &actual[0].object); - constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4}; - CORRADE_VERIFY(cdata.isOffsetOnly()); + constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4, SceneFieldFlag::OrderedMapping}; + CORRADE_COMPARE(cdata.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); CORRADE_COMPARE(cdata.size(), 3); CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte); @@ -997,6 +1086,24 @@ void SceneDataTest::constructFieldTooLargeFieldStride() { "Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got -32769\n"); } +void SceneDataTest::constructFieldOffsetOnlyNotAllowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const UnsignedShort rotationMappingData[3]{}; + const Quaternion rotationFieldData[3]; + + /* This one is fine */ + SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneFieldFlag::OffsetOnly}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OffsetOnly}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view\n"); +} + void SceneDataTest::constructFieldWrongDataAccess() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -1006,8 +1113,8 @@ void SceneDataTest::constructFieldWrongDataAccess() { const Quaternion rotationFieldData[3]; SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)}; SceneFieldData b{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)}; - CORRADE_VERIFY(!a.isOffsetOnly()); - CORRADE_VERIFY(b.isOffsetOnly()); + CORRADE_COMPARE(a.flags(), SceneFieldFlags{}); + CORRADE_COMPARE(b.flags(), SceneFieldFlag::OffsetOnly); a.mappingData(rotationMappingData); /* This is fine, no asserts */ a.fieldData(rotationFieldData); @@ -1229,11 +1336,11 @@ void SceneDataTest::construct() { SceneFieldType::Int, offsetof(TransformParent, parent), sizeof(TransformParent)}; SceneFieldData meshes{SceneField::Mesh, materialMeshRadiusMappingData, - meshFieldData}; + meshFieldData, SceneFieldFlag::OrderedMapping}; /* Custom & array */ SceneFieldData radiuses{sceneFieldCustom(37), materialMeshRadiusMappingData, - Containers::arrayCast<2, Float>(radiusFieldData)}; + Containers::arrayCast<2, Float>(radiusFieldData), SceneFieldFlag::OrderedMapping}; SceneData scene{SceneMappingType::UnsignedShort, 8, std::move(data), { transformations, parents, meshes, radiuses }, &importerState}; @@ -1256,6 +1363,10 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); CORRADE_COMPARE(scene.fieldName(2), SceneField::Mesh); CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(37)); + CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{}); + CORRADE_COMPARE(scene.fieldFlags(1), SceneFieldFlag::OffsetOnly); + CORRADE_COMPARE(scene.fieldFlags(2), SceneFieldFlag::OrderedMapping); + CORRADE_COMPARE(scene.fieldFlags(3), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Matrix4x4); CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::UnsignedByte); @@ -1275,14 +1386,14 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.fieldData(2).mappingType(), SceneMappingType::UnsignedShort); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).mappingData())[1], 6); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(2).fieldData())[1], 7); - CORRADE_VERIFY(!scene.fieldData(2).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(2).flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scene.fieldData(2).fieldType(), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldData(2).fieldArraySize(), 0); /* Offset-only */ CORRADE_COMPARE(scene.fieldData(1).name(), SceneField::Parent); CORRADE_COMPARE(scene.fieldData(1).size(), 5); CORRADE_COMPARE(scene.fieldData(1).mappingType(), SceneMappingType::UnsignedShort); - CORRADE_VERIFY(!scene.fieldData(1).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(1).flags(), SceneFieldFlags{}); CORRADE_COMPARE(scene.fieldData(1).fieldType(), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldData(1).fieldArraySize(), 0); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(1).mappingData())[4], 1); @@ -1291,7 +1402,7 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.fieldData(3).name(), sceneFieldCustom(37)); CORRADE_COMPARE(scene.fieldData(3).size(), 2); CORRADE_COMPARE(scene.fieldData(3).mappingType(), SceneMappingType::UnsignedShort); - CORRADE_VERIFY(!scene.fieldData(3).isOffsetOnly()); + CORRADE_COMPARE(scene.fieldData(3).flags(), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scene.fieldData(3).fieldType(), SceneFieldType::Float); CORRADE_COMPARE(scene.fieldData(3).fieldArraySize(), 2); CORRADE_COMPARE(Containers::arrayCast(scene.fieldData(3).mappingData())[0], 2); @@ -1374,6 +1485,10 @@ void SceneDataTest::construct() { CORRADE_COMPARE(scene.mutableField(3)[0][1], 1.5f); /* Field property access by name */ + CORRADE_COMPARE(scene.fieldFlags(SceneField::Transformation), SceneFieldFlags{}); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Parent), SceneFieldFlag::OffsetOnly); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Mesh), SceneFieldFlag::OrderedMapping); + CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(37)), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4); CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); @@ -1608,6 +1723,7 @@ void SceneDataTest::constructDeprecated() { CORRADE_COMPARE(scene.importerState(), &a); CORRADE_COMPARE(scene.fieldCount(), 1); CORRADE_COMPARE(scene.fieldName(0), SceneField::Parent); + CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{}); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Int); if(data.is2D || data.is3D) { CORRADE_COMPARE_AS(scene.mapping(0), @@ -2113,60 +2229,46 @@ void SceneDataTest::findFieldId() { template void SceneDataTest::findFieldObjectOffset() { setTestCaseTemplateName(NameTraits::name()); - /** @todo update once field flags describing object order are present */ + auto&& data = FindFieldObjectOffsetData[testCaseInstanceId()]; + setTestCaseDescription(data.name); struct Field { T object; UnsignedInt mesh; - } fields[]{ - {4, 1}, - {1, 3}, - {2, 4}, - {0, 5}, - {2, 5} + } fields[5]{ + {T(data.mapping[0]), 0}, + {T(data.mapping[1]), 0}, + {T(data.mapping[2]), 0}, + {T(data.mapping[3]), 0}, + {T(data.mapping[4]), 0} }; Containers::StridedArrayView1D view = fields; SceneData scene{Implementation::sceneMappingTypeFor(), 7, {}, fields, { /* Test also with a completely empty field */ SceneFieldData{SceneField::Parent, Implementation::sceneMappingTypeFor(), nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh), data.flags} }}; - CORRADE_COMPARE(scene.findFieldObjectOffset(0, 4), Containers::NullOpt); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, 4), Containers::NullOpt); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 4), 0); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 1), 1); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2), 2); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 3), 4); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 5), Containers::NullOpt); - CORRADE_COMPARE(scene.findFieldObjectOffset(1, 3), Containers::NullOpt); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 4), 0); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 1), 1); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2), 2); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 3), 4); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 5), Containers::NullOpt); - CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 3), Containers::NullOpt); - - CORRADE_COMPARE(scene.fieldObjectOffset(1, 4), 0); - CORRADE_COMPARE(scene.fieldObjectOffset(1, 1), 1); - CORRADE_COMPARE(scene.fieldObjectOffset(1, 2), 2); - CORRADE_COMPARE(scene.fieldObjectOffset(1, 2, 3), 4); - CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 4), 0); - CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 1), 1); - CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2), 2); - CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2, 3), 4); - - CORRADE_VERIFY(!scene.hasFieldObject(0, 4)); - CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, 4)); - CORRADE_VERIFY(scene.hasFieldObject(1, 4)); - CORRADE_VERIFY(scene.hasFieldObject(1, 1)); - CORRADE_VERIFY(scene.hasFieldObject(1, 2)); - CORRADE_VERIFY(!scene.hasFieldObject(1, 3)); - CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 4)); - CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 1)); - CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 2)); - CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Mesh, 3)); + /* An empty field should not find anything for any query with any flags */ + if(data.offset == 0) { + CORRADE_COMPARE(scene.findFieldObjectOffset(0, data.object), Containers::NullOpt); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, data.object), Containers::NullOpt); + CORRADE_VERIFY(!scene.hasFieldObject(0, data.object)); + CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, data.object)); + } + + CORRADE_COMPARE(scene.findFieldObjectOffset(1, data.object, data.offset), data.expected); + CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, data.object, data.offset), data.expected); + if(data.offset == 0) { + CORRADE_COMPARE(scene.hasFieldObject(1, data.object), !!data.expected); + CORRADE_COMPARE(scene.hasFieldObject(SceneField::Mesh, data.object), !!data.expected); + } + + if(data.expected) { + CORRADE_COMPARE(scene.fieldObjectOffset(1, data.object, data.offset), *data.expected); + CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, data.object, data.offset), *data.expected); + } } void SceneDataTest::findFieldObjectOffsetInvalidOffset() { @@ -4537,6 +4639,7 @@ void SceneDataTest::fieldNotFound() { scene.hasFieldObject(2, 0); scene.fieldData(2); scene.fieldName(2); + scene.fieldFlags(2); scene.fieldType(2); scene.fieldSize(2); scene.fieldArraySize(2); @@ -4548,6 +4651,7 @@ void SceneDataTest::fieldNotFound() { scene.mutableField(2); scene.fieldId(sceneFieldCustom(666)); + scene.fieldFlags(sceneFieldCustom(666)); scene.findFieldObjectOffset(sceneFieldCustom(666), 0); scene.fieldObjectOffset(sceneFieldCustom(666), 0); scene.hasFieldObject(sceneFieldCustom(666), 0); @@ -4597,6 +4701,7 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::hasFieldObject(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" + "Trade::SceneData::fieldFlags(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldSize(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldArraySize(): index 2 out of range for 2 fields\n" @@ -4608,6 +4713,7 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n" + "Trade::SceneData::fieldFlags(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::findFieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::hasFieldObject(): field Trade::SceneField::Custom(666) not found\n" diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index af0411e4a7..dde9a1df06 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -103,6 +103,8 @@ class TextureData; enum class SceneMappingType: UnsignedByte; enum class SceneField: UnsignedInt; enum class SceneFieldType: UnsignedShort; +enum class SceneFieldFlag: UnsignedByte; +typedef Containers::EnumSet SceneFieldFlags; class SceneFieldData; class SceneData; From 78b71e1859167cadaac971fa2c24cc85911a6773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 6 Dec 2021 13:18:53 +0100 Subject: [PATCH 44/49] Trade: preserve field flags in the scene combining tools. Not really important right now as the SceneData from these are used only in internal deprecated APIs, but at least it might speed up the children2D() / children3D() queries. Mainly done so I don't forget to do this later when these APIs are published in the SceneTools library. What's not done is the rather complex logic in the single-function conversion utility, where a field could retain the implicit/ordered flags in *some* scenarios. There's too many corner cases so better be conservative and don't preserve anything rather than mark something as ordered while it's no longer the case. The corner cases are hopefully all checled for (and XFAIL'd) in the test. --- src/Magnum/Trade/Implementation/sceneTools.h | 29 +++- src/Magnum/Trade/Test/SceneToolsTest.cpp | 166 ++++++++++++++++--- 2 files changed, 166 insertions(+), 29 deletions(-) diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index a4242f6c3c..9c1e056059 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -185,7 +185,7 @@ inline SceneData sceneCombine(const SceneMappingType mappingType, const Unsigned /* Map the fields to the new data */ Containers::Array outFields{fields.size()}; for(std::size_t i = 0; i != fields.size(); ++i) { - outFields[i] = SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize()}; + outFields[i] = SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize(), fields[i].flags()}; } return SceneData{mappingType, mappingBound, std::move(outData), std::move(outFields)}; @@ -239,14 +239,26 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con for(std::size_t i = 0; i != scene.fieldCount(); ++i) { const SceneFieldData& field = scene.fieldData(i); - /* If this is a parent, enlarge it for the newly added objects */ + /* If this is a parent, enlarge it for the newly added objects, and if + it was implicit make it ordered */ if(field.name() == SceneField::Parent) { /** @todo some nicer constructor for placeholders once this is in public interest */ - fields[i] = SceneFieldData{SceneField::Parent, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}}; - - /* All other fields are copied as-is */ - } else fields[i] = field; + fields[i] = SceneFieldData{SceneField::Parent, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, + /* If the field is ordered, we preserve that. But if it's + implicit, we can't. */ + field.flags() & ~(SceneFieldFlag::ImplicitMapping & ~SceneFieldFlag::OrderedMapping) + }; + + /* All other fields are copied as-is, but lose the implicit/ordered + flags */ + /** @todo the flags could get preserved for + - fields that don't share their object mapping with any fields in + the fieldsToConvert list + - fields that don't actually get their object mapping touched + during the process (and then all fields that share object + mapping with them) */ + } else fields[i] = SceneFieldData{field.name(), field.mappingType(), field.mappingData(), field.fieldType(), field.fieldData(), field.fieldArraySize(), field.flags() & ~SceneFieldFlag::ImplicitMapping}; } /* Combine the fields into a new SceneData */ @@ -292,6 +304,11 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con fieldObject = newParentMapping[newParentIndex]; /* Move to the next reserved object */ ++newParentIndex; + + /** @todo mark this field as touched here and the restore + the original flags for all fields that didn't have + their object mapping touched */ + } else ++objectAttachmentCount[fieldObject]; } } diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index 2cb3ae373d..807b1839e2 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -59,9 +59,17 @@ struct { const char* name; UnsignedLong originalObjectCount; UnsignedLong expectedObjectCount; + SceneFieldFlags parentFieldFlagsInput; + SceneFieldFlags parentFieldFlagsExpected; } ConvertToSingleFunctionObjectsData[]{ - {"original object count smaller than new", 64, 67}, - {"original object count larger than new", 96, 96} + {"original object count smaller than new", 64, 70, {}, {}}, + {"original object count larger than new", 96, 96, {}, {}}, + {"parent field with ordered mapping", 64, 70, + SceneFieldFlag::OrderedMapping, SceneFieldFlag::OrderedMapping}, + {"parent field with implicit mapping", 64, 70, + /* The mapping is *not* implicit but we're not using the flag for + anything so this should work */ + SceneFieldFlag::ImplicitMapping, SceneFieldFlag::OrderedMapping} }; SceneToolsTest::SceneToolsTest() { @@ -89,8 +97,8 @@ void SceneToolsTest::combine() { const UnsignedInt meshMappingData[]{45, 78, 23}; const UnsignedByte meshFieldData[]{3, 5, 17}; - const UnsignedShort parentMappingData[]{33, 25}; - const Short parentData[]{-1, 33}; + const UnsignedShort parentMappingData[]{0, 1}; + const Short parentData[]{-1, 0}; const UnsignedByte translationMappingData[]{16}; const Vector2d translationFieldData[]{{1.5, -0.5}}; @@ -100,10 +108,10 @@ void SceneToolsTest::combine() { SceneData scene = Implementation::sceneCombine(data.objectType, 167, Containers::arrayView({ SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, - SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentData)}, + SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentData), SceneFieldFlag::ImplicitMapping}, SceneFieldData{SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)}, /* Array field */ - SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D{fooFieldData, {2, 2}}}, + SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D{fooFieldData, {2, 2}}, SceneFieldFlag::OrderedMapping}, /* Empty field */ SceneFieldData{SceneField::Camera, Containers::ArrayView{}, Containers::ArrayView{}} })); @@ -114,6 +122,7 @@ void SceneToolsTest::combine() { CORRADE_COMPARE(scene.fieldCount(), 5); CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh); + CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{}); CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldArraySize(0), 0); CORRADE_COMPARE_AS(scene.mappingAsArray(0), Containers::arrayView({ @@ -124,16 +133,18 @@ void SceneToolsTest::combine() { TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent); + CORRADE_COMPARE(scene.fieldFlags(1), SceneFieldFlag::ImplicitMapping); CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Short); CORRADE_COMPARE(scene.fieldArraySize(1), 0); CORRADE_COMPARE_AS(scene.mappingAsArray(1), Containers::arrayView({ - 33, 25 + 0, 1 }), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.field(1), Containers::arrayView(parentData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(2), SceneField::Translation); + CORRADE_COMPARE(scene.fieldFlags(2), SceneFieldFlags{}); CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::Vector2d); CORRADE_COMPARE(scene.fieldArraySize(2), 0); CORRADE_COMPARE_AS(scene.mappingAsArray(2), @@ -144,6 +155,7 @@ void SceneToolsTest::combine() { TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(15)); + CORRADE_COMPARE(scene.fieldFlags(3), SceneFieldFlag::OrderedMapping); CORRADE_COMPARE(scene.fieldType(3), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldArraySize(3), 2); CORRADE_COMPARE_AS(scene.mappingAsArray(3), @@ -159,6 +171,7 @@ void SceneToolsTest::combine() { TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldName(4), SceneField::Camera); + CORRADE_COMPARE(scene.fieldFlags(4), SceneFieldFlags{}); CORRADE_COMPARE(scene.fieldType(4), SceneFieldType::UnsignedShort); CORRADE_COMPARE(scene.fieldSize(4), 0); CORRADE_COMPARE(scene.fieldArraySize(4), 0); @@ -340,12 +353,12 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Haha now I can use sceneCombine() to conveniently prepare the initial state here, without having to mess with an ArrayTuple */ - const UnsignedShort parentMappingData[]{15, 21, 22, 23, 1}; - const Byte parentFieldData[]{-1, -1, 21, 22, -1}; + const UnsignedShort parentMappingData[]{2, 15, 21, 22, 23}; + const Byte parentFieldData[]{-1, -1, -1, 21, 22}; /* Two objects have two and three mesh assignments respectively, meaning we need three extra */ - const UnsignedShort meshMappingData[]{15, 23, 23, 23, 1, 15, 21}; + const UnsignedShort meshMappingData[]{15, 23, 23, 23, 2, 15, 21}; const Containers::Pair meshMaterialFieldData[]{ {6, 4}, {1, 0}, @@ -357,14 +370,39 @@ void SceneToolsTest::convertToSingleFunctionObjects() { }; /* One camera is attached to an object that already has a mesh, meaning we - need a third extra object */ - const UnsignedShort cameraMappingData[]{22, 1}; + need a fourth extra object */ + const UnsignedShort cameraMappingData[]{22, 2}; const UnsignedInt cameraFieldData[]{1, 5}; + + /* Lights don't conflict with anything so they *could* retain the + ImplicitMapping flag */ + const UnsignedShort lightMappingData[]{0, 1}; + const UnsignedByte lightFieldData[]{15, 23}; + + /* Object 0 and 1 has a light, 2 a mesh already, meaning we need a fifth, + sixth and seventh extra object and we lose the ImplicitMapping flag. */ + const UnsignedShort fooMappingData[]{0, 1, 2, 3}; + const Float fooFieldData[]{1.0f, 2.0f, 3.0f, 4.0f}; + + /* This field is not among the fields to convert so it should preserve the + ImplicitMapping flag */ + const UnsignedShort foo2MappingData[]{0, 1}; + const Byte foo2FieldData[]{-5, -7}; + + /* This field shares mapping with foo (and thus has the ImplicitMapping + flag), but it's not among the fields to convert. Since the mapping gets + changed, it should not retain the ImplicitMapping flag. */ + const Byte foo3FieldData[]{-1, -2, 7, 2}; + SceneData original = Implementation::sceneCombine(SceneMappingType::UnsignedShort, data.originalObjectCount, Containers::arrayView({ - SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData)}, + SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData), data.parentFieldFlagsInput}, SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D{meshMaterialFieldData, &meshMaterialFieldData[0].first(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}}, SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D{meshMaterialFieldData, &meshMaterialFieldData[0].second(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}}, SceneFieldData{SceneField::Camera, Containers::arrayView(cameraMappingData), Containers::arrayView(cameraFieldData)}, + SceneFieldData{SceneField::Light, Containers::arrayView(lightMappingData), Containers::arrayView(lightFieldData), SceneFieldFlag::ImplicitMapping}, + SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::arrayView(fooFieldData), SceneFieldFlag::ImplicitMapping}, + SceneFieldData{sceneFieldCustom(16), Containers::arrayView(foo2MappingData), Containers::arrayView(foo2FieldData), SceneFieldFlag::ImplicitMapping}, + SceneFieldData{sceneFieldCustom(17), Containers::arrayView(fooMappingData), Containers::arrayView(foo3FieldData), SceneFieldFlag::ImplicitMapping} })); SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original, Containers::arrayView({ @@ -373,6 +411,12 @@ void SceneToolsTest::convertToSingleFunctionObjects() { get automatically updated as they share the same object mapping. OTOH including them would break the output. */ SceneField::Camera, + /* A field with implicit mapping that doesn't conflict with anything so + it *could* retain the flag */ + SceneField::Light, + /* A field with implicit mapping, which loses the flag because entries + get reassigned */ + sceneFieldCustom(15), /* Include also a field that's not present -- it should get skipped */ SceneField::ImporterState }), 63); @@ -381,14 +425,31 @@ void SceneToolsTest::convertToSingleFunctionObjects() { it's large enough */ CORRADE_COMPARE(scene.mappingBound(), data.expectedObjectCount); - /* Object 1 should have a new child that has the camera, as it has a mesh */ + /* Object 0 should have new children with "foo", as it has a light */ + CORRADE_COMPARE_AS(scene.childrenFor(0), + Containers::arrayView({67}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.lightsFor(0), + Containers::arrayView({15}), + TestSuite::Compare::Container); + + /* Object 1 should have a new child with "foo", as it has a light */ CORRADE_COMPARE_AS(scene.childrenFor(1), - Containers::arrayView({66}), + Containers::arrayView({68}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.lightsFor(1), + Containers::arrayView({23}), + TestSuite::Compare::Container); + + /* Object 2 should have a new child with the camera and "foo", as it has a + mesh */ + CORRADE_COMPARE_AS(scene.childrenFor(2), + Containers::arrayView({66, 69}), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.meshesMaterialsFor(1), + CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2), (Containers::arrayView>({{7, 2}})), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.camerasFor(1), + CORRADE_COMPARE_AS(scene.camerasFor(2), Containers::arrayView({}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.camerasFor(66), @@ -422,36 +483,95 @@ void SceneToolsTest::convertToSingleFunctionObjects() { TestSuite::Compare::Container); /* To be extra sure, verify the actual data. Parents have a few objects - added, the rest is the same */ + added, the rest is the same. Because new objects are added at the end, + the ordered flag is preserved if present. */ CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView>({ + {2, -1}, {15, -1}, {21, -1}, {22, 21}, {23, 22}, - {1, -1}, {63, 23}, {64, 23}, {65, 15}, - {66, 1} + {66, 2}, + {67, 0}, + {68, 1}, + {69, 2}, })), TestSuite::Compare::Container); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Parent), data.parentFieldFlagsExpected); /* Meshes / materials have certain objects reassigned, field data stay the - same */ + same. There was no flag before so neither is after. */ CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView>>({ {15, {6, 4}}, {23, {1, 0}}, {63, {2, 3}}, {64, {4, 2}}, - {1, {7, 2}}, + {2, {7, 2}}, {65, {3, 1}}, {21, {5, -1}} })), TestSuite::Compare::Container); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Mesh), SceneFieldFlags{}); + CORRADE_COMPARE(scene.fieldFlags(SceneField::MeshMaterial), SceneFieldFlags{}); - /* Cameras have certain objects reassigned, field data stay the same */ + /* Cameras have certain objects reassigned, field data stay the same. There + was no flag before so neither is after. */ CORRADE_COMPARE_AS(scene.camerasAsArray(), (Containers::arrayView>({ {22, 1}, {66, 5} })), TestSuite::Compare::Container); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Camera), SceneFieldFlags{}); + + /* Lights stay the same, thus the implicit flag could be preserved. It's + not currently, though. */ + CORRADE_COMPARE_AS(scene.lightsAsArray(), (Containers::arrayView>({ + {0, 15}, + {1, 23} + })), TestSuite::Compare::Container); + { + CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet."); + CORRADE_COMPARE(scene.fieldFlags(SceneField::Light), SceneFieldFlag::ImplicitMapping); + } { + CORRADE_COMPARE(scene.fieldFlags(SceneField::Light), SceneFieldFlags{}); + } + + /* A custom field gets the last object reassigned, field data stay the + same. The implicit flag gets turned to nothing after that. */ + CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(15)), (Containers::arrayView({ + 67, 68, 69, 3 + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(sceneFieldCustom(15)), + Containers::arrayView(fooFieldData), + TestSuite::Compare::Container); + CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(15)), SceneFieldFlags{}); + + /* A custom field that is not among fields to convert so it preserves the + flag */ + CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(16)), (Containers::arrayView({ + 0, 1 + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(sceneFieldCustom(16)), + Containers::arrayView(foo2FieldData), + TestSuite::Compare::Container); + { + CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet."); + CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(16)), SceneFieldFlag::ImplicitMapping); + } { + CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(16)), SceneFieldFlags{}); + } + + /* A custom field that is not among fields to convert but it shares the + mapping with a field that is and that gets changed. The implicit flag + should thus get removed here as well. */ + CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(17)), (Containers::arrayView({ + 67, 68, 69, 3 + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.field(sceneFieldCustom(17)), + Containers::arrayView(foo3FieldData), + TestSuite::Compare::Container); + CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(17)), SceneFieldFlags{}); + } }}}} From 43c70d8c224c1a27f1f943c67de375922b50df8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 6 Dec 2021 13:23:56 +0100 Subject: [PATCH 45/49] sceneconverter: print scene field flags in --info, if present. Hm, this will soon need some color distinction, it's starting to get hard to read. --- src/Magnum/MeshTools/sceneconverter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index d538fb721c..d4c0bf07e1 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -379,6 +379,7 @@ used.)") struct SceneFieldInfo { Trade::SceneField name; std::string customName; + Trade::SceneFieldFlags flags; Trade::SceneFieldType type; UnsignedInt arraySize; std::size_t size; @@ -439,6 +440,7 @@ used.)") name, Trade::isSceneFieldCustom(name) ? importer->sceneFieldName(name) : "", + scene->fieldFlags(j), scene->fieldType(j), scene->fieldArraySize(j), scene->fieldSize(j)); @@ -679,6 +681,8 @@ used.)") if(field.arraySize) d << Debug::nospace << Utility::formatString("[{}]", field.arraySize); d << Debug::nospace << "," << field.size << "entries"; + if(field.flags) + d << Debug::newline << " " << field.flags; } } From dd9f4747b7fb2309a3b0ea570ad168b681b5d630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 7 Dec 2021 15:43:22 +0100 Subject: [PATCH 46/49] Trade: use 64-bit values in object-oriented SceneData APIs. Mostly just to avoid the return types changing to incompatible types in the future, breaking existing code. The internals are currently not fully ready to operate with 64-bit object IDs, especially the AsArray() APIs -- those I will have to solve in the future somehow. Returning 64-bit values in the pairs would add four byte padding after basically each value, which is way too wasteful for the common case. The Into() APIs could eventually get 64-bit overloads though. --- src/Magnum/Trade/AbstractImporter.cpp | 8 ++-- src/Magnum/Trade/SceneData.cpp | 48 +++++++++++++----------- src/Magnum/Trade/SceneData.h | 36 +++++++++--------- src/Magnum/Trade/Test/SceneDataTest.cpp | 18 ++++----- src/Magnum/Trade/Test/SceneToolsTest.cpp | 10 ++--- 5 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index f63e0b63f5..a35976ae1a 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -621,7 +621,7 @@ std::string AbstractImporter::doObject2DName(const UnsignedInt id) { _cachedScenes->scenes[i]->mappingBound() <= id) continue; - if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) + if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) return doObjectName(*parent); } @@ -678,7 +678,7 @@ Containers::Pointer AbstractImporter::doObject2D(const UnsignedInt std::vector children; /* not const so we can move it */ { - Containers::Array childrenArray = scene.childrenFor(id); + Containers::Array childrenArray = scene.childrenFor(id); children = {childrenArray.begin(), childrenArray.end()}; } const Containers::Array> mesh = scene.meshesMaterialsFor(id); @@ -799,7 +799,7 @@ std::string AbstractImporter::doObject3DName(const UnsignedInt id) { _cachedScenes->scenes[i]->mappingBound() <= id) continue; - if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) + if(Containers::Optional parent = _cachedScenes->scenes[i]->parentFor(id)) return doObjectName(*parent); } @@ -856,7 +856,7 @@ Containers::Pointer AbstractImporter::doObject3D(const UnsignedInt std::vector children; /* not const so we can move it */ { - Containers::Array childrenArray = scene.childrenFor(id); + Containers::Array childrenArray = scene.childrenFor(id); children = {childrenArray.begin(), childrenArray.end()}; } const Containers::Array> mesh = scene.meshesMaterialsFor(id); diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index a2ad3a3252..97294045a7 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -895,7 +895,7 @@ namespace { /* The `objects` view is already adjusted for `offset`, the offset is needed only to return the correct value for ImplicitMapping */ -template std::size_t findObject(const SceneFieldFlags flags, const Containers::StridedArrayView1D& mapping, const std::size_t offset, const UnsignedInt object) { +template std::size_t findObject(const SceneFieldFlags flags, const Containers::StridedArrayView1D& mapping, const std::size_t offset, const UnsignedLong object) { const std::size_t max = mapping.size(); /* Implicit mapping, position equals object ID (if in bounds) and thus an @@ -926,7 +926,7 @@ template std::size_t findObject(const SceneFieldFlags flags, const Cont } -std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedInt object, const std::size_t offset) const { +std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedLong object, const std::size_t offset) const { const Containers::StridedArrayView1D mapping = fieldDataMappingViewInternal(field, offset, field._size - offset); if(field._mappingType == SceneMappingType::UnsignedInt) return offset + findObject(field._flags, mapping, offset, object); @@ -939,7 +939,7 @@ std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -Containers::Optional SceneData::findFieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { +Containers::Optional SceneData::findFieldObjectOffset(const UnsignedInt fieldId, const UnsignedLong object, const std::size_t offset) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), @@ -953,7 +953,7 @@ Containers::Optional SceneData::findFieldObjectOffset(const Unsigne return found == field._size ? Containers::Optional{} : found; } -Containers::Optional SceneData::findFieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { +Containers::Optional SceneData::findFieldObjectOffset(const SceneField fieldName, const UnsignedLong object, const std::size_t offset) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -969,7 +969,7 @@ Containers::Optional SceneData::findFieldObjectOffset(const SceneFi return found == field._size ? Containers::Optional{} : found; } -std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const { +std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const UnsignedLong object, const std::size_t offset) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), @@ -985,7 +985,7 @@ std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const Unsign return found; } -std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const { +std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const UnsignedLong object, const std::size_t offset) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -1003,7 +1003,7 @@ std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const Unsig return found; } -bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt object) const { +bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); CORRADE_ASSERT(fieldId < _fields.size(), @@ -1013,7 +1013,7 @@ bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt obje return findFieldObjectOffsetInternal(field, object, 0) != field._size; } -bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt object) const { +bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2122,7 +2122,7 @@ Containers::Array> SceneData::importe return out; } -Containers::Optional SceneData::parentFor(const UnsignedInt object) const { +Containers::Optional SceneData::parentFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2138,7 +2138,7 @@ Containers::Optional SceneData::parentFor(const UnsignedInt object) const { return *index; } -Containers::Array SceneData::childrenFor(const Int object) const { +Containers::Array SceneData::childrenFor(const Long object) const { CORRADE_ASSERT(object >= -1 && object < Long(_mappingBound), "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2148,11 +2148,15 @@ Containers::Array SceneData::childrenFor(const Int object) const { const SceneFieldData& parentField = _fields[parentFieldId]; /* Collect IDs of all objects that reference this object */ - Containers::Array out; + Containers::Array out; for(std::size_t offset = 0; offset != parentField.size(); ++offset) { Int parentIndex[1]; parentsIntoInternal(parentFieldId, offset, parentIndex); if(*parentIndex == object) { + /** @todo this drops the upper 64 bits, might be a problem + eventually (at this point it's more important to have an API + that won't change the return types in the future, breaking + existing code) */ UnsignedInt child[1]; /** @todo bleh slow, use the children <-> parent field proxying when implemented */ @@ -2164,7 +2168,7 @@ Containers::Array SceneData::childrenFor(const Int object) const { return out; } -Containers::Optional SceneData::transformation2DFor(const UnsignedInt object) const { +Containers::Optional SceneData::transformation2DFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::transformation2DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2185,7 +2189,7 @@ Containers::Optional SceneData::transformation2DFor(const UnsignedInt o return *transformation; } -Containers::Optional> SceneData::translationRotationScaling2DFor(const UnsignedInt object) const { +Containers::Optional> SceneData::translationRotationScaling2DFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::translationRotationScaling2DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2208,7 +2212,7 @@ Containers::Optional> SceneData::t return {InPlaceInit, *translation, *rotation, *scaling}; } -Containers::Optional SceneData::transformation3DFor(const UnsignedInt object) const { +Containers::Optional SceneData::transformation3DFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::transformation3DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2229,7 +2233,7 @@ Containers::Optional SceneData::transformation3DFor(const UnsignedInt o return *transformation; } -Containers::Optional> SceneData::translationRotationScaling3DFor(const UnsignedInt object) const { +Containers::Optional> SceneData::translationRotationScaling3DFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::translationRotationScaling3DFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2252,7 +2256,7 @@ Containers::Optional> SceneData return {InPlaceInit, *translation, *rotation, *scaling}; } -Containers::Array> SceneData::meshesMaterialsFor(const UnsignedInt object) const { +Containers::Array> SceneData::meshesMaterialsFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2276,7 +2280,7 @@ Containers::Array> SceneData::meshesMaterials return out; } -Containers::Array SceneData::lightsFor(const UnsignedInt object) const { +Containers::Array SceneData::lightsFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2299,7 +2303,7 @@ Containers::Array SceneData::lightsFor(const UnsignedInt object) co return out; } -Containers::Array SceneData::camerasFor(const UnsignedInt object) const { +Containers::Array SceneData::camerasFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2322,7 +2326,7 @@ Containers::Array SceneData::camerasFor(const UnsignedInt object) c return out; } -Containers::Array SceneData::skinsFor(const UnsignedInt object) const { +Containers::Array SceneData::skinsFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2345,7 +2349,7 @@ Containers::Array SceneData::skinsFor(const UnsignedInt object) con return out; } -Containers::Optional SceneData::importerStateFor(const UnsignedInt object) const { +Containers::Optional SceneData::importerStateFor(const UnsignedLong object) const { CORRADE_ASSERT(object < _mappingBound, "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _mappingBound << "objects", {}); @@ -2370,7 +2374,7 @@ std::vector SceneData::children2D() const { if(!hasField(SceneField::Parent)) Warning{} << "Trade::SceneData::children2D(): no parent field present, returned array will be empty"; - const Containers::Array children = childrenFor(-1); + const Containers::Array children = childrenFor(-1); return {children.begin(), children.end()}; } @@ -2382,7 +2386,7 @@ std::vector SceneData::children3D() const { if(!hasField(SceneField::Parent)) Warning{} << "Trade::SceneData::children3D(): no parent field present, returned array will be empty"; - const Containers::Array children = childrenFor(-1); + const Containers::Array children = childrenFor(-1); return {children.begin(), children.end()}; } #endif diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 53feab5688..bf3cd4d91c 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -1510,7 +1510,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref hasFieldObject(UnsignedInt, UnsignedInt) const, * @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const */ - Containers::Optional findFieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const; + Containers::Optional findFieldObjectOffset(UnsignedInt fieldId, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Find offset of an object in given named field @@ -1532,7 +1532,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref hasField(), @ref hasFieldObject(SceneField, UnsignedInt) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - Containers::Optional findFieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const; + Containers::Optional findFieldObjectOffset(SceneField fieldName, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Offset of an object in given field @@ -1545,7 +1545,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * You can also use @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const * to directly get offset of an object in given named field. */ - std::size_t fieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const; + std::size_t fieldObjectOffset(UnsignedInt fieldId, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Offset of an object in given named field @@ -1555,7 +1555,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * but @p object is additionally expected to be present in @p fieldName * starting at @p offset. */ - std::size_t fieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const; + std::size_t fieldObjectOffset(SceneField fieldName, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Whether a scene field has given object @@ -1564,7 +1564,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p fieldId is expected to be smaller than @ref fieldCount() and * @p object smaller than @ref mappingBound(). */ - bool hasFieldObject(UnsignedInt fieldId, UnsignedInt object) const; + bool hasFieldObject(UnsignedInt fieldId, UnsignedLong object) const; /** * @brief Whether a named scene field has given object @@ -1574,7 +1574,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * be smaller than @ref mappingBound(). * @see @ref hasField() */ - bool hasFieldObject(SceneField fieldName, UnsignedInt object) const; + bool hasFieldObject(SceneField fieldName, UnsignedLong object) const; /** * @brief Field flags @@ -2510,7 +2510,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref childrenFor() */ - Containers::Optional parentFor(UnsignedInt object) const; + Containers::Optional parentFor(UnsignedLong object) const; /** * @brief Children for given object @@ -2531,7 +2531,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref parentFor() */ - Containers::Array childrenFor(Int object) const; + Containers::Array childrenFor(Long object) const; /** * @brief 2D transformation for given object @@ -2557,7 +2557,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref translationRotationScaling2DFor() */ - Containers::Optional transformation2DFor(UnsignedInt object) const; + Containers::Optional transformation2DFor(UnsignedLong object) const; /** * @brief 2D translation, rotation and scaling for given object @@ -2586,7 +2586,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref transformation2DFor() */ - Containers::Optional> translationRotationScaling2DFor(UnsignedInt object) const; + Containers::Optional> translationRotationScaling2DFor(UnsignedLong object) const; /** * @brief 3D transformation for given object @@ -2612,7 +2612,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref translationRotationScaling3DFor() */ - Containers::Optional transformation3DFor(UnsignedInt object) const; + Containers::Optional transformation3DFor(UnsignedLong object) const; /** * @brief 3D translation, rotation and scaling for given object @@ -2641,7 +2641,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p object is expected to be less than @ref mappingBound(). * @see @ref transformation3DFor() */ - Containers::Optional> translationRotationScaling3DFor(UnsignedInt object) const; + Containers::Optional> translationRotationScaling3DFor(UnsignedLong object) const; /** * @brief Meshes and materials for given object @@ -2663,7 +2663,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p object is expected to be less than @ref mappingBound(). */ - Containers::Array> meshesMaterialsFor(UnsignedInt object) const; + Containers::Array> meshesMaterialsFor(UnsignedLong object) const; /** * @brief Lights for given object @@ -2681,7 +2681,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p object is expected to be less than @ref mappingBound(). */ - Containers::Array lightsFor(UnsignedInt object) const; + Containers::Array lightsFor(UnsignedLong object) const; /** * @brief Cameras for given object @@ -2700,7 +2700,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p object is expected to be less than @ref mappingBound(). */ - Containers::Array camerasFor(UnsignedInt object) const; + Containers::Array camerasFor(UnsignedLong object) const; /** * @brief Skins for given object @@ -2718,7 +2718,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p object is expected to be less than @ref mappingBound(). */ - Containers::Array skinsFor(UnsignedInt object) const; + Containers::Array skinsFor(UnsignedLong object) const; /** * @brief Importer state for given object @@ -2738,7 +2738,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * * The @p object is expected to be less than @ref mappingBound(). */ - Containers::Optional importerStateFor(UnsignedInt object) const; + Containers::Optional importerStateFor(UnsignedLong object) const; #ifdef MAGNUM_BUILD_DEPRECATED /** @@ -2815,7 +2815,7 @@ class MAGNUM_TRADE_EXPORT SceneData { /* Returns the offset at which `object` is for field at index `id`, or the end offset if the object is not found. The returned offset can be then passed to fieldData{Mapping,Field}ViewInternal(). */ - MAGNUM_TRADE_LOCAL std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const; + MAGNUM_TRADE_LOCAL std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedLong object, std::size_t offset) const; /* Like objects() / field(), but returning just a 1D view, sliced from offset to offset + size. The parameterless overloads are equal to diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 19a7978a02..395e033307 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -4985,25 +4985,25 @@ void SceneDataTest::childrenFor() { /* Just one child */ CORRADE_COMPARE_AS(scene.childrenFor(3), - Containers::arrayView({2}), + Containers::arrayView({2}), TestSuite::Compare::Container); /* More */ CORRADE_COMPARE_AS(scene.childrenFor(-1), - Containers::arrayView({4, 0}), + Containers::arrayView({4, 0}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.childrenFor(4), - Containers::arrayView({3, 1, 5}), + Containers::arrayView({3, 1, 5}), TestSuite::Compare::Container); /* Object that is present in the parent array but has no children */ CORRADE_COMPARE_AS(scene.childrenFor(5), - Containers::arrayView({}), + Containers::arrayView({}), TestSuite::Compare::Container); /* Object that is not in the parent array at all */ CORRADE_COMPARE_AS(scene.childrenFor(6), - Containers::arrayView({}), + Containers::arrayView({}), TestSuite::Compare::Container); } @@ -5024,17 +5024,17 @@ void SceneDataTest::childrenForTrivialParent() { /* Trivial children */ CORRADE_COMPARE_AS(scene.childrenFor(-1), - Containers::arrayView({3, 4, 2, 4}), + Containers::arrayView({3, 4, 2, 4}), TestSuite::Compare::Container); /* Object that is present in the parent array but has no children */ CORRADE_COMPARE_AS(scene.childrenFor(4), - Containers::arrayView({}), + Containers::arrayView({}), TestSuite::Compare::Container); /* Object that is not in the parent array */ CORRADE_COMPARE_AS(scene.childrenFor(5), - Containers::arrayView({}), + Containers::arrayView({}), TestSuite::Compare::Container); } @@ -5461,7 +5461,7 @@ void SceneDataTest::fieldForFieldMissing() { CORRADE_COMPARE(scene.parentFor(6), Containers::NullOpt); CORRADE_COMPARE_AS(scene.childrenFor(6), - Containers::arrayView({}), + Containers::arrayView({}), TestSuite::Compare::Container); CORRADE_COMPARE(scene.transformation2DFor(6), Containers::NullOpt); CORRADE_COMPARE(scene.translationRotationScaling2DFor(6), Containers::NullOpt); diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index 807b1839e2..aadad0f516 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -427,7 +427,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Object 0 should have new children with "foo", as it has a light */ CORRADE_COMPARE_AS(scene.childrenFor(0), - Containers::arrayView({67}), + Containers::arrayView({67}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.lightsFor(0), Containers::arrayView({15}), @@ -435,7 +435,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Object 1 should have a new child with "foo", as it has a light */ CORRADE_COMPARE_AS(scene.childrenFor(1), - Containers::arrayView({68}), + Containers::arrayView({68}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.lightsFor(1), Containers::arrayView({23}), @@ -444,7 +444,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Object 2 should have a new child with the camera and "foo", as it has a mesh */ CORRADE_COMPARE_AS(scene.childrenFor(2), - Containers::arrayView({66, 69}), + Containers::arrayView({66, 69}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2), (Containers::arrayView>({{7, 2}})), @@ -458,7 +458,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Object 15 should have a new child that has the second mesh */ CORRADE_COMPARE_AS(scene.childrenFor(15), - Containers::arrayView({65}), + Containers::arrayView({65}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.meshesMaterialsFor(15), (Containers::arrayView>({{6, 4}})), @@ -470,7 +470,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* Object 23 should have two new children that have the second and third mesh */ CORRADE_COMPARE_AS(scene.childrenFor(23), - Containers::arrayView({63, 64}), + Containers::arrayView({63, 64}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(scene.meshesMaterialsFor(23), (Containers::arrayView>({{1, 0}})), From 7dc2dea45ba778c4e01fd52e55b351a900dde7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 7 Dec 2021 15:48:00 +0100 Subject: [PATCH 47/49] Trade: make AnimationData object reference 64-bit. To align with the new SceneData. --- src/Magnum/Trade/AnimationData.cpp | 2 +- src/Magnum/Trade/AnimationData.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Magnum/Trade/AnimationData.cpp b/src/Magnum/Trade/AnimationData.cpp index 930c9c2c8f..a120ab2c7e 100644 --- a/src/Magnum/Trade/AnimationData.cpp +++ b/src/Magnum/Trade/AnimationData.cpp @@ -92,7 +92,7 @@ AnimationTrackTargetType AnimationData::trackTargetType(UnsignedInt id) const { return _tracks[id]._targetType; } -UnsignedInt AnimationData::trackTarget(UnsignedInt id) const { +UnsignedLong AnimationData::trackTarget(UnsignedInt id) const { CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackTarget(): index out of range", {}); return _tracks[id]._target; } diff --git a/src/Magnum/Trade/AnimationData.h b/src/Magnum/Trade/AnimationData.h index 7c54cfbd3c..bed8e9f455 100644 --- a/src/Magnum/Trade/AnimationData.h +++ b/src/Magnum/Trade/AnimationData.h @@ -238,14 +238,14 @@ class AnimationTrackData { * @param target Track target * @param view Type-erased @ref Animation::TrackView instance */ - explicit AnimationTrackData(AnimationTrackType type, AnimationTrackType resultType, AnimationTrackTargetType targetType, UnsignedInt target, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{resultType}, _targetType{targetType}, _target{target}, _view{view} {} + explicit AnimationTrackData(AnimationTrackType type, AnimationTrackType resultType, AnimationTrackTargetType targetType, UnsignedLong target, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{resultType}, _targetType{targetType}, _target{target}, _view{view} {} /** @overload * * Equivalent to the above with @p type used as both value type and * result type. */ - explicit AnimationTrackData(AnimationTrackType type, AnimationTrackTargetType targetType, UnsignedInt target, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{type}, _targetType{targetType}, _target{target}, _view{view} {} + explicit AnimationTrackData(AnimationTrackType type, AnimationTrackTargetType targetType, UnsignedLong target, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{type}, _targetType{targetType}, _target{target}, _view{view} {} /** * @brief Constructor @@ -257,14 +257,14 @@ class AnimationTrackData { * Detects @ref AnimationTrackType from @p view type and delegates to * @ref AnimationTrackData(AnimationTrackType, AnimationTrackType, AnimationTrackTargetType, UnsignedInt, Animation::TrackViewStorage). */ - template explicit AnimationTrackData(AnimationTrackTargetType targetType, UnsignedInt target, Animation::TrackView view) noexcept; + template explicit AnimationTrackData(AnimationTrackTargetType targetType, UnsignedLong target, Animation::TrackView view) noexcept; private: friend AnimationData; AnimationTrackType _type, _resultType; AnimationTrackTargetType _targetType; - UnsignedInt _target; + UnsignedLong _target; Animation::TrackViewStorage _view; }; @@ -512,7 +512,7 @@ class MAGNUM_TRADE_EXPORT AnimationData { * @see @ref trackCount(), @ref AbstractImporter::object2D(), * @ref AbstractImporter::object3D() */ - UnsignedInt trackTarget(UnsignedInt id) const; + UnsignedLong trackTarget(UnsignedInt id) const; /** * @brief Track data storage @@ -649,7 +649,7 @@ namespace Implementation { /* LCOV_EXCL_STOP */ } -template inline AnimationTrackData::AnimationTrackData(AnimationTrackTargetType targetType, UnsignedInt target, Animation::TrackView view) noexcept: AnimationTrackData{Implementation::animationTypeFor(), Implementation::animationTypeFor(), targetType, target, view} {} +template inline AnimationTrackData::AnimationTrackData(AnimationTrackTargetType targetType, UnsignedLong target, Animation::TrackView view) noexcept: AnimationTrackData{Implementation::animationTypeFor(), Implementation::animationTypeFor(), targetType, target, view} {} template const Animation::TrackView& AnimationData::track(UnsignedInt id) const { const Animation::TrackViewStorage& storage = track(id); From b6200d0d93081a289bc49c01b59e76fcabd56002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 7 Dec 2021 21:20:11 +0100 Subject: [PATCH 48/49] Trade: correctly copy skins alongside duplicated multi-mesh objects. This is implemented in a generic way internally, to allow copying of arbitrary fields, not just skins for meshes, in the upcoming SceneTools utility. --- src/Magnum/Trade/AbstractImporter.cpp | 2 +- src/Magnum/Trade/Implementation/sceneTools.h | 110 ++++++++++++++++-- .../Trade/Test/AbstractImporterTest.cpp | 30 +++++ src/Magnum/Trade/Test/SceneToolsTest.cpp | 89 +++++++++++++- 4 files changed, 220 insertions(+), 11 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index a35976ae1a..036ca9ed5f 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -531,7 +531,7 @@ void AbstractImporter::populateCachedScenes() { compatibility code path anyway, so just skip the processing altogether in that case. */ if(_cachedScenes->scenes[i]->hasField(SceneField::Parent)) - _cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), newObjectOffset); + _cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), Containers::arrayView({SceneField::Skin}), newObjectOffset); /* Return the 2D/3D object count based on which scenes are 2D and which not. The objectCount() provided by the importer is ignored diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index 9c1e056059..a7a67e67be 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -191,20 +191,28 @@ inline SceneData sceneCombine(const SceneMappingType mappingType, const Unsigned return SceneData{mappingType, mappingBound, std::move(outData), std::move(outFields)}; } +inline Containers::Optional findField(Containers::ArrayView fields, SceneField field) { + for(std::size_t i = 0; i != fields.size(); ++i) + if(fields[i] == field) return i; + return {}; +} + /* Creates a SceneData copy where each object has at most one of the fields - listed in the passed array. This is done by enlarging the parents array - and moving extraneous features to new objects that are marked as a child of - the original. No transformations or other fields are added for the new - objects. Fields that are connected together (such as meshes and materials) - are assumed to share the same object mapping with only one of them passed in - the fieldsToConvert array, which will result for all fields from the same - set being reassociated to the new object. + listed in the passed `fieldsToConvert` array. This is done by enlarging the + parents array and moving extraneous features to new objects that are marked + as a child of the original. Fields that are connected together (such as + meshes and materials) are assumed to share the same object mapping with only + one of them passed in the fieldsToConvert array, which will result for all + fields from the same set being reassociated to the new object. + + Fields listed in `fieldsToCopy` are copied from the original object. This + is useful for e.g. skins, to preserve them for the separated meshes. Requies a SceneField::Parent to be present -- otherwise it wouldn't be possible to know where to attach the new objects. */ /** @todo when published, (again) add an initializer_list overload and turn all internal asserts into (tested!) message asserts */ -inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Containers::ArrayView fieldsToConvert, const UnsignedInt newObjectOffset) { +inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Containers::ArrayView fieldsToConvert, Containers::ArrayView fieldsToCopy, const UnsignedInt newObjectOffset) { /** @todo assert for really high object counts (where this cast would fail) */ Containers::Array objectAttachmentCount{ValueInit, std::size_t(scene.mappingBound())}; for(const SceneField field: fieldsToConvert) { @@ -224,6 +232,28 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con } } + /* entriesToAddToFieldsToCopy[i] specifies how many fields to add for the + fieldsToCopy[i] field */ + Containers::Array fieldsToCopyAdditionCount{ValueInit, fieldsToCopy.size()}; + for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) { + const SceneField field = fieldsToCopy[i]; + CORRADE_INTERNAL_ASSERT(field != SceneField::Parent); + CORRADE_INTERNAL_ASSERT(!findField(fieldsToConvert, field)); + + /* Skip fields that are not present */ + const Containers::Optional fieldId = scene.findFieldId(field); + if(!fieldId) continue; + + /** @todo use a statically-allocated array & Into() in a loop instead + once this is more than a private backwards-compatibility utility + where PERF WHATEVER WHO CARES */ + for(const UnsignedInt object: scene.mappingAsArray(*fieldId)) { + CORRADE_INTERNAL_ASSERT(object < objectAttachmentCount.size()); + if(objectAttachmentCount[object]) + fieldsToCopyAdditionCount[i] += objectAttachmentCount[object] - 1; + } + } + UnsignedInt objectsToAdd = 0; for(const UnsignedInt count: objectAttachmentCount) if(count > 1) objectsToAdd += count - 1; @@ -239,9 +269,26 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con for(std::size_t i = 0; i != scene.fieldCount(); ++i) { const SceneFieldData& field = scene.fieldData(i); + /* If this field is among the fields we want to copy, enlarge it for + the new entries */ + if(Containers::Optional fieldToCopy = findField(fieldsToCopy, field.name())) { + /** @todo wow this placeholder construction is HIDEOUS */ + fields[i] = SceneFieldData{field.name(), + field.mappingType(), + Containers::ArrayView{nullptr, std::size_t(field.size() + fieldsToCopyAdditionCount[*fieldToCopy])}, + field.fieldType(), + Containers::StridedArrayView1D{ + {nullptr, ~std::size_t{}}, + std::size_t(field.size() + fieldsToCopyAdditionCount[*fieldToCopy]), + std::ptrdiff_t((field.fieldArraySize() ? field.fieldArraySize() : 1)*sceneFieldTypeSize(field.fieldType())) + }, + field.fieldArraySize(), + field.flags() & ~SceneFieldFlag::ImplicitMapping + }; + /* If this is a parent, enlarge it for the newly added objects, and if it was implicit make it ordered */ - if(field.name() == SceneField::Parent) { + } else if(field.name() == SceneField::Parent) { /** @todo some nicer constructor for placeholders once this is in public interest */ fields[i] = SceneFieldData{SceneField::Parent, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView{nullptr, std::size_t(field.size() + objectsToAdd)}, @@ -269,6 +316,19 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con const Containers::StridedArrayView1D outParents = out.mutableField(parentFieldId); CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParentMapping, outParents) == scene.fieldSize(parentFieldId)); + /* Copy existing field-to-copy data to a prefix of the output */ + for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) { + const SceneField field = fieldsToCopy[i]; + + const Containers::Optional fieldId = scene.findFieldId(field); + if(!fieldId) continue; + + const Containers::StridedArrayView1D outMapping = out.mutableMapping(*fieldId); + const Containers::StridedArrayView2D outField = out.mutableField(*fieldId); + CORRADE_INTERNAL_ASSERT_OUTPUT(scene.mappingInto(*fieldId, 0, outMapping) == scene.fieldSize(*fieldId)); + Utility::copy(scene.field(*fieldId), outField.prefix(scene.fieldSize(*fieldId))); + } + /* List new objects at the end of the extended parent field */ const Containers::StridedArrayView1D newParentMapping = outParentMapping.suffix(scene.fieldSize(parentFieldId)); const Containers::StridedArrayView1D newParents = outParents.suffix(scene.fieldSize(parentFieldId)); @@ -282,6 +342,9 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con constexpr UnsignedInt zero[1]{}; Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(scene.mappingBound()), objectAttachmentCount); + /* Clear the fieldsToCopyAdditionCount array to reuse it below */ + Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(fieldsToCopy.size()), fieldsToCopyAdditionCount); + /* For objects with multiple fields move the extra fields to newly added children */ { @@ -298,6 +361,35 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con attached, then attach the field to a new object and make that new object a child of the previous one. */ if(fieldObject < objectAttachmentCount.size() && objectAttachmentCount[fieldObject]) { + /* Go through all fields to copy and copy each entry that + was assigned to the original object */ + for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) { + const Containers::Optional fieldToCopyId = scene.findFieldId(fieldsToCopy[i]); + if(!fieldToCopyId) continue; + + /* View to copy the data from */ + const Containers::StridedArrayView2D fieldToCopyDataSrc = scene.field(*fieldToCopyId); + + /* Views to put the mapping to and copy the data to */ + const std::size_t newFieldToCopyOffset = scene.fieldSize(*fieldToCopyId); + const Containers::StridedArrayView1D newFieldToCopyMapping = out.mutableMapping(*fieldToCopyId).suffix(newFieldToCopyOffset); + const Containers::StridedArrayView2D newFieldToCopy = out.mutableField(*fieldToCopyId).suffix(newFieldToCopyOffset); + + /* As long as there are entries attached to the + original objects, copy them */ + std::size_t offset = 0; + while(Containers::Optional found = scene.findFieldObjectOffset(*fieldToCopyId, fieldObject, offset)) { + /* Assgn a new field entry to the new object */ + newFieldToCopyMapping[fieldsToCopyAdditionCount[i]] = newParentMapping[newParentIndex]; + + /* Copy the data from the old entry to it */ + Utility::copy(fieldToCopyDataSrc[*found], newFieldToCopy[fieldsToCopyAdditionCount[i]]); + + ++fieldsToCopyAdditionCount[i]; + offset = *found + 1; + } + } + /* Use the old object as a parent of the new object */ newParents[newParentIndex] = fieldObject; /* Assign the field to the new object */ diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 836f464177..ad0df45df7 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -2710,13 +2710,19 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { UnsignedInt object; UnsignedInt camera; }; + struct Skin { + UnsignedInt object; + UnsignedInt skin; + }; Containers::StridedArrayView1D parents; Containers::StridedArrayView1D meshes; Containers::StridedArrayView1D cameras; + Containers::StridedArrayView1D skins; Containers::Array dataData = Containers::ArrayTuple{ {NoInit, 5, parents}, {NoInit, 7, meshes}, {NoInit, 2, cameras}, + {NoInit, 2, skins}, }; Utility::copy({{15, -1}, {21, -1}, {22, 21}, {23, 22}, {1, -1}}, parents); Utility::copy({ @@ -2729,6 +2735,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { {21, 5, -1} }, meshes); Utility::copy({{22, 1}, {1, 5}}, cameras); + Utility::copy({{15, 9}, {21, 10}}, skins); /* Second scene that also has a duplicate, to verify the newly added object IDs don't conflict with each other. A potential downside is that @@ -2752,6 +2759,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, + SceneFieldData{SceneField::Skin, skins.slice(&Skin::object), skins.slice(&Skin::skin)}, /* Just to disambiguate this as a 2D scene */ SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix3x3, nullptr}, }}; @@ -2844,6 +2852,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 2); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object2D(15); CORRADE_VERIFY(o); @@ -2854,6 +2863,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 4); + CORRADE_COMPARE(mo.skin(), 9); } { Containers::Pointer o = importer.object2D(21); CORRADE_VERIFY(o); @@ -2864,6 +2874,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), -1); + CORRADE_COMPARE(mo.skin(), 10); } { Containers::Pointer o = importer.object2D(22); CORRADE_VERIFY(o); @@ -2882,6 +2893,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 0); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object2D(63); CORRADE_VERIFY(o); @@ -2892,6 +2904,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 3); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object2D(64); CORRADE_VERIFY(o); @@ -2902,6 +2915,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 2); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object2D(65); CORRADE_VERIFY(o); @@ -2912,6 +2926,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects2D() { TestSuite::Compare::Container); MeshObjectData2D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 1); + CORRADE_COMPARE(mo.skin(), 9); } { Containers::Pointer o = importer.object2D(66); CORRADE_VERIFY(o); @@ -2977,13 +2992,19 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { UnsignedInt object; UnsignedInt camera; }; + struct Skin { + UnsignedInt object; + UnsignedInt skin; + }; Containers::StridedArrayView1D parents; Containers::StridedArrayView1D meshes; Containers::StridedArrayView1D cameras; + Containers::StridedArrayView1D skins; Containers::Array dataData = Containers::ArrayTuple{ {NoInit, 5, parents}, {NoInit, 7, meshes}, {NoInit, 2, cameras}, + {NoInit, 2, skins}, }; Utility::copy({{15, -1}, {21, -1}, {22, 21}, {23, 22}, {1, -1}}, parents); Utility::copy({ @@ -2996,6 +3017,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { {21, 5, -1} }, meshes); Utility::copy({{22, 1}, {1, 5}}, cameras); + Utility::copy({{15, 9}, {21, 10}}, skins); /* Second scene that also has a duplicate, to verify the newly added object IDs don't conflict with each other. A potential downside is that @@ -3019,6 +3041,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { SceneFieldData{SceneField::Mesh, meshes.slice(&Mesh::object), meshes.slice(&Mesh::mesh)}, SceneFieldData{SceneField::MeshMaterial, meshes.slice(&Mesh::object), meshes.slice(&Mesh::meshMaterial)}, SceneFieldData{SceneField::Camera, cameras.slice(&Camera::object), cameras.slice(&Camera::camera)}, + SceneFieldData{SceneField::Skin, skins.slice(&Skin::object), skins.slice(&Skin::skin)}, /* Just to disambiguate this as a 3D scene */ SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Matrix4x4, nullptr}, }}; @@ -3111,6 +3134,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 2); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object3D(15); CORRADE_VERIFY(o); @@ -3121,6 +3145,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 4); + CORRADE_COMPARE(mo.skin(), 9); } { Containers::Pointer o = importer.object3D(21); CORRADE_VERIFY(o); @@ -3131,6 +3156,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), -1); + CORRADE_COMPARE(mo.skin(), 10); } { Containers::Pointer o = importer.object3D(22); CORRADE_VERIFY(o); @@ -3149,6 +3175,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 0); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object3D(63); CORRADE_VERIFY(o); @@ -3159,6 +3186,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 3); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object3D(64); CORRADE_VERIFY(o); @@ -3169,6 +3197,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 2); + CORRADE_COMPARE(mo.skin(), -1); } { Containers::Pointer o = importer.object3D(65); CORRADE_VERIFY(o); @@ -3179,6 +3208,7 @@ void AbstractImporterTest::sceneDeprecatedFallbackMultiFunctionObjects3D() { TestSuite::Compare::Container); MeshObjectData3D& mo = static_cast(*o); CORRADE_COMPARE(mo.material(), 1); + CORRADE_COMPARE(mo.skin(), 9); } { Containers::Pointer o = importer.object3D(66); CORRADE_VERIFY(o); diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index aadad0f516..ccc3d3c76a 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -43,6 +43,7 @@ struct SceneToolsTest: TestSuite::Tester { void combineObjectSharedFieldPlaceholder(); void convertToSingleFunctionObjects(); + void convertToSingleFunctionObjectsFieldsToCopy(); }; struct { @@ -83,6 +84,8 @@ SceneToolsTest::SceneToolsTest() { addInstancedTests({&SceneToolsTest::convertToSingleFunctionObjects}, Containers::arraySize(ConvertToSingleFunctionObjectsData)); + + addTests({&SceneToolsTest::convertToSingleFunctionObjectsFieldsToCopy}); } using namespace Math::Literals; @@ -419,7 +422,7 @@ void SceneToolsTest::convertToSingleFunctionObjects() { sceneFieldCustom(15), /* Include also a field that's not present -- it should get skipped */ SceneField::ImporterState - }), 63); + }), {}, 63); /* There should be three more objects, or the original count preserved if it's large enough */ @@ -571,7 +574,91 @@ void SceneToolsTest::convertToSingleFunctionObjects() { Containers::arrayView(foo3FieldData), TestSuite::Compare::Container); CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(17)), SceneFieldFlags{}); +} + +void SceneToolsTest::convertToSingleFunctionObjectsFieldsToCopy() { + const UnsignedShort parentMappingData[]{2, 15, 21, 22}; + const Byte parentFieldData[]{-1, -1, -1, 21}; + + const UnsignedShort meshMappingData[]{15, 21, 21, 21, 22, 15}; + const UnsignedInt meshFieldData[]{6, 1, 2, 4, 7, 3}; + + const UnsignedShort skinMappingData[]{22, 21}; + const UnsignedInt skinFieldData[]{5, 13}; + + const UnsignedLong fooMappingData[]{15, 23, 15, 21}; + const Int fooFieldData[]{0, 1, 2, 3, 4, 5, 6, 7}; + + SceneData original = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 50, Containers::arrayView({ + SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData)}, + SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)}, + SceneFieldData{SceneField::Skin, Containers::arrayView(skinMappingData), Containers::arrayView(skinFieldData)}, + /* Array field */ + SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D{fooFieldData, {4, 2}}}, + /* Just to disambiguate between 2D and 3D */ + SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedShort, nullptr, SceneFieldType::Matrix4x4, nullptr} + })); + + SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original, + Containers::arrayView({ + /* Include also a field that's not present -- it should get skipped */ + SceneField::ImporterState, + /* Three additional mesh assignments that go to new objects */ + SceneField::Mesh + }), + Containers::arrayView({ + /* One assignment is to an object that has just one mesh, it should + not be copied anywhere, the other should be duplicated two + times */ + SceneField::Skin, + /* Array field with multiple assignments per object -- all should + be copied */ + sceneFieldCustom(15), + /* Include also a field that's not present -- it should get skipped */ + SceneField::Camera + }), 60); + + CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView>({ + {2, -1}, + {15, -1}, + {21, -1}, + {22, 21}, + {60, 21}, /* duplicated mesh assignment to object 21 */ + {61, 21}, /* duplicated mesh assignment to object 21 */ + {62, 15} /* duplicated mesh assignment to object 15 */ + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView>>({ + {15, {6, -1}}, + {21, {1, -1}}, + {60, {2, -1}}, /* duplicated mesh assignment to object 21 */ + {61, {4, -1}}, /* duplicated mesh assignment to object 21 */ + {22, {7, -1}}, + {62, {3, -1}} /* duplicated mesh assignment to object 15 */ + })), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(scene.skinsAsArray(), (Containers::arrayView>({ + {22, 5}, + {21, 13}, + {60, 13}, /* duplicated from object 21 */ + {61, 13}, /* duplicated from object 21 */ + })), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(scene.mapping(sceneFieldCustom(15)), Containers::arrayView({ + 15, 23, 15, 21, + 60, 61, /* duplicated from object 21 (two duplicates of one object) */ + 62, 62, /* duplicated from object 15 (two entries for one object) */ + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS((scene.field(sceneFieldCustom(15)).transposed<0, 1>()[0]), Containers::arrayView({ + 0, 2, 4, 6, + 6, 6, /* duplicated from object 21 (two duplicates of one object) */ + 0, 4, /* duplicated from object 15 (two entries for one object) */ + }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS((scene.field(sceneFieldCustom(15)).transposed<0, 1>()[1]), Containers::arrayView({ + 1, 3, 5, 7, + 7, 7, /* duplicated from object 21 (two duplicates of one object) */ + 1, 5, /* duplicated from object 15 (two entries for one object) */ + }), TestSuite::Compare::Container); } }}}} From 4418bec270cf87ef0befd9044bf2b1c6c4c3265b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Sep 2021 13:36:46 +0200 Subject: [PATCH 49/49] Trade: bump AbstractImporter interface version. With the previous commits, existing plugin implementations built and ran against the new code, however it introduced several ABI breaks meaning that existing plugin binaries would crash. This forces them to be recompiled to match the new version string. --- src/Magnum/Trade/AbstractImporter.cpp | 2 +- src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp | 2 +- src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp | 2 +- src/MagnumPlugins/ObjImporter/ObjImporter.cpp | 2 +- src/MagnumPlugins/TgaImporter/TgaImporter.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 036ca9ed5f..530d267451 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -68,7 +68,7 @@ namespace Magnum { namespace Trade { std::string AbstractImporter::pluginInterface() { return /* [interface] */ -"cz.mosra.magnum.Trade.AbstractImporter/0.3.4" +"cz.mosra.magnum.Trade.AbstractImporter/0.4" /* [interface] */ ; } diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp index d177ad43fb..6ca93fb511 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp @@ -280,4 +280,4 @@ Containers::Optional AnyImageImporter::doImage3D(const UnsignedInt }} CORRADE_PLUGIN_REGISTER(AnyImageImporter, Magnum::Trade::AnyImageImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.4") + "cz.mosra.magnum.Trade.AbstractImporter/0.4") diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp index a583007eff..6bc8c03d8d 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp @@ -269,4 +269,4 @@ Containers::Optional AnySceneImporter::doImage3D(const UnsignedInt }} CORRADE_PLUGIN_REGISTER(AnySceneImporter, Magnum::Trade::AnySceneImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.4") + "cz.mosra.magnum.Trade.AbstractImporter/0.4") diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp index 5c4f37baa2..f8228c4307 100644 --- a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -487,4 +487,4 @@ Containers::Optional ObjImporter::doMesh(UnsignedInt id, UnsignedInt) }} CORRADE_PLUGIN_REGISTER(ObjImporter, Magnum::Trade::ObjImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.4") + "cz.mosra.magnum.Trade.AbstractImporter/0.4") diff --git a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp index 84d3e23414..7bf6a90700 100644 --- a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp +++ b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp @@ -209,4 +209,4 @@ Containers::Optional TgaImporter::doImage2D(UnsignedInt, UnsignedIn }} CORRADE_PLUGIN_REGISTER(TgaImporter, Magnum::Trade::TgaImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.4") + "cz.mosra.magnum.Trade.AbstractImporter/0.4")