Browse Source

Trade: convenience per-object field access in SceneData.

pull/525/head
Vladimír Vondruš 5 years ago
parent
commit
140e60a6ac
  1. 374
      src/Magnum/Trade/SceneData.cpp
  2. 269
      src/Magnum/Trade/SceneData.h
  3. 547
      src/Magnum/Trade/Test/SceneDataTest.cpp

374
src/Magnum/Trade/SceneData.cpp

@ -25,6 +25,8 @@
#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>
@ -839,8 +841,8 @@ template<class Source, class Destination> void applyScaling(const Containers::St
}
std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const {
UnsignedInt fieldToCheckForSize = ~UnsignedInt{};
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{};
@ -849,40 +851,46 @@ std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, U
/* If we find a transformation field, we don't need to look any
further */
if(_fields[i]._name == SceneField::Transformation) {
fieldToCheckForSize = transformationFieldId = i;
fieldWithObjectMapping = transformationFieldId = i;
break;
} else if(_fields[i]._name == SceneField::Translation) {
fieldToCheckForSize = translationFieldId = i;
fieldWithObjectMapping = translationFieldId = i;
} else if(_fields[i]._name == SceneField::Rotation) {
fieldToCheckForSize = rotationFieldId = i;
fieldWithObjectMapping = rotationFieldId = i;
} else if(_fields[i]._name == SceneField::Scaling) {
fieldToCheckForSize = scalingFieldId = i;
fieldWithObjectMapping = scalingFieldId = i;
}
}
if(fieldWithObjectMappingDestination)
*fieldWithObjectMappingDestination = fieldWithObjectMapping;
/* Assuming the caller fires an appropriate assertion */
return fieldToCheckForSize == ~UnsignedInt{} ?
~std::size_t{} : _fields[fieldToCheckForSize]._size;
return fieldWithObjectMapping == ~UnsignedInt{} ?
~std::size_t{} : _fields[fieldWithObjectMapping]._size;
}
std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const {
UnsignedInt fieldToCheckForSize = ~UnsignedInt{};
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) {
fieldToCheckForSize = translationFieldId = i;
fieldWithObjectMapping = translationFieldId = i;
} else if(_fields[i]._name == SceneField::Rotation) {
fieldToCheckForSize = rotationFieldId = i;
fieldWithObjectMapping = rotationFieldId = i;
} else if(_fields[i]._name == SceneField::Scaling) {
fieldToCheckForSize = scalingFieldId = i;
fieldWithObjectMapping = scalingFieldId = i;
}
}
if(fieldWithObjectMappingDestination)
*fieldWithObjectMappingDestination = fieldWithObjectMapping;
/* Assuming the caller fires an appropriate assertion */
return fieldToCheckForSize == ~UnsignedInt{} ?
~std::size_t{} : _fields[fieldToCheckForSize]._size;
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 {
@ -1553,6 +1561,342 @@ Containers::Array<UnsignedInt> SceneData::skinsAsArray() const {
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::Array<UnsignedInt> SceneData::childrenFor(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 = {};

269
src/Magnum/Trade/SceneData.h

@ -102,7 +102,7 @@ enum class SceneField: UnsignedInt {
* Note that the index points to the parent array itself and isn't the
* actual object index --- the object mapping is stored in the
* corresponding @ref SceneData::objects() array.
* @see @ref SceneData::parentsAsArray()
* @see @ref SceneData::parentsAsArray(), @ref SceneData::childrenFor()
*/
Parent = 1,
@ -129,7 +129,9 @@ enum class SceneField: UnsignedInt {
* only for a subset of transformed objects, useful for example when only
* certain objects have these properties animated.
* @see @ref SceneData::transformations2DAsArray(),
* @ref SceneData::transformations3DAsArray()
* @ref SceneData::transformations3DAsArray(),
* @ref SceneData::transformation2DFor(),
* @ref SceneData::transformation3DFor()
*/
Transformation,
@ -149,8 +151,12 @@ enum class SceneField: UnsignedInt {
* details.
* @see @ref SceneData::transformations2DAsArray(),
* @ref SceneData::transformations3DAsArray(),
* @ref SceneData::transformation2DFor(),
* @ref SceneData::transformation3DFor(),
* @ref SceneData::translationsRotationsScalings2DAsArray(),
* @ref SceneData::translationsRotationsScalings3DAsArray()
* @ref SceneData::translationsRotationsScalings3DAsArray(),
* @ref SceneData::translationRotationScaling2DFor(),
* @ref SceneData::translationRotationScaling3DFor()
*/
Translation,
@ -170,8 +176,12 @@ enum class SceneField: UnsignedInt {
* details.
* @see @ref SceneData::transformations2DAsArray(),
* @ref SceneData::transformations3DAsArray(),
* @ref SceneData::transformation2DFor(),
* @ref SceneData::transformation3DFor(),
* @ref SceneData::translationsRotationsScalings2DAsArray(),
* @ref SceneData::translationsRotationsScalings3DAsArray()
* @ref SceneData::translationsRotationsScalings3DAsArray(),
* @ref SceneData::translationRotationScaling2DFor(),
* @ref SceneData::translationRotationScaling3DFor()
*/
Rotation,
@ -191,8 +201,12 @@ enum class SceneField: UnsignedInt {
* details.
* @see @ref SceneData::transformations2DAsArray(),
* @ref SceneData::transformations3DAsArray(),
* @ref SceneData::transformation2DFor(),
* @ref SceneData::transformation3DFor(),
* @ref SceneData::translationsRotationsScalings2DAsArray(),
* @ref SceneData::translationsRotationsScalings3DAsArray()
* @ref SceneData::translationsRotationsScalings3DAsArray(),
* @ref SceneData::translationRotationScaling2DFor(),
* @ref SceneData::translationRotationScaling3DFor()
*/
Scaling,
@ -208,7 +222,8 @@ enum class SceneField: UnsignedInt {
* required. If present, both should share the same object mapping view.
* Objects with multiple meshes then have the Nth mesh associated with the
* Nth material.
* @see @ref SceneData::meshesMaterialsAsArray()
* @see @ref SceneData::meshesMaterialsAsArray(),
* @ref SceneData::meshesMaterialsFor()
*/
Mesh,
@ -219,7 +234,8 @@ enum class SceneField: UnsignedInt {
* but can be also any of @relativeref{SceneFieldType,Byte} or
* @relativeref{SceneFieldType,Short}. Expected to share the
* object mapping view with @ref SceneField::Mesh.
* @see @ref SceneData::meshesMaterialsAsArray()
* @see @ref SceneData::meshesMaterialsAsArray(),
* @ref SceneData::meshesMaterialsFor()
*/
MeshMaterial,
@ -230,7 +246,7 @@ enum class SceneField: UnsignedInt {
* @relativeref{SceneFieldType,UnsignedByte} or
* @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple
* lights associated.
* @see @ref SceneData::lightsAsArray()
* @see @ref SceneData::lightsAsArray(), @ref SceneData::lightsFor()
*/
Light,
@ -241,7 +257,7 @@ enum class SceneField: UnsignedInt {
* @relativeref{SceneFieldType,UnsignedByte} or
* @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple
* cameras associated.
* @see @ref SceneData::camerasAsArray()
* @see @ref SceneData::camerasAsArray(), @ref SceneData::camerasFor()
*/
Camera,
@ -252,7 +268,7 @@ enum class SceneField: UnsignedInt {
* @relativeref{SceneFieldType,UnsignedByte} or
* @relativeref{SceneFieldType,UnsignedShort}. An object can have multiple
* skins associated.
* @see @ref SceneData::skinsAsArray()
* @see @ref SceneData::skinsAsArray(), @ref SceneData::skinsFor()
*/
Skin,
@ -1342,7 +1358,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* is larger than the max representable 32-bit value, this
* function can't be used, only an appropriately typed
* @ref field(SceneField) const.
* @see @ref parentsInto(), @ref hasField()
* @see @ref parentsInto(), @ref hasField(), @ref childrenFor()
*/
Containers::Array<Int> parentsAsArray() const;
@ -1385,7 +1401,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* to 2D, otherwise you're supposed to use
* @ref transformations3DAsArray().
* @see @ref transformations2DInto(), @ref hasField(),
* @ref fieldType(SceneField) const
* @ref fieldType(SceneField) const, @ref transformation2DFor()
*/
Containers::Array<Matrix3> transformations2DAsArray() const;
@ -1431,7 +1447,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* the @relativeref{SceneField,Scaling} field isn't present, the third
* value is an identity scaling (@cpp 1.0f @ce in both dimensions).
* @see @ref translationsRotationsScalings2DInto(), @ref hasField(),
* @ref fieldType(SceneField) const
* @ref fieldType(SceneField) const,
* @ref translationRotationScaling2DFor()
*/
Containers::Array<Containers::Triple<Vector2, Complex, Vector2>> translationsRotationsScalings2DAsArray() const;
@ -1476,7 +1493,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* to 3D, otherwise you're supposed to use
* @ref transformations2DAsArray().
* @see @ref transformations3DInto(), @ref hasField(),
* @ref fieldType(SceneField) const
* @ref fieldType(SceneField) const, @ref transformation3DFor()
*/
Containers::Array<Matrix4> transformations3DAsArray() const;
@ -1522,7 +1539,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* the @relativeref{SceneField,Scaling} field isn't present, the third
* value is an identity scaling (@cpp 1.0f @ce in all dimensions).
* @see @ref translationsRotationsScalings3DInto(), @ref hasField(),
* @ref fieldType(SceneField) const
* @ref fieldType(SceneField) const,
* @ref translationRotationScaling3DFor()
*/
Containers::Array<Containers::Triple<Vector3, Quaternion, Vector3>> translationsRotationsScalings3DAsArray() const;
@ -1563,7 +1581,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* in a newly-allocated array. The @ref SceneField::Mesh field is
* expected to exist, if @ref SceneField::MeshMaterial isn't present,
* the second returned values are all @cpp -1 @ce.
* @see @ref meshesMaterialsInto(), @ref hasField()
* @see @ref meshesMaterialsInto(), @ref hasField(),
* @ref meshesMaterialsFor()
*/
Containers::Array<Containers::Pair<UnsignedInt, Int>> meshesMaterialsAsArray() const;
@ -1600,7 +1619,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref SceneField::Light as the argument that converts the field from
* an arbitrary underlying type and returns it in a newly-allocated
* array. The field is expected to exist.
* @see @ref lightsInto(), @ref hasField()
* @see @ref lightsInto(), @ref hasField(), @ref lightsFor()
*/
Containers::Array<UnsignedInt> lightsAsArray() const;
@ -1636,7 +1655,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref SceneField::Camera as the argument that converts the field from
* an arbitrary underlying type and returns it in a newly-allocated
* array. The field is expected to exist.
* @see @ref camerasInto(), @ref hasField()
* @see @ref camerasInto(), @ref hasField(), @ref camerasFor()
*/
Containers::Array<UnsignedInt> camerasAsArray() const;
@ -1672,7 +1691,7 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref SceneField::Skin as the argument that converts the field from
* an arbitrary underlying type and returns it in a newly-allocated
* array. The field is expected to exist.
* @see @ref skinsInto(), @ref hasField()
* @see @ref skinsInto(), @ref hasField(), @ref skinsFor()
*/
Containers::Array<UnsignedInt> skinsAsArray() const;
@ -1700,6 +1719,209 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/
std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
/**
* @brief Children for given object
* @m_since_latest
*
* Looks up @p object in the object mapping array for
* @ref SceneField::Parent and returns a list of all object IDs that
* have it listed as the parent. The lookup is done in an
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field
* count and @f$ n @f$ the size of the parent field, thus for
* retrieving parent/child info for many objects it's recommended to
* access the field data directly with @ref parentsAsArray() and
* related APIs.
*
* If the @ref SceneField::Parent field doesn't exist or there are no
* objects which would have @p object listed as their parent, returns
* an empty array. Pass @cpp -1 @ce to get a list of top-level objects.
*
* The @p object is expected to be less than @ref objectCount().
*/
Containers::Array<UnsignedInt> childrenFor(Int object) const;
/**
* @brief 2D transformation for given object
* @m_since_latest
*
* Looks up the @ref SceneField::Transformation field for @p object or
* combines it from a @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} in the same way as
* @ref transformations2DAsArray(). The lookup is done in an
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field
* count and @f$ n @f$ the size of the transformation field, thus for
* retrieving transformation info for many objects it's recommended to
* access the field data directly with @ref transformations2DAsArray()
* and related APIs.
*
* If neither @ref SceneField::Transformation nor any of
* @ref SceneField::Translation, @relativeref{SceneField,Rotation} or
* @relativeref{SceneField,Scaling} is present, the fields represent a
* 3D transformation or there's no transformation for @p object,
* returns @ref Containers::NullOpt.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref translationRotationScaling2DFor()
*/
Containers::Optional<Matrix3> transformation2DFor(UnsignedInt object) const;
/**
* @brief 2D translation, rotation and scaling for given object
* @m_since_latest
*
* Looks up the @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} fields for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the transformation
* field, thus for retrieving transformation info for many objects it's
* recommended to access the field data directly with
* @ref translationsRotationsScalings2DAsArray() and related APIs.
*
* If the @ref SceneField::Translation field isn't present, the first
* returned value is a zero vector. If the
* @relativeref{SceneField,Rotation} field isn't present, the second
* value is an identity rotation. If the @relativeref{SceneField,Scaling}
* field isn't present, the third value is an identity scaling
* (@cpp 1.0f @ce in both dimensions). If neither of those fields is
* present, if any of the fields represents a 3D transformation or if
* there's no transformation for @p object, returns
* @ref Containers::NullOpt.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref transformation2DFor()
*/
Containers::Optional<Containers::Triple<Vector2, Complex, Vector2>> translationRotationScaling2DFor(UnsignedInt object) const;
/**
* @brief 3D transformation for given object
* @m_since_latest
*
* Looks up the @ref SceneField::Transformation field for @p object or
* combines it from a @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} in the same way as
* @ref transformations3DAsArray(). The lookup is done in an
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field
* count and @f$ n @f$ the size of the transformation field, thus for
* retrieving transformation info for many objects it's recommended to
* access the field data directly with @ref transformations3DAsArray()
* and related APIs.
*
* If neither @ref SceneField::Transformation nor any of
* @ref SceneField::Translation, @relativeref{SceneField,Rotation} or
* @relativeref{SceneField,Scaling} is present, the fields represent a
* 2D transformation or there's no transformation for @p object,
* returns @ref Containers::NullOpt.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref translationRotationScaling3DFor()
*/
Containers::Optional<Matrix4> transformation3DFor(UnsignedInt object) const;
/**
* @brief 3D translation, rotation and scaling for given object
* @m_since_latest
*
* Looks up the @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} fields for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the transformation
* field, thus for retrieving transformation info for many objects it's
* recommended to access the field data directly with
* @ref translationsRotationsScalings3DAsArray() and related APIs.
*
* If the @ref SceneField::Translation field isn't present, the first
* returned value is a zero vector. If the
* @relativeref{SceneField,Rotation} field isn't present, the second
* value is an identity rotation. If the @relativeref{SceneField,Scaling}
* field isn't present, the third value is an identity scaling
* (@cpp 1.0f @ce in all dimensions). If neither of those fields is
* present, if any of the fields represents a 2D transformation or if
* there's no transformation for @p object, returns
* @ref Containers::NullOpt.
*
* The @p object is expected to be less than @ref objectCount().
* @see @ref transformation3DFor()
*/
Containers::Optional<Containers::Triple<Vector3, Quaternion, Vector3>> translationRotationScaling3DFor(UnsignedInt object) const;
/**
* @brief Meshes and materials for given object
* @m_since_latest
*
* Looks up all @ref SceneField::Mesh and @ref SceneField::MeshMaterial
* @relativeref{SceneField,Scaling} fields for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the mesh field, thus
* for retrieving mesh info for many objects it's recommended to access
* the field data directly with @ref meshesMaterialsAsArray() and
* related APIs.
*
* If the @ref SceneField::MeshMaterial field is not present, the
* second returned value is always @cpp -1 @ce. If
* @ref SceneField::Mesh is not present or if there's no mesh for
* @p object, returns an empty array.
*
* The @p object is expected to be less than @ref objectCount().
*/
Containers::Array<Containers::Pair<UnsignedInt, Int>> meshesMaterialsFor(UnsignedInt object) const;
/**
* @brief Lights for given object
* @m_since_latest
*
* Looks up all @ref SceneField::Light fields for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the light field,
* thus for retrieving light info for many objects it's recommended to
* access the field data directly with @ref lightsAsArray() and related
* APIs.
*
* If the @ref SceneField::Light field is not present or if there's no
* light for @p object, returns an empty array.
*
* The @p object is expected to be less than @ref objectCount().
*/
Containers::Array<UnsignedInt> lightsFor(UnsignedInt object) const;
/**
* @brief Cameras for given object
* @m_since_latest
*
* Looks up all @ref SceneField::Camera fields for @p object. The
* lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with
* @f$ m @f$ being the field count and @f$ n @f$ the size of the camera
* field, thus for retrieving camera info for many objects it's
* recommended to access the field data directly with
* @ref camerasAsArray() and related APIs.
*
* If the @ref SceneField::Camera field is not present or if there's no
* camera for @p object, returns an empty array.
*
* The @p object is expected to be less than @ref objectCount().
*/
Containers::Array<UnsignedInt> camerasFor(UnsignedInt object) const;
/**
* @brief Skins for given object
* @m_since_latest
*
* Looks up all @ref SceneField::Skin fields for @p object. The lookup
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$
* being the field count and @f$ n @f$ the size of the skin field, thus
* for retrieving skin info for many objects it's recommended to access
* the field data directly with @ref skinsAsArray() and related APIs.
*
* If the @ref SceneField::Skin field is not present or if there's no
* skin for @p object, returns an empty array.
*
* The @p object is expected to be less than @ref objectCount().
*/
Containers::Array<UnsignedInt> skinsFor(UnsignedInt object) const;
/**
* @brief Release field data storage
* @m_since_latest
@ -1746,6 +1968,11 @@ class MAGNUM_TRADE_EXPORT SceneData {
/* Internal helper that doesn't assert, unlike fieldId() */
UnsignedInt fieldFor(SceneField name) const;
/* Returns the offset at which `object` is for field at index `id`, or
the end offset if the object is not found. The returned offset can
be then passed to fieldData{Object,Field}ViewInternal(). */
std::size_t fieldFor(const SceneFieldData& field, std::size_t offset, UnsignedInt object) const;
/* Like objects() / field(), but returning just a 1D view, sliced from
offset to offset + size. The parameterless overloads are equal to
offset = 0 and size = field.size(). */
@ -1760,8 +1987,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
MAGNUM_TRADE_LOCAL void objectsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
MAGNUM_TRADE_LOCAL void parentsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const;
MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const;
MAGNUM_TRADE_LOCAL std::size_t findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const;
MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* fieldWithObjectMappingDestination = nullptr) const;
MAGNUM_TRADE_LOCAL std::size_t findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* fieldWithObjectMappingDestination = nullptr) const;
MAGNUM_TRADE_LOCAL void transformations2DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D<Matrix3>& destination) const;
MAGNUM_TRADE_LOCAL void translationsRotationsScalings2DIntoInternal(UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const;
MAGNUM_TRADE_LOCAL void transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D<Matrix4>& destination) const;

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

@ -25,6 +25,7 @@
#include <sstream>
#include <Corrade/Containers/ArrayTuple.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/TestSuite/Tester.h>
@ -150,6 +151,25 @@ struct SceneDataTest: TestSuite::Tester {
void fieldWrongType();
void fieldWrongArrayAccess();
/* Different object types checked just for the childrenFor(), other APIs
use the same helper */
template<class T> void childrenFor();
void transformation2DFor();
void transformation2DForTRS();
template<class T> void transformation2DForBut3DType();
template<class T> void transformation2DForBut3DTypeTRS();
void transformation3DFor();
void transformation3DForTRS();
template<class T> void transformation3DForBut2DType();
template<class T> void transformation3DForBut2DTypeTRS();
void meshesMaterialsFor();
void lightsFor();
void camerasFor();
void skinsFor();
void fieldForFieldMissing();
void fieldForInvalidObject();
void releaseFieldData();
void releaseData();
};
@ -340,6 +360,34 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::fieldWrongType,
&SceneDataTest::fieldWrongArrayAccess,
&SceneDataTest::childrenFor<UnsignedByte>,
&SceneDataTest::childrenFor<UnsignedShort>,
&SceneDataTest::childrenFor<UnsignedInt>,
&SceneDataTest::childrenFor<UnsignedLong>,
&SceneDataTest::transformation2DFor,
&SceneDataTest::transformation2DForTRS,
&SceneDataTest::transformation2DForBut3DType<Matrix4x4>,
&SceneDataTest::transformation2DForBut3DType<Matrix4x4d>,
&SceneDataTest::transformation2DForBut3DType<DualQuaternion>,
&SceneDataTest::transformation2DForBut3DType<DualQuaterniond>,
&SceneDataTest::transformation2DForBut3DTypeTRS<Float>,
&SceneDataTest::transformation2DForBut3DTypeTRS<Double>,
&SceneDataTest::transformation3DFor,
&SceneDataTest::transformation3DForTRS,
&SceneDataTest::transformation3DForBut2DType<Matrix3x3>,
&SceneDataTest::transformation3DForBut2DType<Matrix3x3d>,
&SceneDataTest::transformation3DForBut2DType<DualComplex>,
&SceneDataTest::transformation3DForBut2DType<DualComplex>,
&SceneDataTest::transformation3DForBut2DTypeTRS<Float>,
&SceneDataTest::transformation3DForBut2DTypeTRS<Double>,
&SceneDataTest::meshesMaterialsFor,
&SceneDataTest::lightsFor,
&SceneDataTest::camerasFor,
&SceneDataTest::skinsFor,
&SceneDataTest::fieldForFieldMissing,
&SceneDataTest::fieldForInvalidObject,
&SceneDataTest::releaseFieldData,
&SceneDataTest::releaseData});
}
@ -3992,6 +4040,505 @@ void SceneDataTest::fieldWrongArrayAccess() {
"Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n");
}
template<class T> void SceneDataTest::childrenFor() {
setTestCaseTemplateName(NameTraits<T>::name());
struct Field {
T object;
Int parent;
} fields[]{
{4, -1},
{3, 0},
{2, 1},
{1, 0},
{5, 0},
{0, -1},
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneObjectTypeFor<T>(), 7, {}, fields, {
SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}};
/* Just one child */
CORRADE_COMPARE_AS(scene.childrenFor(3),
Containers::arrayView<UnsignedInt>({2}),
TestSuite::Compare::Container);
/* More */
CORRADE_COMPARE_AS(scene.childrenFor(-1),
Containers::arrayView<UnsignedInt>({4, 0}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.childrenFor(4),
Containers::arrayView<UnsignedInt>({3, 1, 5}),
TestSuite::Compare::Container);
/* Object that is present in the parent array but has no children */
CORRADE_COMPARE_AS(scene.childrenFor(5),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
/* Object that is not in the parent array at all */
CORRADE_COMPARE_AS(scene.childrenFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
}
void SceneDataTest::transformation2DFor() {
const struct Field {
UnsignedInt object;
Matrix3 transformation;
} fields[] {
{1, Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f})},
{0, Matrix3::rotation(35.0_degf)},
{4, Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf)},
{1, Matrix3::translation({1.0f, 2.0f})} /* duplicate, ignored */
};
Containers::StridedArrayView1D<const Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)}
}};
CORRADE_COMPARE(scene.transformation2DFor(4),
Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf));
CORRADE_COMPARE(scene.transformation2DFor(0),
Matrix3::Matrix3::Matrix3::Matrix3::rotation(35.0_degf));
/* Duplicate entries -- only the first one gets used, it doesn't traverse
further */
CORRADE_COMPARE(scene.transformation2DFor(1),
Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f}));
/* Object that's not in the array at all */
CORRADE_COMPARE(scene.transformation2DFor(2),
Containers::NullOpt);
}
void SceneDataTest::transformation2DForTRS() {
const struct Field {
UnsignedInt object;
Vector2 translation;
Complex rotation;
Vector2 scaling;
} fields[] {
{1, {3.0f, 2.0f}, {}, {1.5f, 2.0f}},
{0, {}, Complex::rotation(35.0_degf), {1.0f, 1.0f}},
{4, {3.0f, 2.0f}, Complex::rotation(35.0_degf), {1.0f, 1.0f}},
{1, {1.0f, 2.0f}, {}, {1.0f, 1.0f}} /* duplicate, ignored */
};
Containers::StridedArrayView1D<const Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)},
SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)},
SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)}
}};
CORRADE_COMPARE(scene.transformation2DFor(4),
Matrix3::translation({3.0f, 2.0f})*Matrix3::rotation(35.0_degf));
CORRADE_COMPARE(scene.translationRotationScaling2DFor(4),
Containers::triple(Vector2{3.0f, 2.0f}, Complex::rotation(35.0_degf), Vector2{1.0f}));
CORRADE_COMPARE(scene.transformation2DFor(0),
Matrix3::rotation(35.0_degf));
CORRADE_COMPARE(scene.translationRotationScaling2DFor(0),
Containers::triple(Vector2{}, Complex::rotation(35.0_degf), Vector2{1.0f}));
/* Duplicate entries -- only the first one gets used, it doesn't traverse
further */
CORRADE_COMPARE(scene.transformation2DFor(1),
Matrix3::translation({3.0f, 2.0f})*Matrix3::scaling({1.5f, 2.0f}));
CORRADE_COMPARE(scene.translationRotationScaling2DFor(1),
Containers::triple(Vector2{3.0f, 2.0f}, Complex{}, Vector2{1.5f, 2.0f}));
/* Object that's not in the array at all */
CORRADE_COMPARE(scene.transformation2DFor(2),
Containers::NullOpt);
CORRADE_COMPARE(scene.translationRotationScaling2DFor(2),
Containers::NullOpt);
}
template<class T> void SceneDataTest::transformation2DForBut3DType() {
setTestCaseTemplateName(NameTraits<T>::name());
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<T>::type(), nullptr}
}};
std::ostringstream out;
Error redirectError{&out};
scene.transformation2DFor(0);
CORRADE_COMPARE(out.str(), Utility::formatString(
"Trade::SceneData::transformation2DFor(): field has a 3D transformation type Trade::SceneFieldType::{}\n", NameTraits<T>::name()));
}
template<class T> void SceneDataTest::transformation2DForBut3DTypeTRS() {
setTestCaseTemplateName(NameTraits<T>::name());
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector3<T>>::type(), nullptr}
}};
SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Quaternion<T>>::type(), nullptr}
}};
SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector3<T>>::type(), nullptr}
}};
std::ostringstream out;
Error redirectError{&out};
translation.transformation2DFor(0);
translation.translationRotationScaling2DFor(0);
rotation.transformation2DFor(0);
rotation.translationRotationScaling2DFor(0);
scaling.transformation2DFor(0);
scaling.translationRotationScaling2DFor(0);
CORRADE_COMPARE(out.str(), Utility::formatString(
"Trade::SceneData::transformation2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D translation type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::transformation2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n"
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D rotation type Trade::SceneFieldType::{1}\n"
"Trade::SceneData::transformation2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::translationRotationScaling2DFor(): field has a 3D scaling type Trade::SceneFieldType::{0}\n", NameTraits<Math::Vector3<T>>::name(), NameTraits<Math::Quaternion<T>>::name()));
}
void SceneDataTest::transformation3DFor() {
const struct Field {
UnsignedInt object;
Matrix4 transformation;
} fields[] {
{1, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f})},
{0, Matrix4::rotationX(35.0_degf)},
{4, Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf)},
{1, Matrix4::translation({1.0f, 2.0f, 3.0f})} /* duplicate, ignored */
};
Containers::StridedArrayView1D<const Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Transformation, view.slice(&Field::object), view.slice(&Field::transformation)}
}};
CORRADE_COMPARE(scene.transformation3DFor(4),
Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf));
CORRADE_COMPARE(scene.transformation3DFor(0),
Matrix4::rotationX(35.0_degf));
/* Duplicate entries -- only the first one gets used, it doesn't traverse
further */
CORRADE_COMPARE(scene.transformation3DFor(1),
Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f}));
/* Object that's not in the array at all */
CORRADE_COMPARE(scene.transformation3DFor(2),
Containers::NullOpt);
}
void SceneDataTest::transformation3DForTRS() {
const struct Field {
UnsignedInt object;
Vector3 translation;
Quaternion rotation;
Vector3 scaling;
} fields[] {
{1, {3.0f, 2.0f, 1.0f}, {}, {1.5f, 2.0f, 4.5f}},
{0, {}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), {1.0f, 1.0f, 1.0f}},
{4, {3.0f, 2.0f, 1.0f}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), {1.0f, 1.0f, 1.0f}},
{1, {1.0f, 2.0f, 3.0f}, Quaternion{}, Vector3{1.0f}} /* duplicate, ignored */
};
Containers::StridedArrayView1D<const Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Translation, view.slice(&Field::object), view.slice(&Field::translation)},
SceneFieldData{SceneField::Rotation, view.slice(&Field::object), view.slice(&Field::rotation)},
SceneFieldData{SceneField::Scaling, view.slice(&Field::object), view.slice(&Field::scaling)}
}};
CORRADE_COMPARE(scene.transformation3DFor(4),
Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::rotationX(35.0_degf));
CORRADE_COMPARE(scene.translationRotationScaling3DFor(4),
Containers::triple(Vector3{3.0f, 2.0f, 1.0f}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), Vector3{1.0f}));
CORRADE_COMPARE(scene.transformation3DFor(0),
Matrix4::rotationX(35.0_degf));
CORRADE_COMPARE(scene.translationRotationScaling3DFor(0),
Containers::triple(Vector3{}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), Vector3{1.0f}));
/* Duplicate entries -- only the first one gets used, it doesn't traverse
further */
CORRADE_COMPARE(scene.transformation3DFor(1),
Matrix4::translation({3.0f, 2.0f, 1.0f})*Matrix4::scaling({1.5f, 2.0f, 4.5f}));
CORRADE_COMPARE(scene.translationRotationScaling3DFor(1),
Containers::triple(Vector3{3.0f, 2.0f, 1.0f}, Quaternion{}, Vector3{1.5f, 2.0f, 4.5f}));
/* Object that's not in the array at all */
CORRADE_COMPARE(scene.transformation3DFor(2),
Containers::NullOpt);
CORRADE_COMPARE(scene.translationRotationScaling3DFor(2),
Containers::NullOpt);
}
template<class T> void SceneDataTest::transformation3DForBut2DType() {
setTestCaseTemplateName(NameTraits<T>::name());
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
SceneData scene{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<T>::type(), nullptr}
}};
std::ostringstream out;
Error redirectError{&out};
scene.transformation3DFor(0);
CORRADE_COMPARE(out.str(), Utility::formatString(
"Trade::SceneData::transformation3DFor(): field has a 2D transformation type Trade::SceneFieldType::{}\n", NameTraits<T>::name()));
}
template<class T> void SceneDataTest::transformation3DForBut2DTypeTRS() {
setTestCaseTemplateName(NameTraits<T>::name());
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
SceneData translation{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector2<T>>::type(), nullptr}
}};
SceneData rotation{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Rotation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Complex<T>>::type(), nullptr}
}};
SceneData scaling{SceneObjectType::UnsignedInt, 1, nullptr, {
SceneFieldData{SceneField::Scaling, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector2<T>>::type(), nullptr}
}};
std::ostringstream out;
Error redirectError{&out};
translation.transformation3DFor(0);
translation.translationRotationScaling3DFor(0);
rotation.transformation3DFor(0);
rotation.translationRotationScaling3DFor(0);
scaling.transformation3DFor(0);
scaling.translationRotationScaling3DFor(0);
CORRADE_COMPARE(out.str(), Utility::formatString(
"Trade::SceneData::transformation3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D translation type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::transformation3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n"
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D rotation type Trade::SceneFieldType::{1}\n"
"Trade::SceneData::transformation3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n"
"Trade::SceneData::translationRotationScaling3DFor(): field has a 2D scaling type Trade::SceneFieldType::{0}\n", NameTraits<Math::Vector2<T>>::name(), NameTraits<Math::Complex<T>>::name()));
}
void SceneDataTest::meshesMaterialsFor() {
struct Field {
UnsignedInt object;
UnsignedInt mesh;
Int meshMaterial;
} fields[]{
{4, 1, -1},
{1, 3, 0},
{2, 4, 1},
{2, 5, -1},
{2, 1, 0},
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)},
SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)}
}};
/* Just one */
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(1),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{3, 0}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(4),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{1, -1}})),
TestSuite::Compare::Container);
/* More */
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
{4, 1}, {5, -1}, {1, 0}
})), TestSuite::Compare::Container);
/* Object that is not in the array at all */
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(6),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({})),
TestSuite::Compare::Container);
}
void SceneDataTest::lightsFor() {
struct Field {
UnsignedInt object;
UnsignedInt light;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{2, 5}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)}
}};
/* Just one */
CORRADE_COMPARE_AS(scene.lightsFor(1),
(Containers::arrayView<UnsignedInt>({3})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(4),
(Containers::arrayView<UnsignedInt>({1})),
TestSuite::Compare::Container);
/* More */
CORRADE_COMPARE_AS(scene.lightsFor(2),
(Containers::arrayView<UnsignedInt>({
4, 5
})), TestSuite::Compare::Container);
/* Object that is not in the array at all */
CORRADE_COMPARE_AS(scene.lightsFor(6),
(Containers::arrayView<UnsignedInt>({})),
TestSuite::Compare::Container);
}
void SceneDataTest::camerasFor() {
struct Field {
UnsignedInt object;
UnsignedInt camera;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{2, 5}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)}
}};
/* Just one */
CORRADE_COMPARE_AS(scene.camerasFor(1),
(Containers::arrayView<UnsignedInt>({3})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(4),
(Containers::arrayView<UnsignedInt>({1})),
TestSuite::Compare::Container);
/* More */
CORRADE_COMPARE_AS(scene.camerasFor(2),
(Containers::arrayView<UnsignedInt>({
4, 5
})), TestSuite::Compare::Container);
/* Object that is not in the array at all */
CORRADE_COMPARE_AS(scene.camerasFor(6),
(Containers::arrayView<UnsignedInt>({})),
TestSuite::Compare::Container);
}
void SceneDataTest::skinsFor() {
struct Field {
UnsignedInt object;
UnsignedInt skin;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{2, 5}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)}
}};
/* Just one */
CORRADE_COMPARE_AS(scene.skinsFor(1),
(Containers::arrayView<UnsignedInt>({3})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.skinsFor(4),
(Containers::arrayView<UnsignedInt>({1})),
TestSuite::Compare::Container);
/* More */
CORRADE_COMPARE_AS(scene.skinsFor(2),
(Containers::arrayView<UnsignedInt>({
4, 5
})), TestSuite::Compare::Container);
/* Object that is not in the array at all */
CORRADE_COMPARE_AS(scene.skinsFor(6),
(Containers::arrayView<UnsignedInt>({})),
TestSuite::Compare::Container);
}
void SceneDataTest::fieldForFieldMissing() {
SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}};
CORRADE_COMPARE_AS(scene.childrenFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.transformation2DFor(6), Containers::NullOpt);
CORRADE_COMPARE(scene.translationRotationScaling2DFor(6), Containers::NullOpt);
CORRADE_COMPARE(scene.transformation3DFor(6), Containers::NullOpt);
CORRADE_COMPARE(scene.translationRotationScaling3DFor(6), Containers::NullOpt);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(6),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.skinsFor(6),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
}
void SceneDataTest::fieldForInvalidObject() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}};
std::ostringstream out;
Error redirectError{&out};
scene.childrenFor(-2);
scene.childrenFor(7);
scene.transformation2DFor(7);
scene.translationRotationScaling2DFor(7);
scene.transformation3DFor(7);
scene.translationRotationScaling3DFor(7);
scene.meshesMaterialsFor(7);
scene.lightsFor(7);
scene.camerasFor(7);
scene.skinsFor(7);
CORRADE_COMPARE(out.str(),
"Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n"
"Trade::SceneData::childrenFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::transformation2DFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::translationRotationScaling2DFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::transformation3DFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::translationRotationScaling3DFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::meshesMaterialsFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::lightsFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::camerasFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::skinsFor(): object 7 out of bounds for 7 objects\n");
}
void SceneDataTest::releaseFieldData() {
struct Field {
UnsignedByte object;

Loading…
Cancel
Save