You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1934 lines
106 KiB

/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "SceneData.h"
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Math/DualComplex.h"
#include "Magnum/Math/DualQuaternion.h"
#include "Magnum/Math/PackingBatch.h"
#include "Magnum/Trade/Implementation/arrayUtilities.h"
namespace Magnum { namespace Trade {
Debug& operator<<(Debug& debug, const SceneObjectType value) {
debug << "Trade::SceneObjectType" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(value) case SceneObjectType::value: return debug << "::" #value;
_c(UnsignedByte)
_c(UnsignedInt)
_c(UnsignedShort)
_c(UnsignedLong)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")";
}
UnsignedInt sceneObjectTypeSize(const SceneObjectType type) {
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif
switch(type) {
case SceneObjectType::UnsignedByte: return 1;
case SceneObjectType::UnsignedShort: return 2;
case SceneObjectType::UnsignedInt: return 4;
case SceneObjectType::UnsignedLong: return 8;
}
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic pop
#endif
CORRADE_ASSERT_UNREACHABLE("Trade::sceneObjectTypeSize(): invalid type" << type, {});
}
Debug& operator<<(Debug& debug, const SceneField value) {
debug << "Trade::SceneField" << Debug::nospace;
if(UnsignedInt(value) >= UnsignedInt(SceneField::Custom))
return debug << "::Custom(" << Debug::nospace << (UnsignedInt(value) - UnsignedInt(SceneField::Custom)) << Debug::nospace << ")";
switch(value) {
/* LCOV_EXCL_START */
#define _c(value) case SceneField::value: return debug << "::" #value;
_c(Parent)
_c(Transformation)
_c(Translation)
_c(Rotation)
_c(Scaling)
_c(Mesh)
_c(MeshMaterial)
_c(Light)
_c(Camera)
_c(Skin)
#undef _c
/* LCOV_EXCL_STOP */
/* To silence compiler warning about unhandled values */
case SceneField::Custom: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedInt(value)) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, const SceneFieldType value) {
debug << "Trade::SceneFieldType" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(value) case SceneFieldType::value: return debug << "::" #value;
_c(Float)
_c(Half)
_c(Double)
_c(UnsignedByte)
_c(Byte)
_c(UnsignedShort)
_c(Short)
_c(UnsignedInt)
_c(Int)
_c(UnsignedLong)
_c(Long)
_c(Vector2)
_c(Vector2h)
_c(Vector2d)
_c(Vector2ub)
_c(Vector2b)
_c(Vector2us)
_c(Vector2s)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3h)
_c(Vector3d)
_c(Vector3ub)
_c(Vector3b)
_c(Vector3us)
_c(Vector3s)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4h)
_c(Vector4d)
_c(Vector4ub)
_c(Vector4b)
_c(Vector4us)
_c(Vector4s)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x2h)
_c(Matrix2x2d)
_c(Matrix2x3)
_c(Matrix2x3h)
_c(Matrix2x3d)
_c(Matrix2x4)
_c(Matrix2x4h)
_c(Matrix2x4d)
_c(Matrix3x2)
_c(Matrix3x2h)
_c(Matrix3x2d)
_c(Matrix3x3)
_c(Matrix3x3h)
_c(Matrix3x3d)
_c(Matrix3x4)
_c(Matrix3x4h)
_c(Matrix3x4d)
_c(Matrix4x2)
_c(Matrix4x2h)
_c(Matrix4x2d)
_c(Matrix4x3)
_c(Matrix4x3h)
_c(Matrix4x3d)
_c(Matrix4x4)
_c(Matrix4x4h)
_c(Matrix4x4d)
_c(Range1D)
_c(Range1Dh)
_c(Range1Dd)
_c(Range1Di)
_c(Range2D)
_c(Range2Dh)
_c(Range2Dd)
_c(Range2Di)
_c(Range3D)
_c(Range3Dh)
_c(Range3Dd)
_c(Range3Di)
_c(Complex)
_c(Complexd)
_c(DualComplex)
_c(DualComplexd)
_c(Quaternion)
_c(Quaterniond)
_c(DualQuaternion)
_c(DualQuaterniond)
_c(Deg)
_c(Degh)
_c(Degd)
_c(Rad)
_c(Radh)
_c(Radd)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")";
}
UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif
switch(type) {
case SceneFieldType::UnsignedByte:
case SceneFieldType::Byte:
return 1;
case SceneFieldType::UnsignedShort:
case SceneFieldType::Short:
case SceneFieldType::Half:
case SceneFieldType::Vector2ub:
case SceneFieldType::Vector2b:
case SceneFieldType::Degh:
case SceneFieldType::Radh:
return 2;
case SceneFieldType::Vector3ub:
case SceneFieldType::Vector3b:
return 3;
case SceneFieldType::UnsignedInt:
case SceneFieldType::Int:
case SceneFieldType::Float:
case SceneFieldType::Vector2us:
case SceneFieldType::Vector2s:
case SceneFieldType::Vector2h:
case SceneFieldType::Vector4ub:
case SceneFieldType::Vector4b:
case SceneFieldType::Range1Dh:
case SceneFieldType::Deg:
case SceneFieldType::Rad:
return 4;
case SceneFieldType::Vector3us:
case SceneFieldType::Vector3s:
case SceneFieldType::Vector3h:
return 6;
case SceneFieldType::UnsignedLong:
case SceneFieldType::Long:
case SceneFieldType::Double:
case SceneFieldType::Vector2:
case SceneFieldType::Vector2ui:
case SceneFieldType::Vector2i:
case SceneFieldType::Vector4us:
case SceneFieldType::Vector4s:
case SceneFieldType::Vector4h:
case SceneFieldType::Matrix2x2h:
case SceneFieldType::Range1D:
case SceneFieldType::Range1Di:
case SceneFieldType::Range2Dh:
case SceneFieldType::Complex:
case SceneFieldType::Degd:
case SceneFieldType::Radd:
return 8;
case SceneFieldType::Vector3:
case SceneFieldType::Vector3ui:
case SceneFieldType::Vector3i:
case SceneFieldType::Matrix2x3h:
case SceneFieldType::Matrix3x2h:
case SceneFieldType::Range3Dh:
return 12;
case SceneFieldType::Vector2d:
case SceneFieldType::Vector4:
case SceneFieldType::Vector4ui:
case SceneFieldType::Vector4i:
case SceneFieldType::Matrix2x2:
case SceneFieldType::Matrix2x4h:
case SceneFieldType::Matrix4x2h:
case SceneFieldType::Range1Dd:
case SceneFieldType::Range2D:
case SceneFieldType::Range2Di:
case SceneFieldType::Complexd:
case SceneFieldType::DualComplex:
case SceneFieldType::Quaternion:
return 16;
case SceneFieldType::Matrix3x3h:
return 18;
case SceneFieldType::Vector3d:
case SceneFieldType::Matrix2x3:
case SceneFieldType::Matrix3x4h:
case SceneFieldType::Matrix3x2:
case SceneFieldType::Matrix4x3h:
case SceneFieldType::Range3D:
case SceneFieldType::Range3Di:
return 24;
case SceneFieldType::Vector4d:
case SceneFieldType::Matrix2x2d:
case SceneFieldType::Matrix2x4:
case SceneFieldType::Matrix4x2:
case SceneFieldType::Matrix4x4h:
case SceneFieldType::Range2Dd:
case SceneFieldType::DualComplexd:
case SceneFieldType::Quaterniond:
case SceneFieldType::DualQuaternion:
return 32;
case SceneFieldType::Matrix3x3:
return 36;
case SceneFieldType::Matrix2x3d:
case SceneFieldType::Matrix3x4:
case SceneFieldType::Matrix3x2d:
case SceneFieldType::Matrix4x3:
case SceneFieldType::Range3Dd:
return 48;
case SceneFieldType::Matrix2x4d:
case SceneFieldType::Matrix4x2d:
case SceneFieldType::Matrix4x4:
case SceneFieldType::DualQuaterniond:
return 64;
case SceneFieldType::Matrix3x3d:
return 72;
case SceneFieldType::Matrix3x4d:
case SceneFieldType::Matrix4x3d:
return 96;
case SceneFieldType::Matrix4x4d:
return 128;
}
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic pop
#endif
CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): invalid type" << type, {});
}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& objectData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const UnsignedShort fieldArraySize) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{objectData.data(), ~std::size_t{}}, objectData.size()[0], objectData.stride()[0]}, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize} {
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
#ifndef CORRADE_NO_ASSERT
if(fieldArraySize) CORRADE_ASSERT(fieldData.empty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType)*fieldArraySize,
"Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType << "and field array size" << fieldArraySize, );
else CORRADE_ASSERT(fieldData.empty()[0] || fieldData.size()[1] == sceneFieldTypeSize(fieldType),
"Trade::SceneFieldData: second field view dimension size" << fieldData.size()[1] << "doesn't match" << fieldType, );
#endif
if(objectData.size()[1] == 8) _objectType = SceneObjectType::UnsignedLong;
else if(objectData.size()[1] == 4) _objectType = SceneObjectType::UnsignedInt;
else if(objectData.size()[1] == 2) _objectType = SceneObjectType::UnsignedShort;
else if(objectData.size()[1] == 1) _objectType = SceneObjectType::UnsignedByte;
else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second object view dimension size 1, 2, 4 or 8 but got" << objectData.size()[1], );
CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", );
CORRADE_ASSERT(objectData.isContiguous<1>(), "Trade::SceneFieldData: second object view dimension is not contiguous", );
}
Containers::Array<SceneFieldData> sceneFieldDataNonOwningArray(const Containers::ArrayView<const SceneFieldData> view) {
/* Ugly, eh? */
return Containers::Array<SceneFieldData>{const_cast<SceneFieldData*>(view.data()), view.size(), reinterpret_cast<void(*)(SceneFieldData*, std::size_t)>(Implementation::nonOwnedArrayDeleter)};
}
SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array<char>&& data, Containers::Array<SceneFieldData>&& fields, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _objectType{objectType}, _objectCount{objectCount}, _importerState{importerState}, _fields{std::move(fields)}, _data{std::move(data)} {
/* Check that object type is large enough */
CORRADE_ASSERT(
(objectType == SceneObjectType::UnsignedByte && objectCount <= 0xffull) ||
(objectType == SceneObjectType::UnsignedShort && objectCount <= 0xffffull) ||
(objectType == SceneObjectType::UnsignedInt && objectCount <= 0xffffffffull) ||
objectType == SceneObjectType::UnsignedLong,
"Trade::SceneData:" << objectType << "is too small for" << objectCount << "objects", );
#ifndef CORRADE_NO_ASSERT
/* Check various assumptions about field data */
Math::BoolVector<11> fieldsPresent; /** @todo some constant for this */
const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType);
UnsignedInt translationField = ~UnsignedInt{};
UnsignedInt rotationField = ~UnsignedInt{};
UnsignedInt scalingField = ~UnsignedInt{};
UnsignedInt meshField = ~UnsignedInt{};
UnsignedInt meshMaterialField = ~UnsignedInt{};
for(std::size_t i = 0; i != _fields.size(); ++i) {
const SceneFieldData& field = _fields[i];
/* The object type has to be the same among all fields. Technically it
wouldn't need to be, but if there's 60k objects then using a 8bit
type for certain fields would mean only the first 256 objects can be
referenced, which makes no practical sense, and to improve the
situation there would need to be some additional per-field object
offset and ... it's simpler to just require the object type to be
large enough to reference all objects (checked outside of the loop
above) and that it's the same for all fields. This also makes it
more convenient for the user. */
CORRADE_ASSERT(field._objectType == _objectType,
"Trade::SceneData: inconsistent object type, got" << field._objectType << "for field" << i << "but expected" << _objectType, );
/* We could check that object indices are in bounds, but that's rather
expensive. OTOH it's fine if field size is larger than object count,
as a single object can have multiple instances of the same field
attached, so checking that would be wrong. */
/* Check that there are only unique fields. To avoid a O(n^2) operation
always (or allocating a sorted field map), builtin fields are
checked against a map and only custom fields are checked in an
O(n^2) way with the assumption there isn't many of them (and that
they'll gradually become builtin). */
if(!isSceneFieldCustom(_fields[i]._name)) {
CORRADE_INTERNAL_ASSERT(UnsignedInt(_fields[i]._name) < fieldsPresent.Size);
CORRADE_ASSERT(!fieldsPresent[UnsignedInt(_fields[i]._name)],
"Trade::SceneData: duplicate field" << _fields[i]._name, );
fieldsPresent.set(UnsignedInt(_fields[i]._name), true);
} else for(std::size_t j = 0; j != i; ++j) {
CORRADE_ASSERT(_fields[j]._name != _fields[i]._name,
"Trade::SceneData: duplicate field" << _fields[i]._name, );
}
/* Check that both the object and field view fits into the provided
data array. If the field is empty, we don't check anything --
accessing the memory would be invalid anyway and enforcing this
would lead to unnecessary friction with optional fields. */
if(field._size) {
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)*
(field._fieldArraySize ? field._fieldArraySize : 1);
if(field._isOffsetOnly) {
const std::size_t objectSize = field._objectData.offset + (field._size - 1)*field._objectStride + objectTypeSize;
const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize;
CORRADE_ASSERT(objectSize <= _data.size(),
"Trade::SceneData: offset-only object data of field" << i << "span" << objectSize << "bytes but passed data array has only" << _data.size(), );
CORRADE_ASSERT(fieldSize <= _data.size(),
"Trade::SceneData: offset-only field data of field" << i << "span" << fieldSize << "bytes but passed data array has only" << _data.size(), );
} else {
const void* const objectBegin = field._objectData.pointer;
const void* const fieldBegin = field._fieldData.pointer;
const void* const objectEnd = static_cast<const char*>(field._objectData.pointer) + (field._size - 1)*field._objectStride + objectTypeSize;
const void* const fieldEnd = static_cast<const char*>(field._fieldData.pointer) + (field._size - 1)*field._fieldStride + fieldTypeSize;
CORRADE_ASSERT(objectBegin >= _data.begin() && objectEnd <= _data.end(),
"Trade::SceneData: object data [" << Debug::nospace << objectBegin << Debug::nospace << ":" << Debug::nospace << objectEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
CORRADE_ASSERT(fieldBegin >= _data.begin() && fieldEnd <= _data.end(),
"Trade::SceneData: field data [" << Debug::nospace << fieldBegin << Debug::nospace << ":" << Debug::nospace << fieldEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
}
}
/* Remember TRS and mesh/material fields to check their object mapping
consistency outside of the loop below */
if(_fields[i]._name == SceneField::Translation) {
translationField = i;
} else if(_fields[i]._name == SceneField::Rotation) {
rotationField = i;
} else if(_fields[i]._name == SceneField::Scaling) {
scalingField = i;
} else if(_fields[i]._name == SceneField::Mesh) {
meshField = i;
} else if(_fields[i]._name == SceneField::MeshMaterial) {
meshMaterialField = i;
}
}
/* Check that certain fields share the same object mapping. Printing as if
all would be pointers (and not offset-only), it's not worth the extra
effort just for an assert message. Also, compared to above, where
"begin" was always zero, here we're always comparing four values, so the
message for offset-only wouldn't be simpler either. */
const auto checkFieldObjectDataMatch = [](const SceneFieldData& a, const SceneFieldData& b) {
const std::size_t objectTypeSize = sceneObjectTypeSize(a._objectType);
const void* const aBegin = a._objectData.pointer;
const void* const bBegin = b._objectData.pointer;
const void* const aEnd = static_cast<const char*>(a._objectData.pointer) + a._size*objectTypeSize;
const void* const bEnd = static_cast<const char*>(b._objectData.pointer) + b._size*objectTypeSize;
CORRADE_ASSERT(aBegin == bBegin && aEnd == bEnd,
"Trade::SceneData:" << b._name << "object data [" << Debug::nospace << bBegin << Debug::nospace << ":" << Debug::nospace << bEnd << Debug::nospace << "] is different from" << a._name << "object data [" << Debug::nospace << aBegin << Debug::nospace << ":" << Debug::nospace << aEnd << Debug::nospace << "]", );
};
/* All present TRS fields should share the same object mapping */
if(translationField != ~UnsignedInt{}) {
if(rotationField != ~UnsignedInt{})
checkFieldObjectDataMatch(_fields[translationField], _fields[rotationField]);
if(scalingField != ~UnsignedInt{})
checkFieldObjectDataMatch(_fields[translationField], _fields[scalingField]);
}
if(rotationField != ~UnsignedInt{} && scalingField != ~UnsignedInt{})
checkFieldObjectDataMatch(_fields[rotationField], _fields[scalingField]);
/* Mesh and materials also */
if(meshField != ~UnsignedInt{} && meshMaterialField != ~UnsignedInt{})
checkFieldObjectDataMatch(_fields[meshField], _fields[meshMaterialField]);
#endif
}
SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, Containers::Array<char>&& data, const std::initializer_list<SceneFieldData> fields, const void* const importerState): SceneData{objectType, objectCount, std::move(data), Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {}
SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView<const void> data, Containers::Array<SceneFieldData>&& fields, const void* const importerState) noexcept: SceneData{objectType, objectCount, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, std::move(fields), importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::SceneData: can't construct with non-owned data but" << dataFlags, );
_dataFlags = dataFlags;
}
SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong objectCount, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const std::initializer_list<SceneFieldData> fields, const void* const importerState): SceneData{objectType, objectCount, dataFlags, data, Implementation::initializerListToArrayWithDefaultDeleter(fields), importerState} {}
SceneData::SceneData(SceneData&&) noexcept = default;
SceneData::~SceneData() = default;
SceneData& SceneData::operator=(SceneData&&) noexcept = default;
Containers::ArrayView<char> SceneData::mutableData() & {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableData(): data not mutable", {});
return _data;
}
Containers::StridedArrayView1D<const void> SceneData::fieldDataObjectViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const {
CORRADE_INTERNAL_ASSERT(offset + size <= field._size);
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
{static_cast<const char*>(field._isOffsetOnly ?
_data.data() + field._objectData.offset : field._objectData.pointer)
+ field._fieldStride*offset, ~std::size_t{}},
size, field._objectStride};
}
Containers::StridedArrayView1D<const void> SceneData::fieldDataObjectViewInternal(const SceneFieldData& field) const {
return fieldDataObjectViewInternal(field, 0, field._size);
}
Containers::StridedArrayView1D<const void> SceneData::fieldDataFieldViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const {
CORRADE_INTERNAL_ASSERT(offset + size <= field._size);
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
{static_cast<const char*>(field._isOffsetOnly ?
_data.data() + field._fieldData.offset : field._fieldData.pointer)
+ field._fieldStride*offset, ~std::size_t{}},
size, field._fieldStride};
}
Containers::StridedArrayView1D<const void> SceneData::fieldDataFieldViewInternal(const SceneFieldData& field) const {
return fieldDataFieldViewInternal(field, 0, field._size);
}
SceneFieldData SceneData::fieldData(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldData(): index" << id << "out of range for" << _fields.size() << "fields", SceneFieldData{});
const SceneFieldData& field = _fields[id];
return SceneFieldData{field._name, field._objectType, fieldDataObjectViewInternal(field), field._fieldType, fieldDataFieldViewInternal(field), field._fieldArraySize};
}
SceneField SceneData::fieldName(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldName(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._name;
}
SceneFieldType SceneData::fieldType(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldType(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._fieldType;
}
std::size_t SceneData::fieldSize(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldSize(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._size;
}
UnsignedShort SceneData::fieldArraySize(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldArraySize(): index" << id << "out of range for" << _fields.size() << "fields", {});
return _fields[id]._fieldArraySize;
}
UnsignedInt SceneData::fieldFor(const SceneField name) const {
for(std::size_t i = 0; i != _fields.size(); ++i)
if(_fields[i]._name == name) return i;
return ~UnsignedInt{};
}
bool SceneData::hasField(const SceneField name) const {
return fieldFor(name) != ~UnsignedInt{};
}
UnsignedInt SceneData::fieldId(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldId(): field" << name << "not found", {});
return fieldId;
}
SceneFieldType SceneData::fieldType(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {});
return _fields[fieldId]._fieldType;
}
std::size_t SceneData::fieldSize(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldSize(): field" << name << "not found", {});
return _fields[fieldId]._size;
}
UnsignedShort SceneData::fieldArraySize(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {});
return _fields[fieldId]._fieldArraySize;
}
Containers::StridedArrayView2D<const char> SceneData::objects(const UnsignedInt fieldId) const {
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::objects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[fieldId];
/* Build a 2D view using information about object type size */
return Containers::arrayCast<2, const char>(
fieldDataObjectViewInternal(field),
sceneObjectTypeSize(field._objectType));
}
Containers::StridedArrayView2D<char> SceneData::mutableObjects(const UnsignedInt fieldId) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableObjects(): data not mutable", {});
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::mutableObjects(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[fieldId];
/* Build a 2D view using information about attribute type size */
const auto out = Containers::arrayCast<2, const char>(
fieldDataObjectViewInternal(field),
sceneObjectTypeSize(field._objectType));
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the
view is valid */
{static_cast<char*>(const_cast<void*>(out.data())), ~std::size_t{}},
out.size(), out.stride()};
}
Containers::StridedArrayView2D<const char> SceneData::objects(const SceneField fieldName) const {
const UnsignedInt fieldId = fieldFor(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {});
return objects(fieldId);
}
Containers::StridedArrayView2D<char> SceneData::mutableObjects(const SceneField fieldName) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableObjects(): data not mutable", {});
const UnsignedInt fieldId = fieldFor(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {});
return mutableObjects(fieldId);
}
Containers::StridedArrayView2D<const char> SceneData::field(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
/* Build a 2D view using information about object type size */
return Containers::arrayCast<2, const char>(
fieldDataFieldViewInternal(field),
sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1));
}
Containers::StridedArrayView2D<char> SceneData::mutableField(const UnsignedInt id) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableField(): data not mutable", {});
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::mutableField(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
/* Build a 2D view using information about attribute type size */
const auto out = Containers::arrayCast<2, const char>(
fieldDataFieldViewInternal(field),
sceneFieldTypeSize(field._fieldType)*(field._fieldArraySize ? field._fieldArraySize : 1));
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the
view is valid */
{static_cast<char*>(const_cast<void*>(out.data())), ~std::size_t{}},
out.size(), out.stride()};
}
Containers::StridedArrayView2D<const char> SceneData::field(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::field(): field" << name << "not found", {});
return field(fieldId);
}
Containers::StridedArrayView2D<char> SceneData::mutableField(const SceneField name) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableField(): data not mutable", {});
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::mutableField(): field" << name << "not found", {});
return mutableField(fieldId);
}
void SceneData::objectsIntoInternal(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 */
const SceneFieldData& field = _fields[fieldId];
const Containers::StridedArrayView1D<const void> objectData = fieldDataObjectViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);
if(field._objectType == SceneObjectType::UnsignedInt)
Utility::copy(Containers::arrayCast<const UnsignedInt>(objectData), destination);
else if(field._objectType == SceneObjectType::UnsignedShort)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(objectData, 1), destination1ui);
else if(field._objectType == SceneObjectType::UnsignedByte)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(objectData, 1), destination1ui);
else if(field._objectType == SceneObjectType::UnsignedLong) {
CORRADE_ASSERT(_objectCount <= 0xffffffffull, "Trade::SceneData::objectsInto(): indices for up to" << _objectCount << "objects can't fit into a 32-bit type, access them directly via objects() instead", );
Math::castInto(Containers::arrayCast<2, const UnsignedLong>(objectData, 1), destination1ui);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void SceneData::objectsInto(const UnsignedInt fieldId, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
"Trade::SceneData::objectsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), );
objectsIntoInternal(fieldId, 0, destination);
}
std::size_t SceneData::objectsInto(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::objectsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset);
objectsIntoInternal(fieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<UnsignedInt> SceneData::objectsAsArray(const UnsignedInt fieldId) const {
CORRADE_ASSERT(fieldId < _fields.size(),
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::objectsInto(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
Containers::Array<UnsignedInt> out{NoInit, std::size_t(_fields[fieldId]._size)};
objectsIntoInternal(fieldId, 0, out);
return out;
}
void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::objectsInto(): field" << name << "not found", );
objectsInto(fieldId, destination);
}
std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::objectsInto(): field" << name << "not found", {});
return objectsInto(fieldId, offset, destination);
}
Containers::Array<UnsignedInt> SceneData::objectsAsArray(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::objectsInto(): field" << name << "not found", {});
return objectsAsArray(fieldId);
}
void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const {
/* fieldId, offset and destination.size() is assumed to be in bounds,
checked by the callers */
const SceneFieldData& field = _fields[fieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1i = Containers::arrayCast<2, Int>(destination);
if(field._fieldType == SceneFieldType::Int)
Utility::copy(Containers::arrayCast<const Int>(fieldData), destination);
else if(field._fieldType == SceneFieldType::Short)
Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1i);
else if(field._fieldType == SceneFieldType::Byte)
Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1i);
else if(field._fieldType == SceneFieldType::Long) {
CORRADE_ASSERT(field._size <= 0xffffffffull, "Trade::SceneData::parentsInto(): parent indices for up to" << field._size << "objects can't fit into a 32-bit type, access them directly via field() instead", );
Math::castInto(Containers::arrayCast<2, const Long>(fieldData, 1), destination1i);
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void SceneData::parentsInto(const Containers::StridedArrayView1D<Int>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::parentsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
"Trade::SceneData::parentsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), );
parentsIntoInternal(fieldId, 0, destination);
}
std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::parentsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::parentsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset);
parentsIntoInternal(fieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<Int> SceneData::parentsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::parentsInto(): field not found", {});
Containers::Array<Int> out{NoInit, std::size_t(_fields[fieldId]._size)};
parentsIntoInternal(fieldId, 0, out);
return out;
}
namespace {
template<class Source, class Destination> void convertTransformation(const Containers::StridedArrayView1D<const void>& source, const Containers::StridedArrayView1D<Destination>& destination) {
CORRADE_INTERNAL_ASSERT(source.size() == destination.size());
const auto sourceT = Containers::arrayCast<const Source>(source);
for(std::size_t i = 0; i != sourceT.size(); ++i)
destination[i] = Destination{sourceT[i].toMatrix()};
}
/** @todo these (or the float variants at least) should eventually be replaced
with optimized batched APIs (applyTranslationsInto() updating just the
last matrix column etc.) */
template<class Source, class Destination> void applyTranslation(const Containers::StridedArrayView1D<const void>& source, const Containers::StridedArrayView1D<Destination>& destination) {
CORRADE_INTERNAL_ASSERT(source.size() == destination.size());
const auto sourceT = Containers::arrayCast<const Source>(source);
for(std::size_t i = 0; i != sourceT.size(); ++i)
destination[i] = Destination::translation(Math::Vector<Destination::Size - 1, Float>{sourceT[i]})*destination[i];
}
template<class Source, class Destination> void applyRotation(const Containers::StridedArrayView1D<const void>& source, const Containers::StridedArrayView1D<Destination>& destination) {
CORRADE_INTERNAL_ASSERT(source.size() == destination.size());
const auto sourceT = Containers::arrayCast<const Source>(source);
for(std::size_t i = 0; i != sourceT.size(); ++i)
destination[i] = Destination{Math::Matrix<Destination::Size - 1, Float>{ sourceT[i].toMatrix()}}*destination[i];
}
template<class Source, class Destination> void applyScaling(const Containers::StridedArrayView1D<const void>& source, const Containers::StridedArrayView1D<Destination>& destination) {
CORRADE_INTERNAL_ASSERT(source.size() == destination.size());
const auto sourceT = Containers::arrayCast<const Source>(source);
for(std::size_t i = 0; i != sourceT.size(); ++i)
destination[i] = Destination::scaling(Math::Vector<Destination::Size - 1, Float>{sourceT[i]})*destination[i];
}
}
std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const {
UnsignedInt fieldWithObjectMapping = ~UnsignedInt{};
transformationFieldId = ~UnsignedInt{};
translationFieldId = ~UnsignedInt{};
rotationFieldId = ~UnsignedInt{};
scalingFieldId = ~UnsignedInt{};
for(std::size_t i = 0; i != _fields.size(); ++i) {
/* If we find a transformation field, we don't need to look any
further */
if(_fields[i]._name == SceneField::Transformation) {
fieldWithObjectMapping = transformationFieldId = i;
break;
} else if(_fields[i]._name == SceneField::Translation) {
fieldWithObjectMapping = translationFieldId = i;
} else if(_fields[i]._name == SceneField::Rotation) {
fieldWithObjectMapping = rotationFieldId = i;
} else if(_fields[i]._name == SceneField::Scaling) {
fieldWithObjectMapping = scalingFieldId = i;
}
}
if(fieldWithObjectMappingDestination)
*fieldWithObjectMappingDestination = fieldWithObjectMapping;
/* Assuming the caller fires an appropriate assertion */
return fieldWithObjectMapping == ~UnsignedInt{} ?
~std::size_t{} : _fields[fieldWithObjectMapping]._size;
}
std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const {
UnsignedInt fieldWithObjectMapping = ~UnsignedInt{};
translationFieldId = ~UnsignedInt{};
rotationFieldId = ~UnsignedInt{};
scalingFieldId = ~UnsignedInt{};
for(std::size_t i = 0; i != _fields.size(); ++i) {
if(_fields[i]._name == SceneField::Translation) {
fieldWithObjectMapping = translationFieldId = i;
} else if(_fields[i]._name == SceneField::Rotation) {
fieldWithObjectMapping = rotationFieldId = i;
} else if(_fields[i]._name == SceneField::Scaling) {
fieldWithObjectMapping = scalingFieldId = i;
}
}
if(fieldWithObjectMappingDestination)
*fieldWithObjectMappingDestination = fieldWithObjectMapping;
/* Assuming the caller fires an appropriate assertion */
return fieldWithObjectMapping == ~UnsignedInt{} ?
~std::size_t{} : _fields[fieldWithObjectMapping]._size;
}
void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D<Matrix3>& destination) const {
/* *FieldId, offset and destination.size() is assumed to be in bounds (or
an invalid field ID), checked by the callers */
/** @todo apply scalings as well if dual complex? */
/* Prefer the transformation field, if present */
if(transformationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[transformationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1f = Containers::arrayCast<2, Float>(destination);
if(field._fieldType == SceneFieldType::Matrix3x3) {
Utility::copy(Containers::arrayCast<const Matrix3>(fieldData), destination);
} else if(field._fieldType == SceneFieldType::Matrix3x3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 9), destination1f);
} else if(field._fieldType == SceneFieldType::DualComplex) {
convertTransformation<DualComplex>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualComplexd) {
convertTransformation<DualComplexd>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix4x4 ||
field._fieldType == SceneFieldType::Matrix4x4d ||
field._fieldType == SceneFieldType::DualQuaternion ||
field._fieldType == SceneFieldType::DualQuaterniond) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D transformation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* If not, combine from TRS components */
} else if(translationFieldId != ~UnsignedInt{} || rotationFieldId != ~UnsignedInt{} || scalingFieldId != ~UnsignedInt{}) {
/* First fill the destination with identity matrices */
const Matrix3 identity[1]{Matrix3{Math::IdentityInit}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(destination.size()), destination);
/* Apply scaling first, if present */
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector2) {
applyScaling<Vector2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
applyScaling<Vector2d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3 ||
field._fieldType == SceneFieldType::Vector3d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D scaling type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Apply rotation second, if present */
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Complex) {
applyRotation<Complex>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Complexd) {
applyRotation<Complexd>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Quaternion ||
field._fieldType == SceneFieldType::Quaterniond) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D rotation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Apply translation last, if present */
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector2) {
applyTranslation<Vector2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
applyTranslation<Vector2d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3 ||
field._fieldType == SceneFieldType::Vector3d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations2DInto(): field has a 3D translation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Checked in the caller */
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void SceneData::transformations2DInto(const Containers::StridedArrayView1D<Matrix3>& destination) const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
#ifndef CORRADE_NO_ASSERT
const std::size_t expectedSize =
#endif
findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::transformations2DInto(): no transformation-related field found", );
CORRADE_ASSERT(expectedSize == destination.size(),
"Trade::SceneData::transformations2DInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), );
transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, destination);
}
std::size_t SceneData::transformations2DInto(const std::size_t offset, const Containers::StridedArrayView1D<Matrix3>& destination) const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::transformations2DInto(): no transformation-related field found", {});
CORRADE_ASSERT(offset <= expectedSize,
"Trade::SceneData::transformations2DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {});
const std::size_t size = Math::min(destination.size(), expectedSize - offset);
transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<Matrix3> SceneData::transformations2DAsArray() const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::transformations2DInto(): no transformation-related field found", {});
Containers::Array<Matrix3> out{NoInit, expectedSize};
transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out);
return out;
}
void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const {
/* *FieldId, offset and *Destination.size() is assumed to be in bounds (or
an invalid field ID), checked by the callers */
/* Retrieve translation, if desired. If no field is present, output a zero
vector for all objects. */
if(translationDestination) {
if(translationFieldId == ~UnsignedInt{}) {
constexpr Vector2 identity[]{Vector2{0.0f}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(translationDestination.size()), translationDestination);
} else {
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size());
if(field._fieldType == SceneFieldType::Vector2) {
Utility::copy(Containers::arrayCast<const Vector2>(fieldData), translationDestination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(translationDestination));
} else if(field._fieldType == SceneFieldType::Vector3 ||
field._fieldType == SceneFieldType::Vector3d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D translation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Retrieve rotation, if desired. If no field is present, output an
identity rotation for all objects. */
if(rotationDestination) {
if(rotationFieldId == ~UnsignedInt{}) {
constexpr Complex identity[]{Complex{Math::IdentityInit}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(rotationDestination.size()), rotationDestination);
} else {
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size());
if(field._fieldType == SceneFieldType::Complex) {
Utility::copy(Containers::arrayCast<const Complex>(fieldData), rotationDestination);
} else if(field._fieldType == SceneFieldType::Complexd) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(rotationDestination));
} else if(field._fieldType == SceneFieldType::Quaternion ||
field._fieldType == SceneFieldType::Quaterniond) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D rotation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Retrieve scaling, if desired. If no field is present, output an identity
scaling for all objects. */
if(scalingDestination) {
if(scalingFieldId == ~UnsignedInt{}) {
constexpr Vector2 identity[]{Vector2{1.0f}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(scalingDestination.size()), scalingDestination);
} else {
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size());
if(field._fieldType == SceneFieldType::Vector2) {
Utility::copy(Containers::arrayCast<const Vector2>(fieldData), scalingDestination);
} else if(field._fieldType == SceneFieldType::Vector2d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 2), Containers::arrayCast<2, Float>(scalingDestination));
} else if(field._fieldType == SceneFieldType::Vector3 ||
field._fieldType == SceneFieldType::Vector3d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings2DInto(): field has a 3D scaling type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
}
void SceneData::translationsRotationsScalings2DInto(const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
#ifndef CORRADE_NO_ASSERT
const std::size_t expectedSize =
#endif
findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", );
CORRADE_ASSERT(!translationDestination || translationDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings2DInto(): expected translation destination view either empty or with" << expectedSize << "elements but got" << translationDestination.size(), );
CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings2DInto(): expected rotation destination view either empty or with" << expectedSize << "elements but got" << rotationDestination.size(), );
CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings2DInto(): expected scaling destination view either empty or with" << expectedSize << "elements but got" << scalingDestination.size(), );
translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination);
}
std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t offset, const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", {});
CORRADE_ASSERT(offset <= expectedSize,
"Trade::SceneData::translationsRotationsScalings2DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {});
CORRADE_ASSERT(!translationDestination != !rotationDestination || translationDestination.size() == rotationDestination.size(),
"Trade::SceneData::translationsRotationsScalings2DInto(): translation and rotation destination views have different size," << translationDestination.size() << "vs" << rotationDestination.size(), {});
CORRADE_ASSERT(!translationDestination != !scalingDestination || translationDestination.size() == scalingDestination.size(),
"Trade::SceneData::translationsRotationsScalings2DInto(): translation and scaling destination views have different size," << translationDestination.size() << "vs" << scalingDestination.size(), {});
CORRADE_ASSERT(!rotationDestination != !scalingDestination || rotationDestination.size() == scalingDestination.size(),
"Trade::SceneData::translationsRotationsScalings2DInto(): rotation and scaling destination views have different size," << rotationDestination.size() << "vs" << scalingDestination.size(), {});
const std::size_t size = Math::min(Math::max({translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), expectedSize - offset);
translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset,
translationDestination ? translationDestination.prefix(size) : nullptr,
rotationDestination ? rotationDestination.prefix(size) : nullptr,
scalingDestination ? scalingDestination.prefix(size) : nullptr);
return size;
}
Containers::Array<Containers::Triple<Vector2, Complex, Vector2>> SceneData::translationsRotationsScalings2DAsArray() const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", {});
Containers::Array<Containers::Triple<Vector2, Complex, Vector2>> out{NoInit, expectedSize};
/** @todo use slicing once Triple exposes members somehow */
const Containers::StridedArrayView1D<Vector2> translationsOut{out, reinterpret_cast<Vector2*>(reinterpret_cast<char*>(out.data())), out.size(), sizeof(decltype(out)::Type)};
const Containers::StridedArrayView1D<Complex> rotationsOut{out, reinterpret_cast<Complex*>(reinterpret_cast<char*>(out.data()) + sizeof(Vector2)), out.size(), sizeof(decltype(out)::Type)};
const Containers::StridedArrayView1D<Vector2> scalingsOut{out, reinterpret_cast<Vector2*>(reinterpret_cast<char*>(out.data()) + sizeof(Vector2) + sizeof(Complex)), out.size(), sizeof(decltype(out)::Type)};
translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut);
return out;
}
void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D<Matrix4>& destination) const {
/* *FieldId, offset and destination.size() is assumed to be in bounds (or
an invalid field ID), checked by the callers */
/** @todo apply scalings as well if dual quat? */
/* Prefer the transformation field, if present */
if(transformationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[transformationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1f = Containers::arrayCast<2, Float>(destination);
if(field._fieldType == SceneFieldType::Matrix4x4) {
Utility::copy(Containers::arrayCast<const Matrix4>(fieldData), destination);
} else if(field._fieldType == SceneFieldType::Matrix4x4d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 16), destination1f);
} else if(field._fieldType == SceneFieldType::DualQuaternion) {
convertTransformation<DualQuaternion>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualQuaterniond) {
convertTransformation<DualQuaterniond>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix3x3 ||
field._fieldType == SceneFieldType::Matrix3x3d ||
field._fieldType == SceneFieldType::DualComplex ||
field._fieldType == SceneFieldType::DualComplexd) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D transformation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* If not, combine from TRS components */
} else if(translationFieldId != ~UnsignedInt{} || rotationFieldId != ~UnsignedInt{} || scalingFieldId != ~UnsignedInt{}) {
/* First fill the destination with identity matrices */
const Matrix4 identity[1]{Matrix4{Math::IdentityInit}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(destination.size()), destination);
/* Apply scaling first, if present */
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector3) {
applyScaling<Vector3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
applyScaling<Vector3d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2 ||
field._fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D scaling type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Apply rotation second, if present */
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Quaternion) {
applyRotation<Quaternion>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Quaterniond) {
applyRotation<Quaterniond>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Complex ||
field._fieldType == SceneFieldType::Complexd) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D rotation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Apply translation last, if present */
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
if(field._fieldType == SceneFieldType::Vector3) {
applyTranslation<Vector3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
applyTranslation<Vector3d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Vector2 ||
field._fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::transformations3DInto(): field has a 2D translation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Checked in the caller */
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void SceneData::transformations3DInto(const Containers::StridedArrayView1D<Matrix4>& destination) const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
#ifndef CORRADE_NO_ASSERT
const std::size_t expectedSize =
#endif
findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::transformations3DInto(): no transformation-related field found", );
CORRADE_ASSERT(expectedSize == destination.size(),
"Trade::SceneData::transformations3DInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), );
transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, destination);
}
std::size_t SceneData::transformations3DInto(const std::size_t offset, const Containers::StridedArrayView1D<Matrix4>& destination) const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::transformations3DInto(): no transformation-related field found", {});
CORRADE_ASSERT(offset <= expectedSize,
"Trade::SceneData::transformations3DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {});
const std::size_t size = Math::min(destination.size(), expectedSize - offset);
transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<Matrix4> SceneData::transformations3DAsArray() const {
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::transformations3DInto(): no transformation-related field found", {});
Containers::Array<Matrix4> out{NoInit, expectedSize};
transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out);
return out;
}
void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, const std::size_t offset, const Containers::StridedArrayView1D<Vector3>& translationDestination, const Containers::StridedArrayView1D<Quaternion>& rotationDestination, const Containers::StridedArrayView1D<Vector3>& scalingDestination) const {
/* *FieldId, offset and *Destination.size() is assumed to be in bounds (or
an invalid field ID), checked by the callers */
/* Retrieve translation, if desired. If no field is present, output a zero
vector for all objects. */
if(translationDestination) {
if(translationFieldId == ~UnsignedInt{}) {
constexpr Vector3 identity[]{Vector3{0.0f}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(translationDestination.size()), translationDestination);
} else {
const SceneFieldData& field = _fields[translationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, translationDestination.size());
if(field._fieldType == SceneFieldType::Vector3) {
Utility::copy(Containers::arrayCast<const Vector3>(fieldData), translationDestination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(translationDestination));
} else if(field._fieldType == SceneFieldType::Vector2 ||
field._fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D translation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Retrieve rotation, if desired. If no field is present, output an
identity rotation for all objects. */
if(rotationDestination) {
if(rotationFieldId == ~UnsignedInt{}) {
constexpr Quaternion identity[]{Quaternion{Math::IdentityInit}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(rotationDestination.size()), rotationDestination);
} else {
const SceneFieldData& field = _fields[rotationFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, rotationDestination.size());
if(field._fieldType == SceneFieldType::Quaternion) {
Utility::copy(Containers::arrayCast<const Quaternion>(fieldData), rotationDestination);
} else if(field._fieldType == SceneFieldType::Quaterniond) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 4), Containers::arrayCast<2, Float>(rotationDestination));
} else if(field._fieldType == SceneFieldType::Complex ||
field._fieldType == SceneFieldType::Complexd) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D rotation type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Retrieve scaling, if desired. If no field is present, output an identity
scaling for all objects. */
if(scalingDestination) {
if(scalingFieldId == ~UnsignedInt{}) {
constexpr Vector3 identity[]{Vector3{1.0f}};
Utility::copy(Containers::stridedArrayView(identity).broadcasted<0>(scalingDestination.size()), scalingDestination);
} else {
const SceneFieldData& field = _fields[scalingFieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, scalingDestination.size());
if(field._fieldType == SceneFieldType::Vector3) {
Utility::copy(Containers::arrayCast<const Vector3>(fieldData), scalingDestination);
} else if(field._fieldType == SceneFieldType::Vector3d) {
Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 3), Containers::arrayCast<2, Float>(scalingDestination));
} else if(field._fieldType == SceneFieldType::Vector2 ||
field._fieldType == SceneFieldType::Vector2d) {
CORRADE_ASSERT_UNREACHABLE("Trade::SceneData::translationsRotationsScalings3DInto(): field has a 2D scaling type" << field._fieldType, );
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
}
void SceneData::translationsRotationsScalings3DInto(const Containers::StridedArrayView1D<Vector3>& translationDestination, const Containers::StridedArrayView1D<Quaternion>& rotationDestination, const Containers::StridedArrayView1D<Vector3>& scalingDestination) const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
#ifndef CORRADE_NO_ASSERT
const std::size_t expectedSize =
#endif
findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", );
CORRADE_ASSERT(!translationDestination || translationDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings3DInto(): expected translation destination view either empty or with" << expectedSize << "elements but got" << translationDestination.size(), );
CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings3DInto(): expected rotation destination view either empty or with" << expectedSize << "elements but got" << rotationDestination.size(), );
CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == expectedSize,
"Trade::SceneData::translationsRotationsScalings3DInto(): expected scaling destination view either empty or with" << expectedSize << "elements but got" << scalingDestination.size(), );
translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination);
}
std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t offset, const Containers::StridedArrayView1D<Vector3>& translationDestination, const Containers::StridedArrayView1D<Quaternion>& rotationDestination, const Containers::StridedArrayView1D<Vector3>& scalingDestination) const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
"Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", {});
CORRADE_ASSERT(offset <= expectedSize,
"Trade::SceneData::translationsRotationsScalings3DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {});
CORRADE_ASSERT(!translationDestination != !rotationDestination || translationDestination.size() == rotationDestination.size(),
"Trade::SceneData::translationsRotationsScalings3DInto(): translation and rotation destination views have different size," << translationDestination.size() << "vs" << rotationDestination.size(), {});
CORRADE_ASSERT(!translationDestination != !scalingDestination || translationDestination.size() == scalingDestination.size(),
"Trade::SceneData::translationsRotationsScalings3DInto(): translation and scaling destination views have different size," << translationDestination.size() << "vs" << scalingDestination.size(), {});
CORRADE_ASSERT(!rotationDestination != !scalingDestination || rotationDestination.size() == scalingDestination.size(),
"Trade::SceneData::translationsRotationsScalings3DInto(): rotation and scaling destination views have different size," << rotationDestination.size() << "vs" << scalingDestination.size(), {});
const std::size_t size = Math::min(Math::max({translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), expectedSize - offset);
translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset,
translationDestination ? translationDestination.prefix(size) : nullptr,
rotationDestination ? rotationDestination.prefix(size) : nullptr,
scalingDestination ? scalingDestination.prefix(size) : nullptr);
return size;
}
Containers::Array<Containers::Triple<Vector3, Quaternion, Vector3>> SceneData::translationsRotationsScalings3DAsArray() const {
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId;
const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId);
CORRADE_ASSERT(expectedSize != ~std::size_t{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", {});
Containers::Array<Containers::Triple<Vector3, Quaternion, Vector3>> out{NoInit, expectedSize};
/** @todo use slicing once Triple exposes members somehow */
const Containers::StridedArrayView1D<Vector3> translationsOut{out, reinterpret_cast<Vector3*>(reinterpret_cast<char*>(out.data())), out.size(), sizeof(decltype(out)::Type)};
const Containers::StridedArrayView1D<Quaternion> rotationsOut{out, reinterpret_cast<Quaternion*>(reinterpret_cast<char*>(out.data()) + sizeof(Vector3)), out.size(), sizeof(decltype(out)::Type)};
const Containers::StridedArrayView1D<Vector3> scalingsOut{out, reinterpret_cast<Vector3*>(reinterpret_cast<char*>(out.data()) + sizeof(Vector3) + sizeof(Quaternion)), out.size(), sizeof(decltype(out)::Type)};
translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut);
return out;
}
void SceneData::unsignedIndexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
/* fieldId, offset and destination.size() is assumed to be in bounds,
checked by the callers */
const SceneFieldData& field = _fields[fieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);
if(field._fieldType == SceneFieldType::UnsignedInt)
Utility::copy(Containers::arrayCast<const UnsignedInt>(fieldData), destination);
else if(field._fieldType == SceneFieldType::UnsignedShort)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(fieldData, 1), destination1ui);
else if(field._fieldType == SceneFieldType::UnsignedByte)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(fieldData, 1), destination1ui);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const {
/* fieldId, offset and destination.size() is assumed to be in bounds,
checked by the callers */
const SceneFieldData& field = _fields[fieldId];
const Containers::StridedArrayView1D<const void> fieldData = fieldDataFieldViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, Int>(destination);
if(field._fieldType == SceneFieldType::Int)
Utility::copy(Containers::arrayCast<const Int>(fieldData), destination);
else if(field._fieldType == SceneFieldType::Short)
Math::castInto(Containers::arrayCast<2, const Short>(fieldData, 1), destination1ui);
else if(field._fieldType == SceneFieldType::Byte)
Math::castInto(Containers::arrayCast<2, const Byte>(fieldData, 1), destination1ui);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Array<UnsignedInt> SceneData::unsignedIndexFieldAsArrayInternal(const UnsignedInt fieldId) const {
Containers::Array<UnsignedInt> out{NoInit, std::size_t(_fields[fieldId]._size)};
unsignedIndexFieldIntoInternal(fieldId, 0, out);
return out;
}
void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const {
/* fieldId, offset, meshDestination.size() and
meshMaterialDestination.size() is assumed to be in bounds, checked by
the callers */
if(meshDestination)
unsignedIndexFieldIntoInternal(fieldId, offset, meshDestination);
/* Copy also the material, if desired. If no such field is present, output
-1 for all meshes. */
if(meshMaterialDestination) {
const UnsignedInt materialFieldId = fieldFor(SceneField::MeshMaterial);
if(materialFieldId == ~UnsignedInt{}) {
constexpr Int invalid[]{-1};
Utility::copy(Containers::stridedArrayView(invalid).broadcasted<0>(meshMaterialDestination.size()), meshMaterialDestination);
} else indexFieldIntoInternal(materialFieldId, offset, meshMaterialDestination);
}
}
void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", );
CORRADE_ASSERT(!meshDestination || meshDestination.size() == _fields[fieldId]._size,
"Trade::SceneData::meshesMaterialsInto(): expected mesh destination view either empty or with" << _fields[fieldId]._size << "elements but got" << meshDestination.size(), );
CORRADE_ASSERT(!meshMaterialDestination || meshMaterialDestination.size() == _fields[fieldId]._size,
"Trade::SceneData::meshesMaterialsInto(): expected mesh material destination view either empty or with" << _fields[fieldId]._size << "elements but got" << meshMaterialDestination.size(), );
meshesMaterialsIntoInternal(fieldId, 0, meshDestination, meshMaterialDestination);
}
std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::meshesMaterialsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
CORRADE_ASSERT(!meshDestination != !meshMaterialDestination || meshMaterialDestination.size() == meshDestination.size(),
"Trade::SceneData::meshesMaterialsInto(): mesh and mesh material destination views have different size," << meshDestination.size() << "vs" << meshMaterialDestination.size(), {});
const std::size_t size = Math::min(Math::max(meshDestination.size(), meshMaterialDestination.size()), std::size_t(_fields[fieldId]._size) - offset);
meshesMaterialsIntoInternal(fieldId, offset,
meshDestination ? meshDestination.prefix(size) : nullptr,
meshMaterialDestination ? meshMaterialDestination.prefix(size) : nullptr);
return size;
}
Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterialsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {});
Containers::Array<Containers::Pair<UnsignedInt, Int>> out{NoInit, std::size_t(_fields[fieldId]._size)};
/** @todo use slicing once Pair exposes members somehow */
const Containers::StridedArrayView1D<UnsignedInt> meshesOut{out, reinterpret_cast<UnsignedInt*>(reinterpret_cast<char*>(out.data())), out.size(), sizeof(decltype(out)::Type)};
const Containers::StridedArrayView1D<Int> meshMaterialsOut{out, reinterpret_cast<Int*>(reinterpret_cast<char*>(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)};
meshesMaterialsIntoInternal(fieldId, 0, meshesOut, meshMaterialsOut);
return out;
}
void SceneData::lightsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::lightsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
"Trade::SceneData::lightsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), );
unsignedIndexFieldIntoInternal(fieldId, 0, destination);
}
std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::lightsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::lightsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset);
unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<UnsignedInt> SceneData::lightsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::lightsInto(): field not found", {});
return unsignedIndexFieldAsArrayInternal(fieldId);
}
void SceneData::camerasInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::camerasInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
"Trade::SceneData::camerasInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), );
unsignedIndexFieldIntoInternal(fieldId, 0, destination);
}
std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::camerasInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::camerasInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset);
unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<UnsignedInt> SceneData::camerasAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::camerasInto(): field not found", {});
return unsignedIndexFieldAsArrayInternal(fieldId);
}
void SceneData::skinsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::skinsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
"Trade::SceneData::skinsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), );
unsignedIndexFieldIntoInternal(fieldId, 0, destination);
}
std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::skinsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size,
"Trade::SceneData::skinsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {});
const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset);
unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size));
return size;
}
Containers::Array<UnsignedInt> SceneData::skinsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant
strings in the binary */
"Trade::SceneData::skinsInto(): field not found", {});
return unsignedIndexFieldAsArrayInternal(fieldId);
}
namespace {
template<class T> std::size_t findObject(const Containers::StridedArrayView1D<const void>& objects, const UnsignedInt object) {
const Containers::StridedArrayView1D<const T> objectsT = Containers::arrayCast<const T>(objects);
const std::size_t max = objectsT.size();
/** @todo implement something faster than O(n) when field-specific flags
can annotate how the object mapping is done */
for(std::size_t i = 0; i != max; ++i)
if(objectsT[i] == object) return i;
return max;
}
}
std::size_t SceneData::fieldFor(const SceneFieldData& field, const std::size_t offset, const UnsignedInt object) const {
const Containers::StridedArrayView1D<const void> objects = fieldDataObjectViewInternal(field, offset, field._size - offset);
if(field._objectType == SceneObjectType::UnsignedInt)
return offset + findObject<UnsignedInt>(objects, object);
else if(field._objectType == SceneObjectType::UnsignedShort)
return offset + findObject<UnsignedShort>(objects, object);
else if(field._objectType == SceneObjectType::UnsignedByte)
return offset + findObject<UnsignedByte>(objects, object);
else if(field._objectType == SceneObjectType::UnsignedLong)
return offset + findObject<UnsignedLong>(objects, object);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Optional<Int> SceneData::parentFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Parent);
if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId];
const std::size_t offset = fieldFor(field, 0, object);
if(offset == field._size) return {};
Int index[1];
parentsIntoInternal(fieldId, offset, index);
if(*index == -1) return -1;
UnsignedInt parent[1];
objectsIntoInternal(fieldId, *index, parent);
return Int(*parent);
}
Containers::Array<UnsignedInt> SceneData::childrenFor(const Int object) const {
CORRADE_ASSERT(object >= -1 && object < Long(_objectCount),
"Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt parentFieldId = fieldFor(SceneField::Parent);
if(parentFieldId == ~UnsignedInt{}) return {};
const SceneFieldData& parentField = _fields[parentFieldId];
/* Figure out the parent object index to look for or -1 if we want
top-level objects */
Int parentIndexToLookFor;
if(object == -1) parentIndexToLookFor = -1;
else {
const std::size_t parentObjectIndex = fieldFor(parentField, 0, object);
if(parentObjectIndex == parentField._size) return {};
parentIndexToLookFor = parentObjectIndex;
}
/* Collect IDs of all objects that reference this index */
Containers::Array<UnsignedInt> out;
for(std::size_t offset = 0; offset != parentField.size(); ++offset) {
Int parentIndex[1];
parentsIntoInternal(parentFieldId, offset, parentIndex);
if(*parentIndex == parentIndexToLookFor) {
UnsignedInt child[1];
/** @todo bleh slow, use the children <-> parent field proxying
when implemented */
objectsIntoInternal(parentFieldId, offset, child);
arrayAppend(out, *child);
}
}
return out;
}
Containers::Optional<Matrix3> SceneData::transformation2DFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::transformation2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping;
if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {};
#ifndef CORRADE_NO_ASSERT
if(transformationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[transformationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Matrix3x3 ||
type == SceneFieldType::Matrix3x3d ||
type == SceneFieldType::DualComplex ||
type == SceneFieldType::DualComplexd,
"Trade::SceneData::transformation2DFor(): field has a 3D transformation type" << type, {});
} else {
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[translationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector2 ||
type == SceneFieldType::Vector2d,
"Trade::SceneData::transformation2DFor(): field has a 3D translation type" << type, {});
}
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[rotationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Complex ||
type == SceneFieldType::Complexd,
"Trade::SceneData::transformation2DFor(): field has a 3D rotation type" << type, {});
}
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[scalingFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector2 ||
type == SceneFieldType::Vector2d,
"Trade::SceneData::transformation2DFor(): field has a 3D scaling type" << type, {});
}
}
#endif
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object);
if(offset == _fields[fieldWithObjectMapping]._size) return {};
Matrix3 transformation[1];
transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation);
return *transformation;
}
Containers::Optional<Containers::Triple<Vector2, Complex, Vector2>> SceneData::translationRotationScaling2DFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::translationRotationScaling2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping;
if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {};
#ifndef CORRADE_NO_ASSERT
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[translationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector2 ||
type == SceneFieldType::Vector2d,
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type" << type, {});
}
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[rotationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Complex ||
type == SceneFieldType::Complexd,
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type" << type, {});
}
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[scalingFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector2 ||
type == SceneFieldType::Vector2d,
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type" << type, {});
}
#endif
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object);
if(offset == _fields[fieldWithObjectMapping]._size) return {};
Vector2 translation[1];
Complex rotation[1];
Vector2 scaling[1];
translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translation, rotation, scaling);
return {InPlaceInit, *translation, *rotation, *scaling};
}
Containers::Optional<Matrix4> SceneData::transformation3DFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::transformation3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping;
if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {};
#ifndef CORRADE_NO_ASSERT
if(transformationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[transformationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Matrix4x4 ||
type == SceneFieldType::Matrix4x4d ||
type == SceneFieldType::DualQuaternion ||
type == SceneFieldType::DualQuaterniond,
"Trade::SceneData::transformation3DFor(): field has a 2D transformation type" << type, {});
} else {
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[translationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector3 ||
type == SceneFieldType::Vector3d,
"Trade::SceneData::transformation3DFor(): field has a 2D translation type" << type, {});
}
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[rotationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Quaternion ||
type == SceneFieldType::Quaterniond,
"Trade::SceneData::transformation3DFor(): field has a 2D rotation type" << type, {});
}
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[scalingFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector3 ||
type == SceneFieldType::Vector3d,
"Trade::SceneData::transformation3DFor(): field has a 2D scaling type" << type, {});
}
}
#endif
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object);
if(offset == _fields[fieldWithObjectMapping]._size) return {};
Matrix4 transformation[1];
transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation);
return *transformation;
}
Containers::Optional<Containers::Triple<Vector3, Quaternion, Vector3>> SceneData::translationRotationScaling3DFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::translationRotationScaling3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping;
if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {};
#ifndef CORRADE_NO_ASSERT
if(translationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[translationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector3 ||
type == SceneFieldType::Vector3d,
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type" << type, {});
}
if(rotationFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[rotationFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Quaternion ||
type == SceneFieldType::Quaterniond,
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type" << type, {});
}
if(scalingFieldId != ~UnsignedInt{}) {
const SceneFieldType type = _fields[scalingFieldId]._fieldType;
CORRADE_ASSERT(
type == SceneFieldType::Vector3 ||
type == SceneFieldType::Vector3d,
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type" << type, {});
}
#endif
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object);
if(offset == _fields[fieldWithObjectMapping]._size) return {};
Vector3 translation[1];
Quaternion rotation[1];
Vector3 scaling[1];
translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translation, rotation, scaling);
return {InPlaceInit, *translation, *rotation, *scaling};
}
Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterialsFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt meshFieldId = fieldFor(SceneField::Mesh);
if(meshFieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[meshFieldId];
Containers::Array<Containers::Pair<UnsignedInt, Int>> out;
std::size_t offset = 0;
for(;;) {
offset = fieldFor(field, offset, object);
if(offset == field._size) break;
UnsignedInt mesh[1];
Int material[1];
meshesMaterialsIntoInternal(meshFieldId, offset, mesh, material);
arrayAppend(out, InPlaceInit, *mesh, *material);
++offset;
}
return out;
}
Containers::Array<UnsignedInt> SceneData::lightsFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Light);
if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out;
std::size_t offset = 0;
for(;;) {
offset = fieldFor(field, offset, object);
if(offset == field._size) break;
UnsignedInt index[1];
unsignedIndexFieldIntoInternal(fieldId, offset, index);
arrayAppend(out, InPlaceInit, *index);
++offset;
}
return out;
}
Containers::Array<UnsignedInt> SceneData::camerasFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Camera);
if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out;
std::size_t offset = 0;
for(;;) {
offset = fieldFor(field, offset, object);
if(offset == field._size) break;
UnsignedInt index[1];
unsignedIndexFieldIntoInternal(fieldId, offset, index);
arrayAppend(out, InPlaceInit, *index);
++offset;
}
return out;
}
Containers::Array<UnsignedInt> SceneData::skinsFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Skin);
if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out;
std::size_t offset = 0;
for(;;) {
offset = fieldFor(field, offset, object);
if(offset == field._size) break;
UnsignedInt index[1];
unsignedIndexFieldIntoInternal(fieldId, offset, index);
arrayAppend(out, InPlaceInit, *index);
++offset;
}
return out;
}
Containers::Array<SceneFieldData> SceneData::releaseFieldData() {
Containers::Array<SceneFieldData> out = std::move(_fields);
_fields = {};
return out;
}
Containers::Array<char> SceneData::releaseData() {
_fields = {};
Containers::Array<char> out = std::move(_data);
_data = {};
return out;
}
}}