Browse Source

Trade: support storing string fields in SceneData.

Finally got an idea how to provide various options store these
efficiently, so it's implemented now. Five different storage variants
times four different type sizes.
pull/499/head
Vladimír Vondruš 3 years ago
parent
commit
ae6029d128
  1. 58
      doc/snippets/MagnumTrade.cpp
  2. 404
      src/Magnum/Trade/SceneData.cpp
  3. 703
      src/Magnum/Trade/SceneData.h
  4. 799
      src/Magnum/Trade/Test/SceneDataTest.cpp

58
doc/snippets/MagnumTrade.cpp

@ -26,6 +26,7 @@
#include <unordered_map>
#include <Corrade/Containers/ArrayTuple.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StringIterable.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once file callbacks are <string>-free */
#include <Corrade/Containers/Pair.h>
#include <Corrade/Utility/Algorithms.h>
@ -976,6 +977,17 @@ Trade::SceneData{Trade::SceneMappingType::UnsignedInt, 120, std::move(data),
/* [SceneFieldData-usage-offset-only] */
}
{
/* [SceneFieldData-usage-strings] */
Containers::StridedArrayView1D<UnsignedInt> mapping = DOXYGEN_ELLIPSIS({});
Containers::StringView string = DOXYGEN_ELLIPSIS({});
Containers::StridedArrayView1D<Containers::Pair<UnsignedInt, UnsignedInt>> ranges = DOXYGEN_ELLIPSIS({});
Trade::SceneFieldData field{Trade::sceneFieldCustom(35), mapping,
string.data(), Trade::SceneFieldType::StringRange32, ranges};
/* [SceneFieldData-usage-strings] */
}
{
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
@ -1162,4 +1174,50 @@ static_cast<void>(cellFrustums);
static_cast<void>(cellLights);
}
{
using namespace Containers::Literals;
/* [SceneData-populating-strings] */
constexpr Containers::StringView CategoryStrings =
"wall\0furniture\0lighting\0artwork"_s;
constexpr UnsignedByte CategoryWall = 0;
constexpr UnsignedByte CategoryFurniture = 5;
constexpr UnsignedByte CategoryLighting = 15;
constexpr UnsignedByte CategoryArtwork = 24;
Containers::MutableStringView categoryStrings;
Containers::ArrayView<UnsignedInt> mapping;
Containers::ArrayView<UnsignedByte> categories;
Containers::ArrayTuple data{
{CategoryStrings.size(), categoryStrings},
{DOXYGEN_ELLIPSIS(5), mapping},
{DOXYGEN_ELLIPSIS(5), categories},
};
Utility::copy(CategoryStrings, categoryStrings);
mapping[0] = 7;
categories[0] = CategoryWall;
mapping[1] = 19;
categories[1] = CategoryFurniture;
DOXYGEN_ELLIPSIS(static_cast<void>(CategoryLighting); static_cast<void>(CategoryArtwork);)
constexpr Trade::SceneField SceneFieldCategory = Trade::sceneFieldCustom(25);
Trade::SceneData scene{Trade::SceneMappingType::UnsignedInt, DOXYGEN_ELLIPSIS(5), std::move(data), {
Trade::SceneFieldData{SceneFieldCategory, mapping,
categoryStrings.data(), Trade::SceneFieldType::StringRangeNullTerminated8,
categories}
}};
/* [SceneData-populating-strings] */
}
{
constexpr Trade::SceneField SceneFieldCategory = Trade::sceneFieldCustom(25);
Trade::SceneData scene{{}, 0, nullptr, nullptr};
/* [SceneData-populating-strings-retrieve] */
Containers::StringIterable categories = scene.fieldStrings(SceneFieldCategory);
// Prints "furniture"
Debug{} << categories[scene.fieldObjectOffset(SceneFieldCategory, 19)];
/* [SceneData-populating-strings-retrieve] */
}
}

404
src/Magnum/Trade/SceneData.cpp

