Browse Source

Trade: flags for annotating SceneData field object mappings.

Currently used by the per-object access APIs to make the lookup
constant- or logarithmic-time instead of linear, available for use by
external data consumers as well.
pull/525/head
Vladimír Vondruš 4 years ago
parent
commit
3f38ab1952
  1. 2
      src/Magnum/Trade/Implementation/sceneTools.h
  2. 87
      src/Magnum/Trade/SceneData.cpp
  3. 274
      src/Magnum/Trade/SceneData.h
  4. 262
      src/Magnum/Trade/Test/SceneDataTest.cpp
  5. 2
      src/Magnum/Trade/Trade.h

2
src/Magnum/Trade/Implementation/sceneTools.h

@ -109,7 +109,7 @@ inline SceneData sceneCombine(const SceneMappingType mappingType, const Unsigned
for(std::size_t i = 0; i != fields.size(); ++i) {
const SceneFieldData& field = fields[i];
CORRADE_INTERNAL_ASSERT(!field.isOffsetOnly());
CORRADE_INTERNAL_ASSERT(!(field.flags() & SceneFieldFlag::OffsetOnly));
/* Mapping data. Allocate if the view is a placeholder of if it wasn't
used by other fields yet. */

87
src/Magnum/Trade/SceneData.cpp

@ -25,9 +25,12 @@
#include "SceneData.h"
#include <algorithm>
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayViewStl.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Algorithms.h>
@ -473,7 +476,32 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeAlignment(): invalid type" << type, {});
}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} {
Debug& operator<<(Debug& debug, const SceneFieldFlag value) {
debug << "Trade::SceneFieldFlag" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(value) case SceneFieldFlag::value: return debug << "::" #value;
_c(OffsetOnly)
_c(ImplicitMapping)
_c(OrderedMapping)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, const SceneFieldFlags value) {
return Containers::enumSetDebugOutput(debug, value, "Trade::SceneFieldFlags{}", {
SceneFieldFlag::OffsetOnly,
SceneFieldFlag::ImplicitMapping,
/* This one is implied by ImplicitMapping, so has to be after */
SceneFieldFlag::OrderedMapping
});
}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize, flags} {
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
@ -565,7 +593,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
if(field._size) {
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)*
(field._fieldArraySize ? field._fieldArraySize : 1);
if(field._isOffsetOnly) {
if(field._flags & SceneFieldFlag::OffsetOnly) {
const std::size_t mappingSize = field._mappingData.offset + (field._size - 1)*field._mappingStride + mappingTypeSize;
const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize;
CORRADE_ASSERT(mappingSize <= _data.size(),
@ -780,7 +808,7 @@ Containers::StridedArrayView1D<const void> SceneData::fieldDataMappingViewIntern
CORRADE_INTERNAL_ASSERT(offset + size <= field._size);
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
{static_cast<const char*>(field._isOffsetOnly ?
{static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ?
_data.data() + field._mappingData.offset : field._mappingData.pointer)
+ field._mappingStride*offset, ~std::size_t{}},
size, field._mappingStride
@ -795,7 +823,7 @@ Containers::StridedArrayView1D<const void> SceneData::fieldDataFieldViewInternal
CORRADE_INTERNAL_ASSERT(offset + size <= field._size);
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
{static_cast<const char*>(field._isOffsetOnly ?
{static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ?
_data.data() + field._fieldData.offset : field._fieldData.pointer)
+ field._fieldStride*offset, ~std::size_t{}},
size, field._fieldStride};
@ -809,7 +837,7 @@ SceneFieldData SceneData::fieldData(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldData(): index" << id << "out of range for" << _fields.size() << "fields", SceneFieldData{});
const SceneFieldData& field = _fields[id];
return SceneFieldData{field._name, field._mappingType, fieldDataMappingViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize};
return SceneFieldData{field._name, field._mappingType, fieldDataMappingViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize, field._flags & ~SceneFieldFlag::OffsetOnly};
}
SceneField SceneData::fieldName(const UnsignedInt id) const {
@ -818,6 +846,12 @@ SceneField SceneData::fieldName(const UnsignedInt id) const {
return _fields[id]._name;
}
SceneFieldFlags SceneData::fieldFlags(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldFlags(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._flags;
}
SceneFieldType SceneData::fieldType(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldType(): index" << id << "out of range for" << _fields.size() << "fields", {});
@ -859,11 +893,32 @@ bool SceneData::hasField(const SceneField name) const {
namespace {
template<class T> std::size_t findObject(const Containers::StridedArrayView1D<const void>& mapping, const UnsignedInt object) {
/* The `objects` view is already adjusted for `offset`, the offset is needed
only to return the correct value for ImplicitMapping */
template<class T> std::size_t findObject(const SceneFieldFlags flags, const Containers::StridedArrayView1D<const void>& mapping, const std::size_t offset, const UnsignedInt object) {
const std::size_t max = mapping.size();
/* Implicit mapping, position equals object ID (if in bounds) and thus an
O(1)-complexity search. A superset of OrderedMapping so has to be
before. */
if(flags >= SceneFieldFlag::ImplicitMapping)
return object >= offset && object - offset < mapping.size() ?
object - offset : max;
const Containers::StridedArrayView1D<const T> mappingT = Containers::arrayCast<const T>(mapping);
const std::size_t max = mappingT.size();
/** @todo implement something faster than O(n) when field-specific flags
can annotate how the object mapping is done */
/* Ordered mapping, so an O(log n)-complexity search. It also has to be
noted that STL algorithms generally suck (this needs a
std::iterator_traits specialization for the StridedArrayView, FFS!),
std::binary_search() is useless because it returns just a bool (!!) and
std::lower_bound() is error prone beyond any reason. */
if(flags >= SceneFieldFlag::OrderedMapping) {
const Containers::StridedIterator<1, const T> found = std::lower_bound(mappingT.begin(), mappingT.end(), T(object));
if(found == mappingT.end() || *found != object) return max;
return found - mappingT.begin();
}
/* Generally unordered container, O(n)-complexity search. */
for(std::size_t i = 0; i != max; ++i)
if(mappingT[i] == object) return i;
return max;
@ -874,13 +929,13 @@ template<class T> std::size_t findObject(const Containers::StridedArrayView1D<co
std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedInt object, const std::size_t offset) const {
const Containers::StridedArrayView1D<const void> mapping = fieldDataMappingViewInternal(field, offset, field._size - offset);
if(field._mappingType == SceneMappingType::UnsignedInt)
return offset + findObject<UnsignedInt>(mapping, object);
return offset + findObject<UnsignedInt>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedShort)
return offset + findObject<UnsignedShort>(mapping, object);
return offset + findObject<UnsignedShort>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedByte)
return offset + findObject<UnsignedByte>(mapping, object);
return offset + findObject<UnsignedByte>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedLong)
return offset + findObject<UnsignedLong>(mapping, object);
return offset + findObject<UnsignedLong>(field._flags, mapping, offset, object);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -970,6 +1025,12 @@ bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt obj
return findFieldObjectOffsetInternal(field, object, 0) != field._size;
}
SceneFieldFlags SceneData::fieldFlags(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldFlags(): field" << name << "not found", {});
return _fields[fieldId]._flags;
}
SceneFieldType SceneData::fieldType(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {});

274
src/Magnum/Trade/SceneData.h

@ -26,7 +26,7 @@
*/
/** @file
* @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneMappingType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, function @ref Magnum::sceneMappingTypeSize(), @ref Magnum::sceneMappingTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom()
* @brief Class @ref Magnum::Trade::SceneData, @ref Magnum::Trade::SceneFieldData, enum @ref Magnum::Trade::SceneMappingType, @ref Magnum::Trade::SceneField, @ref Magnum::Trade::SceneFieldType, @ref Magnum::Trade::SceneFieldFlag, enum set @ref Magnum::Trade::SceneFieldFlags, function @ref Magnum::sceneMappingTypeSize(), @ref Magnum::sceneMappingTypeAlignment(), @ref Magnum::sceneFieldTypeSize(), @ref Magnum::sceneFieldTypeAlignment(), @ref Magnum::Trade::isSceneFieldCustom(), @ref Magnum::sceneFieldCustom()
*/
#include <Corrade/Containers/Array.h>
@ -527,6 +527,80 @@ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type);
*/
MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type);
/**
@brief Scene field flag
@m_since_latest
@see @ref SceneFieldFlags, @ref SceneFieldData, @ref SceneFieldData::flags(),
@ref SceneData::fieldFlags()
*/
enum class SceneFieldFlag: UnsignedByte {
/**
* The field is offset-only, i.e. doesn't contain the data views directly
* but referes to unspecified external data. Set implicitly by
* the @ref SceneFieldData::SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort, SceneFieldFlags)
* constructor, can't be used for any other constructor.
* @see @ref SceneFieldData::mappingData(Containers::ArrayView<const void>) const,
* @ref SceneFieldData::fieldData(Containers::ArrayView<const void>) 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<SceneFieldFlag> SceneFieldFlags;
CORRADE_ENUMSET_OPERATORS(SceneFieldFlags)
/**
@debugoperatorenum{SceneFieldFlags}
@m_since_latest
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldFlags value);
/**
@brief Scene field data
@m_since_latest
@ -555,6 +629,25 @@ the data layout is static (and thus can be defined at compile time), but the
actual data is allocated / populated at runtime:
@snippet MagnumTrade.cpp SceneFieldData-usage-offset-only
Offset-only fields are marked with @ref SceneFieldFlag::OffsetOnly in
@ref flags().
@subsection Trade-SceneFieldData-usage-object-mapping Ordered and implicit object mapping
If you can guarantee the object mapping field is monotonically non-decreasing,
it's recommended to annotate it with @ref SceneFieldFlag::OrderedMapping. This
makes certain convenience APIs such as @ref SceneData::findFieldObjectOffset()
or e.g. @relativeref{SceneData,transformation3DFor()} perform the lookup in
@f$ \mathcal{O}(\log{} n) @f$ instead of @f$ \mathcal{O}(n) @f$. Data consuming
algorithms on the application side can then also adapt based on what flags are
present in @ref SceneData::fieldFlags().
In some cases the object mapping is even implicit, i.e. the first entry of the
field specifying data for object @cpp 0 @ce, second entry for object
@cpp 1 @ce, third for object @cpp 2 @ce and so on. You can annotate such fields
with @ref SceneFieldFlag::ImplicitMapping, which is a superset of
@relativeref{SceneFieldFlag,OrderedMapping}.
*/
class MAGNUM_TRADE_EXPORT SceneFieldData {
public:
@ -565,7 +658,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* initialization of the field array for @ref SceneData, expected to be
* replaced with concrete values later.
*/
constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _isOffsetOnly{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {}
constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _flags{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldType{}, _fieldStride{}, _fieldArraySize{}, _fieldData{} {}
/**
* @brief Type-erased constructor
@ -576,12 +669,17 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param fieldData Field data
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is not allowed here.
*
* Expects that @p mappingData and @p fieldData have the same size,
* @p fieldType corresponds to @p name and @p fieldArraySize is zero
* for builtin fields.
*/
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, UnsignedShort fieldArraySize = 0) noexcept;
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;
/** @overload */
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingType, mappingData, fieldType, fieldData, 0, flags} {}
/**
* @brief Constructor
@ -591,6 +689,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param fieldData Field data
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is not allowed here.
*
* Expects that @p mappingData and @p fieldData have the same size in
* the first dimension, that the second dimension of @p mappingData is
@ -600,51 +700,58 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @p fieldArraySize and that @p fieldType corresponds to @p name and
* @p fieldArraySize is zero for builtin attributes.
*/
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, UnsignedShort fieldArraySize = 0) noexcept;
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;
/** @overload */
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& 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<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, UnsignedShort).
* based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, 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<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData) noexcept;
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData, SceneFieldFlags flags = {}) noexcept;
/** @overload */
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::ArrayView<U>& fieldData) noexcept: SceneFieldData{name, mappingData, Containers::stridedArrayView(fieldData)} {}
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::ArrayView<U>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, mappingData, Containers::stridedArrayView(fieldData), flags} {}
/** @overload */
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {}
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {}
/** @overload */
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::ArrayView<U>& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), Containers::stridedArrayView(fieldData)} {}
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::ArrayView<U>& 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<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, UnsignedShort)
* based on @p U and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, 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<T>&, const Containers::StridedArrayView1D<U>&)
* @ref SceneFieldData(SceneField, const Containers::StridedArrayView1D<T>&, const Containers::StridedArrayView1D<U>&, SceneFieldFlags)
* for details about @ref SceneMappingType and @ref SceneFieldType
* detection.
*/
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData) noexcept;
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData, SceneFieldFlags flags = {}) noexcept;
/** @overload */
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData} {}
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {}
/**
* @brief Construct an offset-only field
@ -658,6 +765,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param fieldStride Field data stride
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is set implicitly.
*
* Instances created this way refer to offsets in unspecified
* external scene data instead of containing the data views directly.
@ -668,24 +777,19 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* Note that due to the @cpp constexpr @ce nature of this constructor,
* no @p mappingType checks against @p mappingStride or
* @p fieldType / @p fieldArraySize checks against @p fieldStride can
* be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, UnsignedShort)
* be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, UnsignedShort, SceneFieldFlags)
* constructor if you want additional safeguards.
* @see @ref isOffsetOnly(), @ref fieldArraySize(),
* @see @ref flags(), @ref fieldArraySize(),
* @ref mappingData(Containers::ArrayView<const void>) const,
* @ref fieldData(Containers::ArrayView<const void>) const
*/
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0) noexcept;
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;
/**
* @brief If the field is offset-only
*
* Returns @cpp true @ce if the field doesn't contain the data views
* directly, but instead refers to unspecified external data.
* @see @ref mappingData(Containers::ArrayView<const void>) const,
* @ref fieldData(Containers::ArrayView<const void>) const,
* @ref SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, SceneFieldType, std::size_t, std::ptrdiff_t, UnsignedShort)
*/
constexpr bool isOffsetOnly() const { return _isOffsetOnly; }
/** @overload */
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags) noexcept: SceneFieldData{name, size, mappingType, mappingOffset, mappingStride, fieldType, fieldOffset, fieldStride, 0, flags} {}
/** @brief Field flags */
constexpr SceneFieldFlags flags() const { return _flags; }
/** @brief Field name */
constexpr SceneField name() const { return _name; }
@ -699,30 +803,31 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/**
* @brief Type-erased object mapping data
*
* Expects that the field is not offset-only, in that case use the
* @ref mappingData(Containers::ArrayView<const void>) const overload
* instead.
* @see @ref isOffsetOnly()
* Expects that the field does not have @ref SceneFieldFlag::OffsetOnly
* set, in that case use the @ref mappingData(Containers::ArrayView<const void>) const
* overload instead.
* @see @ref flags()
*/
constexpr Containers::StridedArrayView1D<const void> mappingData() const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
{_mappingData.pointer, ~std::size_t{}}, _size,
(CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array"), _mappingStride)};
(CORRADE_CONSTEXPR_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly), "Trade::SceneFieldData::mappingData(): the field is offset-only, supply a data array"), _mappingStride)};
}
/**
* @brief Type-erased object mapping data for an offset-only attribute
*
* If the field is not offset-only, the @p data parameter is ignored.
* @see @ref isOffsetOnly(), @ref mappingData() const
* If the field does not have @ref SceneFieldFlag::OffsetOnly set, the
* @p data parameter is ignored.
* @see @ref flags(), @ref mappingData() const
*/
Containers::StridedArrayView1D<const void> mappingData(Containers::ArrayView<const void> data) const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
data, _isOffsetOnly ? reinterpret_cast<const char*>(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride};
data, _flags & SceneFieldFlag::OffsetOnly ? reinterpret_cast<const char*>(data.data()) + _mappingData.offset : _mappingData.pointer, _size, _mappingStride};
}
/** @brief Field type */
@ -734,30 +839,31 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/**
* @brief Type-erased field data
*
* Expects that the field is not offset-only, in that case use the
* @ref fieldData(Containers::ArrayView<const void>) const overload
* instead.
* @see @ref isOffsetOnly()
* Expects that the field does not have @ref SceneFieldFlag::OffsetOnly
* set, in that case use the @ref fieldData(Containers::ArrayView<const void>) const
* overload instead.
* @see @ref flags()
*/
constexpr Containers::StridedArrayView1D<const void> fieldData() const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
{_fieldData.pointer, ~std::size_t{}}, _size,
(CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array"), _fieldStride)};
(CORRADE_CONSTEXPR_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly), "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array"), _fieldStride)};
}
/**
* @brief Type-erased field data for an offset-only attribute
*
* If the field is not offset-only, the @p data parameter is ignored.
* @see @ref isOffsetOnly(), @ref fieldData() const
* If the field does not have @ref SceneFieldFlag::OffsetOnly set, the
* @p data parameter is ignored.
* @see @ref flags(), @ref fieldData() const
*/
Containers::StridedArrayView1D<const void> fieldData(Containers::ArrayView<const void> data) const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
data, _isOffsetOnly ? reinterpret_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride};
data, _flags & SceneFieldFlag::OffsetOnly ? reinterpret_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride};
}
private:
@ -776,7 +882,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
UnsignedLong _size;
SceneField _name;
bool _isOffsetOnly;
SceneFieldFlags _flags;
SceneMappingType _mappingType;
Short _mappingStride;
Data _mappingData;
@ -985,10 +1091,12 @@ with a "Chair" object, assuming such object exists:
@snippet MagnumTrade.cpp SceneData-per-object
Since these APIs perform a linear lookup through the field and object arrays,
these are suited mainly for introspection and debugging purposes. Retrieving
field data for many objects is better achieved by accessing the field data
directly.
The actual object ID lookup is done by @ref findFieldObjectOffset() and
depending on what @ref SceneFieldFlags are present for given field, it can be
done in constant, logarithmic or, worst case, linear time. As such, for general
scene representations these are suited mainly for introspection and debugging
purposes and retrieving field data for many objects is better achieved by
accessing the field data directly.
@section Trade-SceneData-usage-mutable Mutable data access
@ -1219,9 +1327,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref fieldSize() and @ref fieldArraySize() accessors. Compared to
* those and to @ref fieldData(UnsignedInt) const, the
* @ref SceneFieldData instances returned by this function may have
* different data pointers, and some of them might be offset-only ---
* use this function only if you *really* know what are you doing.
* @see @ref SceneFieldData::isOffsetOnly()
* different data pointers, and some of them might have
* @ref SceneFieldFlag::OffsetOnly set --- use this function only if
* you *really* know what are you doing.
* @see @ref SceneFieldData::flags()
*/
Containers::ArrayView<const SceneFieldData> fieldData() const & { return _fields; }
@ -1244,9 +1353,9 @@ class MAGNUM_TRADE_EXPORT SceneData {
*
* Unlike with @ref fieldData() and @ref releaseFieldData(), returned
* instances are guaranteed to always have an absolute data pointer
* (i.e., @ref SceneFieldData::isOffsetOnly() always returning
* @cpp false @ce). The @p id is expected to be smaller than
* @ref fieldCount().
* (i.e., @ref SceneFieldData::flags() never having
* @ref SceneFieldFlag::OffsetOnly set). The @p id is expected to be
* smaller than @ref fieldCount().
*/
SceneFieldData fieldData(UnsignedInt id) const;
@ -1261,6 +1370,15 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/
SceneField fieldName(UnsignedInt id) const;
/**
* @brief Field flags
* @m_since_latest
*
* The @p id is expected to be smaller than @ref fieldCount().
* @see @ref findFieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const
*/
SceneFieldFlags fieldFlags(UnsignedInt id) const;
/**
* @brief Field type
* @m_since_latest
@ -1380,8 +1498,12 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref mappingBound() and @p offset not larger than
* @ref fieldSize(UnsignedInt) const.
*
* The lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with
* @f$ n @f$ being the size of the field.
* If the field has @ref SceneFieldFlag::ImplicitMapping, the lookup is
* done in an @f$ \mathcal{O}(1) @f$ complexity. Otherwise, if the
* field has @ref SceneFieldFlag::OrderedMapping, the lookup is done in
* an @f$ \mathcal{O}(\log{} n) @f$ complexity with @f$ n @f$ being the
* size of the field. Otherwise, the lookup is done in an
* @f$ \mathcal{O}(n) @f$ complexity.
*
* You can also use @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* to directly find offset of an object in given named field.
@ -1399,8 +1521,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* exist, @p object is expected to be smaller than @ref mappingBound()
* and @p offset not be larger than @ref fieldSize(SceneField) const.
*
* The lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with
* @f$ m @f$ being the field count and @f$ n @f$ the size of the field.
* If the field has @ref SceneFieldFlag::ImplicitMapping, the lookup is
* done in an @f$ \mathcal{O}(m) @f$ complexity with @f$ m @f$ being
* the * field count. Otherwise, if the field has
* @ref SceneFieldFlag::OrderedMapping, the lookup is done in an
* @f$ \mathcal{O}(m + \log{} n) @f$ complexity with @f$ m @f$ being
* the field count and @f$ n @f$ the size of the field. Otherwise, the
* lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity.
*
* @see @ref hasField(), @ref hasFieldObject(SceneField, UnsignedInt) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
@ -1449,6 +1576,15 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/
bool hasFieldObject(SceneField fieldName, UnsignedInt object) const;
/**
* @brief Field flags
* @m_since_latest
*
* The @p name is expected to exist.
* @see @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/
SceneFieldFlags fieldFlags(SceneField name) const;
/**
* @brief Type of a named field
* @m_since_latest
@ -2632,10 +2768,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* the actual memory might be not. Additionally, the returned
* @ref SceneFieldData instances may have different data pointers and
* sizes than what's returned by the @ref field() and
* @ref fieldData(UnsignedInt) const accessors as some of them might be
* offset-only --- use this function only if you *really* know what are
* you doing.
* @see @ref fieldData(), @ref SceneFieldData::isOffsetOnly()
* @ref fieldData(UnsignedInt) const accessors as some of them might
* have @ref SceneFieldFlag::OffsetOnly --- use this function only if
* you *really* know what are you doing.
* @see @ref fieldData(), @ref SceneFieldData::flags()
*/
Containers::Array<SceneFieldData> releaseFieldData();
@ -2909,12 +3045,13 @@ namespace Implementation {
}
}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const UnsignedShort fieldArraySize) noexcept:
constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept:
_size{(CORRADE_CONSTEXPR_ASSERT(mappingData.size() == fieldData.size(),
"Trade::SceneFieldData: expected mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldData.size()), mappingData.size())},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType),
"Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)},
_isOffsetOnly{false},
_flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::OffsetOnly),
"Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view"), flags)},
_mappingType{mappingType},
_mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingData.stride() >= -32768 && mappingData.stride() <= 32767,
"Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingData.stride()), Short(mappingData.stride()))},
@ -2926,23 +3063,24 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_fieldData{fieldData.data()} {}
template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, Implementation::SceneFieldTypeFor<typename std::remove_const<U>::type>::type(), fieldData, 0} {}
template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView1D<U>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, Implementation::SceneFieldTypeFor<typename std::remove_const<U>::type>::type(), fieldData, 0, flags} {}
template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData) noexcept: SceneFieldData{
template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{
name,
Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(),
mappingData,
Implementation::SceneFieldTypeFor<typename std::remove_const<U>::type>::type(),
Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]},
/* Not using isContiguous<1>() as that's not constexpr */
(CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1]))
(CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == sizeof(U), "Trade::SceneFieldData: second field view dimension is not contiguous"), UnsignedShort(fieldData.size()[1])),
flags
} {}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize) noexcept:
constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept:
_size{size},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType),
"Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)},
_isOffsetOnly{true},
_flags{flags|SceneFieldFlag::OffsetOnly},
_mappingType{mappingType},
_mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingStride >= -32768 && mappingStride <= 32767,
"Trade::SceneFieldData: expected mapping view stride to fit into 16 bits, but got" << mappingStride), Short(mappingStride))},

