Browse Source

Trade: move the internal scene conversion tools to SceneTools.

No functional change, just splitting them to two separate headers and
two separate tests. These will eventually become public SceneTools
APIs... once I figure out better naming.
pull/542/merge
Vladimír Vondruš 4 years ago
parent
commit
aff4a8144d
  1. 7
      src/Magnum/SceneTools/CMakeLists.txt
  2. 196
      src/Magnum/SceneTools/Implementation/combine.h
  3. 197
      src/Magnum/SceneTools/Implementation/convertToSingleFunctionObjects.h
  4. 8
      src/Magnum/SceneTools/Test/CMakeLists.txt
  5. 329
      src/Magnum/SceneTools/Test/CombineTest.cpp
  6. 383
      src/Magnum/SceneTools/Test/ConvertToSingleFunctionObjectsTest.cpp
  7. 6
      src/Magnum/Trade/AbstractImporter.cpp
  8. 3
      src/Magnum/Trade/CMakeLists.txt
  9. 2
      src/Magnum/Trade/Test/CMakeLists.txt
  10. 666
      src/Magnum/Trade/Test/SceneToolsTest.cpp

7
src/Magnum/SceneTools/CMakeLists.txt

@ -28,9 +28,14 @@ set(MagnumSceneTools_HEADERS
visibility.h)
set(MagnumSceneTools_PRIVATE_HEADERS
Implementation/combine.h
Implementation/convertToSingleFunctionObjects.h)
# Main SceneTools library
add_library(MagnumSceneTools INTERFACE
${MagnumSceneTools_HEADERS})
${MagnumSceneTools_HEADERS}
${MagnumSceneTools_PRIVATE_HEADERS})
set_target_properties(MagnumSceneTools PROPERTIES
DEBUG_POSTFIX "-d"
FOLDER "Magnum/SceneTools")

196
src/Magnum/SceneTools/Implementation/combine.h

@ -0,0 +1,196 @@
#ifndef Magnum_SceneTools_Implementation_combine_h
#define Magnum_SceneTools_Implementation_combine_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <unordered_map>
#include <Corrade/Containers/ArrayTuple.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/PackingBatch.h"
#include "Magnum/Trade/SceneData.h"
namespace Magnum { namespace SceneTools { namespace Implementation {
/* These two are needed because there (obviously) isn't any overload of
castInto with the same input and output type */
template<class T, class U> void copyOrCastInto(const Containers::StridedArrayView1D<const T>& src, const Containers::StridedArrayView1D<U>& dst) {
Math::castInto(Containers::arrayCast<2, const T>(src), Containers::arrayCast<2, U>(dst));
}
template<class T> void copyOrCastInto(const Containers::StridedArrayView1D<const T>& src, const Containers::StridedArrayView1D<T>& dst) {
Utility::copy(src, dst);
}
template<class T> void combineCopyObjects(const Containers::ArrayView<const Trade::SceneFieldData> fields, const Containers::ArrayView<const Containers::StridedArrayView2D<char>> itemViews, const Containers::ArrayView<const Containers::Pair<UnsignedInt, UnsignedInt>> itemViewMappings) {
std::size_t latestMapping = 0;
for(std::size_t i = 0; i != fields.size(); ++i) {
/* If there are no aliased object mappings, itemViewMappings should be
monotonically increasing. If it's not, it means the mapping is
shared with something earlier and it got already copied -- skip. */
const std::size_t mapping = itemViewMappings[i].first();
if(i && mapping <= latestMapping) continue;
latestMapping = mapping;
/* If the field has null object data, no need to copy anything. This
covers reserved fields but also fields of zero size. */
if(!fields[i].mappingData()) continue;
const Containers::StridedArrayView1D<const void> src = fields[i].mappingData();
const Containers::StridedArrayView1D<T> dst = Containers::arrayCast<1, T>(itemViews[mapping]);
if(fields[i].mappingType() == Trade::SceneMappingType::UnsignedByte)
copyOrCastInto(Containers::arrayCast<const UnsignedByte>(src), dst);
else if(fields[i].mappingType() == Trade::SceneMappingType::UnsignedShort)
copyOrCastInto(Containers::arrayCast<const UnsignedShort>(src), dst);
else if(fields[i].mappingType() == Trade::SceneMappingType::UnsignedInt)
copyOrCastInto(Containers::arrayCast<const UnsignedInt>(src), dst);
else if(fields[i].mappingType() == Trade::SceneMappingType::UnsignedLong)
copyOrCastInto(Containers::arrayCast<const UnsignedLong>(src), dst);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Combine fields of varying mapping type together into a SceneData of a single
given mappingType. The fields are expected to point to existing
mapping/field memory, which will be then copied to the resulting scene. If
you supply a field with null mapping or field data, the mapping or field
data will not get copied, only a placeholder for copying the data later will
be allocated. If you however need to have placeholder mapping data shared
among multiple fields you have to allocate them upfront. Offset-only fields
are not allowed.
The resulting fields are always tightly packed (not interleaved).
If multiple fields share the same object mapping views, those are preserved,
however they have to have the exact same length. Sharing object mappings
with different lengths will assert. */
/** @todo when published, add an initializer_list overload and turn all
internal asserts into (tested!) message asserts */
inline Trade::SceneData combine(const Trade::SceneMappingType mappingType, const UnsignedLong mappingBound, const Containers::ArrayView<const Trade::SceneFieldData> fields) {
const std::size_t mappingTypeSize = sceneMappingTypeSize(mappingType);
const std::size_t mappingTypeAlignment = sceneMappingTypeAlignment(mappingType);
/* Go through all fields and collect ArrayTuple allocations for these */
std::unordered_map<const void*, UnsignedInt> objectMappings;
Containers::Array<Containers::ArrayTuple::Item> items;
Containers::Array<Containers::Pair<UnsignedInt, UnsignedInt>> itemViewMappings{NoInit, fields.size()};
/* The item views are referenced from ArrayTuple::Item, not using a
growable array in order to avoid an accidental reallocation */
/** @todo once never-reallocating allocators are present, use them instead
of the manual offset */
Containers::Array<Containers::StridedArrayView2D<char>> itemViews{fields.size()*2};
std::size_t itemViewOffset = 0;
for(std::size_t i = 0; i != fields.size(); ++i) {
const Trade::SceneFieldData& field = fields[i];
CORRADE_INTERNAL_ASSERT(!(field.flags() & Trade::SceneFieldFlag::OffsetOnly));
/* Mapping data. Allocate if the view is a placeholder of if it wasn't
used by other fields yet. */
std::pair<std::unordered_map<const void*, UnsignedInt>::iterator, bool> inserted;
if(field.mappingData().data())
inserted = objectMappings.emplace(field.mappingData().data(), itemViewOffset);
if(field.mappingData().data() && !inserted.second) {
itemViewMappings[i].first() = inserted.first->second;
/* Expect that fields sharing the same object mapping view have the
exact same length (the length gets stored in the output view
during the ArrayTuple::Item construction).
We could just ignore the sharing in that case, but that'd only
lead to misery down the line -- imagine a field that shares the
first two items with a mesh and a material object mapping. If it
would be the last, it gets duplicated and everything is great,
however if it's the first then both mesh and the material get
duplicated, and that then asserts inside the SceneData
constructor, as those are always expected to share.
One option that would solve this would be to store pointer+size
in the objectMappings map (and then only mappings that share
also the same size would be shared), another would be to use the
longest used view (and then the shorter prefixes would share
with it). The ultimate option would be to have some range map
where it'd be possible to locate also arbitrary subranges, not
just prefixes. A whole other topic altogether is checking for
the same stride, which is not done at all.
This might theoretically lead to assertions also when two
compile-time arrays share a common prefix and get deduplicated
by the compiler. But that's unlikely, at least for the internal
use case we have right now. */
CORRADE_INTERNAL_ASSERT(itemViews[inserted.first->second].size()[0] == field.size());
} else {
itemViewMappings[i].first() = itemViewOffset;
arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), mappingTypeSize, mappingTypeAlignment, itemViews[itemViewOffset]);
++itemViewOffset;
}
/* Field data. No aliasing here right now, no sharing between object
and field data either. */
/** @todo field aliasing might be useful at some point */
itemViewMappings[i].second() = itemViewOffset;
arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), sceneFieldTypeSize(field.fieldType())*(field.fieldArraySize() ? field.fieldArraySize() : 1), sceneFieldTypeAlignment(field.fieldType()), itemViews[itemViewOffset]);
++itemViewOffset;
}
/* Allocate the data */
Containers::Array<char> outData = Containers::ArrayTuple{items};
CORRADE_INTERNAL_ASSERT(!outData.deleter());
/* Copy the object data over and cast them as necessary */
if(mappingType == Trade::SceneMappingType::UnsignedByte)
combineCopyObjects<UnsignedByte>(fields, itemViews, itemViewMappings);
else if(mappingType == Trade::SceneMappingType::UnsignedShort)
combineCopyObjects<UnsignedShort>(fields, itemViews, itemViewMappings);
else if(mappingType == Trade::SceneMappingType::UnsignedInt)
combineCopyObjects<UnsignedInt>(fields, itemViews, itemViewMappings);
else if(mappingType == Trade::SceneMappingType::UnsignedLong)
combineCopyObjects<UnsignedLong>(fields, itemViews, itemViewMappings);
/* Copy the field data over. No special handling needed here. */
for(std::size_t i = 0; i != fields.size(); ++i) {
/* If the field has null field data, no need to copy anything. This
covers reserved fields but also fields of zero size. */
if(!fields[i].fieldData()) continue;
/** @todo isn't there some less awful way to create a 2D view, sigh */
Utility::copy(Containers::arrayCast<2, const char>(fields[i].fieldData(), sceneFieldTypeSize(fields[i].fieldType())*(fields[i].fieldArraySize() ? fields[i].fieldArraySize() : 1)), itemViews[itemViewMappings[i].second()]);
}
/* Map the fields to the new data */
Containers::Array<Trade::SceneFieldData> outFields{fields.size()};
for(std::size_t i = 0; i != fields.size(); ++i) {
outFields[i] = Trade::SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize(), fields[i].flags()};
}
return Trade::SceneData{mappingType, mappingBound, std::move(outData), std::move(outFields)};
}
}}}
#endif

