#ifndef Magnum_Trade_SceneData_h #define Magnum_Trade_SceneData_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. */ /** @file * @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 #include #include "Magnum/Trade/Data.h" #include "Magnum/Trade/Trade.h" #include "Magnum/Trade/visibility.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include #endif namespace Magnum { namespace Trade { /** @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 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 SceneMappingType: UnsignedByte { /* Zero used for an invalid value */ UnsignedByte = 1, /**< @relativeref{Magnum,UnsignedByte} */ UnsignedShort, /**< @relativeref{Magnum,UnsignedShort} */ UnsignedInt, /**< @relativeref{Magnum,UnsignedInt} */ UnsignedLong /**< @relativeref{Magnum,UnsignedLong} */ }; /** @debugoperatorenum{SceneMappingType} @m_since_latest */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneMappingType value); /** @brief Size of given scene object mapping type @m_since_latest @see @ref sceneMappingTypeAlignment() */ MAGNUM_TRADE_EXPORT UnsignedInt sceneMappingTypeSize(SceneMappingType type); /** @brief Alignment of given scene object mapping type @m_since_latest Returns the same value as @ref sceneMappingTypeSize(). */ MAGNUM_TRADE_EXPORT UnsignedInt sceneMappingTypeAlignment(SceneMappingType type); /** @brief Scene field name @m_since_latest See @ref SceneData for more information. @see @ref SceneFieldData, @ref SceneFieldType, @ref AbstractImporter::sceneFieldForName(), @ref AbstractImporter::sceneFieldName() */ enum class SceneField: UnsignedInt { /* Zero used for an invalid value */ /** * 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. * @see @ref SceneData::parentsAsArray(), @ref SceneData::parentFor(), * @ref SceneData::childrenFor() */ 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,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, * 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. 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::is2D(), @ref SceneData::is3D(), * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor() */ 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 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::is2D(), @ref SceneData::is3D(), * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), * @ref SceneData::translationsRotationsScalings3DAsArray(), * @ref SceneData::translationRotationScaling2DFor(), * @ref SceneData::translationRotationScaling3DFor() */ 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 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::is2D(), @ref SceneData::is3D(), * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), * @ref SceneData::translationsRotationsScalings3DAsArray(), * @ref SceneData::translationRotationScaling2DFor(), * @ref SceneData::translationRotationScaling3DFor() */ 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 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::is2D(), @ref SceneData::is3D(), * @ref SceneData::transformations2DAsArray(), * @ref SceneData::transformations3DAsArray(), * @ref SceneData::transformation2DFor(), * @ref SceneData::transformation3DFor(), * @ref SceneData::translationsRotationsScalings2DAsArray(), * @ref SceneData::translationsRotationsScalings3DAsArray(), * @ref SceneData::translationRotationScaling2DFor(), * @ref SceneData::translationRotationScaling3DFor() */ 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::meshesMaterialsAsArray(), * @ref SceneData::meshesMaterialsFor() */ Mesh, /** * ID of a material for a @ref SceneField::Mesh, corresponding to the ID * 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::meshesMaterialsAsArray(), * @ref SceneData::meshesMaterialsFor() */ 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(), @ref SceneData::lightsFor() */ 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(), @ref SceneData::camerasFor() */ Camera, /** * ID of a skin associated with this object, corresponding to the ID * 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::is2D(), @ref SceneData::is3D(), * @ref SceneData::skinsAsArray(), @ref SceneData::skinsFor() */ 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. * * 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(), @ref sceneFieldTypeAlignment() */ 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} */ /** * @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, }; /** @debugoperatorenum{SceneFieldType} @m_since_latest */ 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 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 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 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: /** * @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{}, _flags{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {} /** * @brief Type-erased constructor * @param name Field name * @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. * @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, 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 * @param name Field name * @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. * @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 * contiguous and its size is either 1, 2, 4 or 8, corresponding to one * 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& 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, 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, SceneFieldFlags flags = {}) noexcept; /** @overload */ 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, 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, 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, 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&, SceneFieldFlags) * for details about @ref SceneMappingType and @ref SceneFieldType * detection. */ 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, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {} /** * @brief Construct an offset-only field * @param name Field name * @param size Number of entries * @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 * @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. * 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 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, SceneFieldFlags) * constructor if you want additional safeguards. * @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, SceneFieldFlags flags = {}) noexcept; /** @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; } /** @brief Number of entries */ constexpr UnsignedLong size() const { return _size; } /** @brief Object mapping type */ constexpr SceneMappingType mappingType() const { return _mappingType; } /** * @brief Type-erased object mapping data * * 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(!(_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 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, _flags & SceneFieldFlag::OffsetOnly ? reinterpret_cast(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride}; } /** @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 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(!(_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 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, _flags & SceneFieldFlag::OffsetOnly ? 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; SceneFieldFlags _flags; SceneMappingType _mappingType; Short _mappingStride; Data _mappingData; 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 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 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 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() */ class MAGNUM_TRADE_EXPORT SceneData { public: /** * @brief Construct scene data * @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 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 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(SceneMappingType, UnsignedLong, DataFlags, Containers::ArrayView, Containers::Array&&, const void*) * constructor or its variants instead. */ 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(SceneMappingType mappingType, UnsignedLong mappingBound, Containers::Array&& data, std::initializer_list fields, const void* importerState = nullptr); /** * @brief Construct non-owned scene data * @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 object mappings * @param fields Description of all scene field data * @param importerState Importer-specific state * @m_since_latest * * 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(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(SceneMappingType mappingType, UnsignedLong mappingBound, 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(SceneMappingType, UnsignedLong, Containers::Array&&, Containers::Array&&, const void*) * instead. */ 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 */ SceneData(const SceneData&) = delete; /** @brief Move constructor */ SceneData(SceneData&&) noexcept; ~SceneData(); /** @brief Copying is not allowed */ SceneData& operator=(const SceneData&) = delete; /** @brief Move assignment */ SceneData& operator=(SceneData&&) noexcept; /** * @brief Data flags * @m_since_latest * * @see @ref releaseData(), @ref mutableData(), @ref mutableMapping(), * @ref mutableField() */ 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 mapping() and @ref mutableMapping(). It's * the same for all fields and is guaranteed to be large enough to fit * @ref mappingBound() objects. */ SceneMappingType mappingType() const { return _mappingType; } /** * @brief Object mapping bound * @m_since_latest * * Upper bound on object mapping indices of all fields in the scene. * @see @ref fieldCount(), @ref fieldSize() */ UnsignedLong mappingBound() const { return _mappingBound; } /** * @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). */ 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 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 * 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; } /** * @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 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. * * Unlike with @ref fieldData() and @ref releaseFieldData(), returned * instances are guaranteed to always have an absolute data pointer * (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; /** * @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 flags * @m_since_latest * * The @p id is expected to be smaller than @ref fieldCount(). * @see @ref findFieldObjectOffset(UnsignedInt, UnsignedLong, std::size_t) const */ SceneFieldFlags fieldFlags(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 mappingType() */ SceneFieldType fieldType(UnsignedInt id) const; /** * @brief Size of given field * @m_since_latest * * 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. * * 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 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 Find an absolute ID of a named field * @m_since_latest * * 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() */ Containers::Optional findFieldId(SceneField name) const; /** * @brief Absolute ID of a named field * @m_since_latest * * 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 mappingBound() and @p offset not larger than * @ref fieldSize(UnsignedInt) const. * * 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, UnsignedLong, std::size_t) const * to directly find offset of an object in given named field. * @see @ref hasFieldObject(UnsignedInt, UnsignedLong) const, * @ref fieldObjectOffset(UnsignedInt, UnsignedLong, std::size_t) const */ Containers::Optional findFieldObjectOffset(UnsignedInt fieldId, UnsignedLong 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 mappingBound() * and @p offset not be larger than @ref fieldSize(SceneField) const. * * 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, UnsignedLong) const, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) const */ Containers::Optional findFieldObjectOffset(SceneField fieldName, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Offset of an object in given field * @m_since_latest * * Like @ref findFieldObjectOffset(UnsignedInt, UnsignedLong, 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, UnsignedLong, std::size_t) const * to directly get offset of an object in given named field. */ std::size_t fieldObjectOffset(UnsignedInt fieldId, UnsignedLong object, std::size_t offset = 0) const; /** * @brief Offset of an object in given named field * @m_since_latest * * Like @ref findFieldObjectOffset(SceneField, UnsignedLong, 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, UnsignedLong 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 mappingBound(). */ bool hasFieldObject(UnsignedInt fieldId, UnsignedLong 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 mappingBound(). * @see @ref hasField() */ bool hasFieldObject(SceneField fieldName, UnsignedLong object) const; /** * @brief Field flags * @m_since_latest * * The @p name is expected to exist. * @see @ref findFieldObjectOffset(SceneField, UnsignedLong, std::size_t) const */ SceneFieldFlags fieldFlags(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 SceneMappingType size) and is guaranteed to be contiguous. * Use the templated overload below to get the mapping in a concrete * type. * @see @ref mutableMapping(UnsignedInt), * @ref Corrade::Containers::StridedArrayView::isContiguous(), * @ref sceneMappingTypeSize() */ Containers::StridedArrayView2D mapping(UnsignedInt fieldId) const; /** * @brief Mutable object mapping data for given field * @m_since_latest * * Like @ref mapping(UnsignedInt) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ 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 mappingType(). * * You can also use the non-templated @ref mappingAsArray() 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 mutableMapping(UnsignedInt) */ template Containers::StridedArrayView1D mapping(UnsignedInt fieldId) const; /** * @brief Mutable object mapping for given field * @m_since_latest * * Like @ref mapping(UnsignedInt) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ template Containers::StridedArrayView1D mutableMapping(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 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 mapping(SceneField fieldName) const; /** * @brief Mutable object mapping data for given named field * @m_since_latest * * Like @ref mapping(SceneField) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ 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 mappingType(). * * You can also use the non-templated @ref mappingAsArray() 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 mapping(UnsignedInt) const, * @ref mutableMapping(UnsignedInt) */ template Containers::StridedArrayView1D mapping(SceneField fieldName) const; /** * @brief Mutable object mapping for given named field * @m_since_latest * * Like @ref mapping(SceneField) const, but returns a mutable view. * Expects that the scene is mutable. * @see @ref dataFlags() */ template Containers::StridedArrayView1D mutableMapping(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 translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), * @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() */ 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 translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), * @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; /** * @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 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(). * * 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 mappingInto(UnsignedInt, const Containers::StridedArrayView1D&) 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 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. * * 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 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 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 * 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, UnsignedLong, std::size_t) 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 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. * * 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 mappingInto(SceneField, const Containers::StridedArrayView1D&) const, * @ref hasField() */ 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 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. * * 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 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 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 * 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, UnsignedLong, std::size_t) 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 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 * array. The field is expected to exist. * @see @ref parentsInto(), @ref hasField(), @ref parentFor(), * @ref childrenFor() */ 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 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 mappingInto() with @ref SceneField::Parent. * @see @ref fieldSize(SceneField) 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 * @m_since_latest * * 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 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, UnsignedLong, std::size_t) 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 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 * @ref SceneField::Translation, @ref SceneField::Rotation and * @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; /** * @brief 2D transformations as 3x3 float matrices into a pre-allocated view * @m_since_latest * * Like @ref transformations2DAsArray(), but puts the result into * @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 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& mappingDestination, 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 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, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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. * 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} * 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 is2D(), @ref translationsRotationsScalings2DInto(), * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling2DFor() */ 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 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 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& 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 * @m_since_latest * * 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 * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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 * @ref SceneField::Translation, @ref SceneField::Rotation and * @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; /** * @brief 3D transformations as 4x4 float matrices into a pre-allocated view * @m_since_latest * * Like @ref transformations3DAsArray(), but puts the result into * @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 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& mappingDestination, 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 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, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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. * 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} * 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 is3D(), @ref translationsRotationsScalings3DInto(), * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling3DFor() */ 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 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 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& 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 * @m_since_latest * * 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 * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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 * 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; /** * @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 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 mappingInto() with @ref SceneField::Mesh. * @see @ref fieldSize(SceneField) 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 * @m_since_latest * * 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 * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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 * field is expected to exist. * @see @ref lightsInto(), @ref hasField(), @ref lightsFor() */ 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 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& mappingDestination, 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 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, * @ref fieldObjectOffset(SceneField, UnsignedLong, std::size_t) 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 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 * array. The field is expected to exist. * @see @ref camerasInto(), @ref hasField(), @ref camerasFor() */ 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 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 mappingInto() with @ref SceneField::Camera. * @see @ref fieldSize(SceneField) 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 * @m_since_latest * * 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 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, UnsignedLong, std::size_t) 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 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 * field is expected to exist. * @see @ref skinsInto(), @ref hasField(), @ref skinsFor() */ 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 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 mappingInto() with @ref SceneField::Skin. * @see @ref fieldSize(SceneField) 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 * @m_since_latest * * 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 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, UnsignedLong, std::size_t) 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 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 * 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 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 mappingInto() with @ref SceneField::ImporterState. * @see @ref fieldSize(SceneField) 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 * @m_since_latest * * 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 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, UnsignedLong, std::size_t) const */ std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& mappingDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Parent for given object * @m_since_latest * * Looks up the @ref SceneField::Parent field for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * is top-level, returns @cpp -1 @ce. * * The @p object is expected to be less than @ref mappingBound(). * @see @ref childrenFor() */ Containers::Optional parentFor(UnsignedLong object) const; /** * @brief Children for given object * @m_since_latest * * Looks up @p object in the object mapping array for * @ref SceneField::Parent equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * 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 mappingBound(). * @see @ref parentFor() */ Containers::Array childrenFor(Long object) const; /** * @brief 2D transformation for given object * @m_since_latest * * Looks up the @ref SceneField::Transformation field for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, std::size_t) const * or combines it from a @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and * @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 * @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 mappingBound(). * @see @ref translationRotationScaling2DFor() */ Containers::Optional transformation2DFor(UnsignedLong 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 equivalently * to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * @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 mappingBound(). * @see @ref transformation2DFor() */ Containers::Optional> translationRotationScaling2DFor(UnsignedLong object) const; /** * @brief 3D transformation for given object * @m_since_latest * * Looks up the @ref SceneField::Transformation field for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, std::size_t) const * or combines it from a @ref SceneField::Translation, * @relativeref{SceneField,Rotation} and * @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 * @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 mappingBound(). * @see @ref translationRotationScaling3DFor() */ Containers::Optional transformation3DFor(UnsignedLong 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 equivalently * to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * @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 mappingBound(). * @see @ref transformation3DFor() */ Containers::Optional> translationRotationScaling3DFor(UnsignedLong 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 * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * @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 mappingBound(). */ Containers::Array> meshesMaterialsFor(UnsignedLong object) const; /** * @brief Lights for given object * @m_since_latest * * Looks up all @ref SceneField::Light fields for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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. * * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array lightsFor(UnsignedLong object) const; /** * @brief Cameras for given object * @m_since_latest * * Looks up all @ref SceneField::Camera fields for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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. * * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array camerasFor(UnsignedLong object) const; /** * @brief Skins for given object * @m_since_latest * * Looks up all @ref SceneField::Skin fields for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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. * * The @p object is expected to be less than @ref mappingBound(). */ Containers::Array skinsFor(UnsignedLong object) const; /** * @brief Importer state for given object * @m_since_latest * * Looks up the @ref SceneField::ImporterState field for @p object * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedLong, 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 * @ref Containers::NullOpt. * * The @p object is expected to be less than @ref mappingBound(). */ Containers::Optional importerStateFor(UnsignedLong 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 * * 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 * 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(); /** * @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 * * 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; } 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 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{Mapping,Field}ViewInternal(). */ 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 offset = 0 and size = field.size(). */ 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; #ifndef CORRADE_NO_ASSERT template bool checkFieldTypeCompatibility(const SceneFieldData& attribute, const char* prefix) const; #endif 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 findTransformationFields(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 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; SceneMappingType _mappingType; UnsignedByte _dimensions; /* 1/5 bytes free */ UnsignedLong _mappingBound; 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 struct SceneFieldTypeFor { constexpr static SceneFieldType type() { return SceneFieldType::Pointer; } }; template struct SceneFieldTypeFor { constexpr static SceneFieldType type() { return SceneFieldType::MutablePointer; } }; template constexpr SceneMappingType sceneMappingTypeFor() { static_assert(sizeof(T) == 0, "unsupported mapping type"); return {}; } #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type) \ template<> constexpr SceneMappingType sceneMappingTypeFor() { return SceneMappingType::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::Matrix3x2 || type == SceneFieldType::Matrix3x2d || type == SceneFieldType::Matrix4x3 || type == SceneFieldType::Matrix4x3d || 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::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)) || (name == SceneField::ImporterState && (type == SceneFieldType::Pointer || type == SceneFieldType::MutablePointer)) || /* Custom fields can be anything */ isSceneFieldCustom(name); } constexpr bool isSceneFieldArrayAllowed(SceneField name) { return isSceneFieldCustom(name); } } 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" << name << "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)}, _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()))}, _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()))}, _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& 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, 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])), 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, 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)}, _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))}, _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))}, _fieldArraySize{(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name), "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, _fieldData{fieldOffset} {} 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::sceneMappingTypeFor() == _mappingType, "Trade::SceneData::mapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, const T>(data); } 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::sceneMappingTypeFor() == _mappingType, "Trade::SceneData::mutableMapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, T>(data); } 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::sceneMappingTypeFor() == _mappingType, "Trade::SceneData::mapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); return Containers::arrayCast<1, const T>(data); } 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::sceneMappingTypeFor() == _mappingType, "Trade::SceneData::mutableMapping(): mapping is" << _mappingType << "but requested" << Implementation::sceneMappingTypeFor(), {}); 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[findFieldIdInternal(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[findFieldIdInternal(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[findFieldIdInternal(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[findFieldIdInternal(name)], "Trade::SceneData::mutableField():")) return {}; #endif return Containers::arrayCast<2, typename std::remove_extent::type>(data); } }} #endif