Browse Source

Trade: add public SceneData APIs for finding fields and objects.

Using hasField() + fieldId() was a bad usage pattern leading to a double
linear lookup, so there's now findFieldId() returning an Optional which
covers both. Similarly for finding an object offset in an field, there's
a findFieldObjectOffset() returning an Optional, fieldObjectOffset()
asserting if an object is not found (for convenience to avoid explicit
error handling on user side) and hasFieldObject().

The internal helpers were also renamed and the offset argument moved to
be last for consistency.
pull/525/head
Vladimír Vondruš 4 years ago
parent
commit
6e1bb5f838
  1. 259
      src/Magnum/Trade/SceneData.cpp
  2. 299
      src/Magnum/Trade/SceneData.h
  3. 218
      src/Magnum/Trade/Test/SceneDataTest.cpp

259
src/Magnum/Trade/SceneData.cpp

@ -831,36 +831,154 @@ UnsignedShort SceneData::fieldArraySize(const UnsignedInt id) const {
return _fields[id]._fieldArraySize; return _fields[id]._fieldArraySize;
} }
UnsignedInt SceneData::fieldFor(const SceneField name) const { UnsignedInt SceneData::findFieldIdInternal(const SceneField name) const {
for(std::size_t i = 0; i != _fields.size(); ++i) for(std::size_t i = 0; i != _fields.size(); ++i)
if(_fields[i]._name == name) return i; if(_fields[i]._name == name) return i;
return ~UnsignedInt{}; return ~UnsignedInt{};
} }
bool SceneData::hasField(const SceneField name) const { Containers::Optional<UnsignedInt> SceneData::findFieldId(const SceneField name) const {
return fieldFor(name) != ~UnsignedInt{}; const UnsignedInt fieldId = findFieldIdInternal(name);
return fieldId == ~UnsignedInt{} ? Containers::Optional<UnsignedInt>{} : fieldId;
} }
UnsignedInt SceneData::fieldId(const SceneField name) const { UnsignedInt SceneData::fieldId(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldId(): field" << name << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldId(): field" << name << "not found", {});
return fieldId; return fieldId;
} }
bool SceneData::hasField(const SceneField name) const {
return findFieldIdInternal(name) != ~UnsignedInt{};
}
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::findFieldObjectOffsetInternal(const SceneFieldData& field, const UnsignedInt object, const std::size_t offset) 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<std::size_t> SceneData::findFieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {});
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::findFieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[fieldId];
CORRADE_ASSERT(offset <= field._size,
"Trade::SceneData::findFieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {});
const std::size_t found = findFieldObjectOffsetInternal(field, object, offset);
return found == field._size ? Containers::Optional<std::size_t>{} : found;
}
Containers::Optional<std::size_t> SceneData::findFieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::findFieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = findFieldIdInternal(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::findFieldObjectOffset(): field" << fieldName << "not found", {});
const SceneFieldData& field = _fields[fieldId];
CORRADE_ASSERT(offset <= field._size,
"Trade::SceneData::findFieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {});
const std::size_t found = findFieldObjectOffsetInternal(field, object, offset);
return found == field._size ? Containers::Optional<std::size_t>{} : found;
}
std::size_t SceneData::fieldObjectOffset(const UnsignedInt fieldId, const UnsignedInt object, const std::size_t offset) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {});
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::fieldObjectOffset(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[fieldId];
CORRADE_ASSERT(offset <= field._size,
"Trade::SceneData::fieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {});
const std::size_t found = findFieldObjectOffsetInternal(field, object, offset);
CORRADE_ASSERT(found != field._size,
"Trade::SceneData::fieldObjectOffset(): object" << object << "not found in field" << field._name << "starting at offset" << offset, {});
return found;
}
std::size_t SceneData::fieldObjectOffset(const SceneField fieldName, const UnsignedInt object, const std::size_t offset) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::fieldObjectOffset(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = findFieldIdInternal(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::fieldObjectOffset(): field" << fieldName << "not found", {});
const SceneFieldData& field = _fields[fieldId];
CORRADE_ASSERT(offset <= field._size,
"Trade::SceneData::fieldObjectOffset(): offset" << offset << "out of bounds for a field of size" << field._size, {});
const std::size_t found = findFieldObjectOffsetInternal(field, object, offset);
CORRADE_ASSERT(found != field._size,
"Trade::SceneData::fieldObjectOffset(): object" << object << "not found in field" << field._name << "starting at offset" << offset, {});
return found;
}
bool SceneData::hasFieldObject(const UnsignedInt fieldId, const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {});
CORRADE_ASSERT(fieldId < _fields.size(),
"Trade::SceneData::hasFieldObject(): index" << fieldId << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[fieldId];
return findFieldObjectOffsetInternal(field, object, 0) != field._size;
}
bool SceneData::hasFieldObject(const SceneField fieldName, const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::hasFieldObject(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = findFieldIdInternal(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::hasFieldObject(): field" << fieldName << "not found", {});
const SceneFieldData& field = _fields[fieldId];
return findFieldObjectOffsetInternal(field, object, 0) != field._size;
}
SceneFieldType SceneData::fieldType(const SceneField name) const { SceneFieldType SceneData::fieldType(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldType(): field" << name << "not found", {});
return _fields[fieldId]._fieldType; return _fields[fieldId]._fieldType;
} }
std::size_t SceneData::fieldSize(const SceneField name) const { std::size_t SceneData::fieldSize(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldSize(): field" << name << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldSize(): field" << name << "not found", {});
return _fields[fieldId]._size; return _fields[fieldId]._size;
} }
UnsignedShort SceneData::fieldArraySize(const SceneField name) const { UnsignedShort SceneData::fieldArraySize(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::fieldArraySize(): field" << name << "not found", {});
return _fields[fieldId]._fieldArraySize; return _fields[fieldId]._fieldArraySize;
} }
@ -894,7 +1012,7 @@ Containers::StridedArrayView2D<char> SceneData::mutableObjects(const UnsignedInt
} }
Containers::StridedArrayView2D<const char> SceneData::objects(const SceneField fieldName) const { Containers::StridedArrayView2D<const char> SceneData::objects(const SceneField fieldName) const {
const UnsignedInt fieldId = fieldFor(fieldName); const UnsignedInt fieldId = findFieldIdInternal(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::objects(): field" << fieldName << "not found", {});
return objects(fieldId); return objects(fieldId);
} }
@ -902,7 +1020,7 @@ Containers::StridedArrayView2D<const char> SceneData::objects(const SceneField f
Containers::StridedArrayView2D<char> SceneData::mutableObjects(const SceneField fieldName) { Containers::StridedArrayView2D<char> SceneData::mutableObjects(const SceneField fieldName) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableObjects(): data not mutable", {}); "Trade::SceneData::mutableObjects(): data not mutable", {});
const UnsignedInt fieldId = fieldFor(fieldName); const UnsignedInt fieldId = findFieldIdInternal(fieldName);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {}); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::mutableObjects(): field" << fieldName << "not found", {});
return mutableObjects(fieldId); return mutableObjects(fieldId);
} }
@ -936,7 +1054,7 @@ Containers::StridedArrayView2D<char> SceneData::mutableField(const UnsignedInt i
} }
Containers::StridedArrayView2D<const char> SceneData::field(const SceneField name) const { Containers::StridedArrayView2D<const char> SceneData::field(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::field(): field" << name << "not found", {}); "Trade::SceneData::field(): field" << name << "not found", {});
return field(fieldId); return field(fieldId);
@ -945,7 +1063,7 @@ Containers::StridedArrayView2D<const char> SceneData::field(const SceneField nam
Containers::StridedArrayView2D<char> SceneData::mutableField(const SceneField name) { Containers::StridedArrayView2D<char> SceneData::mutableField(const SceneField name) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableField(): data not mutable", {}); "Trade::SceneData::mutableField(): data not mutable", {});
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::mutableField(): field" << name << "not found", {}); "Trade::SceneData::mutableField(): field" << name << "not found", {});
return mutableField(fieldId); return mutableField(fieldId);
@ -1000,21 +1118,21 @@ Containers::Array<UnsignedInt> SceneData::objectsAsArray(const UnsignedInt field
} }
void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D<UnsignedInt>& destination) const { void SceneData::objectsInto(const SceneField name, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::objectsInto(): field" << name << "not found", ); "Trade::SceneData::objectsInto(): field" << name << "not found", );
objectsInto(fieldId, destination); objectsInto(fieldId, destination);
} }
std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const { std::size_t SceneData::objectsInto(const SceneField name, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::objectsInto(): field" << name << "not found", {}); "Trade::SceneData::objectsInto(): field" << name << "not found", {});
return objectsInto(fieldId, offset, destination); return objectsInto(fieldId, offset, destination);
} }
Containers::Array<UnsignedInt> SceneData::objectsAsArray(const SceneField name) const { Containers::Array<UnsignedInt> SceneData::objectsAsArray(const SceneField name) const {
const UnsignedInt fieldId = fieldFor(name); const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1043,7 +1161,7 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t
} }
void SceneData::parentsInto(const Containers::StridedArrayView1D<Int>& destination) const { void SceneData::parentsInto(const Containers::StridedArrayView1D<Int>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::parentsInto(): field not found", ); "Trade::SceneData::parentsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
@ -1052,7 +1170,7 @@ void SceneData::parentsInto(const Containers::StridedArrayView1D<Int>& destinati
} }
std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const { std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::parentsInto(): field not found", {}); "Trade::SceneData::parentsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1063,7 +1181,7 @@ std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::S
} }
Containers::Array<Int> SceneData::parentsAsArray() const { Containers::Array<Int> SceneData::parentsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Parent); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1664,7 +1782,7 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std
/* Copy also the material, if desired. If no such field is present, output /* Copy also the material, if desired. If no such field is present, output
-1 for all meshes. */ -1 for all meshes. */
if(meshMaterialDestination) { if(meshMaterialDestination) {
const UnsignedInt materialFieldId = fieldFor(SceneField::MeshMaterial); const UnsignedInt materialFieldId = findFieldIdInternal(SceneField::MeshMaterial);
if(materialFieldId == ~UnsignedInt{}) { if(materialFieldId == ~UnsignedInt{}) {
constexpr Int invalid[]{-1}; constexpr Int invalid[]{-1};
Utility::copy(Containers::stridedArrayView(invalid).broadcasted<0>(meshMaterialDestination.size()), meshMaterialDestination); Utility::copy(Containers::stridedArrayView(invalid).broadcasted<0>(meshMaterialDestination.size()), meshMaterialDestination);
@ -1673,7 +1791,7 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std
} }
void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const { void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Mesh); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", ); "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", );
CORRADE_ASSERT(!meshDestination || meshDestination.size() == _fields[fieldId]._size, CORRADE_ASSERT(!meshDestination || meshDestination.size() == _fields[fieldId]._size,
@ -1684,7 +1802,7 @@ void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D<Unsigne
} }
std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialDestination) const { 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); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1699,7 +1817,7 @@ std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Conta
} }
Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterialsAsArray() const { Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterialsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Mesh); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1713,7 +1831,7 @@ Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterials
} }
void SceneData::lightsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const { void SceneData::lightsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Light); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::lightsInto(): field not found", ); "Trade::SceneData::lightsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
@ -1722,7 +1840,7 @@ void SceneData::lightsInto(const Containers::StridedArrayView1D<UnsignedInt>& de
} }
std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const { std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Light); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::lightsInto(): field not found", {}); "Trade::SceneData::lightsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1733,7 +1851,7 @@ std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::St
} }
Containers::Array<UnsignedInt> SceneData::lightsAsArray() const { Containers::Array<UnsignedInt> SceneData::lightsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Light); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1742,7 +1860,7 @@ Containers::Array<UnsignedInt> SceneData::lightsAsArray() const {
} }
void SceneData::camerasInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const { void SceneData::camerasInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::camerasInto(): field not found", ); "Trade::SceneData::camerasInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
@ -1751,7 +1869,7 @@ void SceneData::camerasInto(const Containers::StridedArrayView1D<UnsignedInt>& d
} }
std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const { std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::camerasInto(): field not found", {}); "Trade::SceneData::camerasInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1762,7 +1880,7 @@ std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::S
} }
Containers::Array<UnsignedInt> SceneData::camerasAsArray() const { Containers::Array<UnsignedInt> SceneData::camerasAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Camera); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1771,7 +1889,7 @@ Containers::Array<UnsignedInt> SceneData::camerasAsArray() const {
} }
void SceneData::skinsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const { void SceneData::skinsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::skinsInto(): field not found", ); "Trade::SceneData::skinsInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
@ -1780,7 +1898,7 @@ void SceneData::skinsInto(const Containers::StridedArrayView1D<UnsignedInt>& des
} }
std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const { std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::skinsInto(): field not found", {}); "Trade::SceneData::skinsInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1791,7 +1909,7 @@ std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::Str
} }
Containers::Array<UnsignedInt> SceneData::skinsAsArray() const { Containers::Array<UnsignedInt> SceneData::skinsAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::Skin); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1810,7 +1928,7 @@ void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std::
} }
void SceneData::importerStateInto(const Containers::StridedArrayView1D<const void*>& destination) const { void SceneData::importerStateInto(const Containers::StridedArrayView1D<const void*>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::importerStateInto(): field not found", ); "Trade::SceneData::importerStateInto(): field not found", );
CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, CORRADE_ASSERT(destination.size() == _fields[fieldId]._size,
@ -1819,7 +1937,7 @@ void SceneData::importerStateInto(const Containers::StridedArrayView1D<const voi
} }
std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D<const void*>& destination) const { std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D<const void*>& destination) const {
const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::importerStateInto(): field not found", {}); "Trade::SceneData::importerStateInto(): field not found", {});
CORRADE_ASSERT(offset <= _fields[fieldId]._size, CORRADE_ASSERT(offset <= _fields[fieldId]._size,
@ -1830,7 +1948,7 @@ std::size_t SceneData::importerStateInto(const std::size_t offset, const Contain
} }
Containers::Array<const void*> SceneData::importerStateAsArray() const { Containers::Array<const void*> SceneData::importerStateAsArray() const {
const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState);
CORRADE_ASSERT(fieldId != ~UnsignedInt{}, CORRADE_ASSERT(fieldId != ~UnsignedInt{},
/* Using the same message as in Into() to avoid too many redundant /* Using the same message as in Into() to avoid too many redundant
strings in the binary */ strings in the binary */
@ -1840,42 +1958,15 @@ Containers::Array<const void*> SceneData::importerStateAsArray() const {
return out; return out;
} }
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 { Containers::Optional<Int> SceneData::parentFor(const UnsignedInt object) const {
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::parentFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Parent); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent);
if(fieldId == ~UnsignedInt{}) return {}; if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId]; const SceneFieldData& field = _fields[fieldId];
const std::size_t offset = fieldFor(field, 0, object); const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {}; if(offset == field._size) return {};
Int index[1]; Int index[1];
@ -1891,7 +1982,7 @@ Containers::Array<UnsignedInt> SceneData::childrenFor(const Int object) const {
CORRADE_ASSERT(object >= -1 && object < Long(_objectCount), CORRADE_ASSERT(object >= -1 && object < Long(_objectCount),
"Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::childrenFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt parentFieldId = fieldFor(SceneField::Parent); const UnsignedInt parentFieldId = findFieldIdInternal(SceneField::Parent);
if(parentFieldId == ~UnsignedInt{}) return {}; if(parentFieldId == ~UnsignedInt{}) return {};
const SceneFieldData& parentField = _fields[parentFieldId]; const SceneFieldData& parentField = _fields[parentFieldId];
@ -1901,7 +1992,7 @@ Containers::Array<UnsignedInt> SceneData::childrenFor(const Int object) const {
Int parentIndexToLookFor; Int parentIndexToLookFor;
if(object == -1) parentIndexToLookFor = -1; if(object == -1) parentIndexToLookFor = -1;
else { else {
const std::size_t parentObjectIndex = fieldFor(parentField, 0, object); const std::size_t parentObjectIndex = findFieldObjectOffsetInternal(parentField, object, 0);
if(parentObjectIndex == parentField._size) return {}; if(parentObjectIndex == parentField._size) return {};
parentIndexToLookFor = parentObjectIndex; parentIndexToLookFor = parentObjectIndex;
} }
@ -1934,8 +2025,9 @@ Containers::Optional<Matrix3> SceneData::transformation2DFor(const UnsignedInt o
is handled above. */ is handled above. */
CORRADE_ASSERT(!is3D(), "Trade::SceneData::transformation2DFor(): scene has a 3D transformation type", {}); CORRADE_ASSERT(!is3D(), "Trade::SceneData::transformation2DFor(): scene has a 3D transformation type", {});
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); const SceneFieldData& field = _fields[fieldWithObjectMapping];
if(offset == _fields[fieldWithObjectMapping]._size) return {}; const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {};
Matrix3 transformation[1]; Matrix3 transformation[1];
transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation);
@ -1953,8 +2045,9 @@ Containers::Optional<Containers::Triple<Vector2, Complex, Vector2>> SceneData::t
is handled above. */ is handled above. */
CORRADE_ASSERT(!is3D(), "Trade::SceneData::translationRotationScaling2DFor(): scene has a 3D transformation type", {}); CORRADE_ASSERT(!is3D(), "Trade::SceneData::translationRotationScaling2DFor(): scene has a 3D transformation type", {});
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); const SceneFieldData& field = _fields[fieldWithObjectMapping];
if(offset == _fields[fieldWithObjectMapping]._size) return {}; const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {};
Vector2 translation[1]; Vector2 translation[1];
Complex rotation[1]; Complex rotation[1];
@ -1974,8 +2067,9 @@ Containers::Optional<Matrix4> SceneData::transformation3DFor(const UnsignedInt o
is handled above. */ is handled above. */
CORRADE_ASSERT(!is2D(), "Trade::SceneData::transformation3DFor(): scene has a 2D transformation type", {}); CORRADE_ASSERT(!is2D(), "Trade::SceneData::transformation3DFor(): scene has a 2D transformation type", {});
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); const SceneFieldData& field = _fields[fieldWithObjectMapping];
if(offset == _fields[fieldWithObjectMapping]._size) return {}; const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {};
Matrix4 transformation[1]; Matrix4 transformation[1];
transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation); transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, transformation);
@ -1993,8 +2087,9 @@ Containers::Optional<Containers::Triple<Vector3, Quaternion, Vector3>> SceneData
is handled above. */ is handled above. */
CORRADE_ASSERT(!is2D(), "Trade::SceneData::translationRotationScaling3DFor(): scene has a 2D transformation type", {}); CORRADE_ASSERT(!is2D(), "Trade::SceneData::translationRotationScaling3DFor(): scene has a 2D transformation type", {});
const std::size_t offset = fieldFor(_fields[fieldWithObjectMapping], 0, object); const SceneFieldData& field = _fields[fieldWithObjectMapping];
if(offset == _fields[fieldWithObjectMapping]._size) return {}; const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {};
Vector3 translation[1]; Vector3 translation[1];
Quaternion rotation[1]; Quaternion rotation[1];
@ -2007,14 +2102,14 @@ Containers::Array<Containers::Pair<UnsignedInt, Int>> SceneData::meshesMaterials
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::meshesMaterialsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt meshFieldId = fieldFor(SceneField::Mesh); const UnsignedInt meshFieldId = findFieldIdInternal(SceneField::Mesh);
if(meshFieldId == ~UnsignedInt{}) return {}; if(meshFieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[meshFieldId]; const SceneFieldData& field = _fields[meshFieldId];
Containers::Array<Containers::Pair<UnsignedInt, Int>> out; Containers::Array<Containers::Pair<UnsignedInt, Int>> out;
std::size_t offset = 0; std::size_t offset = 0;
for(;;) { for(;;) {
offset = fieldFor(field, offset, object); offset = findFieldObjectOffsetInternal(field, object, offset);
if(offset == field._size) break; if(offset == field._size) break;
UnsignedInt mesh[1]; UnsignedInt mesh[1];
@ -2031,14 +2126,14 @@ Containers::Array<UnsignedInt> SceneData::lightsFor(const UnsignedInt object) co
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::lightsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Light); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light);
if(fieldId == ~UnsignedInt{}) return {}; if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId]; const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out; Containers::Array<UnsignedInt> out;
std::size_t offset = 0; std::size_t offset = 0;
for(;;) { for(;;) {
offset = fieldFor(field, offset, object); offset = findFieldObjectOffsetInternal(field, object, offset);
if(offset == field._size) break; if(offset == field._size) break;
UnsignedInt index[1]; UnsignedInt index[1];
@ -2054,14 +2149,14 @@ Containers::Array<UnsignedInt> SceneData::camerasFor(const UnsignedInt object) c
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::camerasFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Camera); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera);
if(fieldId == ~UnsignedInt{}) return {}; if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId]; const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out; Containers::Array<UnsignedInt> out;
std::size_t offset = 0; std::size_t offset = 0;
for(;;) { for(;;) {
offset = fieldFor(field, offset, object); offset = findFieldObjectOffsetInternal(field, object, offset);
if(offset == field._size) break; if(offset == field._size) break;
UnsignedInt index[1]; UnsignedInt index[1];
@ -2077,14 +2172,14 @@ Containers::Array<UnsignedInt> SceneData::skinsFor(const UnsignedInt object) con
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::skinsFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::Skin); const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin);
if(fieldId == ~UnsignedInt{}) return {}; if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId]; const SceneFieldData& field = _fields[fieldId];
Containers::Array<UnsignedInt> out; Containers::Array<UnsignedInt> out;
std::size_t offset = 0; std::size_t offset = 0;
for(;;) { for(;;) {
offset = fieldFor(field, offset, object); offset = findFieldObjectOffsetInternal(field, object, offset);
if(offset == field._size) break; if(offset == field._size) break;
UnsignedInt index[1]; UnsignedInt index[1];
@ -2100,11 +2195,11 @@ Containers::Optional<const void*> SceneData::importerStateFor(const UnsignedInt
CORRADE_ASSERT(object < _objectCount, CORRADE_ASSERT(object < _objectCount,
"Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {});
const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); const UnsignedInt fieldId = findFieldIdInternal(SceneField::ImporterState);
if(fieldId == ~UnsignedInt{}) return {}; if(fieldId == ~UnsignedInt{}) return {};
const SceneFieldData& field = _fields[fieldId]; const SceneFieldData& field = _fields[fieldId];
const std::size_t offset = fieldFor(field, 0, object); const std::size_t offset = findFieldObjectOffsetInternal(field, object, 0);
if(offset == field._size) return {}; if(offset == field._size) return {};
const void* importerState[1]; const void* importerState[1];

299
src/Magnum/Trade/SceneData.h

@ -1080,22 +1080,112 @@ class MAGNUM_TRADE_EXPORT SceneData {
bool is3D() const { return _dimensions == 3; } bool is3D() const { return _dimensions == 3; }
/** /**
* @brief Whether the scene has given field * @brief Find an absolute ID of a named field
* @m_since_latest * @m_since_latest
* *
* @see @ref is2D(), @ref is3D() * If @p name doesn't exist, returns @ref Containers::NullOpt. The
* lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with
* @f$ n @f$ being the field count.
* @see @ref hasField(), @ref fieldId()
*/ */
bool hasField(SceneField name) const; Containers::Optional<UnsignedInt> findFieldId(SceneField name) const;
/** /**
* @brief Absolute ID of a named field * @brief Absolute ID of a named field
* @m_since_latest * @m_since_latest
* *
* The @p name is expected to exist. * Like @ref findFieldId(), but the @p name is expected to exist.
* @see @ref hasField(), @ref fieldName(UnsignedInt) const * @see @ref hasField(), @ref fieldName(UnsignedInt) const
*/ */
UnsignedInt fieldId(SceneField name) const; UnsignedInt fieldId(SceneField name) const;
/**
* @brief Whether the scene has given field
* @m_since_latest
*
* @see @ref is2D(), @ref is3D()
*/
bool hasField(SceneField name) const;
/**
* @brief Find offset of an object in given field
* @m_since_latest
*
* If @p object isn't present in @p fieldId starting at @p offset,
* returns @ref Containers::NullOpt. The @p fieldId is expected to be
* smaller than @ref fieldCount(), @p object smaller than
* @ref objectCount() and @p offset not larger than
* @ref fieldSize(UnsignedInt) const.
*
* The lookup is done in an @f$ \mathcal{O}(n) @f$ complexity with
* @f$ n @f$ being the size of the field.
*
* You can also use @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* to directly find offset of an object in given named field.
* @see @ref hasFieldObject(UnsignedInt, UnsignedInt) const,
* @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const
*/
Containers::Optional<std::size_t> findFieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const;
/**
* @brief Find offset of an object in given named field
* @m_since_latest
*
* If @p object isn't present in @p fieldName starting at @p offset,
* returns @ref Containers::NullOpt. The @p fieldName is expected to
* exist, @p object is expected to be smaller than @ref objectCount()
* and @p offset not be larger than @ref fieldSize(SceneField) const.
*
* The lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with
* @f$ m @f$ being the field count and @f$ n @f$ the size of the field.
*
* @see @ref hasField(), @ref hasFieldObject(SceneField, UnsignedInt) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/
Containers::Optional<std::size_t> findFieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const;
/**
* @brief Offset of an object in given field
* @m_since_latest
*
* Like @ref findFieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const,
* but @p object is additionally expected to be present in @p fieldId
* starting at @p offset.
*
* You can also use @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* to directly get offset of an object in given named field.
*/
std::size_t fieldObjectOffset(UnsignedInt fieldId, UnsignedInt object, std::size_t offset = 0) const;
/**
* @brief Offset of an object in given named field
* @m_since_latest
*
* Like @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const,
* but @p object is additionally expected to be present in @p fieldName
* starting at @p offset.
*/
std::size_t fieldObjectOffset(SceneField fieldName, UnsignedInt object, std::size_t offset = 0) const;
/**
* @brief Whether a scene field has given object
* @m_since_latest
*
* The @p fieldId is expected to be smaller than @ref fieldCount() and
* @p object smaller than @ref objectCount().
*/
bool hasFieldObject(UnsignedInt fieldId, UnsignedInt object) const;
/**
* @brief Whether a named scene field has given object
* @m_since_latest
*
* The @p fieldName is expected to exist and @p object is expected to
* be smaller than @ref objectCount().
* @see @ref hasField()
*/
bool hasFieldObject(SceneField fieldName, UnsignedInt object) const;
/** /**
* @brief Type of a named field * @brief Type of a named field
* @m_since_latest * @m_since_latest
@ -1416,7 +1506,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* and size of the @p destination view, returning the count of items * and size of the @p destination view, returning the count of items
* actually extracted. The @p offset is expected to not be larger than * actually extracted. The @p offset is expected to not be larger than
* the field size. * the field size.
* @see @ref fieldSize(UnsignedInt) const * @see @ref fieldSize(UnsignedInt) const,
* @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const
*/ */
std::size_t objectsInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const; std::size_t objectsInto(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
@ -1458,7 +1549,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* and size of the @p destination view, returning the count of items * and size of the @p destination view, returning the count of items
* actually extracted. The @p offset is expected to not be larger than * actually extracted. The @p offset is expected to not be larger than
* the field size. * the field size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t objectsInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const; std::size_t objectsInto(SceneField fieldName, std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
@ -1500,7 +1592,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const; std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D<Int>& destination) const;
@ -1543,7 +1636,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D<Matrix3>& destination) const; std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D<Matrix3>& destination) const;
@ -1592,7 +1686,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the views, returning the count of items actually extracted. The * of the views, returning the count of items actually extracted. The
* @p offset is expected to not be larger than the field size, views * @p offset is expected to not be larger than the field size, views
* that are not @cpp nullptr @ce are expected to have the same size. * that are not @cpp nullptr @ce are expected to have the same size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const; std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D<Vector2>& translationDestination, const Containers::StridedArrayView1D<Complex>& rotationDestination, const Containers::StridedArrayView1D<Vector2>& scalingDestination) const;
@ -1635,7 +1730,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D<Matrix4>& destination) const; std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D<Matrix4>& destination) const;
@ -1684,7 +1780,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the views, returning the count of items actually extracted. The * of the views, returning the count of items actually extracted. The
* @p offset is expected to not be larger than the field size, views * @p offset is expected to not be larger than the field size, views
* that are not @cpp nullptr @ce are expected to have the same size. * that are not @cpp nullptr @ce are expected to have the same size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t translationsRotationsScalings3DInto(std::size_t offset, const Containers::StridedArrayView1D<Vector3>& translationDestination, const Containers::StridedArrayView1D<Quaternion>& rotationDestination, const Containers::StridedArrayView1D<Vector3>& scalingDestination) const; std::size_t translationsRotationsScalings3DInto(std::size_t offset, const Containers::StridedArrayView1D<Vector3>& translationDestination, const Containers::StridedArrayView1D<Quaternion>& rotationDestination, const Containers::StridedArrayView1D<Vector3>& scalingDestination) const;
@ -1725,7 +1822,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the views, returning the count of items actually extracted. The * of the views, returning the count of items actually extracted. The
* @p offset is expected to not be larger than the field size, views * @p offset is expected to not be larger than the field size, views
* that are not @cpp nullptr @ce are expected to have the same size. * that are not @cpp nullptr @ce are expected to have the same size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialsDestination) const; std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& meshDestination, const Containers::StridedArrayView1D<Int>& meshMaterialsDestination) const;
@ -1761,7 +1859,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const; std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
@ -1797,7 +1896,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const; std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
@ -1833,7 +1933,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const; std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D<UnsignedInt>& destination) const;
@ -1872,7 +1973,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* of the @p destination view, returning the count of items actually * of the @p destination view, returning the count of items actually
* extracted. The @p offset is expected to not be larger than the field * extracted. The @p offset is expected to not be larger than the field
* size. * size.
* @see @ref fieldSize(SceneField) const * @see @ref fieldSize(SceneField) const,
* @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
*/ */
std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D<const void*>& destination) const; std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D<const void*>& destination) const;
@ -1880,12 +1982,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief Parent for given object * @brief Parent for given object
* @m_since_latest * @m_since_latest
* *
* Looks up the @ref SceneField::Parent field for @p object. The lookup * Looks up the @ref SceneField::Parent field for @p object
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the parent field, * and then converts the field from an arbitrary underlying type the
* thus for retrieving parent info for many objects it's recommended to * same way as @ref parentsAsArray(). See the lookup function
* access the field data directly with @ref parentsAsArray() and * documentation for operation complexity --- for retrieving parent
* related APIs. * info for many objects it's recommended to access the field data
* directly.
* *
* If the @ref SceneField::Parent field is not present or if there's no * If the @ref SceneField::Parent field is not present or if there's no
* parent for @p object, returns @ref Containers::NullOpt. If @p object * parent for @p object, returns @ref Containers::NullOpt. If @p object
@ -1901,13 +2004,12 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @m_since_latest * @m_since_latest
* *
* Looks up @p object in the object mapping array for * Looks up @p object in the object mapping array for
* @ref SceneField::Parent and returns a list of all object IDs that * @ref SceneField::Parent equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const,
* have it listed as the parent. The lookup is done in an * converts the fields from an arbitrary underlying type the same way
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field * as @ref parentsAsArray(), returning a list of all object IDs that
* count and @f$ n @f$ the size of the parent field, thus for * have it listed as the parent. See the lookup function documentation
* retrieving parent/child info for many objects it's recommended to * for operation complexity --- for retrieving parent/child info for
* access the field data directly with @ref parentsAsArray() and * many objects it's recommended to access the field data directly.
* related APIs.
* *
* If the @ref SceneField::Parent field doesn't exist or there are no * If the @ref SceneField::Parent field doesn't exist or there are no
* objects which would have @p object listed as their parent, returns * objects which would have @p object listed as their parent, returns
@ -1922,16 +2024,16 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief 2D transformation for given object * @brief 2D transformation for given object
* @m_since_latest * @m_since_latest
* *
* Looks up the @ref SceneField::Transformation field for @p object or * Looks up the @ref SceneField::Transformation field for @p object
* combines it from a @ref SceneField::Translation, * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* or combines it from a @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and * @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} in the same way as * @relativeref{SceneField,Scaling}, converting the fields from
* @ref transformations2DAsArray(). The lookup is done in an * arbitrary underlying types the same way as
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field * @ref transformations2DAsArray(). See the lookup function
* count and @f$ n @f$ the size of the transformation field, thus for * documentation for operation complexity --- for retrieving
* retrieving transformation info for many objects it's recommended to * transformation info for many objects it's recommended to access the
* access the field data directly with @ref transformations2DAsArray() * field data directly.
* and related APIs.
* *
* If neither @ref SceneField::Transformation nor any of * If neither @ref SceneField::Transformation nor any of
* @ref SceneField::Translation, @relativeref{SceneField,Rotation} or * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or
@ -1950,12 +2052,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* *
* Looks up the @ref SceneField::Translation, * Looks up the @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and * @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} fields for @p object. The lookup * @relativeref{SceneField,Scaling} fields for @p object equivalently
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the transformation * and then converts the fields from arbitrary underlying types the
* field, thus for retrieving transformation info for many objects it's * same way as @ref translationsRotationsScalings2DAsArray(). See the
* recommended to access the field data directly with * lookup function documentation for operation complexity --- for
* @ref translationsRotationsScalings2DAsArray() and related APIs. * retrieving transformation info for many objects it's recommended to
* access the field data directly.
* *
* If the @ref SceneField::Translation field isn't present, the first * If the @ref SceneField::Translation field isn't present, the first
* returned value is a zero vector. If the * returned value is a zero vector. If the
@ -1976,16 +2079,16 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief 3D transformation for given object * @brief 3D transformation for given object
* @m_since_latest * @m_since_latest
* *
* Looks up the @ref SceneField::Transformation field for @p object or * Looks up the @ref SceneField::Transformation field for @p object
* combines it from a @ref SceneField::Translation, * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* or combines it from a @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and * @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} in the same way as * @relativeref{SceneField,Scaling}, converting the fields from
* @ref transformations3DAsArray(). The lookup is done in an * arbitrary underlying types the same way as
* @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ being the field * @ref transformations3DAsArray(). See the lookup function
* count and @f$ n @f$ the size of the transformation field, thus for * documentation for operation complexity --- for retrieving
* retrieving transformation info for many objects it's recommended to * transformation info for many objects it's recommended to access the
* access the field data directly with @ref transformations3DAsArray() * field data directly.
* and related APIs.
* *
* If neither @ref SceneField::Transformation nor any of * If neither @ref SceneField::Transformation nor any of
* @ref SceneField::Translation, @relativeref{SceneField,Rotation} or * @ref SceneField::Translation, @relativeref{SceneField,Rotation} or
@ -2004,12 +2107,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* *
* Looks up the @ref SceneField::Translation, * Looks up the @ref SceneField::Translation,
* @relativeref{SceneField,Rotation} and * @relativeref{SceneField,Rotation} and
* @relativeref{SceneField,Scaling} fields for @p object. The lookup * @relativeref{SceneField,Scaling} fields for @p object equivalently
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the transformation * and then converts the fields from arbitrary underlying types the
* field, thus for retrieving transformation info for many objects it's * same way as @ref translationsRotationsScalings2DAsArray(). See the
* recommended to access the field data directly with * lookup function documentation for operation complexity --- for
* @ref translationsRotationsScalings3DAsArray() and related APIs. * retrieving transformation info for many objects it's recommended to
* access the field data directly.
* *
* If the @ref SceneField::Translation field isn't present, the first * If the @ref SceneField::Translation field isn't present, the first
* returned value is a zero vector. If the * returned value is a zero vector. If the
@ -2031,12 +2135,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @m_since_latest * @m_since_latest
* *
* Looks up all @ref SceneField::Mesh and @ref SceneField::MeshMaterial * Looks up all @ref SceneField::Mesh and @ref SceneField::MeshMaterial
* @relativeref{SceneField,Scaling} fields for @p object. The lookup * @relativeref{SceneField,Scaling} fields for @p object
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the mesh field, thus * and then converts the field from an arbitrary underlying type the
* for retrieving mesh info for many objects it's recommended to access * same way as @ref meshesMaterialsAsArray(). See the lookup function
* the field data directly with @ref meshesMaterialsAsArray() and * documentation for operation complexity --- for retrieving mesh and
* related APIs. * material info for many objects it's recommended to access the field
* data directly.
* *
* If the @ref SceneField::MeshMaterial field is not present, the * If the @ref SceneField::MeshMaterial field is not present, the
* second returned value is always @cpp -1 @ce. If * second returned value is always @cpp -1 @ce. If
@ -2051,12 +2156,12 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief Lights for given object * @brief Lights for given object
* @m_since_latest * @m_since_latest
* *
* Looks up all @ref SceneField::Light fields for @p object. The lookup * Looks up all @ref SceneField::Light fields for @p object
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the light field, * and then converts the field from an arbitrary underlying type the
* thus for retrieving light info for many objects it's recommended to * same way as @ref lightsAsArray(). See the lookup function
* access the field data directly with @ref lightsAsArray() and related * documentation for operation complexity --- for retrieving light info
* APIs. * for many objects it's recommended to access the field data directly.
* *
* If the @ref SceneField::Light field is not present or if there's no * If the @ref SceneField::Light field is not present or if there's no
* light for @p object, returns an empty array. * light for @p object, returns an empty array.
@ -2069,12 +2174,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief Cameras for given object * @brief Cameras for given object
* @m_since_latest * @m_since_latest
* *
* Looks up all @ref SceneField::Camera fields for @p object. The * Looks up all @ref SceneField::Camera fields for @p object
* lookup is done in an @f$ \mathcal{O}(m + n) @f$ complexity with * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* @f$ m @f$ being the field count and @f$ n @f$ the size of the camera * and then converts the field from an arbitrary underlying type the
* field, thus for retrieving camera info for many objects it's * same way as @ref camerasAsArray(). See the lookup function
* recommended to access the field data directly with * documentation for operation complexity --- for retrieving camera
* @ref camerasAsArray() and related APIs. * info for many objects it's recommended to access the field data
* directly.
* *
* If the @ref SceneField::Camera field is not present or if there's no * If the @ref SceneField::Camera field is not present or if there's no
* camera for @p object, returns an empty array. * camera for @p object, returns an empty array.
@ -2087,11 +2193,12 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief Skins for given object * @brief Skins for given object
* @m_since_latest * @m_since_latest
* *
* Looks up all @ref SceneField::Skin fields for @p object. The lookup * Looks up all @ref SceneField::Skin fields for @p object
* is done in an @f$ \mathcal{O}(m + n) @f$ complexity with @f$ m @f$ * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* being the field count and @f$ n @f$ the size of the skin field, thus * and then converts the field from an arbitrary underlying type the
* for retrieving skin info for many objects it's recommended to access * same way as @ref skinsAsArray(). See the lookup function
* the field data directly with @ref skinsAsArray() and related APIs. * documentation for operation complexity --- for retrieving skin info
* for many objects it's recommended to access the field data directly.
* *
* If the @ref SceneField::Skin field is not present or if there's no * If the @ref SceneField::Skin field is not present or if there's no
* skin for @p object, returns an empty array. * skin for @p object, returns an empty array.
@ -2104,12 +2211,13 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @brief Importer state for given object * @brief Importer state for given object
* @m_since_latest * @m_since_latest
* *
* Looks up the @ref SceneField::ImporterState field for @p object. The * Looks up the @ref SceneField::ImporterState field for @p object
* lookup is done in a @f$ \mathcal{O}(m + n) @f$ complexity with * equivalently to @ref findFieldObjectOffset(SceneField, UnsignedInt, std::size_t) const
* @f$ m @f$ being the field count and @f$ n @f$ the size of the * and then converts the field from an arbitrary underlying type the
* importer state field, thus for retrieving importer state info for * same way as @ref importerStateAsArray(). See the lookup function
* many objects it's recommended to access the field data directly with * documentation for operation complexity --- for retrieving importer
* @ref importerStateAsArray() and related APIs. * state info for many objects it's recommended to access the field
* data directly.
* *
* If the @ref SceneField::ImporterState field is not present or if * If the @ref SceneField::ImporterState field is not present or if
* there's no importer state for @p object, returns * there's no importer state for @p object, returns
@ -2187,13 +2295,14 @@ class MAGNUM_TRADE_EXPORT SceneData {
implementations. */ implementations. */
friend AbstractImporter; friend AbstractImporter;
/* Internal helper that doesn't assert, unlike fieldId() */ /* Internal helper without the extra overhead from Optional, returns
UnsignedInt fieldFor(SceneField name) const; ~UnsignedInt{} on failure */
UnsignedInt findFieldIdInternal(SceneField name) const;
/* Returns the offset at which `object` is for field at index `id`, or /* 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 the end offset if the object is not found. The returned offset can
be then passed to fieldData{Object,Field}ViewInternal(). */ be then passed to fieldData{Object,Field}ViewInternal(). */
std::size_t fieldFor(const SceneFieldData& field, std::size_t offset, UnsignedInt object) const; std::size_t findFieldObjectOffsetInternal(const SceneFieldData& field, UnsignedInt object, std::size_t offset) const;
/* Like objects() / field(), but returning just a 1D view, sliced from /* Like objects() / field(), but returning just a 1D view, sliced from
offset to offset + size. The parameterless overloads are equal to offset to offset + size. The parameterless overloads are equal to
@ -2566,7 +2675,7 @@ template<class T, class> Containers::StridedArrayView1D<const T> SceneData::fiel
if(!data.stride()[1]) return {}; if(!data.stride()[1]) return {};
#endif #endif
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
if(!checkFieldTypeCompatibility<T>(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; if(!checkFieldTypeCompatibility<T>(_fields[findFieldIdInternal(name)], "Trade::SceneData::field():")) return {};
#endif #endif
return Containers::arrayCast<1, const T>(data); return Containers::arrayCast<1, const T>(data);
} }
@ -2577,7 +2686,7 @@ template<class T, class> Containers::StridedArrayView2D<const typename std::remo
if(!data.stride()[1]) return {}; if(!data.stride()[1]) return {};
#endif #endif
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
if(!checkFieldTypeCompatibility<T>(_fields[fieldFor(name)], "Trade::SceneData::field():")) return {}; if(!checkFieldTypeCompatibility<T>(_fields[findFieldIdInternal(name)], "Trade::SceneData::field():")) return {};
#endif #endif
return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data); return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data);
} }
@ -2588,7 +2697,7 @@ template<class T, class> Containers::StridedArrayView1D<T> SceneData::mutableFie
if(!data.stride()[1]) return {}; if(!data.stride()[1]) return {};
#endif #endif
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
if(!checkFieldTypeCompatibility<T>(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; if(!checkFieldTypeCompatibility<T>(_fields[findFieldIdInternal(name)], "Trade::SceneData::mutableField():")) return {};
#endif #endif
return Containers::arrayCast<1, T>(data); return Containers::arrayCast<1, T>(data);
} }
@ -2599,7 +2708,7 @@ template<class T, class> Containers::StridedArrayView2D<typename std::remove_ext
if(!data.stride()[1]) return {}; if(!data.stride()[1]) return {};
#endif #endif
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
if(!checkFieldTypeCompatibility<T>(_fields[fieldFor(name)], "Trade::SceneData::mutableField():")) return {}; if(!checkFieldTypeCompatibility<T>(_fields[findFieldIdInternal(name)], "Trade::SceneData::mutableField():")) return {};
#endif #endif
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data); return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data);
} }

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

@ -111,6 +111,11 @@ struct SceneDataTest: TestSuite::Tester {
void constructCopy(); void constructCopy();
void constructMove(); void constructMove();
void findFieldId();
template<class T> void findFieldObjectOffset();
void findFieldObjectOffsetInvalidOffset();
void fieldObjectOffsetNotFound();
template<class T> void objectsAsArrayByIndex(); template<class T> void objectsAsArrayByIndex();
template<class T> void objectsAsArrayByName(); template<class T> void objectsAsArrayByName();
void objectsAsArrayLongType(); void objectsAsArrayLongType();
@ -163,9 +168,7 @@ struct SceneDataTest: TestSuite::Tester {
void fieldWrongPointerType(); void fieldWrongPointerType();
void fieldWrongArrayAccess(); void fieldWrongArrayAccess();
/* Different object types checked just for the parentFor(), other APIs void parentFor();
use the same helper */
template<class T> void parentFor();
void childrenFor(); void childrenFor();
void transformation2DFor(); void transformation2DFor();
void transformation2DForTRS(); void transformation2DForTRS();
@ -184,7 +187,7 @@ struct SceneDataTest: TestSuite::Tester {
#endif #endif
void fieldForFieldMissing(); void fieldForFieldMissing();
void fieldForInvalidObject(); void findFieldObjectOffsetInvalidObject();
void releaseFieldData(); void releaseFieldData();
void releaseData(); void releaseData();
@ -290,6 +293,14 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::constructCopy, &SceneDataTest::constructCopy,
&SceneDataTest::constructMove, &SceneDataTest::constructMove,
&SceneDataTest::findFieldId,
&SceneDataTest::findFieldObjectOffset<UnsignedByte>,
&SceneDataTest::findFieldObjectOffset<UnsignedShort>,
&SceneDataTest::findFieldObjectOffset<UnsignedInt>,
&SceneDataTest::findFieldObjectOffset<UnsignedLong>,
&SceneDataTest::findFieldObjectOffsetInvalidOffset,
&SceneDataTest::fieldObjectOffsetNotFound,
&SceneDataTest::objectsAsArrayByIndex<UnsignedByte>, &SceneDataTest::objectsAsArrayByIndex<UnsignedByte>,
&SceneDataTest::objectsAsArrayByIndex<UnsignedShort>, &SceneDataTest::objectsAsArrayByIndex<UnsignedShort>,
&SceneDataTest::objectsAsArrayByIndex<UnsignedInt>, &SceneDataTest::objectsAsArrayByIndex<UnsignedInt>,
@ -396,10 +407,7 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::fieldWrongPointerType, &SceneDataTest::fieldWrongPointerType,
&SceneDataTest::fieldWrongArrayAccess, &SceneDataTest::fieldWrongArrayAccess,
&SceneDataTest::parentFor<UnsignedByte>, &SceneDataTest::parentFor,
&SceneDataTest::parentFor<UnsignedShort>,
&SceneDataTest::parentFor<UnsignedInt>,
&SceneDataTest::parentFor<UnsignedLong>,
&SceneDataTest::childrenFor, &SceneDataTest::childrenFor,
&SceneDataTest::transformation2DFor, &SceneDataTest::transformation2DFor,
&SceneDataTest::transformation2DForTRS, &SceneDataTest::transformation2DForTRS,
@ -419,7 +427,7 @@ SceneDataTest::SceneDataTest() {
#endif #endif
addTests({&SceneDataTest::fieldForFieldMissing, addTests({&SceneDataTest::fieldForFieldMissing,
&SceneDataTest::fieldForInvalidObject, &SceneDataTest::findFieldObjectOffsetInvalidObject,
&SceneDataTest::releaseFieldData, &SceneDataTest::releaseFieldData,
&SceneDataTest::releaseData}); &SceneDataTest::releaseData});
@ -1298,15 +1306,6 @@ void SceneDataTest::construct() {
CORRADE_COMPARE(scene.mutableField<Float[]>(3)[0][1], 1.5f); CORRADE_COMPARE(scene.mutableField<Float[]>(3)[0][1], 1.5f);
/* Field property access by name */ /* Field property access by name */
CORRADE_COMPARE(scene.fieldId(SceneField::Transformation), 0);
CORRADE_COMPARE(scene.fieldId(SceneField::Parent), 1);
CORRADE_COMPARE(scene.fieldId(SceneField::Mesh), 2);
CORRADE_COMPARE(scene.fieldId(sceneFieldCustom(37)), 3);
CORRADE_VERIFY(scene.hasField(SceneField::Transformation));
CORRADE_VERIFY(scene.hasField(SceneField::Parent));
CORRADE_VERIFY(scene.hasField(SceneField::Mesh));
CORRADE_VERIFY(scene.hasField(sceneFieldCustom(37)));
CORRADE_VERIFY(!scene.hasField(SceneField::Skin));
CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4); CORRADE_COMPARE(scene.fieldType(SceneField::Transformation), SceneFieldType::Matrix4x4);
CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int); CORRADE_COMPARE(scene.fieldType(SceneField::Parent), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte); CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte);
@ -1957,6 +1956,151 @@ void SceneDataTest::constructMove() {
CORRADE_VERIFY(std::is_nothrow_move_assignable<SceneData>::value); CORRADE_VERIFY(std::is_nothrow_move_assignable<SceneData>::value);
} }
void SceneDataTest::findFieldId() {
SceneData scene{SceneObjectType::UnsignedInt, 0, {}, nullptr, {
SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr},
SceneFieldData{SceneField::Mesh, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::UnsignedByte, nullptr}
}};
CORRADE_COMPARE(scene.findFieldId(SceneField::Parent), 0);
CORRADE_COMPARE(scene.findFieldId(SceneField::Mesh), 1);
CORRADE_COMPARE(scene.findFieldId(SceneField::MeshMaterial), Containers::NullOpt);
CORRADE_COMPARE(scene.fieldId(SceneField::Parent), 0);
CORRADE_COMPARE(scene.fieldId(SceneField::Mesh), 1);
CORRADE_VERIFY(scene.hasField(SceneField::Parent));
CORRADE_VERIFY(scene.hasField(SceneField::Mesh));
CORRADE_VERIFY(!scene.hasField(SceneField::MeshMaterial));
}
template<class T> void SceneDataTest::findFieldObjectOffset() {
setTestCaseTemplateName(NameTraits<T>::name());
/** @todo update once field flags describing object order are present */
struct Field {
T object;
UnsignedInt mesh;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{0, 5},
{2, 5}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneObjectTypeFor<T>(), 7, {}, fields, {
/* Test also with a completely empty field */
SceneFieldData{SceneField::Parent, Implementation::sceneObjectTypeFor<T>(), nullptr, SceneFieldType::Int, nullptr},
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}
}};
CORRADE_COMPARE(scene.findFieldObjectOffset(0, 4), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Parent, 4), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 4), 0);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 1), 1);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2), 2);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 3), 4);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 2, 5), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(1, 3), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 4), 0);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 1), 1);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2), 2);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 3), 4);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 2, 5), Containers::NullOpt);
CORRADE_COMPARE(scene.findFieldObjectOffset(SceneField::Mesh, 3), Containers::NullOpt);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 4), 0);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 1), 1);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 2), 2);
CORRADE_COMPARE(scene.fieldObjectOffset(1, 2, 3), 4);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 4), 0);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 1), 1);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2), 2);
CORRADE_COMPARE(scene.fieldObjectOffset(SceneField::Mesh, 2, 3), 4);
CORRADE_VERIFY(!scene.hasFieldObject(0, 4));
CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Parent, 4));
CORRADE_VERIFY(scene.hasFieldObject(1, 4));
CORRADE_VERIFY(scene.hasFieldObject(1, 1));
CORRADE_VERIFY(scene.hasFieldObject(1, 2));
CORRADE_VERIFY(!scene.hasFieldObject(1, 3));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 4));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 1));
CORRADE_VERIFY(scene.hasFieldObject(SceneField::Mesh, 2));
CORRADE_VERIFY(!scene.hasFieldObject(SceneField::Mesh, 3));
}
void SceneDataTest::findFieldObjectOffsetInvalidOffset() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
UnsignedInt mesh;
} fields[]{
{4, 1},
{1, 3},
{2, 4}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}
}};
std::ostringstream out;
Error redirectError{&out};
scene.findFieldObjectOffset(0, 1, 4);
scene.findFieldObjectOffset(SceneField::Mesh, 1, 4);
scene.fieldObjectOffset(0, 1, 4);
scene.fieldObjectOffset(SceneField::Mesh, 1, 4);
CORRADE_COMPARE(out.str(),
"Trade::SceneData::findFieldObjectOffset(): offset 4 out of bounds for a field of size 3\n"
"Trade::SceneData::findFieldObjectOffset(): offset 4 out of bounds for a field of size 3\n"
"Trade::SceneData::fieldObjectOffset(): offset 4 out of bounds for a field of size 3\n"
"Trade::SceneData::fieldObjectOffset(): offset 4 out of bounds for a field of size 3\n");
}
void SceneDataTest::fieldObjectOffsetNotFound() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
struct Field {
UnsignedInt object;
UnsignedInt mesh;
} fields[]{
{4, 1},
{1, 3},
{2, 4},
{0, 5},
{2, 5}
};
Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
/* Test also with a completely empty field */
SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr},
SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}
}};
std::ostringstream out;
Error redirectError{&out};
scene.fieldObjectOffset(0, 4);
scene.fieldObjectOffset(SceneField::Parent, 4);
scene.fieldObjectOffset(1, 1, 2);
scene.fieldObjectOffset(SceneField::Mesh, 1, 2);
CORRADE_COMPARE(out.str(),
"Trade::SceneData::fieldObjectOffset(): object 4 not found in field Trade::SceneField::Parent starting at offset 0\n"
"Trade::SceneData::fieldObjectOffset(): object 4 not found in field Trade::SceneField::Parent starting at offset 0\n"
"Trade::SceneData::fieldObjectOffset(): object 1 not found in field Trade::SceneField::Mesh starting at offset 2\n"
"Trade::SceneData::fieldObjectOffset(): object 1 not found in field Trade::SceneField::Mesh starting at offset 2\n");
}
template<class T> void SceneDataTest::objectsAsArrayByIndex() { template<class T> void SceneDataTest::objectsAsArrayByIndex() {
setTestCaseTemplateName(NameTraits<T>::name()); setTestCaseTemplateName(NameTraits<T>::name());
@ -4108,6 +4252,9 @@ void SceneDataTest::fieldNotFound() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
scene.findFieldObjectOffset(2, 0);
scene.fieldObjectOffset(2, 0);
scene.hasFieldObject(2, 0);
scene.fieldData(2); scene.fieldData(2);
scene.fieldName(2); scene.fieldName(2);
scene.fieldType(2); scene.fieldType(2);
@ -4121,6 +4268,9 @@ void SceneDataTest::fieldNotFound() {
scene.mutableField<UnsignedInt[]>(2); scene.mutableField<UnsignedInt[]>(2);
scene.fieldId(sceneFieldCustom(666)); scene.fieldId(sceneFieldCustom(666));
scene.findFieldObjectOffset(sceneFieldCustom(666), 0);
scene.fieldObjectOffset(sceneFieldCustom(666), 0);
scene.hasFieldObject(sceneFieldCustom(666), 0);
scene.fieldType(sceneFieldCustom(666)); scene.fieldType(sceneFieldCustom(666));
scene.fieldSize(sceneFieldCustom(666)); scene.fieldSize(sceneFieldCustom(666));
scene.fieldArraySize(sceneFieldCustom(666)); scene.fieldArraySize(sceneFieldCustom(666));
@ -4162,6 +4312,9 @@ void SceneDataTest::fieldNotFound() {
scene.importerStateInto(nullptr); scene.importerStateInto(nullptr);
scene.importerStateInto(0, nullptr); scene.importerStateInto(0, nullptr);
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"Trade::SceneData::findFieldObjectOffset(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldObjectOffset(): index 2 out of range for 2 fields\n"
"Trade::SceneData::hasFieldObject(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldType(): index 2 out of range for 2 fields\n"
@ -4175,6 +4328,9 @@ void SceneDataTest::fieldNotFound() {
"Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n" "Trade::SceneData::mutableField(): index 2 out of range for 2 fields\n"
"Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldId(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::findFieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldObjectOffset(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::hasFieldObject(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldType(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldType(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldSize(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldSize(): field Trade::SceneField::Custom(666) not found\n"
"Trade::SceneData::fieldArraySize(): field Trade::SceneField::Custom(666) not found\n" "Trade::SceneData::fieldArraySize(): field Trade::SceneField::Custom(666) not found\n"
@ -4370,11 +4526,9 @@ void SceneDataTest::fieldWrongArrayAccess() {
"Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n"); "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is an array field, use T[] to access it\n");
} }
template<class T> void SceneDataTest::parentFor() { void SceneDataTest::parentFor() {
setTestCaseTemplateName(NameTraits<T>::name());
struct Field { struct Field {
T object; UnsignedInt object;
Int parent; Int parent;
} fields[]{ } fields[]{
{3, -1}, {3, -1},
@ -4384,7 +4538,7 @@ template<class T> void SceneDataTest::parentFor() {
}; };
Containers::StridedArrayView1D<Field> view = fields; Containers::StridedArrayView1D<Field> view = fields;
SceneData scene{Implementation::sceneObjectTypeFor<T>(), 7, {}, fields, { SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, {
SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)}
}}; }};
@ -4867,15 +5021,23 @@ void SceneDataTest::fieldForFieldMissing() {
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
void SceneDataTest::fieldForInvalidObject() { void SceneDataTest::findFieldObjectOffsetInvalidObject() {
#ifdef CORRADE_NO_ASSERT #ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif #endif
SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}}; SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {
SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr},
}};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
scene.findFieldObjectOffset(0, 7);
scene.findFieldObjectOffset(SceneField::Parent, 7);
scene.fieldObjectOffset(0, 7);
scene.fieldObjectOffset(SceneField::Parent, 7);
scene.hasFieldObject(0, 7);
scene.hasFieldObject(SceneField::Parent, 7);
scene.parentFor(7); scene.parentFor(7);
scene.childrenFor(-2); scene.childrenFor(-2);
scene.childrenFor(7); scene.childrenFor(7);
@ -4888,6 +5050,12 @@ void SceneDataTest::fieldForInvalidObject() {
scene.camerasFor(7); scene.camerasFor(7);
scene.skinsFor(7); scene.skinsFor(7);
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"Trade::SceneData::findFieldObjectOffset(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::findFieldObjectOffset(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::fieldObjectOffset(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::fieldObjectOffset(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::hasFieldObject(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::hasFieldObject(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::parentFor(): object 7 out of bounds for 7 objects\n" "Trade::SceneData::parentFor(): object 7 out of bounds for 7 objects\n"
"Trade::SceneData::childrenFor(): object -2 out of bounds for 7 objects\n" "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::childrenFor(): object 7 out of bounds for 7 objects\n"

Loading…
Cancel
Save