197
src/Magnum/Trade/Implementation/sceneTools.h → src/Magnum/SceneTools/Implementation/convertToSingleFunctionObjects.h

@ -1,5 +1,5 @@
#ifndef Magnum_Trade_Implementation_sceneTools_h
#define Magnum_Trade_Implementation_sceneTools_h
#ifndef Magnum_SceneTools_Implementation_convertToSingleFunctionObjects_h
#define Magnum_SceneTools_Implementation_convertToSingleFunctionObjects_h
/*
This file is part of Magnum.
@ -34,164 +34,12 @@
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/PackingBatch.h"
#include "Magnum/SceneTools/Implementation/combine.h"
#include "Magnum/Trade/SceneData.h"
namespace Magnum { namespace Trade { namespace Implementation {
namespace Magnum { namespace SceneTools { namespace Implementation {
/* These two are needed because there (obviously) isn't any overload of
castInto with the same input and output type */
template<class T, class U> void copyOrCastInto(const Containers::StridedArrayView1D<const T>& src, const Containers::StridedArrayView1D<U>& dst) {
Math::castInto(Containers::arrayCast<2, const T>(src), Containers::arrayCast<2, U>(dst));
}
template<class T> void copyOrCastInto(const Containers::StridedArrayView1D<const T>& src, const Containers::StridedArrayView1D<T>& dst) {
Utility::copy(src, dst);
}
template<class T> void sceneCombineCopyObjects(const Containers::ArrayView<const SceneFieldData> fields, const Containers::ArrayView<const Containers::StridedArrayView2D<char>> itemViews, const Containers::ArrayView<const Containers::Pair<UnsignedInt, UnsignedInt>> itemViewMappings) {
std::size_t latestMapping = 0;
for(std::size_t i = 0; i != fields.size(); ++i) {
/* If there are no aliased object mappings, itemViewMappings should be
monotonically increasing. If it's not, it means the mapping is
shared with something earlier and it got already copied -- skip. */
const std::size_t mapping = itemViewMappings[i].first();
if(i && mapping <= latestMapping) continue;
latestMapping = mapping;
/* If the field has null object data, no need to copy anything. This
covers reserved fields but also fields of zero size. */
if(!fields[i].mappingData()) continue;
const Containers::StridedArrayView1D<const void> src = fields[i].mappingData();
const Containers::StridedArrayView1D<T> dst = Containers::arrayCast<1, T>(itemViews[mapping]);
if(fields[i].mappingType() == SceneMappingType::UnsignedByte)
copyOrCastInto(Containers::arrayCast<const UnsignedByte>(src), dst);
else if(fields[i].mappingType() == SceneMappingType::UnsignedShort)
copyOrCastInto(Containers::arrayCast<const UnsignedShort>(src), dst);
else if(fields[i].mappingType() == SceneMappingType::UnsignedInt)
copyOrCastInto(Containers::arrayCast<const UnsignedInt>(src), dst);
else if(fields[i].mappingType() == SceneMappingType::UnsignedLong)
copyOrCastInto(Containers::arrayCast<const UnsignedLong>(src), dst);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
/* Combine fields of varying mapping type together into a SceneData of a single
given mappingType. The fields are expected to point to existing
mapping/field memory, which will be then copied to the resulting scene. If
you supply a field with null mapping or field data, the mapping or field
data will not get copied, only a placeholder for copying the data later will
be allocated. If you however need to have placeholder mapping data shared
among multiple fields you have to allocate them upfront. Offset-only fields
are not allowed.
The resulting fields are always tightly packed (not interleaved).
If multiple fields share the same object mapping views, those are preserved,
however they have to have the exact same length. Sharing object mappings
with different lengths will assert. */
/** @todo when published, add an initializer_list overload and turn all
internal asserts into (tested!) message asserts */
inline SceneData sceneCombine(const SceneMappingType mappingType, const UnsignedLong mappingBound, const Containers::ArrayView<const SceneFieldData> fields) {
const std::size_t mappingTypeSize = sceneMappingTypeSize(mappingType);
const std::size_t mappingTypeAlignment = sceneMappingTypeAlignment(mappingType);
/* Go through all fields and collect ArrayTuple allocations for these */
std::unordered_map<const void*, UnsignedInt> objectMappings;
Containers::Array<Containers::ArrayTuple::Item> items;
Containers::Array<Containers::Pair<UnsignedInt, UnsignedInt>> itemViewMappings{NoInit, fields.size()};
/* The item views are referenced from ArrayTuple::Item, not using a
growable array in order to avoid an accidental reallocation */
/** @todo once never-reallocating allocators are present, use them instead
of the manual offset */
Containers::Array<Containers::StridedArrayView2D<char>> itemViews{fields.size()*2};
std::size_t itemViewOffset = 0;
for(std::size_t i = 0; i != fields.size(); ++i) {
const SceneFieldData& field = fields[i];
CORRADE_INTERNAL_ASSERT(!(field.flags() & SceneFieldFlag::OffsetOnly));
/* Mapping data. Allocate if the view is a placeholder of if it wasn't
used by other fields yet. */
std::pair<std::unordered_map<const void*, UnsignedInt>::iterator, bool> inserted;
if(field.mappingData().data())
inserted = objectMappings.emplace(field.mappingData().data(), itemViewOffset);
if(field.mappingData().data() && !inserted.second) {
itemViewMappings[i].first() = inserted.first->second;
/* Expect that fields sharing the same object mapping view have the
exact same length (the length gets stored in the output view
during the ArrayTuple::Item construction).
We could just ignore the sharing in that case, but that'd only
lead to misery down the line -- imagine a field that shares the
first two items with a mesh and a material object mapping. If it
would be the last, it gets duplicated and everything is great,
however if it's the first then both mesh and the material get
duplicated, and that then asserts inside the SceneData
constructor, as those are always expected to share.
One option that would solve this would be to store pointer+size
in the objectMappings map (and then only mappings that share
also the same size would be shared), another would be to use the
longest used view (and then the shorter prefixes would share
with it). The ultimate option would be to have some range map
where it'd be possible to locate also arbitrary subranges, not
just prefixes. A whole other topic altogether is checking for
the same stride, which is not done at all.
This might theoretically lead to assertions also when two
compile-time arrays share a common prefix and get deduplicated
by the compiler. But that's unlikely, at least for the internal
use case we have right now. */
CORRADE_INTERNAL_ASSERT(itemViews[inserted.first->second].size()[0] == field.size());
} else {
itemViewMappings[i].first() = itemViewOffset;
arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), mappingTypeSize, mappingTypeAlignment, itemViews[itemViewOffset]);
++itemViewOffset;
}
/* Field data. No aliasing here right now, no sharing between object
and field data either. */
/** @todo field aliasing might be useful at some point */
itemViewMappings[i].second() = itemViewOffset;
arrayAppend(items, InPlaceInit, NoInit, std::size_t(field.size()), sceneFieldTypeSize(field.fieldType())*(field.fieldArraySize() ? field.fieldArraySize() : 1), sceneFieldTypeAlignment(field.fieldType()), itemViews[itemViewOffset]);
++itemViewOffset;
}
/* Allocate the data */
Containers::Array<char> outData = Containers::ArrayTuple{items};
CORRADE_INTERNAL_ASSERT(!outData.deleter());
/* Copy the object data over and cast them as necessary */
if(mappingType == SceneMappingType::UnsignedByte)
sceneCombineCopyObjects<UnsignedByte>(fields, itemViews, itemViewMappings);
else if(mappingType == SceneMappingType::UnsignedShort)
sceneCombineCopyObjects<UnsignedShort>(fields, itemViews, itemViewMappings);
else if(mappingType == SceneMappingType::UnsignedInt)
sceneCombineCopyObjects<UnsignedInt>(fields, itemViews, itemViewMappings);
else if(mappingType == SceneMappingType::UnsignedLong)
sceneCombineCopyObjects<UnsignedLong>(fields, itemViews, itemViewMappings);
/* Copy the field data over. No special handling needed here. */
for(std::size_t i = 0; i != fields.size(); ++i) {
/* If the field has null field data, no need to copy anything. This
covers reserved fields but also fields of zero size. */
if(!fields[i].fieldData()) continue;
/** @todo isn't there some less awful way to create a 2D view, sigh */
Utility::copy(Containers::arrayCast<2, const char>(fields[i].fieldData(), sceneFieldTypeSize(fields[i].fieldType())*(fields[i].fieldArraySize() ? fields[i].fieldArraySize() : 1)), itemViews[itemViewMappings[i].second()]);
}
/* Map the fields to the new data */
Containers::Array<SceneFieldData> outFields{fields.size()};
for(std::size_t i = 0; i != fields.size(); ++i) {
outFields[i] = SceneFieldData{fields[i].name(), itemViews[itemViewMappings[i].first()], fields[i].fieldType(), itemViews[itemViewMappings[i].second()], fields[i].fieldArraySize(), fields[i].flags()};
}
return SceneData{mappingType, mappingBound, std::move(outData), std::move(outFields)};
}
inline Containers::Optional<std::size_t> findField(Containers::ArrayView<const SceneField> fields, SceneField field) {
inline Containers::Optional<std::size_t> findField(Containers::ArrayView<const Trade::SceneField> fields, Trade::SceneField field) {
for(std::size_t i = 0; i != fields.size(); ++i)
if(fields[i] == field) return i;
return {};
@ -212,11 +60,12 @@ inline Containers::Optional<std::size_t> findField(Containers::ArrayView<const S
possible to know where to attach the new objects. */
/** @todo when published, (again) add an initializer_list overload and turn all
internal asserts into (tested!) message asserts */
inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Containers::ArrayView<const SceneField> fieldsToConvert, Containers::ArrayView<const SceneField> fieldsToCopy, const UnsignedInt newObjectOffset) {
/** @todo it also needs a better name ("make fields unique"?) */
inline Trade::SceneData convertToSingleFunctionObjects(const Trade::SceneData& scene, Containers::ArrayView<const Trade::SceneField> fieldsToConvert, Containers::ArrayView<const Trade::SceneField> fieldsToCopy, const UnsignedInt newObjectOffset) {
/** @todo assert for really high object counts (where this cast would fail) */
Containers::Array<UnsignedInt> objectAttachmentCount{ValueInit, std::size_t(scene.mappingBound())};
for(const SceneField field: fieldsToConvert) {
CORRADE_INTERNAL_ASSERT(field != SceneField::Parent);
for(const Trade::SceneField field: fieldsToConvert) {
CORRADE_INTERNAL_ASSERT(field != Trade::SceneField::Parent);
/* Skip fields that are not present -- is it's not present, then it
definitely won't be responsible for multi-function objects */
@ -236,8 +85,8 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
fieldsToCopy[i] field */
Containers::Array<UnsignedInt> fieldsToCopyAdditionCount{ValueInit, fieldsToCopy.size()};
for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) {
const SceneField field = fieldsToCopy[i];
CORRADE_INTERNAL_ASSERT(field != SceneField::Parent);
const Trade::SceneField field = fieldsToCopy[i];
CORRADE_INTERNAL_ASSERT(field != Trade::SceneField::Parent);
CORRADE_INTERNAL_ASSERT(!findField(fieldsToConvert, field));
/* Skip fields that are not present */
@ -264,16 +113,16 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
CORRADE_INTERNAL_ASSERT(newObjectOffset + objectsToAdd >= newObjectOffset);
/* Copy the fields over, enlarging them as necessary */
const UnsignedInt parentFieldId = scene.fieldId(SceneField::Parent);
Containers::Array<SceneFieldData> fields{scene.fieldCount()};
const UnsignedInt parentFieldId = scene.fieldId(Trade::SceneField::Parent);
Containers::Array<Trade::SceneFieldData> fields{scene.fieldCount()};
for(std::size_t i = 0; i != scene.fieldCount(); ++i) {
const SceneFieldData& field = scene.fieldData(i);
const Trade::SceneFieldData& field = scene.fieldData(i);
/* If this field is among the fields we want to copy, enlarge it for
the new entries */
if(Containers::Optional<std::size_t> fieldToCopy = findField(fieldsToCopy, field.name())) {
/** @todo wow this placeholder construction is HIDEOUS */
fields[i] = SceneFieldData{field.name(),
fields[i] = Trade::SceneFieldData{field.name(),
field.mappingType(),
Containers::ArrayView<const UnsignedInt>{nullptr, std::size_t(field.size() + fieldsToCopyAdditionCount[*fieldToCopy])},
field.fieldType(),
@ -283,18 +132,18 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
std::ptrdiff_t((field.fieldArraySize() ? field.fieldArraySize() : 1)*sceneFieldTypeSize(field.fieldType()))
},
field.fieldArraySize(),
field.flags() & ~SceneFieldFlag::ImplicitMapping
field.flags() & ~Trade::SceneFieldFlag::ImplicitMapping
};
/* If this is a parent, enlarge it for the newly added objects, and if
it was implicit make it ordered */
} else if(field.name() == SceneField::Parent) {
} else if(field.name() == Trade::SceneField::Parent) {
/** @todo some nicer constructor for placeholders once this is in
public interest */
fields[i] = SceneFieldData{SceneField::Parent, Containers::ArrayView<const UnsignedInt>{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView<const Int>{nullptr, std::size_t(field.size() + objectsToAdd)},
fields[i] = Trade::SceneFieldData{Trade::SceneField::Parent, Containers::ArrayView<const UnsignedInt>{nullptr, std::size_t(field.size() + objectsToAdd)}, Containers::ArrayView<const Int>{nullptr, std::size_t(field.size() + objectsToAdd)},
/* If the field is ordered, we preserve that. But if it's
implicit, we can't. */
field.flags() & ~(SceneFieldFlag::ImplicitMapping & ~SceneFieldFlag::OrderedMapping)
field.flags() & ~(Trade::SceneFieldFlag::ImplicitMapping & ~Trade::SceneFieldFlag::OrderedMapping)
};
/* All other fields are copied as-is, but lose the implicit/ordered
@ -305,11 +154,11 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
- fields that don't actually get their object mapping touched
during the process (and then all fields that share object
mapping with them) */
} else fields[i] = SceneFieldData{field.name(), field.mappingType(), field.mappingData(), field.fieldType(), field.fieldData(), field.fieldArraySize(), field.flags() & ~SceneFieldFlag::ImplicitMapping};
} else fields[i] = Trade::SceneFieldData{field.name(), field.mappingType(), field.mappingData(), field.fieldType(), field.fieldData(), field.fieldArraySize(), field.flags() & ~Trade::SceneFieldFlag::ImplicitMapping};
}
/* Combine the fields into a new SceneData */
SceneData out = sceneCombine(SceneMappingType::UnsignedInt, Math::max(scene.mappingBound(), UnsignedLong(newObjectOffset) + objectsToAdd), fields);
Trade::SceneData out = combine(Trade::SceneMappingType::UnsignedInt, Math::max(scene.mappingBound(), UnsignedLong(newObjectOffset) + objectsToAdd), fields);
/* Copy existing parent object/field data to a prefix of the output */
const Containers::StridedArrayView1D<UnsignedInt> outParentMapping = out.mutableMapping<UnsignedInt>(parentFieldId);
@ -318,7 +167,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
/* Copy existing field-to-copy data to a prefix of the output */
for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) {
const SceneField field = fieldsToCopy[i];
const Trade::SceneField field = fieldsToCopy[i];
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
if(!fieldId) continue;
@ -349,7 +198,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con
children */
{
std::size_t newParentIndex = 0;
for(const SceneField field: fieldsToConvert) {
for(const Trade::SceneField field: fieldsToConvert) {
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
if(!fieldId) continue;

8
src/Magnum/SceneTools/Test/CMakeLists.txt

@ -22,3 +22,11 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
corrade_add_test(SceneToolsCombineTest CombineTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(SceneToolsConvertToSingleFunctionObjectsTest ConvertToSingleFunctionObjectsTest.cpp LIBRARIES MagnumTrade)
set_target_properties(
SceneToolsCombineTest
SceneToolsConvertToSingleFunctionObjectsTest
PROPERTIES FOLDER "Magnum/SceneTools/Test")

329
src/Magnum/SceneTools/Test/CombineTest.cpp

@ -0,0 +1,329 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include "Magnum/Math/Complex.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/SceneTools/Implementation/combine.h"
namespace Magnum { namespace SceneTools { namespace Test { namespace {
struct CombineTest: TestSuite::Tester {
explicit CombineTest();
void test();
void alignment();
void objectsShared();
void objectsPlaceholderFieldPlaceholder();
void objectSharedFieldPlaceholder();
};
struct {
const char* name;
Trade::SceneMappingType objectType;
} TestData[]{
{"UnsignedByte output", Trade::SceneMappingType::UnsignedByte},
{"UnsignedShort output", Trade::SceneMappingType::UnsignedShort},
{"UnsignedInt output", Trade::SceneMappingType::UnsignedInt},
{"UnsignedLong output", Trade::SceneMappingType::UnsignedLong},
};
CombineTest::CombineTest() {
addInstancedTests({&CombineTest::test},
Containers::arraySize(TestData));
addTests({&CombineTest::alignment,
&CombineTest::objectsShared,
&CombineTest::objectsPlaceholderFieldPlaceholder,
&CombineTest::objectSharedFieldPlaceholder});
}
using namespace Math::Literals;
void CombineTest::test() {
auto&& data = TestData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Testing the four possible object types, it should be possible to combine
them */
const UnsignedInt meshMappingData[]{45, 78, 23};
const UnsignedByte meshFieldData[]{3, 5, 17};
const UnsignedShort parentMappingData[]{0, 1};
const Short parentData[]{-1, 0};
const UnsignedByte translationMappingData[]{16};
const Vector2d translationFieldData[]{{1.5, -0.5}};
const UnsignedLong fooMappingData[]{15, 23};
const Int fooFieldData[]{0, 1, 2, 3};
Trade::SceneData scene = Implementation::combine(data.objectType, 167, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
Trade::SceneFieldData{Trade::SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentData), Trade::SceneFieldFlag::ImplicitMapping},
Trade::SceneFieldData{Trade::SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)},
/* Array field */
Trade::SceneFieldData{Trade::sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}}, Trade::SceneFieldFlag::OrderedMapping},
/* Empty field */
Trade::SceneFieldData{Trade::SceneField::Camera, Containers::ArrayView<const UnsignedByte>{}, Containers::ArrayView<const UnsignedShort>{}}
}));
CORRADE_COMPARE(scene.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), data.objectType);
CORRADE_COMPARE(scene.mappingBound(), 167);
CORRADE_COMPARE(scene.fieldCount(), 5);
CORRADE_COMPARE(scene.fieldName(0), Trade::SceneField::Mesh);
CORRADE_COMPARE(scene.fieldFlags(0), Trade::SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(0), Trade::SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(0), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(0), Containers::arrayView<UnsignedInt>({
45, 78, 23
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(1), Trade::SceneField::Parent);
CORRADE_COMPARE(scene.fieldFlags(1), Trade::SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(scene.fieldType(1), Trade::SceneFieldType::Short);
CORRADE_COMPARE(scene.fieldArraySize(1), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(1), Containers::arrayView<UnsignedInt>({
0, 1
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Short>(1),
Containers::arrayView(parentData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(2), Trade::SceneField::Translation);
CORRADE_COMPARE(scene.fieldFlags(2), Trade::SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(2), Trade::SceneFieldType::Vector2d);
CORRADE_COMPARE(scene.fieldArraySize(2), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(2),
Containers::arrayView<UnsignedInt>({16}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Vector2d>(2),
Containers::arrayView(translationFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(3), Trade::sceneFieldCustom(15));
CORRADE_COMPARE(scene.fieldFlags(3), Trade::SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldType(3), Trade::SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldArraySize(3), 2);
CORRADE_COMPARE_AS(scene.mappingAsArray(3),
Containers::arrayView<UnsignedInt>({15, 23}),
TestSuite::Compare::Container);
/** @todo clean up once it's possible to compare multidimensional
containers */
CORRADE_COMPARE_AS(scene.field<Int[]>(3)[0],
(Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}})[0],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Int[]>(3)[1],
(Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}})[1],
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(4), Trade::SceneField::Camera);
CORRADE_COMPARE(scene.fieldFlags(4), Trade::SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(4), Trade::SceneFieldType::UnsignedShort);
CORRADE_COMPARE(scene.fieldSize(4), 0);
CORRADE_COMPARE(scene.fieldArraySize(4), 0);
}
void CombineTest::alignment() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
const UnsignedShort translationMappingData[]{5}; /* 1 byte padding before */
const Vector2d translationFieldData[]{{1.5, 3.0}}; /* 4 byte padding before */
Trade::SceneData scene = Implementation::combine(Trade::SceneMappingType::UnsignedShort, 167, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
Trade::SceneFieldData{Trade::SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)}
}));
CORRADE_COMPARE(scene.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), Trade::SceneMappingType::UnsignedShort);
CORRADE_COMPARE(scene.mappingBound(), 167);
CORRADE_COMPARE(scene.fieldCount(), 2);
CORRADE_COMPARE(scene.fieldName(0), Trade::SceneField::Mesh);
CORRADE_COMPARE(scene.fieldType(0), Trade::SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(0), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(0),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.mapping(0).data()), 2, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.mapping(0).data(), scene.data());
CORRADE_COMPARE(scene.mapping(0).stride()[0], 2);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.field(0).data()), 1, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.field(0).data(), scene.data() + 3*2);
CORRADE_COMPARE(scene.field(0).stride()[0], 1);
CORRADE_COMPARE(scene.fieldName(1), Trade::SceneField::Translation);
CORRADE_COMPARE(scene.fieldType(1), Trade::SceneFieldType::Vector2d);
CORRADE_COMPARE(scene.fieldArraySize(1), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(1),
Containers::arrayView(translationMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Vector2d>(1),
Containers::arrayView(translationFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.mapping(1).data()), 2, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.mapping(1).data(), scene.data() + 3*2 + 3 + 1);
CORRADE_COMPARE(scene.mapping(1).stride()[0], 2);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.field(1).data()), 8, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.field(1).data(), scene.data() + 3*2 + 3 + 1 + 2 + 4);
CORRADE_COMPARE(scene.field(1).stride()[0], 16);
}
void CombineTest::objectsShared() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
const Int meshMaterialFieldData[]{72, -1, 23};
const UnsignedShort translationRotationMappingData[]{14, 22};
const Vector2 translationFieldData[]{{-1.0f, 25.3f}, {2.2f, 2.1f}};
const Complex rotationFieldData[]{Complex::rotation(35.0_degf), Complex::rotation(22.5_degf)};
Trade::SceneData scene = Implementation::combine(Trade::SceneMappingType::UnsignedInt, 173, Containers::arrayView({
/* Deliberately in an arbitrary order to avoid false assumptions like
fields sharing the same object mapping always being after each
other */
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
Trade::SceneFieldData{Trade::SceneField::Translation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(translationFieldData)},
Trade::SceneFieldData{Trade::SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::arrayView(meshMaterialFieldData)},
Trade::SceneFieldData{Trade::SceneField::Rotation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(rotationFieldData)}
}));
CORRADE_COMPARE(scene.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), Trade::SceneMappingType::UnsignedInt);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 4);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::Mesh), 3);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::MeshMaterial), 3);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Mesh).data(), scene.mapping(Trade::SceneField::MeshMaterial).data());
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::Translation), 2);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::Rotation), 2);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Translation).data(), scene.mapping(Trade::SceneField::Rotation).data());
}
void CombineTest::objectsPlaceholderFieldPlaceholder() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
Trade::SceneData scene = Implementation::combine(Trade::SceneMappingType::UnsignedShort, 173, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Camera, Containers::ArrayView<UnsignedByte>{nullptr, 1}, Containers::ArrayView<UnsignedShort>{nullptr, 1}},
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
/* Looks like sharing object mapping with the Camera field, but
actually both are placeholders */
Trade::SceneFieldData{Trade::SceneField::Light, Containers::ArrayView<UnsignedShort>{nullptr, 2}, Containers::ArrayView<UnsignedInt>{nullptr, 2}},
/* Array field */
Trade::SceneFieldData{Trade::sceneFieldCustom(15), Containers::ArrayView<UnsignedShort>{nullptr, 2}, Containers::StridedArrayView2D<Short>{{nullptr, 16}, {2, 4}}},
}));
CORRADE_COMPARE(scene.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), Trade::SceneMappingType::UnsignedShort);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 4);
CORRADE_COMPARE(scene.fieldType(Trade::SceneField::Camera), Trade::SceneFieldType::UnsignedShort);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::Camera), 1);
CORRADE_COMPARE(scene.fieldArraySize(Trade::SceneField::Camera), 0);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Camera).data(), scene.data());
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Camera).stride()[0], 2);
CORRADE_COMPARE(scene.field(Trade::SceneField::Camera).data(), scene.data() + 2);
CORRADE_COMPARE(scene.field(Trade::SceneField::Camera).stride()[0], 2);
CORRADE_COMPARE(scene.fieldType(Trade::SceneField::Mesh), Trade::SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(Trade::SceneField::Mesh), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(Trade::SceneField::Mesh),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(Trade::SceneField::Mesh),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldType(Trade::SceneField::Light), Trade::SceneFieldType::UnsignedInt);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::Light), 2);
CORRADE_COMPARE(scene.fieldArraySize(Trade::SceneField::Light), 0);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::Light).stride()[0], 2);
CORRADE_COMPARE(scene.field(Trade::SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2);
CORRADE_COMPARE(scene.field(Trade::SceneField::Light).stride()[0], 4);
CORRADE_COMPARE(scene.fieldType(Trade::sceneFieldCustom(15)), Trade::SceneFieldType::Short);
CORRADE_COMPARE(scene.fieldSize(Trade::sceneFieldCustom(15)), 2);
CORRADE_COMPARE(scene.fieldArraySize(Trade::sceneFieldCustom(15)), 4);
CORRADE_COMPARE(scene.mapping(Trade::sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4);
CORRADE_COMPARE(scene.mapping(Trade::sceneFieldCustom(15)).stride()[0], 2);
CORRADE_COMPARE(scene.field(Trade::sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4 + 2*2);
CORRADE_COMPARE(scene.field(Trade::sceneFieldCustom(15)).stride()[0], 4*2);
}
void CombineTest::objectSharedFieldPlaceholder() {
const UnsignedInt meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
Trade::SceneData scene = Implementation::combine(Trade::SceneMappingType::UnsignedInt, 173, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
Trade::SceneFieldData{Trade::SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::ArrayView<Int>{nullptr, 3}},
}));
CORRADE_COMPARE(scene.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), Trade::SceneMappingType::UnsignedInt);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 2);
CORRADE_COMPARE(scene.fieldType(Trade::SceneField::Mesh), Trade::SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(Trade::SceneField::Mesh), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(0),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldType(Trade::SceneField::MeshMaterial), Trade::SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldSize(Trade::SceneField::MeshMaterial), 3);
CORRADE_COMPARE(scene.fieldArraySize(Trade::SceneField::MeshMaterial), 0);
CORRADE_COMPARE(scene.mapping(Trade::SceneField::MeshMaterial).data(), scene.mapping(Trade::SceneField::Mesh).data());
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(Trade::SceneField::MeshMaterial),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.field(Trade::SceneField::MeshMaterial).data(), scene.data() + 3*4 + 3 + 1);
CORRADE_COMPARE(scene.field(Trade::SceneField::MeshMaterial).stride()[0], 4);
}
}}}}
CORRADE_TEST_MAIN(Magnum::SceneTools::Test::CombineTest)