@ -31,6 +31,8 @@
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayViewStl.h>
#include <Corrade/Containers/StringIterable.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Algorithms.h>
@ -238,6 +240,18 @@ Debug& operator<<(Debug& debug, const SceneFieldType value) {
_c(Radd)
_c(Pointer)
_c(MutablePointer)
_c(StringOffset8)
_c(StringOffset16)
_c(StringOffset32)
_c(StringOffset64)
_c(StringRange8)
_c(StringRange16)
_c(StringRange32)
_c(StringRange64)
_c(StringRangeNullTerminated8)
_c(StringRangeNullTerminated16)
_c(StringRangeNullTerminated32)
_c(StringRangeNullTerminated64)
#undef _c
/* LCOV_EXCL_STOP */
}
@ -253,6 +267,8 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
switch(type) {
case SceneFieldType::UnsignedByte:
case SceneFieldType::Byte:
case SceneFieldType::StringOffset8:
case SceneFieldType::StringRangeNullTerminated8:
return 1;
case SceneFieldType::UnsignedShort:
case SceneFieldType::Short:
@ -261,6 +277,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
case SceneFieldType::Vector2b:
case SceneFieldType::Degh:
case SceneFieldType::Radh:
case SceneFieldType::StringOffset16:
case SceneFieldType::StringRange8:
case SceneFieldType::StringRangeNullTerminated16:
return 2;
case SceneFieldType::Vector3ub:
case SceneFieldType::Vector3b:
@ -276,6 +295,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
case SceneFieldType::Range1Dh:
case SceneFieldType::Deg:
case SceneFieldType::Rad:
case SceneFieldType::StringOffset32:
case SceneFieldType::StringRange16:
case SceneFieldType::StringRangeNullTerminated32:
return 4;
case SceneFieldType::Vector3us:
case SceneFieldType::Vector3s:
@ -297,6 +319,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
case SceneFieldType::Complex:
case SceneFieldType::Degd:
case SceneFieldType::Radd:
case SceneFieldType::StringOffset64:
case SceneFieldType::StringRange32:
case SceneFieldType::StringRangeNullTerminated64:
return 8;
case SceneFieldType::Vector3:
case SceneFieldType::Vector3ui:
@ -318,6 +343,7 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
case SceneFieldType::Complexd:
case SceneFieldType::DualComplex:
case SceneFieldType::Quaternion:
case SceneFieldType::StringRange64:
return 16;
case SceneFieldType::Matrix3x3h:
return 18;
@ -384,6 +410,9 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
case SceneFieldType::Vector2b:
case SceneFieldType::Vector3b:
case SceneFieldType::Vector4b:
case SceneFieldType::StringOffset8:
case SceneFieldType::StringRange8:
case SceneFieldType::StringRangeNullTerminated8:
return 1;
case SceneFieldType::UnsignedShort:
case SceneFieldType::Vector2us:
@ -411,6 +440,9 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
case SceneFieldType::Range3Dh:
case SceneFieldType::Degh:
case SceneFieldType::Radh:
case SceneFieldType::StringOffset16:
case SceneFieldType::StringRange16:
case SceneFieldType::StringRangeNullTerminated16:
return 2;
case SceneFieldType::UnsignedInt:
case SceneFieldType::Vector2ui:
@ -445,6 +477,9 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
case SceneFieldType::DualQuaternion:
case SceneFieldType::Deg:
case SceneFieldType::Rad:
case SceneFieldType::StringOffset32:
case SceneFieldType::StringRange32:
case SceneFieldType::StringRangeNullTerminated32:
return 4;
case SceneFieldType::UnsignedLong:
case SceneFieldType::Long:
@ -470,6 +505,9 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
case SceneFieldType::DualQuaterniond:
case SceneFieldType::Degd:
case SceneFieldType::Radd:
case SceneFieldType::StringOffset64:
case SceneFieldType::StringRange64:
case SceneFieldType::StringRangeNullTerminated64:
return 8;
case SceneFieldType::Pointer:
case SceneFieldType::MutablePointer:
@ -494,6 +532,7 @@ Debug& operator<<(Debug& debug, const SceneFieldFlag value) {
_c(OffsetOnly)
_c(ImplicitMapping)
_c(OrderedMapping)
_c(NullTerminatedString)
#undef _c
/* LCOV_EXCL_STOP */
}
@ -506,7 +545,8 @@ Debug& operator<<(Debug& debug, const SceneFieldFlags value) {
SceneFieldFlag::OffsetOnly,
SceneFieldFlag::ImplicitMapping,
/* This one is implied by ImplicitMapping, so has to be after */
SceneFieldFlag::OrderedMapping
SceneFieldFlag::OrderedMapping,
SceneFieldFlag::NullTerminatedString
});
}
@ -521,10 +561,37 @@ SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedA
"Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType, );
#endif
if(mappingData.size()[1] == 8) _mappingType = SceneMappingType::UnsignedLong;
else if(mappingData.size()[1] == 4) _mappingType = SceneMappingType::UnsignedInt;
else if(mappingData.size()[1] == 2) _mappingType = SceneMappingType::UnsignedShort;
else if(mappingData.size()[1] == 1) _mappingType = SceneMappingType::UnsignedByte;
if(mappingData.size()[1] == 8)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedLong);
else if(mappingData.size()[1] == 4)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedInt);
else if(mappingData.size()[1] == 2)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedShort);
else if(mappingData.size()[1] == 1)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedByte);
else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second mapping view dimension size 1, 2, 4 or 8 but got" << mappingData.size()[1], );
CORRADE_ASSERT(mappingData.isContiguous<1>(), "Trade::SceneFieldData: second mapping view dimension is not contiguous", );
CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", );
}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const char* const stringData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, stringData, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, 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 */
CORRADE_ASSERT(fieldData.isEmpty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType),
"Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType, );
/* Merge the mapping type with the string type already writen by the
delegated-to constructor -- that's why the |=. */
if(mappingData.size()[1] == 8)
_mappingTypeStringType |= UnsignedByte(SceneMappingType::UnsignedLong);
else if(mappingData.size()[1] == 4)
_mappingTypeStringType |= UnsignedByte(SceneMappingType::UnsignedInt);
else if(mappingData.size()[1] == 2)
_mappingTypeStringType |= UnsignedByte(SceneMappingType::UnsignedShort);
else if(mappingData.size()[1] == 1)
_mappingTypeStringType |= UnsignedByte(SceneMappingType::UnsignedByte);
else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second mapping view dimension size 1, 2, 4 or 8 but got" << mappingData.size()[1], );
CORRADE_ASSERT(mappingData.isContiguous<1>(), "Trade::SceneFieldData: second mapping view dimension is not contiguous", );
@ -553,14 +620,49 @@ Containers::StridedArrayView1D<const void> SceneFieldData::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, _fieldStride};
{_fieldData.pointer, ~std::size_t{}}, _size, _field.data.stride};
}
Containers::StridedArrayView1D<const void> SceneFieldData::fieldData(const 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, _flags & SceneFieldFlag::OffsetOnly ? static_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _fieldStride};
data, _flags & SceneFieldFlag::OffsetOnly ? static_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _field.data.stride};
}
namespace {
/* https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend ;
stride extraction doesn't need a helper as we can access the union field
directly. It's also not directly using bitfields in the SceneFieldData
privates as I expect nasty portability / endian issues and compiler warnings
coming from this. */
inline Long extractStringFieldOffset(const UnsignedLong strideOffset) {
union {
struct {
Short:16;
Long offset:48;
} s;
UnsignedLong u;
} caster;
caster.u = strideOffset;
return caster.s.offset;
}
}
const char* SceneFieldData::stringData() const {
CORRADE_ASSERT(_mappingTypeStringType & Implementation::SceneMappingStringTypeMask,
"Trade::SceneFieldData::stringData(): the field is" << _field.data.type << Debug::nospace << ", not a string", {});
CORRADE_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly),
"Trade::SceneFieldData::stringData(): the field is offset-only, supply a data array", {});
return static_cast<const char*>(_fieldData.pointer) + extractStringFieldOffset(_field.strideOffset);
}
const char* SceneFieldData::stringData(Containers::ArrayView<const void> data) const {
CORRADE_ASSERT(_mappingTypeStringType & Implementation::SceneMappingStringTypeMask,
"Trade::SceneFieldData::stringData(): the field is" << _field.data.type << Debug::nospace << ", not a string", {});
return static_cast<const char*>(_flags & SceneFieldFlag::OffsetOnly ? data.data() : _fieldData.pointer) + extractStringFieldOffset(_field.strideOffset);
}
Containers::Array<SceneFieldData> sceneFieldDataNonOwningArray(const Containers::ArrayView<const SceneFieldData> view) {
@ -603,8 +705,8 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
large enough to reference all objects (checked outside of the loop
above) and that it's the same for all fields. This also makes it
more convenient for the user. */
CORRADE_ASSERT(field._mappingType == _mappingType,
"Trade::SceneData: inconsistent mapping type, got" << field._mappingType << "for field" << i << "but expected" << _mappingType, );
CORRADE_ASSERT(field.mappingType() == _mappingType,
"Trade::SceneData: inconsistent mapping type, got" << field.mappingType() << "for field" << i << "but expected" << _mappingType, );
/* We could check that object indices are in bounds, but that's rather
expensive. OTOH it's fine if field size is larger than object count,
@ -631,8 +733,8 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
accessing the memory would be invalid anyway and enforcing this
would lead to unnecessary friction with optional fields. */
if(field._size) {
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)*
(field._fieldArraySize ? field._fieldArraySize : 1);
const UnsignedShort fieldArraySize = field.fieldArraySize();
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field.fieldType())*(fieldArraySize ? fieldArraySize : 1);
/* Both pointer and offset-only rely on basically same calculation,
do it with offsets in a single place and only interpret as
@ -641,7 +743,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
std::size_t mappingBegin = field._mappingData.offset;
std::size_t fieldBegin = field._fieldData.offset;
std::size_t mappingEnd = mappingBegin + (field._size - 1)*field._mappingStride;
std::size_t fieldEnd = fieldBegin + (field._size - 1)*field._fieldStride;
std::size_t fieldEnd = fieldBegin + (field._size - 1)*field._field.data.stride;
/* Flip for negative strides */
if(mappingBegin > mappingEnd) std::swap(mappingBegin, mappingEnd);
if(fieldBegin > fieldEnd) std::swap(fieldBegin, fieldEnd);
@ -660,6 +762,15 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
CORRADE_ASSERT(reinterpret_cast<const void*>(fieldBegin) >= _data.begin() && reinterpret_cast<const void*>(fieldEnd) <= _data.end(),
"Trade::SceneData: field data [" << Debug::nospace << reinterpret_cast<const void*>(fieldBegin) << Debug::nospace << ":" << Debug::nospace << reinterpret_cast<const void*>(fieldEnd) << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
}
/* If a string field, check that the string data pointer is in
bounds. Not checking the offsets/sizes in the field itself
though, as that'd be prohibitively expensive. */
if(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask) {
const char* const stringData = field.stringData(_data);
CORRADE_ASSERT(stringData >= _data.begin() && stringData <= _data.end(),
"Trade::SceneData: field string data" << reinterpret_cast<const void*>(stringData) << "of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
}
}
#endif
@ -693,7 +804,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
"begin" was always zero, here we're always comparing four values, so the
message for offset-only wouldn't be simpler either. */
const auto checkFieldMappingDataMatch = [](const SceneFieldData& a, const SceneFieldData& b) {
const std::size_t mappingTypeSize = sceneMappingTypeSize(a._mappingType);
const std::size_t mappingTypeSize = sceneMappingTypeSize(a.mappingType());
const void* const aBegin = a._mappingData.pointer;
const void* const bBegin = b._mappingData.pointer;
const void* const aEnd = static_cast<const char*>(a._mappingData.pointer) + a._size*mappingTypeSize;
@ -719,7 +830,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
/* Decide about dimensionality based on transformation type, if present */
if(transformationField != ~UnsignedInt{}) {
const SceneFieldType fieldType = _fields[transformationField]._fieldType;
const SceneFieldType fieldType = _fields[transformationField]._field.data.type;
if(fieldType == SceneFieldType::Matrix3x3 ||
fieldType == SceneFieldType::Matrix3x3d ||
fieldType == SceneFieldType::Matrix3x2 ||
@ -740,7 +851,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
/* Use TRS fields to decide about dimensionality, if the transformation
field is not present. If it is, verify that they match it. */
if(translationField != ~UnsignedInt{}) {
const SceneFieldType fieldType = _fields[translationField]._fieldType;
const SceneFieldType fieldType = _fields[translationField]._field.data.type;
if(fieldType == SceneFieldType::Vector2 ||
fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT(!_dimensions || _dimensions == 2,
@ -754,7 +865,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
if(rotationField != ~UnsignedInt{}) {
const SceneFieldType fieldType = _fields[rotationField]._fieldType;
const SceneFieldType fieldType = _fields[rotationField]._field.data.type;
if(fieldType == SceneFieldType::Complex ||
fieldType == SceneFieldType::Complexd) {
CORRADE_ASSERT(!_dimensions || _dimensions == 2,
@ -768,7 +879,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
if(scalingField != ~UnsignedInt{}) {
const SceneFieldType fieldType = _fields[scalingField]._fieldType;
const SceneFieldType fieldType = _fields[scalingField]._field.data.type;
if(fieldType == SceneFieldType::Vector2 ||
fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT(!_dimensions || _dimensions == 2,
@ -875,8 +986,8 @@ Containers::StridedArrayView1D<const void> SceneData::fieldDataFieldViewInternal
/* We're *sure* the view is correct, so faking the view size */
{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};
+ field._field.data.stride*offset, ~std::size_t{}},
size, field._field.data.stride};
}
Containers::StridedArrayView1D<const void> SceneData::fieldDataFieldViewInternal(const SceneFieldData& field) const {
@ -894,7 +1005,11 @@ 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, field._flags & ~SceneFieldFlag::OffsetOnly};
const SceneFieldFlags flags = field._flags & ~SceneFieldFlag::OffsetOnly;
if(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask)
return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field.stringData(_data), field.fieldType(), fieldDataFieldViewInternal(field), flags};
else
return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field._field.data.type, fieldDataFieldViewInternal(field), field._field.data.arraySize, flags};
}
SceneField SceneData::fieldName(const UnsignedInt id) const {
@ -912,7 +1027,7 @@ SceneFieldFlags SceneData::fieldFlags(const UnsignedInt id) const {
SceneFieldType SceneData::fieldType(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldType(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._fieldType;
return _fields[id].fieldType();
}
std::size_t SceneData::fieldSize(const UnsignedInt id) const {
@ -924,7 +1039,7 @@ std::size_t SceneData::fieldSize(const UnsignedInt id) const {
UnsignedShort SceneData::fieldArraySize(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldArraySize(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._fieldArraySize;
return _fields[id].fieldArraySize();
}
UnsignedInt SceneData::findFieldIdInternal(const SceneField name) const {
@ -985,13 +1100,14 @@ template<class T> std::size_t findObject(const SceneFieldFlags flags, const Cont
std::size_t SceneData::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedLong object, const std::size_t offset) const {
const Containers::StridedArrayView1D<const void> mapping = fieldDataMappingViewInternal(field, offset, field._size - offset);
if(field._mappingType == SceneMappingType::UnsignedInt)
const SceneMappingType mappingType = field.mappingType();
if(mappingType == SceneMappingType::UnsignedInt)
return offset + findObject<UnsignedInt>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedShort)
else if(mappingType == SceneMappingType::UnsignedShort)
return offset + findObject<UnsignedShort>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedByte)
else if(mappingType == SceneMappingType::UnsignedByte)
return offset + findObject<UnsignedByte>(field._flags, mapping, offset, object);
else if(field._mappingType == SceneMappingType::UnsignedLong)
else if(mappingType == SceneMappingType::UnsignedLong)
return offset + findObject<UnsignedLong>(field._flags, mapping, offset, object);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1091,7 +1207,7 @@ SceneFieldFlags SceneData::fieldFlags(const SceneField name) const {
SceneFieldType SceneData::fieldType(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {});
return _fields[fieldId]._fieldType;
return _fields[fieldId].fieldType();
}
std::size_t SceneData::fieldSize(const SceneField name) const {
@ -1103,7 +1219,7 @@ std::size_t SceneData::fieldSize(const SceneField name) const {
UnsignedShort SceneData::fieldArraySize(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {});
return _fields[fieldId]._fieldArraySize;
return _fields[fieldId].fieldArraySize();
}
Containers::StridedArrayView2D<const char> SceneData::mapping(const UnsignedInt fieldId) const {
@ -1113,7 +1229,7 @@ Containers::StridedArrayView2D<const char> SceneData::mapping(const UnsignedInt
/* Build a 2D view using information about mapping type size */
return Containers::arrayCast<2, const char>(
fieldDataMappingViewInternal(field),
sceneMappingTypeSize(field._mappingType));
sceneMappingTypeSize(field.mappingType()));
}
Containers::StridedArrayView2D<char> SceneData::mutableMapping(const UnsignedInt fieldId) {
@ -1125,7 +1241,7 @@ Containers::StridedArrayView2D<char> SceneData::mutableMapping(const UnsignedInt
/* Build a 2D view using information about attribute type size */
const auto out = Containers::arrayCast<2, const char>(
fieldDataMappingViewInternal(field),
sceneMappingTypeSize(field._mappingType));
sceneMappingTypeSize(field.mappingType()));
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the
@ -1152,10 +1268,11 @@ Containers::StridedArrayView2D<const char> SceneData::field(const UnsignedInt id
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
const UnsignedShort fieldArraySize = field.fieldArraySize();
/* Build a 2D view using information about mapping type size */
return Containers::arrayCast<2, const char>(
fieldDataFieldViewInternal(field),
sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1));
sceneFieldTypeSize(field.fieldType())*(fieldArraySize ? fieldArraySize : 1));
}
Containers::StridedArrayView2D<char> SceneData::mutableField(const UnsignedInt id) {
@ -1164,10 +1281,11 @@ Containers::StridedArrayView2D<char> SceneData::mutableField(const UnsignedInt i
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::mutableField(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
const UnsignedShort fieldArraySize = field.fieldArraySize();
/* Build a 2D view using information about attribute type size */
const auto out = Containers::arrayCast<2, const char>(
fieldDataFieldViewInternal(field),
sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1));
sceneFieldTypeSize(field.fieldType())*(fieldArraySize ? fieldArraySize : 1));
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the
@ -1192,6 +1310,123 @@ Containers::StridedArrayView2D<char> SceneData::mutableField(const SceneField na
return mutableField(fieldId);
}
const char* SceneData::fieldDataStringDataInternal(const SceneFieldData& field) const {
return static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ? _data.data() : field._fieldData.pointer) + extractStringFieldOffset(field._field.strideOffset);
}
const char* SceneData::fieldStringData(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldStringData(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask,
"Trade::SceneData::fieldStringData():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a string", {});
return fieldDataStringDataInternal(field);
}
const char* SceneData::fieldStringData(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::fieldStringData(): field" << name << "not found", {});
return fieldStringData(fieldId);
}
namespace {
/* These two take the `SceneFieldData` as a context pointer, instead of
`const char*`, in order to special-case the first element */
template<class T> Containers::StringView fieldStringsAccessorOffset(const void* const data, const void* const context, const std::ptrdiff_t stride, const std::size_t i) {
const std::size_t currentOffset = *static_cast<const T*>(data);
const std::size_t prevOffset = i == 0 ? 0 : *reinterpret_cast<const T*>(static_cast<const char*>(data) - stride);
return Containers::StringView{static_cast<const char*>(context) + prevOffset, currentOffset - prevOffset};
}
template<class T> Containers::StringView fieldStringsAccessorNullTerminatedOffset(const void* const data, const void* const context, const std::ptrdiff_t stride, const std::size_t i) {
const std::size_t currentOffset = *static_cast<const T*>(data);
const std::size_t prevOffset = i == 0 ? 0 : *reinterpret_cast<const T*>(static_cast<const char*>(data) - stride);
return Containers::StringView{static_cast<const char*>(context) + prevOffset, currentOffset - prevOffset - 1, Containers::StringViewFlag::NullTerminated};
}
template<class T> Containers::StringView fieldStringsAccessorRange(const void* const data, const void* const context, std::ptrdiff_t, std::size_t) {
const auto& dataI = *static_cast<const Containers::Pair<T, T>*>(data);
return Containers::StringView{static_cast<const char*>(context) + dataI.first(), std::size_t(dataI.second())};
}
/* The difference between the two is that the first is for
SceneFieldType::StringRange* + SceneFieldFlag::NullTerminatedString, while
the second for SceneFieldType::StringRange*NullTerminated */
template<class T> Containers::StringView fieldStringsAccessorNullTerminatedRange(const void* data, const void* context, std::ptrdiff_t, std::size_t) {
const auto& dataI = *static_cast<const Containers::Pair<T, T>*>(data);
return Containers::StringView{static_cast<const char*>(context) + dataI.first(), std::size_t(dataI.second()), Containers::StringViewFlag::NullTerminated};
}
template<class T> Containers::StringView fieldStringsAccessorRangeNullTerminated(const void* data, const void* context, std::ptrdiff_t, std::size_t) {
const auto& dataI = *static_cast<const T*>(data);
/* StringViewFlag::NullTerminated is added implicitly, as the function
uses strlen() to calculate the size */
return Containers::StringView{static_cast<const char*>(context) + dataI};
}
}
Containers::StringIterable SceneData::fieldStrings(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldStrings(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask,
"Trade::SceneData::fieldStrings():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a string", {});
/* Decide on the accessor callback */
const SceneFieldType type = field.fieldType();
Containers::StringView(*accessor)(const void*, const void*, std::ptrdiff_t, std::size_t);
if(type == SceneFieldType::StringOffset8)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedOffset<UnsignedByte> :
fieldStringsAccessorOffset<UnsignedByte>;
else if(type == SceneFieldType::StringOffset16)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedOffset<UnsignedShort> :
fieldStringsAccessorOffset<UnsignedShort>;
else if(type == SceneFieldType::StringOffset32)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedOffset<UnsignedInt> :
fieldStringsAccessorOffset<UnsignedInt>;
else if(type == SceneFieldType::StringOffset64)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedOffset<UnsignedLong> :
fieldStringsAccessorOffset<UnsignedLong>;
else if(type == SceneFieldType::StringRange8)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedRange<UnsignedByte> :
fieldStringsAccessorRange<UnsignedByte>;
else if(type == SceneFieldType::StringRange16)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedRange<UnsignedShort> :
fieldStringsAccessorRange<UnsignedShort>;
else if(type == SceneFieldType::StringRange32)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedRange<UnsignedInt> :
fieldStringsAccessorRange<UnsignedInt>;
else if(type == SceneFieldType::StringRange64)
accessor = field._flags & SceneFieldFlag::NullTerminatedString ?
fieldStringsAccessorNullTerminatedRange<UnsignedLong> :
fieldStringsAccessorRange<UnsignedLong>;
else if(type == SceneFieldType::StringRangeNullTerminated8)
accessor = fieldStringsAccessorRangeNullTerminated<UnsignedByte>;
else if(type == SceneFieldType::StringRangeNullTerminated16)
accessor = fieldStringsAccessorRangeNullTerminated<UnsignedShort>;
else if(type == SceneFieldType::StringRangeNullTerminated32)
accessor = fieldStringsAccessorRangeNullTerminated<UnsignedInt>;
else if(type == SceneFieldType::StringRangeNullTerminated64)
accessor = fieldStringsAccessorRangeNullTerminated<UnsignedLong>;
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
const Containers::StridedArrayView1D<const void> data = fieldDataFieldViewInternal(field);
return Containers::StringIterable{data.data(), fieldDataStringDataInternal(field), data.size(), data.stride(), accessor};
}
Containers::StringIterable SceneData::fieldStrings(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::fieldStrings(): field" << name << "not found", {});
return fieldStrings(fieldId);
}
void SceneData::mappingIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
/* fieldId, offset and destination.size() is assumed to be in bounds,
checked by the callers */
@ -1199,14 +1434,15 @@ void SceneData::mappingIntoInternal(const UnsignedInt fieldId, const std::size_t
const SceneFieldData& field = _fields[fieldId];
const Containers::StridedArrayView1D<const void> mappingData = fieldDataMappingViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);
const SceneMappingType mappingType = field.mappingType();
if(field._mappingType == SceneMappingType::UnsignedInt)
if(mappingType == SceneMappingType::UnsignedInt)
Utility::copy(Containers::arrayCast<const UnsignedInt>(mappingData), destination);
else if(field._mappingType == SceneMappingType::UnsignedShort)
else if(mappingType == SceneMappingType::UnsignedShort)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(mappingData, 1), destination1ui);
else if(field._mappingType == SceneMappingType::UnsignedByte)
else if(mappingType == SceneMappingType::UnsignedByte)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(mappingData, 1), destination1ui);
else if(field._mappingType == SceneMappingType::UnsignedLong) {
else if(mappingType == SceneMappingType::UnsignedLong) {
Math::castInto(Containers::arrayCast<2, const UnsignedLong>(mappingData, 1), destination1ui);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1270,13 +1506,13 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1i = Containers::arrayCast<2, Int>(destination);
if(field._fieldType == SceneFieldType::Int)
if(field._field.data.type == SceneFieldType::Int)
Utility::copy(Containers::arrayCast<const Int>(fieldData), destination);
else if(field._fieldType == SceneFieldType::Short)
else if(field._field.data.type == SceneFieldType::Short)
Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1i);
else if(field._fieldType == SceneFieldType::Byte)
else if(field._field.data.type == SceneFieldType::Byte)
Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1i);
else if(field._fieldType == SceneFieldType::Long) {
else if(field._field.data.type == SceneFieldType::Long) {
Math::castInto(Containers::arrayCast<2, const Long>(fieldData, 1), destination1i);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1435,17 +1671,17 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1f = Containers::arrayCast<2, Float>(destination);
if(field._fieldType == SceneFieldType::Matrix3x3) {
if(field._field.data.type == SceneFieldType::Matrix3x3) {
Utility::copy(Containers::arrayCast<const Matrix3>(fieldData), destination);
} else if(field._fieldType == SceneFieldType::Matrix3x3d) {
} else if(field._field.data.type == SceneFieldType::Matrix3x3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 9), destination1f);
} else if(field._fieldType == SceneFieldType::Matrix3x2) {
} else if(field._field.data.type == SceneFieldType::Matrix3x2) {
expandTransformationMatrix<Matrix3x2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix3x2d) {
} else if(field._field.data.type == SceneFieldType::Matrix3x2d) {
expandTransformationMatrix<Matrix3x2d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualComplex) {
} else if(field._field.data.type == SceneFieldType::DualComplex) {
convertTransformation<DualComplex>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualComplexd) {
} else if(field._field.data.type == SceneFieldType::DualComplexd) {
convertTransformation<DualComplexd>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
@ -1460,9 +1696,9 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector2) {
if(field._field.data.type == SceneFieldType::Vector2) {
applyScaling<Vector2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
} else if(field._field.data.type == SceneFieldType::Vector2d) {
applyScaling<Vector2d>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1472,9 +1708,9 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Complex) {
if(field._field.data.type == SceneFieldType::Complex) {
applyRotation<Complex>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Complexd) {
} else if(field._field.data.type == SceneFieldType::Complexd) {
applyRotation<Complexd>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1484,9 +1720,9 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector2) {
if(field._field.data.type == SceneFieldType::Vector2) {
applyTranslation<Vector2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
} else if(field._field.data.type == SceneFieldType::Vector2d) {
applyTranslation<Vector2d>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1556,9 +1792,9 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size());
if(field._fieldType == SceneFieldType::Vector2) {
if(field._field.data.type == SceneFieldType::Vector2) {
Utility::copy(Containers::arrayCast<const Vector2>(fieldData), translationDestination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
} else if(field._field.data.type == SceneFieldType::Vector2d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(translationDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1574,9 +1810,9 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size());
if(field._fieldType == SceneFieldType::Complex) {
if(field._field.data.type == SceneFieldType::Complex) {
Utility::copy(Containers::arrayCast<const Complex>(fieldData), rotationDestination);
} else if(field._fieldType == SceneFieldType::Complexd) {
} else if(field._field.data.type == SceneFieldType::Complexd) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(rotationDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1592,9 +1828,9 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size());
if(field._fieldType == SceneFieldType::Vector2) {
if(field._field.data.type == SceneFieldType::Vector2) {
Utility::copy(Containers::arrayCast<const Vector2>(fieldData), scalingDestination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
} else if(field._field.data.type == SceneFieldType::Vector2d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(scalingDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1681,17 +1917,17 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1f = Containers::arrayCast<2, Float>(destination);
if(field._fieldType == SceneFieldType::Matrix4x4) {
if(field._field.data.type == SceneFieldType::Matrix4x4) {
Utility::copy(Containers::arrayCast<const Matrix4>(fieldData), destination);
} else if(field._fieldType == SceneFieldType::Matrix4x4d) {
} else if(field._field.data.type == SceneFieldType::Matrix4x4d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 16), destination1f);
} else if(field._fieldType == SceneFieldType::Matrix4x3) {
} else if(field._field.data.type == SceneFieldType::Matrix4x3) {
expandTransformationMatrix<Matrix4x3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix4x3d) {
} else if(field._field.data.type == SceneFieldType::Matrix4x3d) {
expandTransformationMatrix<Matrix4x3d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualQuaternion) {
} else if(field._field.data.type == SceneFieldType::DualQuaternion) {
convertTransformation<DualQuaternion>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualQuaterniond) {
} else if(field._field.data.type == SceneFieldType::DualQuaterniond) {
convertTransformation<DualQuaterniond>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
@ -1706,9 +1942,9 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector3) {
if(field._field.data.type == SceneFieldType::Vector3) {
applyScaling<Vector3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
} else if(field._field.data.type == SceneFieldType::Vector3d) {
applyScaling<Vector3d>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1718,9 +1954,9 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Quaternion) {
if(field._field.data.type == SceneFieldType::Quaternion) {
applyRotation<Quaternion>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Quaterniond) {
} else if(field._field.data.type == SceneFieldType::Quaterniond) {
applyRotation<Quaterniond>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1730,9 +1966,9 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector3) {
if(field._field.data.type == SceneFieldType::Vector3) {
applyTranslation<Vector3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
} else if(field._field.data.type == SceneFieldType::Vector3d) {
applyTranslation<Vector3d>(fieldData, destination);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1802,9 +2038,9 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size());
if(field._fieldType == SceneFieldType::Vector3) {
if(field._field.data.type == SceneFieldType::Vector3) {
Utility::copy(Containers::arrayCast<const Vector3>(fieldData), translationDestination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
} else if(field._field.data.type == SceneFieldType::Vector3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(translationDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1820,9 +2056,9 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size());
if(field._fieldType == SceneFieldType::Quaternion) {
if(field._field.data.type == SceneFieldType::Quaternion) {
Utility::copy(Containers::arrayCast<const Quaternion>(fieldData), rotationDestination);
} else if(field._fieldType == SceneFieldType::Quaterniond) {
} else if(field._field.data.type == SceneFieldType::Quaterniond) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 4), Containers::arrayCast<2, Float>(rotationDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1838,9 +2074,9 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size());
if(field._fieldType == SceneFieldType::Vector3) {
if(field._field.data.type == SceneFieldType::Vector3) {
Utility::copy(Containers::arrayCast<const Vector3>(fieldData), scalingDestination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
} else if(field._field.data.type == SceneFieldType::Vector3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(scalingDestination));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1919,11 +2155,11 @@ void SceneData::unsignedIndexFieldIntoInternal(const UnsignedInt fieldId, const
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);
if(field._fieldType == SceneFieldType::UnsignedInt)
if(field._field.data.type == SceneFieldType::UnsignedInt)
Utility::copy(Containers::arrayCast<const UnsignedInt>(fieldData), destination);
else if(field._fieldType == SceneFieldType::UnsignedShort)
else if(field._field.data.type == SceneFieldType::UnsignedShort)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(fieldData, 1), destination1ui);
else if(field._fieldType == SceneFieldType::UnsignedByte)
else if(field._field.data.type == SceneFieldType::UnsignedByte)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(fieldData, 1), destination1ui);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -1936,11 +2172,11 @@ void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::siz
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, Int>(destination);
if(field._fieldType == SceneFieldType::Int)
if(field._field.data.type == SceneFieldType::Int)
Utility::copy(Containers::arrayCast<const Int>(fieldData), destination);
else if(field._fieldType == SceneFieldType::Short)
else if(field._field.data.type == SceneFieldType::Short)
Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1ui);
else if(field._fieldType == SceneFieldType::Byte)
else if(field._field.data.type == SceneFieldType::Byte)
Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1ui);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
@ -2136,8 +2372,8 @@ void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std::
checked by the callers */
const SceneFieldData& field = _fields[fieldId];
CORRADE_INTERNAL_ASSERT(field._fieldType == SceneFieldType::Pointer ||
field._fieldType == SceneFieldType::MutablePointer);
CORRADE_INTERNAL_ASSERT(field._field.data.type == SceneFieldType::Pointer ||
field._field.data.type == SceneFieldType::MutablePointer);
Utility::copy(Containers::arrayCast<const void* const>(fieldDataFieldViewInternal(field, offset, destination.size())), destination);
}

703
src/Magnum/Trade/SceneData.h

@ -61,8 +61,20 @@ enum class SceneMappingType: UnsignedByte {
UnsignedShort, /**< @relativeref{Magnum,UnsignedShort} */
UnsignedInt, /**< @relativeref{Magnum,UnsignedInt} */
UnsignedLong /**< @relativeref{Magnum,UnsignedLong} */
/* Bits 3-6 used to store `SceneFieldType::String* << 3` in
SceneFieldData::_mappingTypeStringType */
};
namespace Implementation {
enum: UnsignedByte {
SceneMappingTypeMask = 0x07, /* covers SceneMappingType values */
SceneMappingStringTypeMask = 0xf8 /* covers `SceneFieldType::String* << 3` */
};
}
/**
@debugoperatorenum{SceneMappingType}
@m_since_latest
@ -382,9 +394,12 @@ information.
enum class SceneFieldType: UnsignedShort {
/* Zero used for an invalid value */
/* 1 reserved for Bool (Bit?), which needs [Strided]BitArray[View] first */
/* 1-12 used by String* types defined below as they need to fit into 4 bits
to be stored in a single byte together with SceneMappingType */
Float = 2, /**< @relativeref{Magnum,Float} */
/* 13 reserved for Bit, which needs StridedBitArrayView first */
Float = 14, /**< @relativeref{Magnum,Float} */
Half, /**< @relativeref{Magnum,Half} */
Double, /**< @relativeref{Magnum,Double} */
UnsignedByte, /**< @relativeref{Magnum,UnsignedByte} */
@ -508,6 +523,156 @@ enum class SceneFieldType: UnsignedShort {
* an arbitrary `T` but the user has to ensure the type is correct.
*/
MutablePointer,
/* String types defined at the end because of exquisite complexity */
/**
* 32-bit string offsets with implicit sizes. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* Internally, the first string starts at @ref SceneData::fieldStringData(),
* second string starts at @relativeref{SceneData,fieldStringData()} plus
* @ref SceneData::field() "field<UnsignedInt>()[0]", etc. String sizes are
* implicitly the distance between two successive offsets or
* @cpp field<UnsignedInt>()[0] @ce in case of the first string; if
* @ref SceneFieldFlag::NullTerminatedString is set the distance includes
* the null terminator.
*
* The `StringOffset*` types are useful mainly for cases where each string
* is unique, for strings with many duplicates
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" or
* @ref SceneFieldType::StringRangeNullTerminated32 "StringRangeNullTerminated32*"
* may be a more space-efficient option.
*/
StringOffset32 = 3,
/**
* 8-bit string offsets with implicit sizes. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringOffset32
* except that @relativeref{Magnum,UnsignedByte} is used as the type, see
* its documentation for more information.
*/
StringOffset8 = 1,
/**
* 16-bit string offsets with implicit sizes. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringOffset32
* except that @relativeref{Magnum,UnsignedShort} is used as the type, see
* its documentation for more information.
*/
StringOffset16 = 2,
/**
* 64-bit string offsets with implicit sizes. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringOffset32
* except that @relativeref{Magnum,UnsignedLong} is used as the type, see
* its documentation for more information.
*/
StringOffset64 = 4,
/**
* @relativeref{Corrade,Containers::Pair} of 32-bit string offsets and
* sizes. Use @ref SceneData::fieldStrings() for convenient access.
*
* Internally, string `i` starts at @ref SceneData::fieldStringData() plus
* @ref SceneData::field() "field<Containers::Pair<UnsignedInt, UnsignedInt>>()[i].first()"
* and has a size of @cpp field<Containers::Pair<UnsignedInt, UnsignedInt>>()[i].second() @ce.
*
* The main use case for `StringRange*` types is to be able to reference
* the same string from multiple field entries without having to duplicate
* it. For strings without duplicates
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*" may
* be a more space-efficient option, as the size is implicit.
* Alternatively, @ref SceneFieldType::StringRangeNullTerminated32 "StringRangeNullTerminated32*"
* has the same space requirements as
* @ref SceneFieldType::StringOffset32 "StringOffset*" with
* @ref SceneFieldFlag::NullTerminatedString set and allows deduplication,
* however at the cost of a @ref std::strlen() call on every access.
*/
StringRange32 = 7,
/**
* @relativeref{Corrade,Containers::Pair} of 8-bit string offsets and
* sizes. Use @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringRange32
* except that @relativeref{Magnum,UnsignedByte} is used for the pair
* types, see its documentation for more information.
*/
StringRange8 = 5,
/**
* @relativeref{Corrade,Containers::Pair} of 16-bit string offsets and
* sizes. Use @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringRange32
* except that @relativeref{Magnum,UnsignedShort} is used for the pair
* types, see its documentation for more information.
*/
StringRange16 = 6,
/**
* @relativeref{Corrade,Containers::Pair} of 64-bit string offsets and
* sizes. Use @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to @ref SceneFieldType::StringRange32
* except that @relativeref{Magnum,UnsignedLong} is used for the pair
* types, see its documentation for more information.
*/
StringRange64 = 8,
/**
* 32-bit offsets to null-terminated strings. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* Compared to @ref SceneFieldType::StringRange32 stores just the offset,
* the size is calculated on-the-fly with @ref std::strlen().
*
* Internally, string `i` starts at @ref SceneData::fieldStringData() plus
* @ref SceneData::field() "field<UnsignedInt>()[i]", size is implicitly
* until the first @cpp '\0' @ce byte. See
* @ref SceneFieldType::StringRange32 for use case recommendations.
*/
StringRangeNullTerminated32 = 11,
/**
* 8-bit offsets to null-terminated strings. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to
* @ref SceneFieldType::StringRangeNullTerminated32 except that
* @relativeref{Magnum,UnsignedByte} is used as the type, see its
* documentation for more information.
*/
StringRangeNullTerminated8 = 9,
/**
* 16-bit offsets to null-terminated strings. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to
* @ref SceneFieldType::StringRangeNullTerminated32 except that
* @relativeref{Magnum,UnsignedShort} is used as the type, see its
* documentation for more information.
*/
StringRangeNullTerminated16 = 10,
/**
* 32-bit offsets to null-terminated strings. Use
* @ref SceneData::fieldStrings() for convenient access.
*
* The internal layout is similar to
* @ref SceneFieldType::StringRangeNullTerminated32 except that
* @relativeref{Magnum,UnsignedLong} is used as the type, see its
* documentation for more information.
*/
StringRangeNullTerminated64 = 12,
};
/**
@ -582,6 +747,22 @@ enum class SceneFieldFlag: UnsignedByte {
* @f$ \mathcal{O}(n) @f$ lookup complexity.
*/
ImplicitMapping = (1 << 2)|OrderedMapping,
/**
* The string field is null-terminated, i.e. string views returned from
* @ref SceneData::fieldStrings() will always have
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} set.
* Internally it means that the distance between successive
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*"
* entries includes the null terminator;
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" size
* is excluding the null terminator but assumes it's present right after;
* for @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType,StringRangeNullTerminated*"
* it's set implicitly as that's the default behavior.
*
* Can only be set for string @ref SceneFieldType.
*/
NullTerminatedString = 1 << 3
};
/**
@ -610,8 +791,8 @@ 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.
Convenience type for populating @ref SceneData, see
@ref Trade-SceneData-populating "its documentation" for an introduction.
@section Trade-SceneFieldData-usage Usage
@ -653,6 +834,20 @@ 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}.
@subsection Trade-SceneFieldData-usage-strings String fields
String fields have to be constructed using dedicated constructors that
additionally take a @cpp const char* @ce base string pointer, and because a
particular type can correspond to more than one @ref SceneFieldType (such as
@ref SceneFieldType::StringOffset32 and
@ref SceneFieldType::StringRangeNullTerminated32 being both represented with an
@relativeref{Magnum,UnsignedInt}), the type has to be specified explicitly:
@snippet MagnumTrade.cpp SceneFieldData-usage-strings
Offset-only constructors have it similar, containing an extra base string
offset. Due to packing in the internal layout, string fields can't be arrays.
*/
class MAGNUM_TRADE_EXPORT SceneFieldData {
public:
@ -663,7 +858,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{}, _flags{}, _mappingType{}, _mappingStride{}, _mappingData{}, _fieldStride{}, _fieldType{}, _fieldArraySize{}, _fieldData{} {}
constexpr explicit SceneFieldData() noexcept: _size{}, _name{}, _flags{}, _mappingTypeStringType{}, _mappingStride{}, _mappingData{}, _field{}, _fieldData{} {}
/**
* @brief Construct from type-erased views
@ -675,7 +870,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is not allowed here.
* @ref SceneFieldFlag::OffsetOnly and
* @ref SceneFieldFlag::NullTerminatedString is not allowed here.
*
* Expects that @p mappingData and @p fieldData have the same size; and
* for builtin fields that @p fieldType corresponds to @p name and
@ -695,7 +891,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is not allowed here.
* @ref SceneFieldFlag::OffsetOnly and
* @ref SceneFieldFlag::NullTerminatedString 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
@ -715,8 +912,9 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is
* not allowed here.
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString 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, SceneFieldFlags).
@ -740,8 +938,9 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is
* not allowed here.
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString 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, SceneFieldFlags)
@ -758,6 +957,87 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/** @overload */
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 a string field from type-erased views
* @param name Field name
* @param mappingType Object mapping type
* @param mappingData Object mapping data
* @param stringData String to which the field offset or range data
* are relative to
* @param fieldType Field type. Only `SceneFieldType::String*`
* values are allowed here.
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is
* not allowed here. @ref SceneFieldFlag::NullTerminatedString is
* set implicitly for
* @ref SceneFieldType::StringRangeNullTerminated8,
* @relativeref{SceneFieldType,StringRangeNullTerminated16},
* @relativeref{SceneFieldType,StringRangeNullTerminated32} and
* @relativeref{SceneFieldType,StringRangeNullTerminated64}.
*
* 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.
*/
/* While a StringView could provide the range assertions with more
context inside the SceneFieldData constructor, the main range
checking happens in the SceneField constructor, at which point the
size would be gone anyway as SceneFieldData can store only the begin
pointer inside. Using it would also mean we'd need to include its
full definition in this header. */
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, SceneFieldFlags flags = {}) noexcept;
/**
* @brief Construct a string field from 2D views
* @param name Field name
* @param mappingData Object mapping data
* @param stringData String to which the field offset or range data
* are relative to
* @param fieldType Field type. Only `SceneFieldType::String*`
* values are allowed here.
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is
* not allowed here. @ref SceneFieldFlag::NullTerminatedString is
* set implicitly for
* @ref SceneFieldType::StringRangeNullTerminated8,
* @relativeref{SceneFieldType,StringRangeNullTerminated16},
* @relativeref{SceneFieldType,StringRangeNullTerminated32} and
* @relativeref{SceneFieldType,StringRangeNullTerminated64}.
*
* 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 for builtin fields @p fieldType
* corresponds to @p name and @p fieldArraySize is zero.
*/
/* See above for why const char* is used instead of StringView */
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, SceneFieldFlags flags = {}) noexcept;
/**
* @brief Construct a string field
* @param name Field name
* @param mappingData Object mapping data
* @param stringData String to which the field offset or range data
* are relative to
* @param fieldType Field type. Only `SceneFieldType::String*`
* values are allowed here.
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly is
* not allowed here. @ref SceneFieldFlag::NullTerminatedString is
* set implicitly for
* @ref SceneFieldType::StringRangeNullTerminated8,
* @relativeref{SceneFieldType,StringRangeNullTerminated16},
* @relativeref{SceneFieldType,StringRangeNullTerminated32} and
* @relativeref{SceneFieldType,StringRangeNullTerminated64}.
*/
/* See above for why const char* is used instead of StringView */
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, SceneFieldFlags flags = {}) noexcept;
/** @overload */
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), stringData, fieldType, fieldData, flags} {}
/**
* @brief Construct an offset-only field
* @param name Field name
@ -772,6 +1052,7 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is set implicitly.
* @ref SceneFieldFlag::NullTerminatedString is not allowed here.
*
* Instances created this way refer to offsets in unspecified
* external scene data instead of containing the data views directly.
@ -793,6 +1074,44 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/** @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 Construct an offset-only string 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 stringOffset String data offset to which the field
* offset or range data are relative to
* @param fieldType Field type. Only `SceneFieldType::String*`
* values are allowed here.
* @param fieldOffset Field data offset
* @param fieldStride Field data stride
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is set implicitly.
* @ref SceneFieldFlag::NullTerminatedString is set implicitly for
* @ref SceneFieldType::StringRangeNullTerminated8,
* @relativeref{SceneFieldType,StringRangeNullTerminated16},
* @relativeref{SceneFieldType,StringRangeNullTerminated32} and
* @relativeref{SceneFieldType,StringRangeNullTerminated64}.
*
* 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 for builtin fields
* @p fieldType corresponds to @p name and @p fieldArraySize is zero.
*
* Note that due to the @cpp constexpr @ce nature of this constructor,
* no @p mappingType checks against @p mappingStride or
* @p fieldType 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, SceneFieldFlags)
* constructor if you want additional safeguards.
* @see @ref flags(),
* @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, std::size_t stringOffset, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags = {}) noexcept;
/** @brief Field flags */
constexpr SceneFieldFlags flags() const { return _flags; }
@ -803,7 +1122,9 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
constexpr UnsignedLong size() const { return _size; }
/** @brief Object mapping type */
constexpr SceneMappingType mappingType() const { return _mappingType; }
constexpr SceneMappingType mappingType() const {
return SceneMappingType(_mappingTypeStringType & Implementation::SceneMappingTypeMask);
}
/**
* @brief Type-erased object mapping data
@ -825,10 +1146,16 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
Containers::StridedArrayView1D<const void> mappingData(Containers::ArrayView<const void> data) const;
/** @brief Field type */
constexpr SceneFieldType fieldType() const { return _fieldType; }
SceneFieldType fieldType() const {
return _mappingTypeStringType & Implementation::SceneMappingStringTypeMask ?
SceneFieldType((_mappingTypeStringType & Implementation::SceneMappingStringTypeMask) >> 3) : _field.data.type;
}
/** @brief Field array size */
constexpr UnsignedShort fieldArraySize() const { return _fieldArraySize; }
UnsignedShort fieldArraySize() const {
return _mappingTypeStringType & Implementation::SceneMappingStringTypeMask ?
0 : _field.data.arraySize;
}
/**
* @brief Type-erased field data
@ -849,6 +1176,39 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
*/
Containers::StridedArrayView1D<const void> fieldData(Containers::ArrayView<const void> data) const;
/**
* @brief Base data pointer for a string field
*
* Offsets and ranges returned from @ref fieldData() are relative to
* this pointer. Can be only called on
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "StringRange*"
* and @ref SceneFieldType::StringRangeNullTerminated32 "StringRangeNullTerminated*"
* fields.
*
* Expects that the field does not have @ref SceneFieldFlag::OffsetOnly
* set, in that case use the @ref stringData(Containers::ArrayView<const void>) const
* overload instead.
* @see @ref flags()
*/
const char* stringData() const;
/**
* @brief Base data pointer for an offset-only string field
*
* Offsets and ranges returned from @ref SceneData::field() are
* relative to this pointer. Can be only called on
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "StringRange*"
* and @ref SceneFieldType::StringRangeNullTerminated32 "StringRangeNullTerminated*"
* fields.
*
* If the field does not have @ref SceneFieldFlag::OffsetOnly set, the
* @p data parameter is ignored.
* @see @ref flags(), @ref fieldData() const
*/
const char* stringData(Containers::ArrayView<const void> data) const;
private:
friend SceneData;
@ -866,14 +1226,49 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
UnsignedLong _size;
SceneField _name;
SceneFieldFlags _flags;
SceneMappingType _mappingType;
/* Contains SceneMappingType in the lower 3 bits. If the
next 4 bits are non-zero, they encode one of the
`SceneFieldType::String* << 3` values. 1 remaining bit unused. */
UnsignedByte _mappingTypeStringType;
Short _mappingStride;
Data _mappingData;
Short _fieldStride;
SceneFieldType _fieldType;
UnsignedShort _fieldArraySize;
/* 2 bytes free */
/* Contains either of the two following layouts depending on whether
_mappingTypeStringType marks this as a string field. In that case
the type is stored in _mappingTypeStringType already and the
remaining 6 bytes store a *signed* string data offset:
- if SceneFieldFlag::OffsetOnly is not set, relative to
_fieldData.pointer
- if OffsetOnly is set, absolute
The 6 bytes allow addressing up to ±128 TB of data, which should be
plenty, making it relative to _fieldData.pointer accounts for cases
where an absolute pointer itself wouldn't fit into 48 bits.
0 2 4 6 8 LE
+------------+------------+------------+------------+
| | type | array size | (unused) |
+ stride +------------+------------+------------+
| | string data offset |
+------------+--------------------------------------+
8 6 4 2 0 BE */
union Field {
/* C++, if you wouldn't be stupid, these constructors wouldn't be
needed. I just want to initialize one or the other union
field! */
constexpr explicit Field(): data{} {}
constexpr explicit Field(Short stride, SceneFieldType type, UnsignedShort arraySize): data{stride, type, arraySize} {}
constexpr explicit Field(Short stride, Long offset): strideOffset{(UnsignedLong(stride) & 0xffff)|((UnsignedLong(offset) & 0xffffffffffffull) << 16)} {}
struct {
Short stride;
SceneFieldType type;
UnsignedShort arraySize;
/* 2 bytes unused */
} data;
UnsignedLong strideOffset; /* Upper 48 bits on LE, lower 48 on BE */
} _field;
Data _fieldData;
};
@ -1142,6 +1537,57 @@ The light references are actually a 2D array (8 lights for each cell), so a
@snippet MagnumTrade.cpp SceneData-populating-custom-retrieve
@subsection Trade-SceneData-populating-strings String fields
Besides fixed-size types and their arrays, the @ref SceneData class is capable
of storing strings. A string field consists of a base pointer and a list
containing offsets or ranges relative to that base pointer. Such separation
allows more efficient storage compared to storing a full pointer (or a pair of
a pointer and size) for every field, as it's possible to choose a smaller
offset type if the referenced strings fit into a 8- or 16-bit range.
To cover different use cases, there's multiple ways how to store the string
references:
- @ref SceneFieldType::StringOffset32 and its 8-, 16- and 64-bit variants
store a running offset into the string array. For example, offsets
@cpp {3, 11, 23} @ce would mean the first string is from byte 0 to 3,
second is from byte 3 to 11 and third from byte 11 to 23. This storage type
is most efficient if the strings are unique for each entry --- such as
various names or external data filenames.
- @ref SceneFieldType::StringRange32 and its 8-, 16- and 64-bit variants
store a pair of offset and size. For example, ranges
@cpp {{11, 5}, {4, 7}, {11, 5}} @ce would mean the first and third string
is from byte 11 to 16 and the second string is from byte 4 to 11. This
storage type is thus most efficient when there's many duplicates --- such
as various object tags or categories.
- @ref SceneFieldType::StringRangeNullTerminated32 and its 8-, 16- and 64-bit
variants are similar to @ref SceneFieldType::StringRange32, but stores just
the offset and size is implicitly until the next @cpp '\0' @ce byte. Thus
it trades a slightly better space efficiency for the cost of a runtime
@ref std::strlen() call on every access.
String fields can also have @ref SceneFieldFlag::NullTerminatedString set, in
which case the returned @ref Containers::StringView instances will have
@relativeref{Corrade,Containers::StringViewFlag::NullTerminated} set, which may
be useful for example to avoid an allocation when passing filenames to OS APIs
in @ref Utility::Path::read(). The null terminators of course have to be stored
in the data itself, see a particular @ref SceneFieldType for information about
how it affects the field encoding.
The following example shows populating a @ref SceneData with a "tag" string
field, stored as null-terminated 8-bit string array ranges. In other words ---
assuming there's enough stored data --- the space efficiency is the same as if
a just a numeric value of an 8-bit @cpp enum @ce would be stored, but here it
includes human-readable string names.
@snippet MagnumTrade.cpp SceneData-populating-strings
While there's many options how to store the string, retrieving of any string
@ref SceneFieldType can be conveniently done using @ref fieldStrings():
@snippet MagnumTrade.cpp SceneData-populating-strings-retrieve
@see @ref AbstractImporter::scene()
*/
class MAGNUM_TRADE_EXPORT SceneData {
@ -1413,10 +1859,15 @@ class MAGNUM_TRADE_EXPORT SceneData {
* 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 field. 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.
* @ref SceneField is an array field. Additionally, fields with
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" or
* @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType::StringRangeNullTerminated*"
* can't be arrays, for them the function always returns @cpp 0 @ce.
*
* 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
@ -1890,6 +2341,70 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableField(SceneField name);
/**
* @brief Base data pointer for given string field
* @m_since_latest
*
* Raw string offsets and ranges returned from @ref field() are
* relative to this pointer. For more convenient access use
* @ref fieldStrings() instead. Expects that @p id is smaller than
* @ref fieldCount() const and that the field is
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" or
* @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType::StringRangeNullTerminated*".
* You can also use @ref fieldStringData(SceneField) const to directly
* get a data pointer for given named string field.
* @see @ref fieldType(UnsignedInt) const
*/
const char* fieldStringData(UnsignedInt id) const;
/**
* @brief Base data pointer for given named string field
* @m_since_latest
*
* Expects that @p name exists and is
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" or
* @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType::StringRangeNullTerminated*". See
* @ref fieldStringData(UnsignedInt) const for more information.
* @see @ref hasField()
*/
const char* fieldStringData(SceneField name) const;
/**
* @brief Contents of given string field
* @m_since_latest
*
* The returned views point to strings owned by this instance. If the
* field has @ref SceneFieldFlag::NullTerminatedString set, the
* returned views all have @relativeref{Corrade,Containers::StringViewFlag::NullTerminated}
* set. Expects that @p id is smaller than @ref fieldCount() const and
* that the field is
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*" or
* @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType::StringRangeNullTerminated*". You can also use
* @ref fieldStrings(SceneField) const to directly get contents of
* given named string field.
*
* The raw string data can be accessed using @ref fieldStringData() and
* @ref field(). See a particular @ref SceneFieldType for information
* about how to interpret the data.
*/
Containers::StringIterable fieldStrings(UnsignedInt id) const;
/**
* @brief Contents of given string field
* @m_since_latest
*
* Expects that @p name exists and that the field is
* @ref SceneFieldType::StringOffset32 "SceneFieldType::StringOffset*",
* @ref SceneFieldType::StringRange32 "SceneFieldType::StringRange*",
* @ref SceneFieldType::StringRangeNullTerminated32 "SceneFieldType::StringRangeNullTerminated*". See
* @ref fieldStrings(UnsignedInt) const for more information.
* @see @ref hasField()
*/
Containers::StringIterable fieldStrings(SceneField name) const;
/**
* @brief Object mapping for given field as 32-bit integers
* @m_since_latest
@ -2839,6 +3354,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D<const void> fieldDataMappingViewInternal(const SceneFieldData& field) const;
MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D<const void> fieldDataFieldViewInternal(const SceneFieldData& field, std::size_t offset, std::size_t size) const;
MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D<const void> fieldDataFieldViewInternal(const SceneFieldData& field) const;
/* Assertion-less helper for fieldStringData() and fieldStrings() */
MAGNUM_TRADE_LOCAL const char* fieldDataStringDataInternal(const SceneFieldData& field) const;
#ifndef CORRADE_NO_ASSERT
template<class T> bool checkFieldTypeCompatibility(const SceneFieldData& field, const char* prefix) const;
@ -2876,11 +3393,12 @@ namespace Implementation {
static_assert(sizeof(T) == 0, "unsupported field type");
};
#ifndef DOXYGEN_GENERATING_OUTPUT
#define _c(type_) template<> struct SceneFieldTypeFor<type_> { \
#define _cn(name, ...) template<> struct SceneFieldTypeFor<__VA_ARGS__> { \
constexpr static SceneFieldType type() { \
return SceneFieldType::type_; \
return SceneFieldType::name; \
} \
};
#define _c(type_) _cn(type_, type_)
/* Bool needs a special type */
_c(Float)
_c(Half)
@ -2973,7 +3491,12 @@ namespace Implementation {
_c(Rad)
_c(Radh)
_c(Radd)
_cn(StringRange8, Containers::Pair<UnsignedByte, UnsignedByte>)
_cn(StringRange16, Containers::Pair<UnsignedShort, UnsignedShort>)
_cn(StringRange32, Containers::Pair<UnsignedInt, UnsignedInt>)
_cn(StringRange64, Containers::Pair<UnsignedLong, UnsignedLong>)
#undef _c
#undef _cn
#endif
/** @todo this doesn't handle RectangleMatrix<size, size, T> and Vector<size, T> at the moment, do we need those? */
template<class T> struct SceneFieldTypeFor<Math::Color3<T>>: SceneFieldTypeFor<Math::Vector3<T>> {};
@ -2999,6 +3522,34 @@ namespace Implementation {
return type == SceneFieldTypeFor<T>::type();
}
};
template<> struct SceneFieldTypeTraits<UnsignedByte> {
constexpr static bool isCompatible(SceneFieldType type) {
return type == SceneFieldType::UnsignedByte ||
type == SceneFieldType::StringOffset8 ||
type == SceneFieldType::StringRangeNullTerminated8;
}
};
template<> struct SceneFieldTypeTraits<UnsignedShort> {
constexpr static bool isCompatible(SceneFieldType type) {
return type == SceneFieldType::UnsignedShort ||
type == SceneFieldType::StringOffset16 ||
type == SceneFieldType::StringRangeNullTerminated16;
}
};
template<> struct SceneFieldTypeTraits<UnsignedInt> {
constexpr static bool isCompatible(SceneFieldType type) {
return type == SceneFieldType::UnsignedInt ||
type == SceneFieldType::StringOffset32 ||
type == SceneFieldType::StringRangeNullTerminated32;
}
};
template<> struct SceneFieldTypeTraits<UnsignedLong> {
constexpr static bool isCompatible(SceneFieldType type) {
return type == SceneFieldType::UnsignedLong ||
type == SceneFieldType::StringOffset64 ||
type == SceneFieldType::StringRangeNullTerminated64;
}
};
template<class T> constexpr SceneMappingType sceneMappingTypeFor() {
static_assert(sizeof(T) == 0, "unsupported mapping type");
@ -3067,6 +3618,31 @@ namespace Implementation {
constexpr bool isSceneFieldArrayAllowed(SceneField name) {
return isSceneFieldCustom(name);
}
constexpr bool isSceneFieldTypeString(SceneFieldType type) {
return
type == SceneFieldType::StringOffset8 ||
type == SceneFieldType::StringOffset16 ||
type == SceneFieldType::StringOffset32 ||
type == SceneFieldType::StringOffset64 ||
type == SceneFieldType::StringRange8 ||
type == SceneFieldType::StringRange16 ||
type == SceneFieldType::StringRange32 ||
type == SceneFieldType::StringRange64 ||
type == SceneFieldType::StringRangeNullTerminated8 ||
type == SceneFieldType::StringRangeNullTerminated16 ||
type == SceneFieldType::StringRangeNullTerminated32 ||
type == SceneFieldType::StringRangeNullTerminated64;
}
constexpr SceneFieldFlags implicitSceneFieldFlagsFor(SceneFieldType type) {
return
type == SceneFieldType::StringRangeNullTerminated8 ||
type == SceneFieldType::StringRangeNullTerminated16 ||
type == SceneFieldType::StringRangeNullTerminated32 ||
type == SceneFieldType::StringRangeNullTerminated64 ?
SceneFieldFlag::NullTerminatedString : SceneFieldFlags{};
}
}
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:
@ -3074,17 +3650,19 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi
"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},
_flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)),
"Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)) << "for a view of" << fieldType), flags)},
_mappingTypeStringType{UnsignedByte(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()},
_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()))},
_fieldType{fieldType},
_fieldArraySize{(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_field{
(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())),
(CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: use a string constructor for" << fieldType), fieldType),
(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"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, 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} {}
@ -3100,20 +3678,59 @@ template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneF
flags
} {}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const char* const stringData, const SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, 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" << (flags & SceneFieldFlag::OffsetOnly) << "for a view"), flags|Implementation::implicitSceneFieldFlagsFor(fieldType))},
_mappingTypeStringType{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: can't use a string constructor for" << fieldType), UnsignedByte(UnsignedByte(mappingType)|(UnsignedShort(fieldType) << 3)))},
_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()},
_field{
(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())),
stringData - static_cast<const char*>(fieldData.data())},
_fieldData{fieldData.data()} {}
template<class T> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, stringData, fieldType, fieldData, 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},
_flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::NullTerminatedString),
"Trade::SceneFieldData: can't pass" << (flags & SceneFieldFlag::NullTerminatedString) << "for" << fieldType), flags|SceneFieldFlag::OffsetOnly)},
_mappingTypeStringType{UnsignedByte(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},
_field{
(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)),
(CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: use a string constructor for" << fieldType), fieldType),
(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_fieldData{fieldOffset} {}
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 std::size_t stringOffset, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, 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|Implementation::implicitSceneFieldFlagsFor(fieldType)},
_mappingTypeStringType{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: can't use a string constructor for" << fieldType), UnsignedByte(UnsignedByte(mappingType)|(UnsignedShort(fieldType) << 3)))},
_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},
_fieldStride{(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride))},
_fieldType{fieldType},
_fieldArraySize{(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_field{
(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)),
Long(stringOffset)},
_fieldData{fieldOffset} {}
template<class T> Containers::StridedArrayView1D<const T> SceneData::mapping(const UnsignedInt fieldId) const {
@ -3158,9 +3775,9 @@ template<class T> Containers::StridedArrayView1D<T> SceneData::mutableMapping(co
#ifndef CORRADE_NO_ASSERT
template<class T> bool SceneData::checkFieldTypeCompatibility(const SceneFieldData& field, const char* const prefix) const {
CORRADE_ASSERT(Implementation::SceneFieldTypeTraits<typename std::remove_extent<T>::type>::isCompatible(field._fieldType),
prefix << field._name << "is" << field._fieldType << "but requested a type equivalent to" << Implementation::SceneFieldTypeFor<typename std::remove_extent<T>::type>::type(), false);
if(field._fieldArraySize) CORRADE_ASSERT(std::is_array<T>::value,
CORRADE_ASSERT(Implementation::SceneFieldTypeTraits<typename std::remove_extent<T>::type>::isCompatible(field.fieldType()),
prefix << field._name << "is" << field.fieldType() << "but requested a type equivalent to" << Implementation::SceneFieldTypeFor<typename std::remove_extent<T>::type>::type(), false);
if(field.fieldArraySize()) CORRADE_ASSERT(std::is_array<T>::value,
prefix << field._name << "is an array field, use T[] to access it", false);
else CORRADE_ASSERT(!std::is_array<T>::value,
prefix << field._name << "is not an array field, can't use T[] to access it", false);

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

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save