262
src/Magnum/Trade/Test/SceneDataTest.cpp

@ -63,6 +63,10 @@ struct SceneDataTest: TestSuite::Tester {
void fieldTypeSizeAlignmentInvalid();
void debugFieldType();
void debugFieldFlag();
void debugFieldFlags();
void debugFieldFlagsSupersets();
void constructField();
void constructFieldDefault();
void constructFieldCustom();
@ -79,6 +83,7 @@ struct SceneDataTest: TestSuite::Tester {
void constructFieldInconsistentViewSize();
void constructFieldTooLargeMappingStride();
void constructFieldTooLargeFieldStride();
void constructFieldOffsetOnlyNotAllowed();
void constructFieldWrongDataAccess();
void constructField2DWrongSize();
void constructField2DNonContiguous();
@ -203,6 +208,57 @@ const struct {
{"mutable", DataFlag::Mutable}
};
const struct {
const char* name;
SceneFieldFlags flags;
UnsignedInt mapping[5];
UnsignedInt object;
UnsignedInt offset;
Containers::Optional<std::size_t> expected;
} FindFieldObjectOffsetData[]{
{"", {},
{4, 2, 1, 0, 2}, 2, 0, 1},
{"not found", {},
{4, 2, 1, 0, 2}, 3, 0, Containers::NullOpt},
{"offset", {},
{4, 2, 1, 0, 2}, 2, 2, 4},
{"offset, not found", {},
{4, 2, 1, 0, 2}, 2, 5, Containers::NullOpt},
{"ordered", SceneFieldFlag::OrderedMapping,
{1, 3, 4, 4, 5}, 4, 0, 2},
{"ordered, not found", SceneFieldFlag::OrderedMapping,
/* It *is* there but the binary search expects an ordered range and thus
should not even see it */
{1, 3, 4, 4, 2}, 2, 0, Containers::NullOpt},
{"ordered, not found, too small", SceneFieldFlag::OrderedMapping,
{1, 3, 4, 4, 5}, 0, 0, Containers::NullOpt},
{"ordered, not found, too large", SceneFieldFlag::OrderedMapping,
{1, 3, 4, 4, 5}, 6, 0, Containers::NullOpt},
{"ordered, offset", SceneFieldFlag::OrderedMapping,
{1, 3, 4, 4, 5}, 4, 3, 3},
{"ordered, offset, not found", SceneFieldFlag::OrderedMapping,
{1, 3, 4, 4, 5}, 4, 4, Containers::NullOpt},
{"implicit", SceneFieldFlag::ImplicitMapping,
/* Not there but the assumption is that the ID matches the offset */
{5, 5, 5, 5, 5}, 3, 0, 3},
{"implicit, not found", SceneFieldFlag::ImplicitMapping,
/* Is there but the assumption is that the ID matches the offset, which
is out of bounds */
{5, 5, 5, 5, 5}, 5, 0, Containers::NullOpt},
{"implicit, offset", SceneFieldFlag::ImplicitMapping,
/* Not there but the assumption is that the ID matches the offset;
verifying that the offset is properly accounted for */
{5, 5, 5, 5, 5}, 3, 3, 3},
{"implicit, offset, not found, less than offset", SceneFieldFlag::ImplicitMapping,
/* Cerifying that the offset is properly accounted for -- it's never
found if offset > id */
{5, 5, 5, 5, 5}, 3, 4, Containers::NullOpt},
{"implicit, offset, not found, out of bounds", SceneFieldFlag::ImplicitMapping,
{5, 5, 5, 5, 5}, 5, 4, Containers::NullOpt}
};
const struct {
const char* name;
std::size_t offset;
@ -298,6 +354,10 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::fieldTypeSizeAlignmentInvalid,
&SceneDataTest::debugFieldType,
&SceneDataTest::debugFieldFlag,
&SceneDataTest::debugFieldFlags,
&SceneDataTest::debugFieldFlagsSupersets,
&SceneDataTest::constructField,
&SceneDataTest::constructFieldDefault,
&SceneDataTest::constructFieldCustom,
@ -314,6 +374,7 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::constructFieldInconsistentViewSize,
&SceneDataTest::constructFieldTooLargeMappingStride,
&SceneDataTest::constructFieldTooLargeFieldStride,
&SceneDataTest::constructFieldOffsetOnlyNotAllowed,
&SceneDataTest::constructFieldWrongDataAccess,
&SceneDataTest::constructField2DWrongSize,
&SceneDataTest::constructField2DNonContiguous,
@ -353,12 +414,16 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::constructCopy,
&SceneDataTest::constructMove,
&SceneDataTest::findFieldId,
&SceneDataTest::findFieldObjectOffset<UnsignedByte>,
&SceneDataTest::findFieldObjectOffset<UnsignedShort>,
&SceneDataTest::findFieldObjectOffset<UnsignedInt>,
&SceneDataTest::findFieldObjectOffset<UnsignedLong>,
&SceneDataTest::findFieldObjectOffsetInvalidOffset,
&SceneDataTest::findFieldId});
addInstancedTests<SceneDataTest>({
&SceneDataTest::findFieldObjectOffset<UnsignedByte>,
&SceneDataTest::findFieldObjectOffset<UnsignedShort>,
&SceneDataTest::findFieldObjectOffset<UnsignedInt>,
&SceneDataTest::findFieldObjectOffset<UnsignedLong>
}, Containers::arraySize(FindFieldObjectOffsetData));
addTests({&SceneDataTest::findFieldObjectOffsetInvalidOffset,
&SceneDataTest::fieldObjectOffsetNotFound,
&SceneDataTest::mappingAsArrayByIndex<UnsignedByte>,
@ -645,6 +710,30 @@ void SceneDataTest::debugFieldType() {
CORRADE_COMPARE(out.str(), "Trade::SceneFieldType::Matrix3x4h Trade::SceneFieldType(0xdead)\n");
}
void SceneDataTest::debugFieldFlag() {
std::ostringstream out;
Debug(&out) << SceneFieldFlag::OffsetOnly << SceneFieldFlag(0xbe);
CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::OffsetOnly Trade::SceneFieldFlag(0xbe)\n");
}
void SceneDataTest::debugFieldFlags() {
std::ostringstream out;
Debug{&out} << (SceneFieldFlag::OffsetOnly|SceneFieldFlag(0xe0)) << SceneFieldFlags{};
CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag(0xe0) Trade::SceneFieldFlags{}\n");
}
void SceneDataTest::debugFieldFlagsSupersets() {
/* ImplicitMapping is a superset of OrderedMapping, so only one should be
printed */
{
std::ostringstream out;
Debug{&out} << (SceneFieldFlag::ImplicitMapping|SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(out.str(), "Trade::SceneFieldFlag::ImplicitMapping\n");
}
}
const UnsignedShort RotationMapping2D[3] {
17,
35,
@ -660,8 +749,8 @@ void SceneDataTest::constructField() {
const UnsignedShort rotationMappingData[3]{};
const Complexd rotationFieldData[3];
SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)};
CORRADE_VERIFY(!rotations.isOffsetOnly());
SceneFieldData rotations{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OrderedMapping};
CORRADE_COMPARE(rotations.flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(rotations.name(), SceneField::Rotation);
CORRADE_COMPARE(rotations.size(), 3);
CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort);
@ -685,16 +774,16 @@ void SceneDataTest::constructField() {
CORRADE_VERIFY(rotations.mappingData(someArray).data() == rotationMappingData);
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Won't bother anymore */
constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationMapping2D), Containers::arrayView(RotationField2D)};
constexpr bool isOffsetOnly = crotations.isOffsetOnly();
constexpr SceneFieldData crotations{SceneField::Rotation, Containers::arrayView(RotationMapping2D), Containers::arrayView(RotationField2D), SceneFieldFlag::ImplicitMapping};
constexpr SceneField name = crotations.name();
constexpr SceneFieldFlags flags = crotations.flags();
constexpr SceneMappingType mappingType = crotations.mappingType();
constexpr Containers::StridedArrayView1D<const void> mappingData = crotations.mappingData();
constexpr SceneFieldType fieldType = crotations.fieldType();
constexpr UnsignedShort fieldArraySize = crotations.fieldArraySize();
constexpr Containers::StridedArrayView1D<const void> fieldData = crotations.fieldData();
CORRADE_VERIFY(!isOffsetOnly);
CORRADE_COMPARE(name, SceneField::Rotation);
CORRADE_COMPARE(flags, SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(mappingType, SceneMappingType::UnsignedShort);
CORRADE_COMPARE(mappingData.size(), 3);
CORRADE_COMPARE(mappingData.stride(), sizeof(UnsignedShort));
@ -739,8 +828,8 @@ void SceneDataTest::constructField2D() {
auto rotationMappingView = Containers::StridedArrayView2D<char>{rotationMappingData, {6, sizeof(UnsignedShort)}}.every(2);
auto rotationFieldView = Containers::StridedArrayView2D<char>{rotationFieldData, {6, sizeof(Complexd)}}.every(2);
SceneFieldData rotations{SceneField::Rotation, rotationMappingView, SceneFieldType::Complexd, rotationFieldView};
CORRADE_VERIFY(!rotations.isOffsetOnly());
SceneFieldData rotations{SceneField::Rotation, rotationMappingView, SceneFieldType::Complexd, rotationFieldView, SceneFieldFlag::ImplicitMapping};
CORRADE_COMPARE(rotations.flags(), SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(rotations.name(), SceneField::Rotation);
CORRADE_COMPARE(rotations.size(), 3);
CORRADE_COMPARE(rotations.mappingType(), SceneMappingType::UnsignedShort);
@ -757,8 +846,8 @@ void SceneDataTest::constructField2D() {
void SceneDataTest::constructFieldTypeErased() {
const UnsignedLong scalingMappingData[3]{};
const Vector3 scalingFieldData[3];
SceneFieldData scalings{SceneField::Scaling, SceneMappingType::UnsignedLong, Containers::arrayCast<const char>(Containers::stridedArrayView(scalingMappingData)), SceneFieldType::Vector3, Containers::arrayCast<const char>(Containers::stridedArrayView(scalingFieldData))};
CORRADE_VERIFY(!scalings.isOffsetOnly());
SceneFieldData scalings{SceneField::Scaling, SceneMappingType::UnsignedLong, Containers::arrayCast<const char>(Containers::stridedArrayView(scalingMappingData)), SceneFieldType::Vector3, Containers::arrayCast<const char>(Containers::stridedArrayView(scalingFieldData)), SceneFieldFlag::OrderedMapping};
CORRADE_COMPARE(scalings.flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scalings.name(), SceneField::Scaling);
CORRADE_COMPARE(scalings.size(), 3);
CORRADE_COMPARE(scalings.mappingType(), SceneMappingType::UnsignedLong);
@ -789,8 +878,8 @@ void SceneDataTest::constructFieldOffsetOnly() {
{0, 15, {67.0f, -1.1f}}
};
SceneFieldData a{SceneField::Translation, 2, SceneMappingType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data)};
CORRADE_VERIFY(a.isOffsetOnly());
SceneFieldData a{SceneField::Translation, 2, SceneMappingType::UnsignedShort, offsetof(Data, object), sizeof(Data), SceneFieldType::Vector2, offsetof(Data, translation), sizeof(Data), SceneFieldFlag::ImplicitMapping};
CORRADE_COMPARE(a.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(a.name(), SceneField::Translation);
CORRADE_COMPARE(a.size(), 2);
CORRADE_COMPARE(a.mappingType(), SceneMappingType::UnsignedShort);
@ -814,8 +903,8 @@ constexpr Int ArrayOffsetFieldData[3*4]{};
void SceneDataTest::constructFieldArray() {
UnsignedByte offsetMappingData[3];
Int offsetFieldData[3*4];
SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D<Int>{offsetFieldData, {3, 4}}};
CORRADE_VERIFY(!data.isOffsetOnly());
SceneFieldData data{sceneFieldCustom(34), Containers::arrayView(offsetMappingData), Containers::StridedArrayView2D<Int>{offsetFieldData, {3, 4}}, SceneFieldFlag::ImplicitMapping};
CORRADE_COMPARE(data.flags(), SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(data.name(), sceneFieldCustom(34));
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte);
@ -828,8 +917,8 @@ void SceneDataTest::constructFieldArray() {
CORRADE_COMPARE(data.fieldData().stride(), 4*sizeof(Int));
CORRADE_VERIFY(data.fieldData().data() == offsetFieldData);
constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetMappingData), Containers::StridedArrayView2D<const Int>{ArrayOffsetFieldData, {3, 4}}};
CORRADE_VERIFY(!cdata.isOffsetOnly());
constexpr SceneFieldData cdata{sceneFieldCustom(34), Containers::arrayView(ArrayOffsetMappingData), Containers::StridedArrayView2D<const Int>{ArrayOffsetFieldData, {3, 4}}, SceneFieldFlag::OrderedMapping};
CORRADE_COMPARE(cdata.flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34));
CORRADE_COMPARE(cdata.size(), 3);
CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte);
@ -846,8 +935,8 @@ void SceneDataTest::constructFieldArray() {
void SceneDataTest::constructFieldArray2D() {
char offsetMappingData[3*sizeof(UnsignedByte)];
char offsetFieldData[3*4*sizeof(Int)];
SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D<char>{offsetMappingData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D<char>{offsetFieldData, {3, 4*sizeof(Int)}}, 4};
CORRADE_VERIFY(!data.isOffsetOnly());
SceneFieldData data{sceneFieldCustom(34), Containers::StridedArrayView2D<char>{offsetMappingData, {3, sizeof(UnsignedByte)}}, SceneFieldType::Int, Containers::StridedArrayView2D<char>{offsetFieldData, {3, 4*sizeof(Int)}}, 4, SceneFieldFlag::ImplicitMapping};
CORRADE_COMPARE(data.flags(), SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(data.name(), sceneFieldCustom(34));
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte);
@ -865,8 +954,8 @@ void SceneDataTest::constructFieldArrayTypeErased() {
UnsignedByte offsetMappingData[3];
Int offsetFieldData[3*4];
Containers::StridedArrayView1D<Int> offset{offsetFieldData, 3, 4*sizeof(Int)};
SceneFieldData data{sceneFieldCustom(34), SceneMappingType::UnsignedByte, Containers::arrayCast<const char>(Containers::stridedArrayView(offsetMappingData)), SceneFieldType::Int, Containers::arrayCast<const char>(offset), 4};
CORRADE_VERIFY(!data.isOffsetOnly());
SceneFieldData data{sceneFieldCustom(34), SceneMappingType::UnsignedByte, Containers::arrayCast<const char>(Containers::stridedArrayView(offsetMappingData)), SceneFieldType::Int, Containers::arrayCast<const char>(offset), 4, SceneFieldFlag::OrderedMapping};
CORRADE_COMPARE(data.flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(data.name(), sceneFieldCustom(34));
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.fieldType(), SceneFieldType::Int);
@ -887,8 +976,8 @@ void SceneDataTest::constructFieldArrayOffsetOnly() {
Int offset[4];
};
SceneFieldData data{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4};
CORRADE_VERIFY(data.isOffsetOnly());
SceneFieldData data{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4, SceneFieldFlag::ImplicitMapping};
CORRADE_COMPARE(data.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(data.name(), sceneFieldCustom(34));
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.mappingType(), SceneMappingType::UnsignedByte);
@ -903,8 +992,8 @@ void SceneDataTest::constructFieldArrayOffsetOnly() {
CORRADE_COMPARE(data.mappingData(actual).stride(), sizeof(Data));
CORRADE_VERIFY(data.mappingData(actual).data() == &actual[0].object);
constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4};
CORRADE_VERIFY(cdata.isOffsetOnly());
constexpr SceneFieldData cdata{sceneFieldCustom(34), 3, SceneMappingType::UnsignedByte, offsetof(Data, object), sizeof(Data), SceneFieldType::Int, offsetof(Data, offset), sizeof(Data), 4, SceneFieldFlag::OrderedMapping};
CORRADE_COMPARE(cdata.flags(), SceneFieldFlag::OffsetOnly|SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(cdata.name(), sceneFieldCustom(34));
CORRADE_COMPARE(cdata.size(), 3);
CORRADE_COMPARE(cdata.mappingType(), SceneMappingType::UnsignedByte);
@ -997,6 +1086,24 @@ void SceneDataTest::constructFieldTooLargeFieldStride() {
"Trade::SceneFieldData: expected field view stride to fit into 16 bits, but got -32769\n");
}
void SceneDataTest::constructFieldOffsetOnlyNotAllowed() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
const UnsignedShort rotationMappingData[3]{};
const Quaternion rotationFieldData[3];
/* This one is fine */
SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneFieldFlag::OffsetOnly};
std::ostringstream out;
Error redirectError{&out};
SceneFieldData{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OffsetOnly};
CORRADE_COMPARE(out.str(),
"Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view\n");
}
void SceneDataTest::constructFieldWrongDataAccess() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
@ -1006,8 +1113,8 @@ void SceneDataTest::constructFieldWrongDataAccess() {
const Quaternion rotationFieldData[3];
SceneFieldData a{SceneField::Rotation, Containers::arrayView(rotationMappingData), Containers::arrayView(rotationFieldData)};
SceneFieldData b{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion)};
CORRADE_VERIFY(!a.isOffsetOnly());
CORRADE_VERIFY(b.isOffsetOnly());
CORRADE_COMPARE(a.flags(), SceneFieldFlags{});
CORRADE_COMPARE(b.flags(), SceneFieldFlag::OffsetOnly);
a.mappingData(rotationMappingData); /* This is fine, no asserts */
a.fieldData(rotationFieldData);
@ -1229,11 +1336,11 @@ void SceneDataTest::construct() {
SceneFieldType::Int, offsetof(TransformParent, parent), sizeof(TransformParent)};
SceneFieldData meshes{SceneField::Mesh,
materialMeshRadiusMappingData,
meshFieldData};
meshFieldData, SceneFieldFlag::OrderedMapping};
/* Custom & array */
SceneFieldData radiuses{sceneFieldCustom(37),
materialMeshRadiusMappingData,
Containers::arrayCast<2, Float>(radiusFieldData)};
Containers::arrayCast<2, Float>(radiusFieldData), SceneFieldFlag::OrderedMapping};
SceneData scene{SceneMappingType::UnsignedShort, 8, std::move(data), {
transformations, parents, meshes, radiuses
}, &importerState};
@ -1256,6 +1363,10 @@ void SceneDataTest::construct() {
CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent);
CORRADE_COMPARE(scene.fieldName(2), SceneField::Mesh);
CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(37));
CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldFlags(1), SceneFieldFlag::OffsetOnly);
CORRADE_COMPARE(scene.fieldFlags(2), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldFlags(3), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Matrix4x4);
CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::UnsignedByte);
@ -1275,14 +1386,14 @@ void SceneDataTest::construct() {
CORRADE_COMPARE(scene.fieldData(2).mappingType(), SceneMappingType::UnsignedShort);
CORRADE_COMPARE(Containers::arrayCast<const UnsignedShort>(scene.fieldData(2).mappingData())[1], 6);
CORRADE_COMPARE(Containers::arrayCast<const UnsignedByte>(scene.fieldData(2).fieldData())[1], 7);
CORRADE_VERIFY(!scene.fieldData(2).isOffsetOnly());
CORRADE_COMPARE(scene.fieldData(2).flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldData(2).fieldType(), SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldData(2).fieldArraySize(), 0);
/* Offset-only */
CORRADE_COMPARE(scene.fieldData(1).name(), SceneField::Parent);
CORRADE_COMPARE(scene.fieldData(1).size(), 5);
CORRADE_COMPARE(scene.fieldData(1).mappingType(), SceneMappingType::UnsignedShort);
CORRADE_VERIFY(!scene.fieldData(1).isOffsetOnly());
CORRADE_COMPARE(scene.fieldData(1).flags(), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldData(1).fieldType(), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldData(1).fieldArraySize(), 0);
CORRADE_COMPARE(Containers::arrayCast<const UnsignedShort>(scene.fieldData(1).mappingData())[4], 1);
@ -1291,7 +1402,7 @@ void SceneDataTest::construct() {
CORRADE_COMPARE(scene.fieldData(3).name(), sceneFieldCustom(37));
CORRADE_COMPARE(scene.fieldData(3).size(), 2);
CORRADE_COMPARE(scene.fieldData(3).mappingType(), SceneMappingType::UnsignedShort);
CORRADE_VERIFY(!scene.fieldData(3).isOffsetOnly());
CORRADE_COMPARE(scene.fieldData(3).flags(), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldData(3).fieldType(), SceneFieldType::Float);
CORRADE_COMPARE(scene.fieldData(3).fieldArraySize(), 2);
CORRADE_COMPARE(Containers::arrayCast<const UnsignedShort>(scene.fieldData(3).mappingData())[0], 2);
@ -1374,6 +1485,10 @@ void SceneDataTest::construct() {
CORRADE_COMPARE(scene.mutableField<Float[]>(3)[0][1], 1.5f);
/* Field property access by name */
CORRADE_COMPARE(scene.fieldFlags(SceneField::Transformation), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldFlags(SceneField::Parent), SceneFieldFlag::OffsetOnly);
CORRADE_COMPARE(scene.fieldFlags(SceneField::Mesh), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(37)), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4);
CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte);
@ -1608,6 +1723,7 @@ void SceneDataTest::constructDeprecated() {
CORRADE_COMPARE(scene.importerState(), &a);
CORRADE_COMPARE(scene.fieldCount(), 1);
CORRADE_COMPARE(scene.fieldName(0), SceneField::Parent);
CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::Int);
if(data.is2D || data.is3D) {
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(0),
@ -2113,60 +2229,46 @@ void SceneDataTest::findFieldId() {
template<class T> void SceneDataTest::findFieldObjectOffset() {
setTestCaseTemplateName(NameTraits<T>::name());
/** @todo update once field flags describing object order are present */
auto&& data = FindFieldObjectOffsetData[testCaseInstanceId()];
setTestCaseDescription(data.name);
struct Field {
T object;
UnsignedInt mesh;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{0, 5},
{2, 5}
} fields[5]{
{T(data.mapping[0]), 0},
{T(data.mapping[1]), 0},
{T(data.mapping[2]), 0},
{T(data.mapping[3]), 0},
{T(data.mapping[4]), 0}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneMappingTypeFor<T>(), 7, {}, fields, {
/* Test also with a completely empty field */
SceneFieldData{SceneField::Parent, Implementation::sceneMappingTypeFor<T>(), nullptr, SceneFieldType::Int, nullptr},
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh), data.flags}
}};
CORRADE_COMPARE(scene.findFieldObjectOffset(0, 4), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, 4), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 4), 0);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 1), 1);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2), 2);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 3), 4);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 5), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 3), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 4), 0);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 1), 1);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2), 2);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 3), 4);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 5), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 3), Containers::NullOpt);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 4), 0);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 1), 1);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 2), 2);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 2, 3), 4);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 4), 0);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 1), 1);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2), 2);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2, 3), 4);
CORRADE_VERIFY(!scene.hasFieldObject(0, 4));
CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, 4));
CORRADE_VERIFY(scene.hasFieldObject(1, 4));
CORRADE_VERIFY(scene.hasFieldObject(1, 1));
CORRADE_VERIFY(scene.hasFieldObject(1, 2));
CORRADE_VERIFY(!scene.hasFieldObject(1, 3));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 4));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 1));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 2));
CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Mesh, 3));
/* An empty field should not find anything for any query with any flags */
if(data.offset == 0) {
CORRADE_COMPARE(scene.findFieldObjectOffset(0, data.object), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, data.object), Containers::NullOpt);
CORRADE_VERIFY(!scene.hasFieldObject(0, data.object));
CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, data.object));
}
CORRADE_COMPARE(scene.findFieldObjectOffset(1, data.object, data.offset), data.expected);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, data.object, data.offset), data.expected);
if(data.offset == 0) {
CORRADE_COMPARE(scene.hasFieldObject(1, data.object), !!data.expected);
CORRADE_COMPARE(scene.hasFieldObject(SceneField::Mesh, data.object), !!data.expected);
}
if(data.expected) {
CORRADE_COMPARE(scene.fieldObjectOffset(1, data.object, data.offset), *data.expected);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, data.object, data.offset), *data.expected);
}
}
void SceneDataTest::findFieldObjectOffsetInvalidOffset() {
@ -4537,6 +4639,7 @@ void SceneDataTest::fieldNotFound() {
scene.hasFieldObject(2, 0);
scene.fieldData(2);
scene.fieldName(2);
scene.fieldFlags(2);
scene.fieldType(2);
scene.fieldSize(2);
scene.fieldArraySize(2);
@ -4548,6 +4651,7 @@ void SceneDataTest::fieldNotFound() {
scene.mutableField<UnsignedInt[]>(2);
scene.fieldId(sceneFieldCustom(666));
scene.fieldFlags(sceneFieldCustom(666));
scene.findFieldObjectOffset(sceneFieldCustom(666), 0);
scene.fieldObjectOffset(sceneFieldCustom(666), 0);
scene.hasFieldObject(sceneFieldCustom(666), 0);
@ -4597,6 +4701,7 @@ void SceneDataTest::fieldNotFound() {
"Trade::SceneData::hasFieldObject(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldFlags(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldSize(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldArraySize(): index 2 out of range for 2 fields\n"
@ -4608,6 +4713,7 @@ void SceneDataTest::fieldNotFound() {
"Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldFlags(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::findFieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::hasFieldObject(): field Trade::SceneField::Custom(666) not found\n"

2
src/Magnum/Trade/Trade.h

@ -103,6 +103,8 @@ class TextureData;
enum class SceneMappingType: UnsignedByte;
enum class SceneField: UnsignedInt;
enum class SceneFieldType: UnsignedShort;
enum class SceneFieldFlag: UnsignedByte;
typedef Containers::EnumSet<SceneFieldFlag> SceneFieldFlags;
class SceneFieldData;
class SceneData;

Loading…
Cancel
Save