diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 5f4db4737e..184f085926 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -769,49 +769,49 @@ struct Trs { Containers::ArrayView transformations; -#error TODO accept strided array views here as well? -Containers::ArrayTuple data{ - {objectCount, transformation}, - {objectCount, parent}, - {objectCount, transformationParentObjectMapping}, - {animatedObjectCount, trs}, - {animatedObjectCount, animationId}, - {animatedObjectCount, animatedObjectMapping}, - {meshCount, meshMaterialId}, - {meshCount, meshMaterialIdObjectMapping}, - {lightCount, lightId}, - {lightCount, lightIdObjectMapping}, - {1, cameraId}, - {1, cameraIdObjectMapping}, -}; - -Trade::SceneData scene{objectCount, SceneObjectType::UnsignedInt, { - Trade::SceneFieldData{Trade::SceneField::Transformation, - transformation, transformationParentObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::Parent, - transformation, transformationParentObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::Translation, - Containers::stridedArrayView(transformation).slice(&Trs::translation), - animatedObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::Rotation, - Containers::stridedArrayView(transformation).slice(&Trs::rotation), - animatedObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::Scaling, - Containers::stridedArrayView(transformation).slice(&Trs::scaling), - animatedObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::AnimationId, - animationId, animatedObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::MeshId, - Containers::stridedArrayView(meshMaterialId).slice(&MeshMaterial::mesh), - meshMaterialIdObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::MeshMaterialId, - Containers::stridedArrayView(meshMaterialId).slice(&MeshMaterial::material), - meshMaterialIdObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::LightId, - lightId, lightIdObjectMapping}, - Trade::SceneFieldData{Trade::SceneField::CameraId, - cameraId, cameraIdObjectMapping}, -}}; +// #error TODO accept strided array views here as well? +// Containers::ArrayTuple data{ +// {objectCount, transformation}, +// {objectCount, parent}, +// {objectCount, transformationParentObjectMapping}, +// {animatedObjectCount, trs}, +// {animatedObjectCount, animationId}, +// {animatedObjectCount, animatedObjectMapping}, +// {meshCount, meshMaterialId}, +// {meshCount, meshMaterialIdObjectMapping}, +// {lightCount, lightId}, +// {lightCount, lightIdObjectMapping}, +// {1, cameraId}, +// {1, cameraIdObjectMapping}, +// }; +// +// Trade::SceneData scene{objectCount, SceneObjectType::UnsignedInt, { +// Trade::SceneFieldData{Trade::SceneField::Transformation, +// transformation, transformationParentObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::Parent, +// transformation, transformationParentObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::Translation, +// Containers::stridedArrayView(transformation).slice(&Trs::translation), +// animatedObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::Rotation, +// Containers::stridedArrayView(transformation).slice(&Trs::rotation), +// animatedObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::Scaling, +// Containers::stridedArrayView(transformation).slice(&Trs::scaling), +// animatedObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::AnimationId, +// animationId, animatedObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::MeshId, +// Containers::stridedArrayView(meshMaterialId).slice(&MeshMaterial::mesh), +// meshMaterialIdObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::MeshMaterialId, +// Containers::stridedArrayView(meshMaterialId).slice(&MeshMaterial::material), +// meshMaterialIdObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::LightId, +// lightId, lightIdObjectMapping}, +// Trade::SceneFieldData{Trade::SceneField::CameraId, +// cameraId, cameraIdObjectMapping}, +// }}; /* [SceneData-populating] */ } 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 f8f0b75396..46630c68d4 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -25,13 +25,15 @@ #include "SceneData.h" +#include "Magnum/Trade/Implementation/arrayUtilities.h" + namespace Magnum { namespace Trade { Debug& operator<<(Debug& debug, const SceneField value) { debug << "Trade::SceneField" << Debug::nospace; - if(UnsignedShort(value) >= UnsignedShort(SceneField::Custom)) - return debug << "::Custom(" << Debug::nospace << (UnsignedShort(value) - UnsignedShort(SceneField::Custom)) << 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 */ @@ -41,12 +43,12 @@ Debug& operator<<(Debug& debug, const SceneField value) { _c(Translation) _c(Rotation) _c(Scaling) - _c(MeshId) - _c(MeshMaterialId) - _c(LightId) - _c(CameraId) - _c(AnimationId) - _c(SkinId) + _c(Mesh) + _c(MeshMaterial) + _c(Light) + _c(Camera) + _c(Animation) + _c(Skin) #undef _c /* LCOV_EXCL_STOP */ @@ -54,7 +56,7 @@ Debug& operator<<(Debug& debug, const SceneField value) { case SceneField::Custom: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const SceneFieldType value) { @@ -276,12 +278,12 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): invalid type" << type, {}); } -Debug& operator<<(Debug& debug, const SceneIndexType value) { - debug << "Trade::SceneIndexType" << Debug::nospace; +Debug& operator<<(Debug& debug, const SceneObjectType value) { + debug << "Trade::SceneObjectType" << Debug::nospace; switch(value) { /* LCOV_EXCL_START */ - #define _c(value) case SceneIndexType::value: return debug << "::" #value; + #define _c(value) case SceneObjectType::value: return debug << "::" #value; _c(UnsignedByte) _c(UnsignedInt) _c(UnsignedShort) @@ -293,15 +295,53 @@ Debug& operator<<(Debug& debug, const SceneIndexType value) { return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } -UnsignedInt sceneIndexTypeSize(const SceneIndexType type) { +UnsignedInt sceneObjectTypeSize(const SceneObjectType type) { switch(type) { - case SceneIndexType::UnsignedByte: return 1; - case SceneIndexType::UnsignedShort: return 2; - case SceneIndexType::UnsignedInt: return 4; - case SceneIndexType::UnsignedLong: return 8; + case SceneObjectType::UnsignedByte: return 1; + case SceneObjectType::UnsignedShort: return 2; + case SceneObjectType::UnsignedInt: return 4; + case SceneObjectType::UnsignedLong: return 8; } - CORRADE_ASSERT_UNREACHABLE("Trade::sceneIndexTypeSize(): invalid type" << type, {}); + CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {}); +} + +SceneFieldData::SceneFieldData(const SceneField name, const SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const Containers::StridedArrayView2D& objectData, const UnsignedShort arraySize) noexcept: SceneFieldData{name, fieldType, Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, {}, Containers::StridedArrayView1D{{objectData.data(), ~std::size_t{}}, objectData.size()[0], objectData.stride()[0]}, arraySize} { + /* 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(arraySize) CORRADE_ASSERT(fieldData.empty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType)*arraySize, + "Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType << "and array size" << arraySize, ); + 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(Containers::Array&& data, const UnsignedLong objectCount, const SceneObjectType objectType, 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)} { + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); // TODO } +SceneData::SceneData(Containers::Array&& data, const UnsignedLong objectCount, const SceneObjectType objectType, const std::initializer_list fields, const void* const importerState): SceneData{std::move(data), objectCount, objectType, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {} + +SceneData::SceneData(SceneData&&) noexcept = default; + +SceneData::~SceneData() = default; + +SceneData& SceneData::operator=(SceneData&&) noexcept = default; + }} diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index efdaeefff9..2c19f3b012 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::SceneData + * @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, @ref Magnum::Trade::SceneObjectType, function @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneObjectTypeSize() */ #include @@ -43,8 +43,6 @@ namespace Magnum { namespace Trade { -#error for an archetype-like ECS there's multiple arrays of a single field, have that controlled globally on a SceneData level with a flag? new SceneIndexType? ahah - /** @brief Scene field name @m_since_latest @@ -54,7 +52,7 @@ See @ref SceneData for more information. @ref AbstractImporter::sceneFieldForName(), @ref AbstractImporter::sceneFieldName() */ -enum class SceneField: UnsignedShort { +enum class SceneField: UnsignedInt { /* Zero used for an invalid value */ /** @@ -71,13 +69,7 @@ enum class SceneField: UnsignedShort { * corresponding @ref SceneData::fieldObject() array. * @see @ref SceneData::parentAsArray() */ - Parent = 1, // TODO rename to ParentTransformation? would no longer work for "query a hand and all the fingers on it" - // TODO drop the self-referencability, the app should build an accel struct on top? - - // TODO: InheritFrom? BaseObject? where it'd inherit fields from given - // obj? not sure how that could be batch-processed, sigh (expanded at - // runtime? ugh) - // TODO have SceneField as a type as well? what would that be useful for + Parent = 1, /** * Transformation. Type is usually @ref SceneFieldType::Matrix3x3 for 2D @@ -180,18 +172,18 @@ enum class SceneField: UnsignedShort { * with the Nth material. * @see @ref SceneData::meshIdsAsArray() */ - MeshId, + Mesh, /** - * ID of a material for a @ref SceneFieldType::MeshId, corresponding to the + * ID of a material for a @ref SceneFieldType::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 SceneFieldType::MeshMaterialId. + * object mapping view with @ref SceneFieldType::Mesh. * @see @ref SceneData::materialIdsAsArray() */ - MeshMaterialId, + MeshMaterial, /** * ID of a light associated with this object, corresponding to the ID @@ -202,7 +194,7 @@ enum class SceneField: UnsignedShort { * lights associated. * @see @ref SceneData::lightIdsAsArray() */ - LightId, + Light, /** * ID of a camera associated with this object, corresponding to the ID @@ -213,7 +205,7 @@ enum class SceneField: UnsignedShort { * cameras associated. * @see @ref SceneData::cameraIdsAsArray() */ - CameraId, + Camera, /** * ID of an animation associated with this object, corresponding to the ID @@ -224,7 +216,7 @@ enum class SceneField: UnsignedShort { * animations associated. * @see @ref SceneData::lightIdsAsArray() */ - AnimationId, + Animation, /** * ID of a skin associated with this object, corresponding to the ID @@ -235,27 +227,19 @@ enum class SceneField: UnsignedShort { * skins associated. * @see @ref SceneData::skinIdsAsArray() */ - SkinId, - -// MeshVertexOffset, // TODO all should have the same object mapping, if present -// MeshIndexOffset, -// MeshCount, // TODO ehh? -// MeshInstanceOffset, -// MeshInstanceCount, -// -// InstanceMeshId, // TODO instances? would be cool, eh? - - // TODO texture transform + layer so we don't need 100 materials - // TODO visible, shadow casting, collidable ... bits - + 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(UnsignedShort) */ - Custom = 32768 // TODO more? for user-made entities, flags etc, this might not be enough + Custom = 0x80000000u }; /** @@ -270,10 +254,10 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneField value); Returns @cpp true @ce if @p name has a value larger or equal to @ref SceneField::Custom, @cpp false @ce otherwise. -@see @ref sceneFieldCustom(UnsignedShort), @ref sceneFieldCustom(SceneField) +@see @ref sceneFieldCustom(UnsignedInt), @ref sceneFieldCustom(SceneField) */ constexpr bool isSceneFieldCustom(SceneField name) { - return UnsignedShort(name) >= UnsignedShort(SceneField::Custom); + return UnsignedInt(name) >= UnsignedInt(SceneField::Custom); } /** @@ -286,24 +270,24 @@ to get the index back. */ /* Constexpr so it's usable for creating compile-time SceneFieldData instances */ -constexpr SceneField sceneFieldCustom(UnsignedShort id) { - return CORRADE_CONSTEXPR_ASSERT(id < UnsignedShort(SceneField::Custom), +constexpr SceneField sceneFieldCustom(UnsignedInt id) { + return CORRADE_CONSTEXPR_ASSERT(id < UnsignedInt(SceneField::Custom), "Trade::sceneFieldCustom(): index" << id << "too large"), - SceneField(UnsignedShort(SceneField::Custom) + id); + SceneField(UnsignedInt(SceneField::Custom) + id); } /** @brief Get index of a custom scene field @m_since_latest -Inverse to @ref sceneFieldCustom(UnsignedShort). Expects that the field is +Inverse to @ref sceneFieldCustom(UnsignedInt). Expects that the field is custom. @see @ref isSceneFieldCustom() */ -constexpr UnsignedShort sceneFieldCustom(SceneField name) { +constexpr UnsignedInt sceneFieldCustom(SceneField name) { return CORRADE_CONSTEXPR_ASSERT(isSceneFieldCustom(name), "Trade::sceneFieldCustom():" << name << "is not custom"), - UnsignedShort(name) - UnsignedShort(SceneField::Custom); + UnsignedInt(name) - UnsignedInt(SceneField::Custom); } /** @@ -317,9 +301,7 @@ information. enum class SceneFieldType: UnsignedShort { /* Zero used for an invalid value */ - /* 1 reserved for Bool, which needs [Strided]BoolArray[View] first */ -// Bool = 1, // TODO needs a BoolArray! and also special overloads, sigh - // also StridedBoolArrayView?! + /* 1 reserved for Bool (Bit?), which needs [Strided]BitArray[View] first */ Float = 2, /**< @relativeref{Magnum,Float} */ Half, /**< @relativeref{Magnum,Half} */ @@ -429,16 +411,7 @@ enum class SceneFieldType: UnsignedShort { Degd, /**< @relativeref{Magnum,Degh} */ Rad, /**< @relativeref{Magnum,Rad} */ Radh, /**< @relativeref{Magnum,Radh} */ - Radd, /**< @relativeref{Magnum,Radd} */ - - // TODO: pointer, like with MaterialData? could be for having real MeshData - // LightData / ... references - - // TODO: doc that no need to store any custom types here so it can be just - // 16 bits - - /* Can be at most 8191 types, as it's stored together with - SceneFieldIndexType and isOffsetOnly in 16 bits */ // TODO outdated! + Radd /**< @relativeref{Magnum,Radd} */ }; /** @@ -454,15 +427,16 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldType value); MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type); /** -@brief Scene object index type +@brief Scene object type @m_since_latest -Type used for associating fields with corresponding objects. Unlike +Type used for mapping fields to corresponding objects. Unlike @ref SceneFieldType that is different for different fields, the index type is -the same for all fields. -@see @ref SceneData::objectIndexType(), @ref sceneIndexTypeSize() +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 SceneIndexType: UnsignedByte { // TODO SceneObjectType? +enum class SceneObjectType: UnsignedByte { /* Zero used for an invalid value */ UnsignedByte = 1, /**< @relativeref{Magnum,UnsignedByte} */ @@ -478,24 +452,19 @@ enum class SceneIndexType: UnsignedByte { // TODO SceneObjectType? * @ref fieldObject(). */ UnsignedLong - - // TODO make the largest bit a sparse identifier if given field has a sparse flag }; /** -@debugoperatorenum{SceneIndexType} +@debugoperatorenum{SceneObjectType} @m_since_latest */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneIndexType value); - -// TODO where to have string to object mapping? some serializable way? yep, a -// designated StringMapData +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneObjectType value); /** -@brief Size of given scene index type +@brief Size of given scene object type @m_since_latest */ -MAGNUM_TRADE_EXPORT UnsignedInt sceneIndexTypeSize(SceneIndexType type); +MAGNUM_TRADE_EXPORT UnsignedInt sceneObjectTypeSize(SceneObjectType type); /** @brief Scene field data @@ -535,102 +504,104 @@ 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: _countTypeOffsetOnly{}, _name{}, _fieldStride{}, _objectStride{}, _arraySize{}, _fieldData{}, _objectData{} {} + constexpr explicit SceneFieldData() noexcept: _count{}, _name{}, _fieldType{}, _fieldStride{}, _objectStride{}, _arraySize{}, _objectType{}, _isOffsetOnly{}, _fieldData{}, _objectData{} {} /** * @brief Type-erased constructor - * @param name Field name - * @param type Field type - * @param data Field data - * @param indexType Object index type - * @param indexData Object index data - * @param arraySize Array size. Use @cpp 0 @ce for non-array fields. + * @param name Field name + * @param fieldType Field type + * @param fieldData Field data + * @param objectType Object type + * @param objectData Object data + * @param arraySize Array size. Use @cpp 0 @ce for non-array + * fields. * - * Expects that @p data and @p indexData have the same size, @p data - * stride is large enough to fit all @p arraySize items of @p type, - * @p type corresponds to @p name and @p arraySize is zero for builtin - * fields. + * Expects that @p fieldData and @p objectData have the same size, + * @p fieldType corresponds to @p name and @p arraySize is zero for + * builtin fields. */ - explicit SceneFieldData(SceneField name, SceneFieldType type, const Containers::StridedArrayView1D& data, SceneIndexType indexType, const Containers::StridedArrayView1D& indexData, UnsignedShort arraySize = 0) noexcept; + constexpr explicit SceneFieldData(SceneField name, SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, UnsignedShort arraySize = 0) noexcept; /** * @brief Constructor - * @param name Field name - * @param type Field type - * @param data Field data - * @param indexData Object index data - * @param arraySize Array size. Use @cpp 0 @ce for non-array fie. + * @param name Field name + * @param fieldType Field type + * @param fieldData Field data + * @param objectData Object index data + * @param arraySize Array size. Use @cpp 0 @ce for non-array + * fields. * - * Expects that @p data and @p indexData have the same size in the - * first dimension; the second dimension of @p data is contiguous and - * its size matches @p type and @p arraSize, that @p type corresponds - * to @p name and @p arraySize is zero for builtin attributes; that - * the second dimension of @p indexData is contiguous and its size is - * either 1, 2, 4 or 8, corresponding to one of the - * @ref SceneFieldIndexType values. + * Expects that @p fieldData and @p objectData have the same size in + * the first dimension, that the second dimension of @p fieldData is + * contiguous and its size matches @p fieldType and @p arraySize, that + * @p fieldType corresponds to @p name and @p arraySize is zero for + * builtin attributes, 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. */ - explicit SceneFieldData(SceneField name, SceneFieldType type, const Containers::StridedArrayView2D& data, const Containers::StridedArrayView2D& indexData, UnsignedShort arraySize = 0) noexcept; + explicit SceneFieldData(SceneField name, SceneFieldType fieldType, const Containers::StridedArrayView2D& fieldData, const Containers::StridedArrayView2D& objectData, UnsignedShort arraySize = 0) noexcept; /** * @brief Constructor - * @param name Field name - * @param data Field data - * @param indexData Object index data + * @param name Field name + * @param fieldData Field data + * @param objectData Object index data * - * Detects @ref SceneFieldType based on @p T and @ref SceneIndexType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneFieldType, const Containers::StridedArrayView1D&, SceneFieldIndexType, const Containers::StridedArrayView1D&, UnsignedShort). + * Detects @ref SceneFieldType based on @p T and @ref SceneObjectType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneFieldType, const Containers::StridedArrayView1D&, SceneObjectType, 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& data, const Containers::StridedArrayView1D& indexData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& fieldData, const Containers::StridedArrayView1D& objectData) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& data, const Containers::ArrayView& indexData) noexcept: SceneFieldData{name, data, Containers::stridedArrayView(indexData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D& fieldData, const Containers::ArrayView& objectData) noexcept: SceneFieldData{name, fieldData, Containers::stridedArrayView(objectData)} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& data, const Containers::StridedArrayView1D& indexData) noexcept: SceneFieldData{name, Containers::stridedArrayView(data), indexData} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& fieldData, const Containers::StridedArrayView1D& objectData) noexcept: SceneFieldData{name, Containers::stridedArrayView(fieldData), objectData} {} /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& data, const Containers::ArrayView& indexData) noexcept: SceneFieldData{name, Containers::stridedArrayView(data), Containers::stridedArrayView(indexData)} {} + template constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView& fieldData, const Containers::ArrayView& objectData) noexcept: SceneFieldData{name, Containers::stridedArrayView(fieldData), Containers::stridedArrayView(objectData)} {} /** * @brief Construct an array field - * @param name Field name - * @param data Field data - * @param indexData Object index data - * - * Detects @ref SceneFieldType based on @p T and @ref SceneIndexType - * based on @p U and calls @ref SceneFieldData(SceneField, SceneFieldType, const Containers::StridedArrayView1D&, SceneFieldIndexType, const Containers::StridedArrayView1D&, UnsignedShort) - * with the @p data second dimension size passed to @p arraySize. - * Expects that the second dimension of @p data 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 MeshAttributeData(SceneField, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) - * for details about @ref SceneFieldType and @ref SceneIndexType + * @param name Field name + * @param fieldData Field data + * @param objectData Object data + * + * Detects @ref SceneFieldType based on @p T and @ref SceneObjectType + * based on @p U and calls @ref SceneFieldData(SceneField, SceneFieldType, const Containers::StridedArrayView1D&, SceneObjectType, const Containers::StridedArrayView1D&, UnsignedShort) + * with the @p fieldData second dimension size passed to @p arraySize. + * 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 SceneFieldType and @ref SceneObjectType * detection. */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& data, const Containers::StridedArrayView1D& indexData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& data, const Containers::StridedArrayView1D& objectData) noexcept; /** @overload */ - template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& data, const Containers::ArrayView& indexData) noexcept; + template constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D& data, const Containers::ArrayView& objectData) noexcept: SceneFieldData{name, data, Containers::stridedArrayView(objectData)} {} /** * @brief Construct an offset-only field * @param name Field name - * @param count Object count - * @param type Field type - * @param offset Field data offset - * @param stride Field data stride - * @param indexOffset Object index data offset - * @param indexStride Object index data stride + * @param count Entry count + * @param fieldType Field type + * @param fieldOffset Field data offset + * @param fieldStride Field data stride + * @param objectType Object type + * @param objectOffset Object data offset + * @param objectStride Object data stride * @param arraySize 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 arraySize is zero for - * builtin attributes. + * field construction time. Expects that @p fieldType corresponds to + * @p name and @p arraySize is zero for builtin attributes. * * Note that due to the @cpp constexpr @ce nature of this constructor, * no @p type / @p arraySize checks against @p stride can be done. @@ -639,7 +610,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @see @ref isOffsetOnly(), @ref arraySize(), * @ref data(Containers::ArrayView) const */ - explicit constexpr SceneFieldData(SceneField name, UnsignedLong count, SceneFieldType type, std::size_t offset, std::ptrdiff_t stride, std::size_t indexOffset, std::ptrdiff_t indexStride, UnsignedShort arraySize = 0) noexcept; + explicit constexpr SceneFieldData(SceneField name, std::size_t count, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneObjectType objectType, std::size_t objectOffset, std::ptrdiff_t objectStride, UnsignedShort arraySize = 0) noexcept; /** * @brief If the field is offset-only @@ -647,16 +618,19 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * Returns @cpp true @ce if the field doesn't contain the data views * directly, but instead refers to unspecified external data. * @see @ref data(Containers::ArrayView) const, - * @ref indexData(Containers::ArrayView) const, - * @ref MeshAttributeData(SceneField, SceneFieldType, std::size_t, UnsignedInt, std::ptrdiff_t, UnsignedShort) + * @ref objectData(Containers::ArrayView) const, + * @ref SceneFieldData(SceneField, std::size_t, SceneFieldType, std::size_t, std::ptrdiff_t, SceneObjectType, std::size_t, std::ptrdiff_t, UnsignedShort) */ - constexpr bool isOffsetOnly() const; + constexpr bool isOffsetOnly() const { return _isOffsetOnly; } /** @brief Field name */ constexpr SceneField name() const { return _name; } /** @brief Field type */ - constexpr SceneFieldType type() const; + constexpr SceneFieldType fieldType() const { return _fieldType; } + + /** @brief Object type */ + constexpr SceneObjectType objectType() const { return _objectType; } /** @brief Field array size */ constexpr UnsignedShort arraySize() const { return _arraySize; } @@ -665,61 +639,94 @@ class MAGNUM_TRADE_EXPORT SceneFieldData { * @brief Type-erased field data * * Expects that the field is not offset-only, in that case use the - * @ref data(Containers::ArrayView) const overload instead. + * @ref fieldData(Containers::ArrayView) const overload + * instead. * @see @ref isOffsetOnly() */ - constexpr Containers::StridedArrayView1D data() const; + 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{}}, _count, + (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 sceneData parameter is - * ignored. + * If the field is not offset-only, the @p data parameter is ignored. * @see @ref isOffsetOnly(), @ref data() const */ - Containers::StridedArrayView1D data(Containers::ArrayView sceneData) 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, _count, _fieldStride}; + } /** - * @brief Type-erased object index data + * @brief Type-erased object data * * Expects that the field is not offset-only, in that case use the - * @ref data(Containers::ArrayView) const overload instead. + * @ref objectData(Containers::ArrayView) const overload + * instead. * @see @ref isOffsetOnly() */ - constexpr Containers::StridedArrayView1D indexData() const; + 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{}}, _count, + (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::objectData(): the field is offset-only, supply a data array"), _objectStride)}; + } /** - * @brief Type-erased object index data for an offset-only attribute + * @brief Type-erased object data for an offset-only attribute * - * If the field is not offset-only, the @p vertexData parameter is - * ignored. + * If the field is not offset-only, the @p data parameter is ignored. * @see @ref isOffsetOnly(), @ref data() const */ - Containers::StridedArrayView1D indexData(Containers::ArrayView sceneData) 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, _count, _objectStride}; + } private: - /* Count (48) | SceneFieldType (15) | isOffsetOnly (1) */ - // TODO has to store index type so SceneData can verify all are the same ffs - // TODO should store a bit describing if it's sorted, for perf - UnsignedLong _countTypeOffsetOnly; + UnsignedLong _count; SceneField _name; - UnsignedShort _fieldStride; - UnsignedShort _objectStride; + SceneFieldType _fieldType; + Short _fieldStride; + Short _objectStride; UnsignedShort _arraySize; - // TODO fuck, for bools we need additional 3 bits to store bit position - // TODO or store that in array size in that case? eugh - - // TODO: use extra 8 bytes? 2 for SceneFieldType, 1 for offset/sorted flags, 1 for bit position, 1 for index type, 3 left - // TODO also a "sparse" flag + SceneObjectType _objectType; + bool _isOffsetOnly; + /* 2 bytes free */ 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; }; - Data _fieldData, _objectData; // TODO object data = 0 means ~implicit~? for implicit the data could contain a single monotonic array shared by all - // TODO oooh implicit could be useful for *huge* bitfields that span all objects, because there it makes no sense to multiply the data size 9x just to specify a range the bits affect + Data _fieldData, _objectData; }; +/** @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 @@ -783,18 +790,17 @@ are Objects and Fields. */ class MAGNUM_TRADE_EXPORT SceneData { public: - - explicit SceneData(UnsignedLong objectCount, SceneIndexType objectIndexType, Containers::Array&& data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + explicit SceneData(Containers::Array&& data, UnsignedLong objectCount, SceneObjectType objectType, Containers::Array&& fields, const void* importerState = nullptr) noexcept; /** @overload */ /* Not noexcept because allocation happens inside */ - explicit SceneData(UnsignedLong objectCount, SceneIndexType objectIndexType, Containers::Array&& data, std::initializer_list fields, const void* importerState = nullptr); + explicit SceneData(Containers::Array&& data, UnsignedLong objectCount, SceneObjectType objectType, std::initializer_list fields, const void* importerState = nullptr); - explicit SceneData(UnsignedLong objectCount, SceneIndexType objectIndexType, DataFlags dataFlags, Containers::ArrayView data, Containers::Array&& fields, const void* importerState = nullptr) noexcept; + explicit SceneData(DataFlags dataFlags, Containers::ArrayView data, UnsignedLong objectCount, SceneObjectType objectType, Containers::Array&& fields, const void* importerState = nullptr) noexcept; /** @overload */ /* Not noexcept because allocation happens inside */ - explicit SceneData(UnsignedLong objectCount, SceneIndexType objectIndexType, DataFlags dataFlags, Containers::ArrayView data, std::initializer_list fields, const void* importerState = nullptr); + explicit SceneData(DataFlags dataFlags, Containers::ArrayView data, UnsignedLong objectCount, SceneObjectType objectType, std::initializer_list fields, const void* importerState = nullptr); /** @brief Copying is not allowed */ SceneData(const SceneData&) = delete; @@ -884,6 +890,28 @@ class MAGNUM_TRADE_EXPORT SceneData { */ UnsignedInt fieldCount() const { return _fields.size(); } + /** + * @brief Number of entries for given field + * @m_since_latest + * + * In other words, size of the view returned by @ref field() / + * @ref mutableField and @ref fieldObject() / @ref mutableFieldObject(). + * Since an object can have multiple entries of the same field (for + * example multiple meshes associated with an object), the entry count + * doesn't necessarily match the number of objects having given field. + * The @p id is expected to be smaller than @ref fieldCount(). + */ + UnsignedLong fieldEntryCount(UnsignedInt id) const; + + /** + * @brief Number of entries for given named field + * @m_since_latest + * + * The @p name is expected to exist. + * @see @ref hasField(), @ref objectCount(UnsignedInt) + */ + UnsignedLong fieldEntryCount(SceneField name) const; + /** * @brief Raw field data * @m_since_latest @@ -973,43 +1001,20 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Total object count * @m_since_latest * - * Total number of objects contained in the scene. Use - * @ref objectCount(SceneField) to get the number of objects associated - * with given field. - * @see @ref fieldCount() + * Total number of objects contained in the scene. + * @see @ref fieldCount(), @ref fieldEntryCount() */ UnsignedLong objectCount() const { return _objectCount; } - /** - * @brief Number of objects associated with given field - * @m_since_latest - * - * In other words, size of the view returned by @ref field() / - * @ref mutableField and @ref fieldObject() / @ref mutableFieldObject(). - * Note that an object can have a certain field associated with it - * multiple times with different values (for example an object having - * multiple meshes). The @p id is expected to be smaller than - * @ref fieldCount(). - */ - UnsignedLong objectCount(UnsignedInt id) const; - - /** - * @brief Number of objects associated with given field - * @m_since_latest - * - * The @p name is expected to exist. - * @see @ref hasField(), @ref objectCount(UnsignedInt) - */ - UnsignedLong objectCount(SceneField name) const; // TODO better name, fieldInstanceCount? fieldElementCount? - /** * @brief Type used for object indexing * @m_since_latest * * Type returned from @ref fieldObject() and @ref mutableFieldObject(). - * For simplicity it's the same for all fields. + * It's the same for all fields and is guaranteed to be large enough to + * fit all @ref objectCount() objects. */ - SceneIndexType objectIndexType() const { return _indexType; } + SceneObjectType objectType() const { return _objectType; } /** * @brief Data for given field @@ -1168,7 +1173,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template::value>::type> Containers::StridedArrayView2D mutableField(SceneField name); /** - * @brief Object index data for given field + * @brief Object mapping data for given field * @m_since_latest * * The @p id is expected to be smaller than @ref fieldCount(). The @@ -1179,10 +1184,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref Corrade::Containers::StridedArrayView::isContiguous(), * @ref sceneIndexTypeSize() */ - Containers::StridedArrayView2D fieldObject(UnsignedInt id) const; // TODO renmae to fieldObjectMapping + Containers::StridedArrayView2D fieldObject(UnsignedInt id) const; /** - * @brief Mutable object index data for given field + * @brief Mutable object mapping data for given field * @m_since_latest * * Like @ref fieldObject(UnsignedInt), but returns a mutable view. @@ -1192,7 +1197,7 @@ class MAGNUM_TRADE_EXPORT SceneData { Containers::StridedArrayView2D mutableFieldObject(UnsignedInt id) const; /** - * @brief Object indices for given field + * @brief Object mapping for given field * @m_since_latest * * The @p id is expected to be smaller than @ref fieldCount() and @p T @@ -1202,7 +1207,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template Containers::StridedArrayView1D fieldObject(UnsignedInt id) const; /** - * @brief Mutable object indices for given field + * @brief Mutable object mapping for given field * @m_since_latest * * Like @ref fieldObject(UnsignedInt), but returns a mutable view. @@ -1212,7 +1217,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template Containers::StridedArrayView1D mutableFieldObject(UnsignedInt id) const; /** - * @brief Object index data for given named field + * @brief Object mapping data for given named field * @m_since_latest * * The @p name is expected to exist. The second dimension represents @@ -1226,7 +1231,7 @@ class MAGNUM_TRADE_EXPORT SceneData { Containers::StridedArrayView2D fieldObject(SceneField name) const; /** - * @brief Mutable object index data for given named field + * @brief Mutable object mapping data for given named field * @m_since_latest * * Like @ref fieldObject(SceneField), but returns a mutable view. @@ -1236,7 +1241,7 @@ class MAGNUM_TRADE_EXPORT SceneData { Containers::StridedArrayView2D mutableFieldObject(SceneField name) const; /** - * @brief Object indices for given named field + * @brief Object mapping for given named field * @m_since_latest * * The @p name is expected to exist and @p T is expected to correspond @@ -1247,7 +1252,7 @@ class MAGNUM_TRADE_EXPORT SceneData { template Containers::StridedArrayView1D fieldObject(SceneField name) const; /** - * @brief Mutable object indices for given named field + * @brief Mutable object mapping for given named field * @m_since_latest * * Like @ref fieldObject(SceneField), but returns a mutable view. @@ -1256,13 +1261,8 @@ class MAGNUM_TRADE_EXPORT SceneData { */ template Containers::StridedArrayView1D mutableFieldObject(SceneField name); - // TODO: iterable version? could paper over the differences in index type and the case when the ID is implicit (or... fuck that also?); offset is no more so that doesn't need to be handled - // TODO or iterable together with some field? hmmmm - - // TODO for all these if they are sparse or if an object is "inactive" what should this do? return less? have a separate bitflag? ughhhhh - /** - * @brief Object indices for given field as 32-bit integers + * @brief Object mapping for given field as 32-bit integers * @m_since_latest * * Convenience alternative to the templated @@ -1279,7 +1279,7 @@ class MAGNUM_TRADE_EXPORT SceneData { Containers::Array fieldObjectAsArray(UnsignedInt id) const; /** - * @brief Object indices for given field as 32-bit integers into a pre-allocated view + * @brief Object mapping for given field as 32-bit integers into a pre-allocated view * @m_since_latest * * Like @ref fieldObjectAsArray(UnsignedInt), but puts the result into @@ -1290,7 +1290,7 @@ class MAGNUM_TRADE_EXPORT SceneData { void fieldObjectInto(UnsignedInt id, Containers::StridedArrayView1D destination) const; /** - * @brief Object indices for given named field as 32-bit integers + * @brief Object mapping for given named field as 32-bit integers * @m_since_latest * * Convenience alternative to the templated @@ -1307,7 +1307,7 @@ class MAGNUM_TRADE_EXPORT SceneData { Containers::Array fieldObjectAsArray(SceneField name) const; /** - * @brief Object indices for given named field as 32-bit integers into a pre-allocated view + * @brief Object mapping for given named field as 32-bit integers into a pre-allocated view * @m_since_latest * * Like @ref fieldObjectAsArray(SceneField), but puts the result into @@ -1541,7 +1541,7 @@ class MAGNUM_TRADE_EXPORT SceneData { private: DataFlags _dataFlags; - SceneIndexType _indexType; + SceneObjectType _objectType; /* 2/6 bytes free */ UnsignedLong _objectCount; const void* _importerState; @@ -1551,20 +1551,6 @@ class MAGNUM_TRADE_EXPORT SceneData { namespace Implementation { - template constexpr SceneIndexType sceneIndexTypeFor() { - static_assert(sizeof(T) == 0, "unsupported index type"); - return {}; - } - #ifndef DOXYGEN_GENERATING_OUTPUT - #define _c(type) \ - template<> constexpr SceneIndexType sceneIndexTypeFor() { return SceneIndexType::type; } - _c(UnsignedByte) - _c(UnsignedShort) - _c(UnsignedInt) - _c(UnsignedLong) - #undef _c - #endif - template constexpr SceneFieldType sceneFieldTypeFor() { static_assert(sizeof(T) == 0, "unsupported field type"); return {}; @@ -1639,12 +1625,15 @@ namespace Implementation { _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) @@ -1663,8 +1652,112 @@ namespace Implementation { _c(Radd) #undef _c #endif + + 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::Animation || + 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 SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, SceneObjectType objectType, const Containers::StridedArrayView1D& objectData, UnsignedShort arraySize) noexcept: + _count{(CORRADE_CONSTEXPR_ASSERT(fieldData.size() == objectData.size(), + "Trade::SceneFieldData: expected field and object view to have the same size but got" << fieldData.size() << "and" << objectData.size()), fieldData.size())}, + _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), + "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, + _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()))}, + _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()))}, + _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isSceneFieldArrayAllowed(name), + "Trade::SceneFieldData:" << name << "can't be an array field"), arraySize)}, + _objectType{objectType}, + _isOffsetOnly{false}, + _fieldData{fieldData.data()}, + _objectData{objectData.data()} {} + +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D& fieldData, const Containers::StridedArrayView1D& objectData) noexcept: SceneFieldData{name, Implementation::sceneFieldTypeFor::type>(), fieldData, Implementation::sceneObjectTypeFor::type>(), objectData, 0} {} + +template constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D& fieldData, const Containers::StridedArrayView1D& objectData) noexcept: SceneFieldData{ + name, + Implementation::sceneFieldTypeFor::type>(), + Containers::StridedArrayView1D{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, + Implementation::sceneObjectTypeFor::type>(), + objectData, + /* Not using isContiguous<1>() as that's not constexpr */ + (CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(T), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])) +} {} + +constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t count, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const SceneObjectType objectType, const std::size_t objectOffset, const std::ptrdiff_t objectStride, const UnsignedShort arraySize) noexcept: + _count{count}, + _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), + "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, + _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))}, + _objectStride{(CORRADE_CONSTEXPR_ASSERT(objectStride >= -32768 && objectStride <= 32767, + "Trade::SceneFieldData: expected object view stride to fit into 16 bits, but got" << objectStride), Short(objectStride))}, + _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isSceneFieldArrayAllowed(name), + "Trade::SceneFieldData:" << name << "can't be an array field"), arraySize)}, + _objectType{objectType}, + _isOffsetOnly{true}, + _fieldData{fieldOffset}, + _objectData{objectOffset} {} + }} #endif diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index 6013933e70..c026c2b5cf 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -1480,8 +1480,8 @@ void AbstractImporterTest::scene() { return {}; } Containers::Optional doScene(UnsignedInt id) override { - if(id == 7) return SceneData{{}, {}, &state}; - return SceneData{{}, {}}; + if(id == 7) return SceneData{{}, {}, {}, {}, &state}; + return SceneData{{}, {}, {}, {}}; } } 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 afbb951c96..7d32ccfd96 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -23,9 +23,16 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include +#include #include "Magnum/Magnum.h" +#include "Magnum/Math/Half.h" +#include "Magnum/Math/Complex.h" +#include "Magnum/Math/DualQuaternion.h" +#include "Magnum/Math/Range.h" #include "Magnum/Trade/SceneData.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -38,34 +45,37 @@ struct SceneDataTest: TestSuite::Tester { void customFieldNameNotCustom(); void debugFieldName(); - template void fieldTypeSize(); + void fieldTypeSize(); + void fieldTypeSizeInvalid(); void debugFieldType(); - void debugObjectIndexType(); + + void objectTypeSize(); + void objectTypeSizeInvalid(); + void debugObjectType(); void constructField(); void constructFieldDefault(); void constructFieldCustom(); void constructField2D(); - void constructField2DWrongSize(); - void constructField2DNonContiguous(); void constructFieldTypeErased(); void constructFieldNonOwningArray(); void constructFieldOffsetOnly(); - void constructFieldInconsistentViewSize(); - void constructFieldWrongType(); - void constructFieldWrongStride(); - void constructFieldWrongDataAccess(); - void constructArrayField(); - void constructArrayFieldNonContiguous(); void constructArrayField2D(); - void constructArrayField2DWrongSize(); - void constructArrayField2DNonContiguous(); void constructArrayFieldTypeErased(); void constructArrayFieldOffsetOnly(); - void constructArrayFieldNotAllowed(); - // TODO where that sorted flag? + void constructFieldWrongType(); + void constructFieldInconsistentViewSize(); + void constructFieldTooLargeFieldStride(); + void constructFieldTooLargeObjectStride(); + void constructFieldWrongDataAccess(); + void constructField2DWrongSize(); + void constructField2DNonContiguous(); + void constructArrayFieldNonContiguous(); + void constructArrayFieldNotAllowed(); + void constructArrayField2DWrongSize(); + void constructArrayField2DNonContiguous(); void construct(); void constructZeroFields(); @@ -75,6 +85,7 @@ struct SceneDataTest: TestSuite::Tester { void constructDuplicateField(); void constructInconsistentIndexType(); + void constructNotContained(); void constructIndexTypeTooSmall(); void constructNotOwnedFlagOwned(); void constructMismatchedTRSViews(); @@ -126,18 +137,676 @@ struct SceneDataTest: TestSuite::Tester { }; SceneDataTest::SceneDataTest() { - addTests({&SceneDataTest::construct, - &SceneDataTest::constructCopy, - &SceneDataTest::constructMove}); + addTests({&SceneDataTest::customFieldName, + &SceneDataTest::customFieldNameTooLarge, + &SceneDataTest::customFieldNameNotCustom, + &SceneDataTest::debugFieldName, + + &SceneDataTest::fieldTypeSize, + &SceneDataTest::fieldTypeSizeInvalid, + &SceneDataTest::debugFieldType, + + &SceneDataTest::objectTypeSize, + &SceneDataTest::objectTypeSizeInvalid, + &SceneDataTest::debugObjectType, + + &SceneDataTest::constructField, + &SceneDataTest::constructFieldDefault, + &SceneDataTest::constructFieldCustom, + &SceneDataTest::constructField2D, + &SceneDataTest::constructFieldTypeErased, + &SceneDataTest::constructFieldNonOwningArray, + &SceneDataTest::constructFieldOffsetOnly, + &SceneDataTest::constructArrayField, + &SceneDataTest::constructArrayField2D, + &SceneDataTest::constructArrayFieldTypeErased, + &SceneDataTest::constructArrayFieldOffsetOnly, + + &SceneDataTest::constructFieldWrongType, + &SceneDataTest::constructFieldInconsistentViewSize, + &SceneDataTest::constructFieldTooLargeFieldStride, + &SceneDataTest::constructFieldTooLargeObjectStride, + &SceneDataTest::constructFieldWrongDataAccess, + &SceneDataTest::constructField2DWrongSize, + &SceneDataTest::constructField2DNonContiguous, + &SceneDataTest::constructArrayFieldNonContiguous, + &SceneDataTest::constructArrayFieldNotAllowed, + &SceneDataTest::constructArrayField2DWrongSize, + &SceneDataTest::constructArrayField2DNonContiguous, + }); +} + +using namespace Math::Literals; + +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"); +} + +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"); +} + +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 Complexd rotationData[3]; + const UnsignedShort rotationObjectData[3]{}; + + SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationData), Containers::arrayView(rotationObjectData)}; + CORRADE_VERIFY(!rotations.isOffsetOnly()); + CORRADE_COMPARE(rotations.name(), SceneField::Rotation); + CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); + CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(rotations.arraySize(), 0); + CORRADE_COMPARE(rotations.fieldData().size(), 3); + CORRADE_COMPARE(rotations.fieldData().stride(), sizeof(Complexd)); + CORRADE_VERIFY(rotations.fieldData().data() == rotationData); + CORRADE_COMPARE(rotations.objectData().size(), 3); + CORRADE_COMPARE(rotations.objectData().stride(), sizeof(UnsignedShort)); + CORRADE_VERIFY(rotations.objectData().data() == rotationObjectData); + + /* 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() == rotationData); + 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(Rotations2D), Containers::arrayView(RotationObjects2D)}; + constexpr bool isOffsetOnly = crotations.isOffsetOnly(); + constexpr SceneField name = crotations.name(); + constexpr SceneFieldType fieldType = crotations.fieldType(); + constexpr SceneObjectType objectType = crotations.objectType(); + constexpr UnsignedShort arraySize = crotations.arraySize(); + constexpr Containers::StridedArrayView1D fieldData = crotations.fieldData(); + constexpr Containers::StridedArrayView1D objectData = crotations.objectData(); + CORRADE_VERIFY(!isOffsetOnly); + CORRADE_COMPARE(name, SceneField::Rotation); + CORRADE_COMPARE(fieldType, SceneFieldType::Complexd); + CORRADE_COMPARE(objectType, SceneObjectType::UnsignedShort); + CORRADE_COMPARE(arraySize, 0); + CORRADE_COMPARE(fieldData.size(), 3); + CORRADE_COMPARE(fieldData.stride(), sizeof(Complexd)); + CORRADE_COMPARE(fieldData.data(), Rotations2D); + CORRADE_COMPARE(objectData.size(), 3); + CORRADE_COMPARE(objectData.stride(), sizeof(UnsignedShort)); + CORRADE_COMPARE(objectData.data(), RotationObjects2D); +} + +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 Range2Dh rangeData[3]; + const UnsignedByte rangeObjectData[3]{}; + SceneFieldData ranges{sceneFieldCustom(13), Containers::arrayView(rangeData), Containers::arrayView(rangeObjectData)}; + CORRADE_COMPARE(ranges.name(), sceneFieldCustom(13)); + CORRADE_COMPARE(ranges.fieldType(), SceneFieldType::Range2Dh); + CORRADE_COMPARE(ranges.objectType(), SceneObjectType::UnsignedByte); + CORRADE_VERIFY(ranges.fieldData().data() == rangeData); + CORRADE_VERIFY(ranges.objectData().data() == rangeObjectData); +} + +void SceneDataTest::constructField2D() { + char rotationData[6*sizeof(Complexd)]; + char rotationObjectData[6*sizeof(UnsignedShort)]; + auto rotationView = Containers::StridedArrayView2D{rotationData, {6, sizeof(Complexd)}}.every(2); + auto rotationObjectView = Containers::StridedArrayView2D{rotationObjectData, {6, sizeof(UnsignedShort)}}.every(2); + + SceneFieldData rotations{SceneField::Rotation, SceneFieldType::Complexd, rotationView, rotationObjectView}; + CORRADE_VERIFY(!rotations.isOffsetOnly()); + CORRADE_COMPARE(rotations.name(), SceneField::Rotation); + CORRADE_COMPARE(rotations.fieldType(), SceneFieldType::Complexd); + CORRADE_COMPARE(rotations.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(rotations.arraySize(), 0); + CORRADE_COMPARE(rotations.fieldData().size(), 3); + CORRADE_COMPARE(rotations.fieldData().stride(), 2*sizeof(Complexd)); + CORRADE_COMPARE(rotations.fieldData().data(), rotationView.data()); + CORRADE_COMPARE(rotations.objectData().size(), 3); + CORRADE_COMPARE(rotations.objectData().stride(), 2*sizeof(UnsignedShort)); + CORRADE_COMPARE(rotations.objectData().data(), rotationObjectView.data()); +} + +void SceneDataTest::constructFieldTypeErased() { + const Vector3 scalingData[3]; + const UnsignedLong scalingObjectData[3]{}; + SceneFieldData scalings{SceneField::Scaling, SceneFieldType::Vector3, Containers::arrayCast(Containers::stridedArrayView(scalingData)), SceneObjectType::UnsignedLong, Containers::arrayCast(Containers::stridedArrayView(scalingObjectData))}; + CORRADE_VERIFY(!scalings.isOffsetOnly()); + CORRADE_COMPARE(scalings.name(), SceneField::Scaling); + CORRADE_COMPARE(scalings.fieldType(), SceneFieldType::Vector3); + CORRADE_COMPARE(scalings.objectType(), SceneObjectType::UnsignedLong); + CORRADE_COMPARE(scalings.arraySize(), 0); + CORRADE_COMPARE(scalings.fieldData().size(), 3); + CORRADE_COMPARE(scalings.fieldData().stride(), sizeof(Vector3)); + CORRADE_COMPARE(scalings.fieldData().data(), scalingData); + CORRADE_COMPARE(scalings.objectData().size(), 3); + CORRADE_COMPARE(scalings.objectData().stride(), sizeof(UnsignedLong)); + CORRADE_COMPARE(scalings.objectData().data(), scalingObjectData); +} + +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[] { + {{}, 2, {2.0f, 3.0f}}, + {{}, 15, {67.0f, -1.1f}} + }; + + SceneFieldData a{SceneField::Translation, 2, SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data), SceneObjectType::UnsignedShort, offsetof(Data, object), sizeof(Data)}; + CORRADE_VERIFY(a.isOffsetOnly()); + CORRADE_COMPARE(a.name(), SceneField::Translation); + CORRADE_COMPARE(a.fieldType(), SceneFieldType::Vector2); + CORRADE_COMPARE(a.objectType(), SceneObjectType::UnsignedShort); + CORRADE_COMPARE(a.arraySize(), 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); + 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); +} + +constexpr Int ArrayOffsetData[3*4]{}; +constexpr UnsignedByte ArrayOffsetObjectData[3]{}; + +void SceneDataTest::constructArrayField() { + Int offsetData[3*4]; + UnsignedByte offsetObjectData[3]; + SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetData, {3, 4}}, Containers::arrayView(offsetObjectData)}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.arraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetData); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); + + constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::StridedArrayView2D{ArrayOffsetData, {3, 4}}, Containers::arrayView(ArrayOffsetObjectData)}; + CORRADE_VERIFY(!cdata.isOffsetOnly()); + CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(cdata.arraySize(), 4); + CORRADE_COMPARE(cdata.fieldData().size(), 3); + CORRADE_COMPARE(cdata.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(cdata.fieldData().data() == ArrayOffsetData); + CORRADE_COMPARE(cdata.objectData().size(), 3); + CORRADE_COMPARE(cdata.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(cdata.objectData().data() == ArrayOffsetObjectData); +} + +void SceneDataTest::constructArrayField2D() { + char offsetData[3*4*sizeof(Int)]; + char offsetObjectData[3*sizeof(UnsignedByte)]; + SceneFieldData data{sceneFieldCustom(34), SceneFieldType::Int, Containers::StridedArrayView2D{offsetData, {3, 4*sizeof(Int)}}, Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedByte)}}, 4}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.arraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetData); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); +} + +void SceneDataTest::constructArrayFieldTypeErased() { + Int offsetData[3*4]; + Containers::StridedArrayView1D offset{offsetData, 3, 4*sizeof(Int)}; + UnsignedByte offsetObjectData[3]; + SceneFieldData data{sceneFieldCustom(34), SceneFieldType::Int, Containers::arrayCast(offset), SceneObjectType::UnsignedByte, Containers::arrayCast(Containers::stridedArrayView(offsetObjectData)), 4}; + CORRADE_VERIFY(!data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.arraySize(), 4); + CORRADE_COMPARE(data.fieldData().size(), 3); + CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int)); + CORRADE_VERIFY(data.fieldData().data() == offsetData); + CORRADE_COMPARE(data.objectData().size(), 3); + CORRADE_COMPARE(data.objectData().stride(), sizeof(UnsignedByte)); + CORRADE_VERIFY(data.objectData().data() == offsetObjectData); +} + +void SceneDataTest::constructArrayFieldOffsetOnly() { + struct Data { + Byte parent; + UnsignedByte object; + Int offset[4]; + }; + + SceneFieldData data{sceneFieldCustom(34), 3, SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), 4}; + CORRADE_VERIFY(data.isOffsetOnly()); + CORRADE_COMPARE(data.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(data.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(data.arraySize(), 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, SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), SceneObjectType::UnsignedByte, offsetof(Data, object), sizeof(Data), 4}; + CORRADE_VERIFY(cdata.isOffsetOnly()); + CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34)); + CORRADE_COMPARE(cdata.fieldType(), SceneFieldType::Int); + CORRADE_COMPARE(cdata.objectType(), SceneObjectType::UnsignedByte); + CORRADE_COMPARE(cdata.arraySize(), 4); +} + +void SceneDataTest::constructFieldInconsistentViewSize() { + const Complexd rotationData[2]; + const UnsignedShort rotationObjectData[3]{}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationData), Containers::arrayView(rotationObjectData)}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: expected field and object view to have the same size but got 2 and 3\n"); +} + +void SceneDataTest::constructFieldWrongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const Quaternion rotationData[3]; + const UnsignedShort rotationObjectData[3]{}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Transformation, Containers::arrayView(rotationData), Containers::arrayView(rotationObjectData)}; + SceneFieldData{SceneField::Transformation, 3, SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort)}; + 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::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, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}, SceneObjectType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>(), SceneObjectType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 32767, SceneObjectType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 65536, -32768, SceneObjectType::UnsignedInt, 0, 4}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}, SceneObjectType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>(), SceneObjectType::UnsignedInt, enough}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 32768, SceneObjectType::UnsignedInt, 0, 4}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 65538, -32769, SceneObjectType::UnsignedInt, 0, 4}; + 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::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, SceneFieldType::UnsignedInt, enough, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, enough, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 4, SceneObjectType::UnsignedInt, 0, 32767}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 4, SceneObjectType::UnsignedInt, 65536, -32768}; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, enough, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; + SceneFieldData{SceneField::Mesh, SceneFieldType::UnsignedInt, enough, SceneObjectType::UnsignedInt, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>()}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 4, SceneObjectType::UnsignedInt, 0, 32768}; + SceneFieldData{SceneField::Mesh, 2, SceneFieldType::UnsignedInt, 0, 4, SceneObjectType::UnsignedInt, 65538, -32769}; + 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::constructFieldWrongDataAccess() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const Quaternion rotationData[3]; + const UnsignedShort rotationObjectData[3]{}; + SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationData), Containers::arrayView(rotationObjectData)}; + SceneFieldData b{SceneField::Rotation, 3, SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort)}; + CORRADE_VERIFY(!a.isOffsetOnly()); + CORRADE_VERIFY(b.isOffsetOnly()); + + a.fieldData(rotationData); /* This is fine, no asserts */ + a.objectData(rotationObjectData); + + std::ostringstream out; + Error redirectError{&out}; + b.fieldData(); + b.objectData(); + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array\n" + "Trade::SceneFieldData::objectData(): 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 rotationData[5*sizeof(Complex)]; + char rotationObjectData[5*sizeof(UnsignedInt)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Translation, SceneFieldType::Vector3, + Containers::StridedArrayView2D{rotationData, {4, sizeof(Complex)}}.every(2), + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2)}; + SceneFieldData{SceneField::Rotation, SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationData, {4, sizeof(Complex)}}.every(2), + Containers::StridedArrayView2D{rotationObjectData, {4, 5}}.every(2)}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second field view dimension size 8 doesn't match Trade::SceneFieldType::Vector3\n" + "Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got 5\n"); +} + +void SceneDataTest::constructField2DNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char rotationData[8*sizeof(Complex)]; + char rotationObjectData[8*sizeof(UnsignedInt)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationData, {4, 2*sizeof(Complex)}}.every({1, 2}), + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}}; + SceneFieldData{SceneField::Rotation, SceneFieldType::Complex, + Containers::StridedArrayView2D{rotationData, {4, sizeof(Complex)}}, + Containers::StridedArrayView2D{rotationObjectData, {4, 2*sizeof(UnsignedInt)}}.every({1, 2})}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second field view dimension is not contiguous\n" + "Trade::SceneFieldData: second object view dimension is not contiguous\n"); +} + +void SceneDataTest::constructArrayFieldNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Int offsetData[3*4]; + UnsignedByte offsetObjectData[3]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D{offsetData, {3, 4}}.every({1, 2}), Containers::arrayView(offsetObjectData)}; + CORRADE_COMPARE(out.str(), "Trade::SceneFieldData: second field view dimension is not contiguous\n"); +} + +void SceneDataTest::constructArrayFieldNotAllowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Quaternion rotationData[3]; + UnsignedShort rotationObjectData[3]{}; + Containers::ArrayView rotations = rotationData; + Containers::ArrayView rotationObjects = rotationObjectData; + Containers::StridedArrayView2D rotations2D{rotationData, {3, 3}, {0, sizeof(Quaternion)}}; + auto rotations2DChar = Containers::arrayCast<2, const char>(rotations2D); + auto rotationObjectsChar = Containers::arrayCast<2, const char>(rotationObjects); + + /* This is all fine */ + SceneFieldData{SceneField::Rotation, + SceneFieldType::Quaternion, rotations, + SceneObjectType::UnsignedShort, rotationObjects, 0}; + SceneFieldData{SceneField::Rotation, 3, + SceneFieldType::Quaternion, 0, sizeof(Quaternion), + SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), 0}; + SceneFieldData{sceneFieldCustom(37), + rotations2D, + rotationObjects}; + SceneFieldData{sceneFieldCustom(37), + SceneFieldType::Quaternion, rotations2DChar, + rotationObjectsChar, 3}; + SceneFieldData{sceneFieldCustom(37), 3, + SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), 3}; + + /* This is not */ + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{SceneField::Rotation, + SceneFieldType::Quaternion, rotations, + SceneObjectType::UnsignedShort, rotationObjects, 3}; + SceneFieldData{SceneField::Rotation, 3, + SceneFieldType::Quaternion, 0, sizeof(Quaternion), + SceneObjectType::UnsignedShort, 0, sizeof(UnsignedShort), 3}; + SceneFieldData{SceneField::Rotation, + rotations2D, + rotationObjects}; + SceneFieldData{SceneField::Rotation, + SceneFieldType::Quaternion, rotations2DChar, + rotationObjectsChar, 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::constructArrayField2DWrongSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char rotationData[4*sizeof(Complex)]; + char rotationObjectData[4*sizeof(UnsignedInt)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{sceneFieldCustom(37), SceneFieldType::Int, + Containers::StridedArrayView2D{rotationData, {4, sizeof(Complex)}}.every(2), + Containers::StridedArrayView2D{rotationObjectData, {4, sizeof(UnsignedInt)}}.every(2), 3}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second field view dimension size 8 doesn't match Trade::SceneFieldType::Int and array size 3\n"); +} + +void SceneDataTest::constructArrayField2DNonContiguous() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + char offsetData[18*sizeof(Int)]; + char offsetObjectData[18*sizeof(UnsignedInt)]; + + std::ostringstream out; + Error redirectError{&out}; + SceneFieldData{sceneFieldCustom(37), SceneFieldType::Int, + Containers::StridedArrayView2D{offsetData, {3, 6*sizeof(Int)}}.every({1, 2}), + Containers::StridedArrayView2D{offsetObjectData, {3, sizeof(UnsignedInt)}}, 3}; + SceneFieldData{sceneFieldCustom(37), SceneFieldType::Int, + Containers::StridedArrayView2D{offsetData, {3, 3*sizeof(Int)}}, + Containers::StridedArrayView2D{offsetObjectData, {3, 2*sizeof(UnsignedInt)}}.every({1, 2}), 3}; + CORRADE_COMPARE(out.str(), + "Trade::SceneFieldData: second field view dimension is not contiguous\n" + "Trade::SceneFieldData: second object view dimension is not contiguous\n"); } void SceneDataTest::construct() { - const int a{}; - const SceneData data{{0, 1, 4}, {2, 5}, &a}; - CORRADE_COMPARE(data.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(data.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(data.importerState(), &a); } void SceneDataTest::constructCopy() { @@ -146,25 +815,7 @@ void SceneDataTest::constructCopy() { } void SceneDataTest::constructMove() { - const int a{}; - SceneData data{{0, 1, 4}, {2, 5}, &a}; - - SceneData b{std::move(data)}; - - CORRADE_COMPARE(b.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(b.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(b.importerState(), &a); - - const int c{}; - SceneData d{{1, 3}, {1, 4, 5}, &c}; - d = std::move(b); - - CORRADE_COMPARE(d.children2D(), (std::vector{0, 1, 4})); - CORRADE_COMPARE(d.children3D(), (std::vector{2, 5})); - CORRADE_COMPARE(d.importerState(), &a); - CORRADE_VERIFY(std::is_nothrow_move_constructible::value); - CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } }}}}