383
src/Magnum/SceneTools/Test/ConvertToSingleFunctionObjectsTest.cpp

@ -0,0 +1,383 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include "Magnum/Math/Complex.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/SceneTools/Implementation/convertToSingleFunctionObjects.h"
namespace Magnum { namespace SceneTools { namespace Test { namespace {
struct ConvertToSingleFunctionObjectsTest: TestSuite::Tester {
explicit ConvertToSingleFunctionObjectsTest();
void test();
void fieldsToCopy();
};
struct {
const char* name;
UnsignedLong originalObjectCount;
UnsignedLong expectedObjectCount;
Trade::SceneFieldFlags parentFieldFlagsInput;
Trade::SceneFieldFlags parentFieldFlagsExpected;
} TestData[]{
{"original object count smaller than new", 64, 70, {}, {}},
{"original object count larger than new", 96, 96, {}, {}},
{"parent field with ordered mapping", 64, 70,
Trade::SceneFieldFlag::OrderedMapping, Trade::SceneFieldFlag::OrderedMapping},
{"parent field with implicit mapping", 64, 70,
/* The mapping is *not* implicit but we're not using the flag for
anything so this should work */
Trade::SceneFieldFlag::ImplicitMapping, Trade::SceneFieldFlag::OrderedMapping}
};
ConvertToSingleFunctionObjectsTest::ConvertToSingleFunctionObjectsTest() {
addInstancedTests({&ConvertToSingleFunctionObjectsTest::test},
Containers::arraySize(TestData));
addTests({&ConvertToSingleFunctionObjectsTest::fieldsToCopy});
}
using namespace Math::Literals;
void ConvertToSingleFunctionObjectsTest::test() {
auto&& data = TestData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Haha now I can use sceneCombine() to conveniently prepare the initial
state here, without having to mess with an ArrayTuple */
const UnsignedShort parentMappingData[]{2, 15, 21, 22, 23};
const Byte parentFieldData[]{-1, -1, -1, 21, 22};
/* Two objects have two and three mesh assignments respectively, meaning we
need three extra */
const UnsignedShort meshMappingData[]{15, 23, 23, 23, 2, 15, 21};
const Containers::Pair<UnsignedInt, Int> meshMaterialFieldData[]{
{6, 4},
{1, 0},
{2, 3},
{4, 2},
{7, 2},
{3, 1},
{5, -1}
};
/* One camera is attached to an object that already has a mesh, meaning we
need a fourth extra object */
const UnsignedShort cameraMappingData[]{22, 2};
const UnsignedInt cameraFieldData[]{1, 5};
/* Lights don't conflict with anything so they *could* retain the
ImplicitMapping flag */
const UnsignedShort lightMappingData[]{0, 1};
const UnsignedByte lightFieldData[]{15, 23};
/* Object 0 and 1 has a light, 2 a mesh already, meaning we need a fifth,
sixth and seventh extra object and we lose the ImplicitMapping flag. */
const UnsignedShort fooMappingData[]{0, 1, 2, 3};
const Float fooFieldData[]{1.0f, 2.0f, 3.0f, 4.0f};
/* This field is not among the fields to convert so it should preserve the
ImplicitMapping flag */
const UnsignedShort foo2MappingData[]{0, 1};
const Byte foo2FieldData[]{-5, -7};
/* This field shares mapping with foo (and thus has the ImplicitMapping
flag), but it's not among the fields to convert. Since the mapping gets
changed, it should not retain the ImplicitMapping flag. */
const Byte foo3FieldData[]{-1, -2, 7, 2};
Trade::SceneData original = Implementation::combine(Trade::SceneMappingType::UnsignedShort, data.originalObjectCount, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData), data.parentFieldFlagsInput},
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D<const UnsignedInt>{meshMaterialFieldData, &meshMaterialFieldData[0].first(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}},
Trade::SceneFieldData{Trade::SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D<const Int>{meshMaterialFieldData, &meshMaterialFieldData[0].second(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}},
Trade::SceneFieldData{Trade::SceneField::Camera, Containers::arrayView(cameraMappingData), Containers::arrayView(cameraFieldData)},
Trade::SceneFieldData{Trade::SceneField::Light, Containers::arrayView(lightMappingData), Containers::arrayView(lightFieldData), Trade::SceneFieldFlag::ImplicitMapping},
Trade::SceneFieldData{Trade::sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::arrayView(fooFieldData), Trade::SceneFieldFlag::ImplicitMapping},
Trade::SceneFieldData{Trade::sceneFieldCustom(16), Containers::arrayView(foo2MappingData), Containers::arrayView(foo2FieldData), Trade::SceneFieldFlag::ImplicitMapping},
Trade::SceneFieldData{Trade::sceneFieldCustom(17), Containers::arrayView(fooMappingData), Containers::arrayView(foo3FieldData), Trade::SceneFieldFlag::ImplicitMapping}
}));
Trade::SceneData scene = Implementation::convertToSingleFunctionObjects(original, Containers::arrayView({
Trade::SceneField::Mesh,
/* Deliberately not including MeshMaterial in the list -- these should
get automatically updated as they share the same object mapping.
OTOH including them would break the output. */
Trade::SceneField::Camera,
/* A field with implicit mapping that doesn't conflict with anything so
it *could* retain the flag */
Trade::SceneField::Light,
/* A field with implicit mapping, which loses the flag because entries
get reassigned */
Trade::sceneFieldCustom(15),
/* Include also a field that's not present -- it should get skipped */
Trade::SceneField::ImporterState
}), {}, 63);
/* There should be three more objects, or the original count preserved if
it's large enough */
CORRADE_COMPARE(scene.mappingBound(), data.expectedObjectCount);
/* Object 0 should have new children with "foo", as it has a light */
CORRADE_COMPARE_AS(scene.childrenFor(0),
Containers::arrayView<UnsignedLong>({67}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(0),
Containers::arrayView<UnsignedInt>({15}),
TestSuite::Compare::Container);
/* Object 1 should have a new child with "foo", as it has a light */
CORRADE_COMPARE_AS(scene.childrenFor(1),
Containers::arrayView<UnsignedLong>({68}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(1),
Containers::arrayView<UnsignedInt>({23}),
TestSuite::Compare::Container);
/* Object 2 should have a new child with the camera and "foo", as it has a
mesh */
CORRADE_COMPARE_AS(scene.childrenFor(2),
Containers::arrayView<UnsignedLong>({66, 69}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{7, 2}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(2),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(66),
Containers::arrayView<UnsignedInt>({5}),
TestSuite::Compare::Container);
/* Object 15 should have a new child that has the second mesh */
CORRADE_COMPARE_AS(scene.childrenFor(15),
Containers::arrayView<UnsignedLong>({65}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(15),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{6, 4}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(65),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{3, 1}})),
TestSuite::Compare::Container);
/* Object 23 should have two new children that have the second and third
mesh */
CORRADE_COMPARE_AS(scene.childrenFor(23),
Containers::arrayView<UnsignedLong>({63, 64}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(23),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{1, 0}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(63),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{2, 3}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(64),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{4, 2}})),
TestSuite::Compare::Container);
/* To be extra sure, verify the actual data. Parents have a few objects
added, the rest is the same. Because new objects are added at the end,
the ordered flag is preserved if present. */
CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
{2, -1},
{15, -1},
{21, -1},
{22, 21},
{23, 22},
{63, 23},
{64, 23},
{65, 15},
{66, 2},
{67, 0},
{68, 1},
{69, 2},
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::Parent), data.parentFieldFlagsExpected);
/* Meshes / materials have certain objects reassigned, field data stay the
same. There was no flag before so neither is after. */
CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>>({
{15, {6, 4}},
{23, {1, 0}},
{63, {2, 3}},
{64, {4, 2}},
{2, {7, 2}},
{65, {3, 1}},
{21, {5, -1}}
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::Mesh), Trade::SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::MeshMaterial), Trade::SceneFieldFlags{});
/* Cameras have certain objects reassigned, field data stay the same. There
was no flag before so neither is after. */
CORRADE_COMPARE_AS(scene.camerasAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{22, 1},
{66, 5}
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::Camera), Trade::SceneFieldFlags{});
/* Lights stay the same, thus the implicit flag could be preserved. It's
not currently, though. */
CORRADE_COMPARE_AS(scene.lightsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{0, 15},
{1, 23}
})), TestSuite::Compare::Container);
{
CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet.");
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::Light), Trade::SceneFieldFlag::ImplicitMapping);
} {
CORRADE_COMPARE(scene.fieldFlags(Trade::SceneField::Light), Trade::SceneFieldFlags{});
}
/* A custom field gets the last object reassigned, field data stay the
same. The implicit flag gets turned to nothing after that. */
CORRADE_COMPARE_AS(scene.mappingAsArray(Trade::sceneFieldCustom(15)), (Containers::arrayView<UnsignedInt>({
67, 68, 69, 3
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Float>(Trade::sceneFieldCustom(15)),
Containers::arrayView(fooFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(Trade::sceneFieldCustom(15)), Trade::SceneFieldFlags{});
/* A custom field that is not among fields to convert so it preserves the
flag */
CORRADE_COMPARE_AS(scene.mappingAsArray(Trade::sceneFieldCustom(16)), (Containers::arrayView<UnsignedInt>({
0, 1
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Byte>(Trade::sceneFieldCustom(16)),
Containers::arrayView(foo2FieldData),
TestSuite::Compare::Container);
{
CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet.");
CORRADE_COMPARE(scene.fieldFlags(Trade::sceneFieldCustom(16)), Trade::SceneFieldFlag::ImplicitMapping);
} {
CORRADE_COMPARE(scene.fieldFlags(Trade::sceneFieldCustom(16)), Trade::SceneFieldFlags{});
}
/* A custom field that is not among fields to convert but it shares the
mapping with a field that is and that gets changed. The implicit flag
should thus get removed here as well. */
CORRADE_COMPARE_AS(scene.mappingAsArray(Trade::sceneFieldCustom(17)), (Containers::arrayView<UnsignedInt>({
67, 68, 69, 3
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Byte>(Trade::sceneFieldCustom(17)),
Containers::arrayView(foo3FieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(Trade::sceneFieldCustom(17)), Trade::SceneFieldFlags{});
}
void ConvertToSingleFunctionObjectsTest::fieldsToCopy() {
const UnsignedShort parentMappingData[]{2, 15, 21, 22};
const Byte parentFieldData[]{-1, -1, -1, 21};
const UnsignedShort meshMappingData[]{15, 21, 21, 21, 22, 15};
const UnsignedInt meshFieldData[]{6, 1, 2, 4, 7, 3};
const UnsignedShort skinMappingData[]{22, 21};
const UnsignedInt skinFieldData[]{5, 13};
const UnsignedLong fooMappingData[]{15, 23, 15, 21};
const Int fooFieldData[]{0, 1, 2, 3, 4, 5, 6, 7};
Trade::SceneData original = Implementation::combine(Trade::SceneMappingType::UnsignedShort, 50, Containers::arrayView({
Trade::SceneFieldData{Trade::SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData)},
Trade::SceneFieldData{Trade::SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
Trade::SceneFieldData{Trade::SceneField::Skin, Containers::arrayView(skinMappingData), Containers::arrayView(skinFieldData)},
/* Array field */
Trade::SceneFieldData{Trade::sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D<const Int>{fooFieldData, {4, 2}}},
/* Just to disambiguate between 2D and 3D */
Trade::SceneFieldData{Trade::SceneField::Transformation, Trade::SceneMappingType::UnsignedShort, nullptr, Trade::SceneFieldType::Matrix4x4, nullptr}
}));
Trade::SceneData scene = Implementation::convertToSingleFunctionObjects(original,
Containers::arrayView({
/* Include also a field that's not present -- it should get skipped */
Trade::SceneField::ImporterState,
/* Three additional mesh assignments that go to new objects */
Trade::SceneField::Mesh
}),
Containers::arrayView({
/* One assignment is to an object that has just one mesh, it should
not be copied anywhere, the other should be duplicated two
times */
Trade::SceneField::Skin,
/* Array field with multiple assignments per object -- all should
be copied */
Trade::sceneFieldCustom(15),
/* Include also a field that's not present -- it should get skipped */
Trade::SceneField::Camera
}), 60);
CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
{2, -1},
{15, -1},
{21, -1},
{22, 21},
{60, 21}, /* duplicated mesh assignment to object 21 */
{61, 21}, /* duplicated mesh assignment to object 21 */
{62, 15} /* duplicated mesh assignment to object 15 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>>({
{15, {6, -1}},
{21, {1, -1}},
{60, {2, -1}}, /* duplicated mesh assignment to object 21 */
{61, {4, -1}}, /* duplicated mesh assignment to object 21 */
{22, {7, -1}},
{62, {3, -1}} /* duplicated mesh assignment to object 15 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.skinsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{22, 5},
{21, 13},
{60, 13}, /* duplicated from object 21 */
{61, 13}, /* duplicated from object 21 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(Trade::sceneFieldCustom(15)), Containers::arrayView<UnsignedInt>({
15, 23, 15, 21,
60, 61, /* duplicated from object 21 (two duplicates of one object) */
62, 62, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((scene.field<Int[]>(Trade::sceneFieldCustom(15)).transposed<0, 1>()[0]), Containers::arrayView<Int>({
0, 2, 4, 6,
6, 6, /* duplicated from object 21 (two duplicates of one object) */
0, 4, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((scene.field<Int[]>(Trade::sceneFieldCustom(15)).transposed<0, 1>()[1]), Containers::arrayView<Int>({
1, 3, 5, 7,
7, 7, /* duplicated from object 21 (two duplicates of one object) */
1, 5, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
}
}}}}
CORRADE_TEST_MAIN(Magnum::SceneTools::Test::ConvertToSingleFunctionObjectsTest)

6
src/Magnum/Trade/AbstractImporter.cpp

@ -48,7 +48,9 @@
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/Triple.h>
#include "Magnum/Trade/Implementation/sceneTools.h"
/* This is a header-only tool, meaning no link-time dependency on SceneTools */
/** @todo once this compat is dropped, drop the header-only implementation */
#include "Magnum/SceneTools/Implementation/convertToSingleFunctionObjects.h"
#define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */
#define _MAGNUM_NO_DEPRECATED_OBJECTDATA /* So it doesn't yell here */
@ -531,7 +533,7 @@ void AbstractImporter::populateCachedScenes() {
compatibility code path anyway, so just skip the processing
altogether in that case. */
if(_cachedScenes->scenes[i]->hasField(SceneField::Parent))
_cachedScenes->scenes[i] = Implementation::sceneConvertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), Containers::arrayView({SceneField::Skin}), newObjectOffset);
_cachedScenes->scenes[i] = SceneTools::Implementation::convertToSingleFunctionObjects(*_cachedScenes->scenes[i], Containers::arrayView({SceneField::Mesh, SceneField::Camera, SceneField::Light}), Containers::arrayView({SceneField::Skin}), newObjectOffset);
/* Return the 2D/3D object count based on which scenes are 2D and
which not. The objectCount() provided by the importer is ignored

3
src/Magnum/Trade/CMakeLists.txt

@ -76,8 +76,7 @@ set(MagnumTrade_HEADERS
set(MagnumTrade_PRIVATE_HEADERS
Implementation/arrayUtilities.h
Implementation/converterUtilities.h
Implementation/materialAttributeProperties.hpp
Implementation/sceneTools.h)
Implementation/materialAttributeProperties.hpp)
if(MAGNUM_BUILD_DEPRECATED)
list(APPEND MagnumTrade_SRCS

2
src/Magnum/Trade/Test/CMakeLists.txt

@ -59,7 +59,6 @@ corrade_add_test(TradePbrMetallicRoughnessMate___Test PbrMetallicRoughnessMateri
corrade_add_test(TradePbrSpecularGlossinessMat___Test PbrSpecularGlossinessMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib)
corrade_add_test(TradePhongMaterialDataTest PhongMaterialDataTest.cpp LIBRARIES MagnumTradeTestLib)
corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTradeTestLib)
corrade_add_test(TradeSceneToolsTest SceneToolsTest.cpp LIBRARIES MagnumTrade)
corrade_add_test(TradeSkinDataTest SkinDataTest.cpp LIBRARIES MagnumTradeTestLib)
corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade)
@ -85,7 +84,6 @@ set_target_properties(
TradePbrSpecularGlossinessMat___Test
TradePhongMaterialDataTest
TradeSceneDataTest
TradeSceneToolsTest
TradeTextureDataTest
PROPERTIES FOLDER "Magnum/Trade/Test")

666
src/Magnum/Trade/Test/SceneToolsTest.cpp

@ -1,666 +0,0 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include "Magnum/Math/Complex.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Trade/Implementation/sceneTools.h"
namespace Magnum { namespace Trade { namespace Test { namespace {
struct SceneToolsTest: TestSuite::Tester {
explicit SceneToolsTest();
void combine();
void combineAlignment();
void combineObjectsShared();
void combineObjectsPlaceholderFieldPlaceholder();
void combineObjectSharedFieldPlaceholder();
void convertToSingleFunctionObjects();
void convertToSingleFunctionObjectsFieldsToCopy();
};
struct {
const char* name;
SceneMappingType objectType;
} CombineData[]{
{"UnsignedByte output", SceneMappingType::UnsignedByte},
{"UnsignedShort output", SceneMappingType::UnsignedShort},
{"UnsignedInt output", SceneMappingType::UnsignedInt},
{"UnsignedLong output", SceneMappingType::UnsignedLong},
};
struct {
const char* name;
UnsignedLong originalObjectCount;
UnsignedLong expectedObjectCount;
SceneFieldFlags parentFieldFlagsInput;
SceneFieldFlags parentFieldFlagsExpected;
} ConvertToSingleFunctionObjectsData[]{
{"original object count smaller than new", 64, 70, {}, {}},
{"original object count larger than new", 96, 96, {}, {}},
{"parent field with ordered mapping", 64, 70,
SceneFieldFlag::OrderedMapping, SceneFieldFlag::OrderedMapping},
{"parent field with implicit mapping", 64, 70,
/* The mapping is *not* implicit but we're not using the flag for
anything so this should work */
SceneFieldFlag::ImplicitMapping, SceneFieldFlag::OrderedMapping}
};
SceneToolsTest::SceneToolsTest() {
addInstancedTests({&SceneToolsTest::combine},
Containers::arraySize(CombineData));
addTests({&SceneToolsTest::combineAlignment,
&SceneToolsTest::combineObjectsShared,
&SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder,
&SceneToolsTest::combineObjectSharedFieldPlaceholder});
addInstancedTests({&SceneToolsTest::convertToSingleFunctionObjects},
Containers::arraySize(ConvertToSingleFunctionObjectsData));
addTests({&SceneToolsTest::convertToSingleFunctionObjectsFieldsToCopy});
}
using namespace Math::Literals;
void SceneToolsTest::combine() {
auto&& data = CombineData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Testing the four possible object types, it should be possible to combine
them */
const UnsignedInt meshMappingData[]{45, 78, 23};
const UnsignedByte meshFieldData[]{3, 5, 17};
const UnsignedShort parentMappingData[]{0, 1};
const Short parentData[]{-1, 0};
const UnsignedByte translationMappingData[]{16};
const Vector2d translationFieldData[]{{1.5, -0.5}};
const UnsignedLong fooMappingData[]{15, 23};
const Int fooFieldData[]{0, 1, 2, 3};
SceneData scene = Implementation::sceneCombine(data.objectType, 167, Containers::arrayView({
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentData), SceneFieldFlag::ImplicitMapping},
SceneFieldData{SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)},
/* Array field */
SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}}, SceneFieldFlag::OrderedMapping},
/* Empty field */
SceneFieldData{SceneField::Camera, Containers::ArrayView<const UnsignedByte>{}, Containers::ArrayView<const UnsignedShort>{}}
}));
CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), data.objectType);
CORRADE_COMPARE(scene.mappingBound(), 167);
CORRADE_COMPARE(scene.fieldCount(), 5);
CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh);
CORRADE_COMPARE(scene.fieldFlags(0), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(0), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(0), Containers::arrayView<UnsignedInt>({
45, 78, 23
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(1), SceneField::Parent);
CORRADE_COMPARE(scene.fieldFlags(1), SceneFieldFlag::ImplicitMapping);
CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Short);
CORRADE_COMPARE(scene.fieldArraySize(1), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(1), Containers::arrayView<UnsignedInt>({
0, 1
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Short>(1),
Containers::arrayView(parentData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(2), SceneField::Translation);
CORRADE_COMPARE(scene.fieldFlags(2), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(2), SceneFieldType::Vector2d);
CORRADE_COMPARE(scene.fieldArraySize(2), 0);
CORRADE_COMPARE_AS(scene.mappingAsArray(2),
Containers::arrayView<UnsignedInt>({16}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Vector2d>(2),
Containers::arrayView(translationFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(3), sceneFieldCustom(15));
CORRADE_COMPARE(scene.fieldFlags(3), SceneFieldFlag::OrderedMapping);
CORRADE_COMPARE(scene.fieldType(3), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldArraySize(3), 2);
CORRADE_COMPARE_AS(scene.mappingAsArray(3),
Containers::arrayView<UnsignedInt>({15, 23}),
TestSuite::Compare::Container);
/** @todo clean up once it's possible to compare multidimensional
containers */
CORRADE_COMPARE_AS(scene.field<Int[]>(3)[0],
(Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}})[0],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Int[]>(3)[1],
(Containers::StridedArrayView2D<const Int>{fooFieldData, {2, 2}})[1],
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldName(4), SceneField::Camera);
CORRADE_COMPARE(scene.fieldFlags(4), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldType(4), SceneFieldType::UnsignedShort);
CORRADE_COMPARE(scene.fieldSize(4), 0);
CORRADE_COMPARE(scene.fieldArraySize(4), 0);
}
void SceneToolsTest::combineAlignment() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
const UnsignedShort translationMappingData[]{5}; /* 1 byte padding before */
const Vector2d translationFieldData[]{{1.5, 3.0}}; /* 4 byte padding before */
SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 167, Containers::arrayView({
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
SceneFieldData{SceneField::Translation, Containers::arrayView(translationMappingData), Containers::arrayView(translationFieldData)}
}));
CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort);
CORRADE_COMPARE(scene.mappingBound(), 167);
CORRADE_COMPARE(scene.fieldCount(), 2);
CORRADE_COMPARE(scene.fieldName(0), SceneField::Mesh);
CORRADE_COMPARE(scene.fieldType(0), SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(0), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(0),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.mapping(0).data()), 2, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.mapping(0).data(), scene.data());
CORRADE_COMPARE(scene.mapping(0).stride()[0], 2);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.field(0).data()), 1, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.field(0).data(), scene.data() + 3*2);
CORRADE_COMPARE(scene.field(0).stride()[0], 1);
CORRADE_COMPARE(scene.fieldName(1), SceneField::Translation);
CORRADE_COMPARE(scene.fieldType(1), SceneFieldType::Vector2d);
CORRADE_COMPARE(scene.fieldArraySize(1), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(1),
Containers::arrayView(translationMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Vector2d>(1),
Containers::arrayView(translationFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.mapping(1).data()), 2, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.mapping(1).data(), scene.data() + 3*2 + 3 + 1);
CORRADE_COMPARE(scene.mapping(1).stride()[0], 2);
CORRADE_COMPARE_AS(reinterpret_cast<std::ptrdiff_t>(scene.field(1).data()), 8, TestSuite::Compare::Divisible);
CORRADE_COMPARE(scene.field(1).data(), scene.data() + 3*2 + 3 + 1 + 2 + 4);
CORRADE_COMPARE(scene.field(1).stride()[0], 16);
}
void SceneToolsTest::combineObjectsShared() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
const Int meshMaterialFieldData[]{72, -1, 23};
const UnsignedShort translationRotationMappingData[]{14, 22};
const Vector2 translationFieldData[]{{-1.0f, 25.3f}, {2.2f, 2.1f}};
const Complex rotationFieldData[]{Complex::rotation(35.0_degf), Complex::rotation(22.5_degf)};
SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedInt, 173, Containers::arrayView({
/* Deliberately in an arbitrary order to avoid false assumptions like
fields sharing the same object mapping always being after each
other */
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
SceneFieldData{SceneField::Translation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(translationFieldData)},
SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::arrayView(meshMaterialFieldData)},
SceneFieldData{SceneField::Rotation, Containers::arrayView(translationRotationMappingData), Containers::arrayView(rotationFieldData)}
}));
CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 4);
CORRADE_COMPARE(scene.fieldSize(SceneField::Mesh), 3);
CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3);
CORRADE_COMPARE(scene.mapping(SceneField::Mesh).data(), scene.mapping(SceneField::MeshMaterial).data());
CORRADE_COMPARE(scene.fieldSize(SceneField::Translation), 2);
CORRADE_COMPARE(scene.fieldSize(SceneField::Rotation), 2);
CORRADE_COMPARE(scene.mapping(SceneField::Translation).data(), scene.mapping(SceneField::Rotation).data());
}
void SceneToolsTest::combineObjectsPlaceholderFieldPlaceholder() {
const UnsignedShort meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 173, Containers::arrayView({
SceneFieldData{SceneField::Camera, Containers::ArrayView<UnsignedByte>{nullptr, 1}, Containers::ArrayView<UnsignedShort>{nullptr, 1}},
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
/* Looks like sharing object mapping with the Camera field, but
actually both are placeholders */
SceneFieldData{SceneField::Light, Containers::ArrayView<UnsignedShort>{nullptr, 2}, Containers::ArrayView<UnsignedInt>{nullptr, 2}},
/* Array field */
SceneFieldData{sceneFieldCustom(15), Containers::ArrayView<UnsignedShort>{nullptr, 2}, Containers::StridedArrayView2D<Short>{{nullptr, 16}, {2, 4}}},
}));
CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedShort);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 4);
CORRADE_COMPARE(scene.fieldType(SceneField::Camera), SceneFieldType::UnsignedShort);
CORRADE_COMPARE(scene.fieldSize(SceneField::Camera), 1);
CORRADE_COMPARE(scene.fieldArraySize(SceneField::Camera), 0);
CORRADE_COMPARE(scene.mapping(SceneField::Camera).data(), scene.data());
CORRADE_COMPARE(scene.mapping(SceneField::Camera).stride()[0], 2);
CORRADE_COMPARE(scene.field(SceneField::Camera).data(), scene.data() + 2);
CORRADE_COMPARE(scene.field(SceneField::Camera).stride()[0], 2);
CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedShort>(SceneField::Mesh),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(SceneField::Mesh),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldType(SceneField::Light), SceneFieldType::UnsignedInt);
CORRADE_COMPARE(scene.fieldSize(SceneField::Light), 2);
CORRADE_COMPARE(scene.fieldArraySize(SceneField::Light), 0);
CORRADE_COMPARE(scene.mapping(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1);
CORRADE_COMPARE(scene.mapping(SceneField::Light).stride()[0], 2);
CORRADE_COMPARE(scene.field(SceneField::Light).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2);
CORRADE_COMPARE(scene.field(SceneField::Light).stride()[0], 4);
CORRADE_COMPARE(scene.fieldType(sceneFieldCustom(15)), SceneFieldType::Short);
CORRADE_COMPARE(scene.fieldSize(sceneFieldCustom(15)), 2);
CORRADE_COMPARE(scene.fieldArraySize(sceneFieldCustom(15)), 4);
CORRADE_COMPARE(scene.mapping(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4);
CORRADE_COMPARE(scene.mapping(sceneFieldCustom(15)).stride()[0], 2);
CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).data(), scene.data() + 2 + 2 + 3*2 + 3 + 1 + 2*2 + 2 + 2*4 + 2*2);
CORRADE_COMPARE(scene.field(sceneFieldCustom(15)).stride()[0], 4*2);
}
void SceneToolsTest::combineObjectSharedFieldPlaceholder() {
const UnsignedInt meshMappingData[]{15, 23, 47};
const UnsignedByte meshFieldData[]{0, 1, 2};
SceneData scene = Implementation::sceneCombine(SceneMappingType::UnsignedInt, 173, Containers::arrayView({
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::ArrayView<Int>{nullptr, 3}},
}));
CORRADE_COMPARE(scene.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(scene.mappingType(), SceneMappingType::UnsignedInt);
CORRADE_COMPARE(scene.mappingBound(), 173);
CORRADE_COMPARE(scene.fieldCount(), 2);
CORRADE_COMPARE(scene.fieldType(SceneField::Mesh), SceneFieldType::UnsignedByte);
CORRADE_COMPARE(scene.fieldArraySize(SceneField::Mesh), 0);
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(0),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<UnsignedByte>(0),
Containers::arrayView(meshFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldType(SceneField::MeshMaterial), SceneFieldType::Int);
CORRADE_COMPARE(scene.fieldSize(SceneField::MeshMaterial), 3);
CORRADE_COMPARE(scene.fieldArraySize(SceneField::MeshMaterial), 0);
CORRADE_COMPARE(scene.mapping(SceneField::MeshMaterial).data(), scene.mapping(SceneField::Mesh).data());
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(SceneField::MeshMaterial),
Containers::arrayView(meshMappingData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).data(), scene.data() + 3*4 + 3 + 1);
CORRADE_COMPARE(scene.field(SceneField::MeshMaterial).stride()[0], 4);
}
void SceneToolsTest::convertToSingleFunctionObjects() {
auto&& data = ConvertToSingleFunctionObjectsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Haha now I can use sceneCombine() to conveniently prepare the initial
state here, without having to mess with an ArrayTuple */
const UnsignedShort parentMappingData[]{2, 15, 21, 22, 23};
const Byte parentFieldData[]{-1, -1, -1, 21, 22};
/* Two objects have two and three mesh assignments respectively, meaning we
need three extra */
const UnsignedShort meshMappingData[]{15, 23, 23, 23, 2, 15, 21};
const Containers::Pair<UnsignedInt, Int> meshMaterialFieldData[]{
{6, 4},
{1, 0},
{2, 3},
{4, 2},
{7, 2},
{3, 1},
{5, -1}
};
/* One camera is attached to an object that already has a mesh, meaning we
need a fourth extra object */
const UnsignedShort cameraMappingData[]{22, 2};
const UnsignedInt cameraFieldData[]{1, 5};
/* Lights don't conflict with anything so they *could* retain the
ImplicitMapping flag */
const UnsignedShort lightMappingData[]{0, 1};
const UnsignedByte lightFieldData[]{15, 23};
/* Object 0 and 1 has a light, 2 a mesh already, meaning we need a fifth,
sixth and seventh extra object and we lose the ImplicitMapping flag. */
const UnsignedShort fooMappingData[]{0, 1, 2, 3};
const Float fooFieldData[]{1.0f, 2.0f, 3.0f, 4.0f};
/* This field is not among the fields to convert so it should preserve the
ImplicitMapping flag */
const UnsignedShort foo2MappingData[]{0, 1};
const Byte foo2FieldData[]{-5, -7};
/* This field shares mapping with foo (and thus has the ImplicitMapping
flag), but it's not among the fields to convert. Since the mapping gets
changed, it should not retain the ImplicitMapping flag. */
const Byte foo3FieldData[]{-1, -2, 7, 2};
SceneData original = Implementation::sceneCombine(SceneMappingType::UnsignedShort, data.originalObjectCount, Containers::arrayView({
SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData), data.parentFieldFlagsInput},
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D<const UnsignedInt>{meshMaterialFieldData, &meshMaterialFieldData[0].first(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}},
SceneFieldData{SceneField::MeshMaterial, Containers::arrayView(meshMappingData), Containers::StridedArrayView1D<const Int>{meshMaterialFieldData, &meshMaterialFieldData[0].second(), Containers::arraySize(meshMaterialFieldData), sizeof(meshMaterialFieldData[0])}},
SceneFieldData{SceneField::Camera, Containers::arrayView(cameraMappingData), Containers::arrayView(cameraFieldData)},
SceneFieldData{SceneField::Light, Containers::arrayView(lightMappingData), Containers::arrayView(lightFieldData), SceneFieldFlag::ImplicitMapping},
SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::arrayView(fooFieldData), SceneFieldFlag::ImplicitMapping},
SceneFieldData{sceneFieldCustom(16), Containers::arrayView(foo2MappingData), Containers::arrayView(foo2FieldData), SceneFieldFlag::ImplicitMapping},
SceneFieldData{sceneFieldCustom(17), Containers::arrayView(fooMappingData), Containers::arrayView(foo3FieldData), SceneFieldFlag::ImplicitMapping}
}));
SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original, Containers::arrayView({
SceneField::Mesh,
/* Deliberately not including MeshMaterial in the list -- these should
get automatically updated as they share the same object mapping.
OTOH including them would break the output. */
SceneField::Camera,
/* A field with implicit mapping that doesn't conflict with anything so
it *could* retain the flag */
SceneField::Light,
/* A field with implicit mapping, which loses the flag because entries
get reassigned */
sceneFieldCustom(15),
/* Include also a field that's not present -- it should get skipped */
SceneField::ImporterState
}), {}, 63);
/* There should be three more objects, or the original count preserved if
it's large enough */
CORRADE_COMPARE(scene.mappingBound(), data.expectedObjectCount);
/* Object 0 should have new children with "foo", as it has a light */
CORRADE_COMPARE_AS(scene.childrenFor(0),
Containers::arrayView<UnsignedLong>({67}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(0),
Containers::arrayView<UnsignedInt>({15}),
TestSuite::Compare::Container);
/* Object 1 should have a new child with "foo", as it has a light */
CORRADE_COMPARE_AS(scene.childrenFor(1),
Containers::arrayView<UnsignedLong>({68}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.lightsFor(1),
Containers::arrayView<UnsignedInt>({23}),
TestSuite::Compare::Container);
/* Object 2 should have a new child with the camera and "foo", as it has a
mesh */
CORRADE_COMPARE_AS(scene.childrenFor(2),
Containers::arrayView<UnsignedLong>({66, 69}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(2),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{7, 2}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(2),
Containers::arrayView<UnsignedInt>({}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.camerasFor(66),
Containers::arrayView<UnsignedInt>({5}),
TestSuite::Compare::Container);
/* Object 15 should have a new child that has the second mesh */
CORRADE_COMPARE_AS(scene.childrenFor(15),
Containers::arrayView<UnsignedLong>({65}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(15),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{6, 4}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(65),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{3, 1}})),
TestSuite::Compare::Container);
/* Object 23 should have two new children that have the second and third
mesh */
CORRADE_COMPARE_AS(scene.childrenFor(23),
Containers::arrayView<UnsignedLong>({63, 64}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(23),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{1, 0}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(63),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{2, 3}})),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsFor(64),
(Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({{4, 2}})),
TestSuite::Compare::Container);
/* To be extra sure, verify the actual data. Parents have a few objects
added, the rest is the same. Because new objects are added at the end,
the ordered flag is preserved if present. */
CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
{2, -1},
{15, -1},
{21, -1},
{22, 21},
{23, 22},
{63, 23},
{64, 23},
{65, 15},
{66, 2},
{67, 0},
{68, 1},
{69, 2},
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(SceneField::Parent), data.parentFieldFlagsExpected);
/* Meshes / materials have certain objects reassigned, field data stay the
same. There was no flag before so neither is after. */
CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>>({
{15, {6, 4}},
{23, {1, 0}},
{63, {2, 3}},
{64, {4, 2}},
{2, {7, 2}},
{65, {3, 1}},
{21, {5, -1}}
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(SceneField::Mesh), SceneFieldFlags{});
CORRADE_COMPARE(scene.fieldFlags(SceneField::MeshMaterial), SceneFieldFlags{});
/* Cameras have certain objects reassigned, field data stay the same. There
was no flag before so neither is after. */
CORRADE_COMPARE_AS(scene.camerasAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{22, 1},
{66, 5}
})), TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(SceneField::Camera), SceneFieldFlags{});
/* Lights stay the same, thus the implicit flag could be preserved. It's
not currently, though. */
CORRADE_COMPARE_AS(scene.lightsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{0, 15},
{1, 23}
})), TestSuite::Compare::Container);
{
CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet.");
CORRADE_COMPARE(scene.fieldFlags(SceneField::Light), SceneFieldFlag::ImplicitMapping);
} {
CORRADE_COMPARE(scene.fieldFlags(SceneField::Light), SceneFieldFlags{});
}
/* A custom field gets the last object reassigned, field data stay the
same. The implicit flag gets turned to nothing after that. */
CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(15)), (Containers::arrayView<UnsignedInt>({
67, 68, 69, 3
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Float>(sceneFieldCustom(15)),
Containers::arrayView(fooFieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(15)), SceneFieldFlags{});
/* A custom field that is not among fields to convert so it preserves the
flag */
CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(16)), (Containers::arrayView<UnsignedInt>({
0, 1
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Byte>(sceneFieldCustom(16)),
Containers::arrayView(foo2FieldData),
TestSuite::Compare::Container);
{
CORRADE_EXPECT_FAIL("Logic for preserving flags of untouched fields is rather complex and thus not implemented yet.");
CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(16)), SceneFieldFlag::ImplicitMapping);
} {
CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(16)), SceneFieldFlags{});
}
/* A custom field that is not among fields to convert but it shares the
mapping with a field that is and that gets changed. The implicit flag
should thus get removed here as well. */
CORRADE_COMPARE_AS(scene.mappingAsArray(sceneFieldCustom(17)), (Containers::arrayView<UnsignedInt>({
67, 68, 69, 3
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.field<Byte>(sceneFieldCustom(17)),
Containers::arrayView(foo3FieldData),
TestSuite::Compare::Container);
CORRADE_COMPARE(scene.fieldFlags(sceneFieldCustom(17)), SceneFieldFlags{});
}
void SceneToolsTest::convertToSingleFunctionObjectsFieldsToCopy() {
const UnsignedShort parentMappingData[]{2, 15, 21, 22};
const Byte parentFieldData[]{-1, -1, -1, 21};
const UnsignedShort meshMappingData[]{15, 21, 21, 21, 22, 15};
const UnsignedInt meshFieldData[]{6, 1, 2, 4, 7, 3};
const UnsignedShort skinMappingData[]{22, 21};
const UnsignedInt skinFieldData[]{5, 13};
const UnsignedLong fooMappingData[]{15, 23, 15, 21};
const Int fooFieldData[]{0, 1, 2, 3, 4, 5, 6, 7};
SceneData original = Implementation::sceneCombine(SceneMappingType::UnsignedShort, 50, Containers::arrayView({
SceneFieldData{SceneField::Parent, Containers::arrayView(parentMappingData), Containers::arrayView(parentFieldData)},
SceneFieldData{SceneField::Mesh, Containers::arrayView(meshMappingData), Containers::arrayView(meshFieldData)},
SceneFieldData{SceneField::Skin, Containers::arrayView(skinMappingData), Containers::arrayView(skinFieldData)},
/* Array field */
SceneFieldData{sceneFieldCustom(15), Containers::arrayView(fooMappingData), Containers::StridedArrayView2D<const Int>{fooFieldData, {4, 2}}},
/* Just to disambiguate between 2D and 3D */
SceneFieldData{SceneField::Transformation, SceneMappingType::UnsignedShort, nullptr, SceneFieldType::Matrix4x4, nullptr}
}));
SceneData scene = Implementation::sceneConvertToSingleFunctionObjects(original,
Containers::arrayView({
/* Include also a field that's not present -- it should get skipped */
SceneField::ImporterState,
/* Three additional mesh assignments that go to new objects */
SceneField::Mesh
}),
Containers::arrayView({
/* One assignment is to an object that has just one mesh, it should
not be copied anywhere, the other should be duplicated two
times */
SceneField::Skin,
/* Array field with multiple assignments per object -- all should
be copied */
sceneFieldCustom(15),
/* Include also a field that's not present -- it should get skipped */
SceneField::Camera
}), 60);
CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Int>>({
{2, -1},
{15, -1},
{21, -1},
{22, 21},
{60, 21}, /* duplicated mesh assignment to object 21 */
{61, 21}, /* duplicated mesh assignment to object 21 */
{62, 15} /* duplicated mesh assignment to object 15 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>>({
{15, {6, -1}},
{21, {1, -1}},
{60, {2, -1}}, /* duplicated mesh assignment to object 21 */
{61, {4, -1}}, /* duplicated mesh assignment to object 21 */
{22, {7, -1}},
{62, {3, -1}} /* duplicated mesh assignment to object 15 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.skinsAsArray(), (Containers::arrayView<Containers::Pair<UnsignedInt, UnsignedInt>>({
{22, 5},
{21, 13},
{60, 13}, /* duplicated from object 21 */
{61, 13}, /* duplicated from object 21 */
})), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(scene.mapping<UnsignedInt>(sceneFieldCustom(15)), Containers::arrayView<UnsignedInt>({
15, 23, 15, 21,
60, 61, /* duplicated from object 21 (two duplicates of one object) */
62, 62, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((scene.field<Int[]>(sceneFieldCustom(15)).transposed<0, 1>()[0]), Containers::arrayView<Int>({
0, 2, 4, 6,
6, 6, /* duplicated from object 21 (two duplicates of one object) */
0, 4, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((scene.field<Int[]>(sceneFieldCustom(15)).transposed<0, 1>()[1]), Containers::arrayView<Int>({
1, 3, 5, 7,
7, 7, /* duplicated from object 21 (two duplicates of one object) */
1, 5, /* duplicated from object 15 (two entries for one object) */
}), TestSuite::Compare::Container);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Trade::Test::SceneToolsTest)
Loading…
Cancel
Save