mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
14 KiB
266 lines
14 KiB
|
4 years ago
|
#ifndef Magnum_SceneTools_Implementation_convertToSingleFunctionObjects_h
|
||
|
|
#define Magnum_SceneTools_Implementation_convertToSingleFunctionObjects_h
|
||
|
5 years ago
|
/*
|
||
|
|
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>
|
||
|
5 years ago
|
#include <Corrade/Containers/Optional.h>
|
||
|
5 years ago
|
#include <Corrade/Containers/Pair.h>
|
||
|
|
#include <Corrade/Utility/Algorithms.h>
|
||
|
|
|
||
|
5 years ago
|
#include "Magnum/Math/Functions.h"
|
||
|
5 years ago
|
#include "Magnum/Math/PackingBatch.h"
|
||
|
4 years ago
|
#include "Magnum/SceneTools/Implementation/combine.h"
|
||
|
5 years ago
|
#include "Magnum/Trade/SceneData.h"
|
||
|
|
|
||
|
4 years ago
|
namespace Magnum { namespace SceneTools { namespace Implementation {
|
||
|
5 years ago
|
|
||
|
4 years ago
|
inline Containers::Optional<std::size_t> findField(Containers::ArrayView<const Trade::SceneField> fields, Trade::SceneField field) {
|
||
|
4 years ago
|
for(std::size_t i = 0; i != fields.size(); ++i)
|
||
|
|
if(fields[i] == field) return i;
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
/* Creates a SceneData copy where each object has at most one of the fields
|
||
|
4 years ago
|
listed in the passed `fieldsToConvert` array. This is done by enlarging the
|
||
|
|
parents array and moving extraneous features to new objects that are marked
|
||
|
|
as a child of the original. Fields that are connected together (such as
|
||
|
|
meshes and materials) are assumed to share the same object mapping with only
|
||
|
|
one of them passed in the fieldsToConvert array, which will result for all
|
||
|
|
fields from the same set being reassociated to the new object.
|
||
|
|
|
||
|
|
Fields listed in `fieldsToCopy` are copied from the original object. This
|
||
|
|
is useful for e.g. skins, to preserve them for the separated meshes.
|
||
|
5 years ago
|
|
||
|
|
Requies a SceneField::Parent to be present -- otherwise it wouldn't be
|
||
|
|
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 */
|
||
|
4 years ago
|
/** @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) {
|
||
|
5 years ago
|
/** @todo assert for really high object counts (where this cast would fail) */
|
||
|
5 years ago
|
Containers::Array<UnsignedInt> objectAttachmentCount{ValueInit, std::size_t(scene.mappingBound())};
|
||
|
4 years ago
|
for(const Trade::SceneField field: fieldsToConvert) {
|
||
|
|
CORRADE_INTERNAL_ASSERT(field != Trade::SceneField::Parent);
|
||
|
5 years ago
|
|
||
|
|
/* Skip fields that are not present -- is it's not present, then it
|
||
|
|
definitely won't be responsible for multi-function objects */
|
||
|
|
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
|
||
|
|
if(!fieldId) continue;
|
||
|
|
|
||
|
|
/** @todo use a statically-allocated array & Into() in a loop instead
|
||
|
|
once this is more than a private backwards-compatibility utility
|
||
|
|
where PERF WHATEVER WHO CARES */
|
||
|
5 years ago
|
for(const UnsignedInt object: scene.mappingAsArray(*fieldId)) {
|
||
|
5 years ago
|
CORRADE_INTERNAL_ASSERT(object < objectAttachmentCount.size());
|
||
|
|
++objectAttachmentCount[object];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
4 years ago
|
/* entriesToAddToFieldsToCopy[i] specifies how many fields to add for the
|
||
|
|
fieldsToCopy[i] field */
|
||
|
|
Containers::Array<UnsignedInt> fieldsToCopyAdditionCount{ValueInit, fieldsToCopy.size()};
|
||
|
|
for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) {
|
||
|
4 years ago
|
const Trade::SceneField field = fieldsToCopy[i];
|
||
|
|
CORRADE_INTERNAL_ASSERT(field != Trade::SceneField::Parent);
|
||
|
4 years ago
|
CORRADE_INTERNAL_ASSERT(!findField(fieldsToConvert, field));
|
||
|
|
|
||
|
|
/* Skip fields that are not present */
|
||
|
|
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
|
||
|
|
if(!fieldId) continue;
|
||
|
|
|
||
|
|
/** @todo use a statically-allocated array & Into() in a loop instead
|
||
|
|
once this is more than a private backwards-compatibility utility
|
||
|
|
where PERF WHATEVER WHO CARES */
|
||
|
|
for(const UnsignedInt object: scene.mappingAsArray(*fieldId)) {
|
||
|
|
CORRADE_INTERNAL_ASSERT(object < objectAttachmentCount.size());
|
||
|
|
if(objectAttachmentCount[object])
|
||
|
|
fieldsToCopyAdditionCount[i] += objectAttachmentCount[object] - 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
UnsignedInt objectsToAdd = 0;
|
||
|
|
for(const UnsignedInt count: objectAttachmentCount)
|
||
|
|
if(count > 1) objectsToAdd += count - 1;
|
||
|
|
|
||
|
|
/* Ensure we don't overflow the 32-bit object count with the objects to
|
||
|
|
add. This should also cover the case when the parent field would not be
|
||
|
|
representable in 32 bits. */
|
||
|
|
CORRADE_INTERNAL_ASSERT(newObjectOffset + objectsToAdd >= newObjectOffset);
|
||
|
|
|
||
|
|
/* Copy the fields over, enlarging them as necessary */
|
||
|
4 years ago
|
const UnsignedInt parentFieldId = scene.fieldId(Trade::SceneField::Parent);
|
||
|
|
Containers::Array<Trade::SceneFieldData> fields{scene.fieldCount()};
|
||
|
5 years ago
|
for(std::size_t i = 0; i != scene.fieldCount(); ++i) {
|
||
|
4 years ago
|
const Trade::SceneFieldData& field = scene.fieldData(i);
|
||
|
5 years ago
|
|
||
|
4 years ago
|
/* 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 */
|
||
|
4 years ago
|
fields[i] = Trade::SceneFieldData{field.name(),
|
||
|
4 years ago
|
field.mappingType(),
|
||
|
|
Containers::ArrayView<const UnsignedInt>{nullptr, std::size_t(field.size() + fieldsToCopyAdditionCount[*fieldToCopy])},
|
||
|
|
field.fieldType(),
|
||
|
|
Containers::StridedArrayView1D<const void>{
|
||
|
|
{nullptr, ~std::size_t{}},
|
||
|
|
std::size_t(field.size() + fieldsToCopyAdditionCount[*fieldToCopy]),
|
||
|
|
std::ptrdiff_t((field.fieldArraySize() ? field.fieldArraySize() : 1)*sceneFieldTypeSize(field.fieldType()))
|
||
|
|
},
|
||
|
|
field.fieldArraySize(),
|
||
|
4 years ago
|
field.flags() & ~Trade::SceneFieldFlag::ImplicitMapping
|
||
|
4 years ago
|
};
|
||
|
|
|
||
|
4 years ago
|
/* If this is a parent, enlarge it for the newly added objects, and if
|
||
|
|
it was implicit make it ordered */
|
||
|
4 years ago
|
} else if(field.name() == Trade::SceneField::Parent) {
|
||
|
5 years ago
|
/** @todo some nicer constructor for placeholders once this is in
|
||
|
|
public interest */
|
||
|
4 years ago
|
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)},
|
||
|
4 years ago
|
/* If the field is ordered, we preserve that. But if it's
|
||
|
|
implicit, we can't. */
|
||
|
4 years ago
|
field.flags() & ~(Trade::SceneFieldFlag::ImplicitMapping & ~Trade::SceneFieldFlag::OrderedMapping)
|
||
|
4 years ago
|
};
|
||
|
|
|
||
|
|
/* All other fields are copied as-is, but lose the implicit/ordered
|
||
|
|
flags */
|
||
|
|
/** @todo the flags could get preserved for
|
||
|
|
- fields that don't share their object mapping with any fields in
|
||
|
|
the fieldsToConvert list
|
||
|
|
- fields that don't actually get their object mapping touched
|
||
|
|
during the process (and then all fields that share object
|
||
|
|
mapping with them) */
|
||
|
4 years ago
|
} else fields[i] = Trade::SceneFieldData{field.name(), field.mappingType(), field.mappingData(), field.fieldType(), field.fieldData(), field.fieldArraySize(), field.flags() & ~Trade::SceneFieldFlag::ImplicitMapping};
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
|
/* Combine the fields into a new SceneData */
|
||
|
4 years ago
|
Trade::SceneData out = combine(Trade::SceneMappingType::UnsignedInt, Math::max(scene.mappingBound(), UnsignedLong(newObjectOffset) + objectsToAdd), fields);
|
||
|
5 years ago
|
|
||
|
|
/* Copy existing parent object/field data to a prefix of the output */
|
||
|
5 years ago
|
const Containers::StridedArrayView1D<UnsignedInt> outParentMapping = out.mutableMapping<UnsignedInt>(parentFieldId);
|
||
|
5 years ago
|
const Containers::StridedArrayView1D<Int> outParents = out.mutableField<Int>(parentFieldId);
|
||
|
5 years ago
|
CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParentMapping, outParents) == scene.fieldSize(parentFieldId));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
/* Copy existing field-to-copy data to a prefix of the output */
|
||
|
|
for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) {
|
||
|
4 years ago
|
const Trade::SceneField field = fieldsToCopy[i];
|
||
|
4 years ago
|
|
||
|
|
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
|
||
|
|
if(!fieldId) continue;
|
||
|
|
|
||
|
|
const Containers::StridedArrayView1D<UnsignedInt> outMapping = out.mutableMapping<UnsignedInt>(*fieldId);
|
||
|
|
const Containers::StridedArrayView2D<char> outField = out.mutableField(*fieldId);
|
||
|
|
CORRADE_INTERNAL_ASSERT_OUTPUT(scene.mappingInto(*fieldId, 0, outMapping) == scene.fieldSize(*fieldId));
|
||
|
|
Utility::copy(scene.field(*fieldId), outField.prefix(scene.fieldSize(*fieldId)));
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
/* List new objects at the end of the extended parent field */
|
||
|
5 years ago
|
const Containers::StridedArrayView1D<UnsignedInt> newParentMapping = outParentMapping.suffix(scene.fieldSize(parentFieldId));
|
||
|
5 years ago
|
const Containers::StridedArrayView1D<Int> newParents = outParents.suffix(scene.fieldSize(parentFieldId));
|
||
|
5 years ago
|
for(std::size_t i = 0; i != newParentMapping.size(); ++i) {
|
||
|
|
newParentMapping[i] = newObjectOffset + i;
|
||
|
5 years ago
|
newParents[i] = -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Clear the objectAttachmentCount array to reuse it below */
|
||
|
|
/** @todo use a BitArray instead once it exists? */
|
||
|
|
constexpr UnsignedInt zero[1]{};
|
||
|
5 years ago
|
Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(scene.mappingBound()), objectAttachmentCount);
|
||
|
5 years ago
|
|
||
|
4 years ago
|
/* Clear the fieldsToCopyAdditionCount array to reuse it below */
|
||
|
|
Utility::copy(Containers::stridedArrayView(zero).broadcasted<0>(fieldsToCopy.size()), fieldsToCopyAdditionCount);
|
||
|
|
|
||
|
5 years ago
|
/* For objects with multiple fields move the extra fields to newly added
|
||
|
|
children */
|
||
|
|
{
|
||
|
|
std::size_t newParentIndex = 0;
|
||
|
4 years ago
|
for(const Trade::SceneField field: fieldsToConvert) {
|
||
|
5 years ago
|
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
|
||
|
|
if(!fieldId) continue;
|
||
|
|
|
||
|
5 years ago
|
for(UnsignedInt& fieldObject: out.mutableMapping<UnsignedInt>(*fieldId)) {
|
||
|
5 years ago
|
/* If the object is not new (could happen when an object
|
||
|
|
mapping array is shared among multiple fields, in which case
|
||
|
|
it *might* have been updated already to an ID larger than
|
||
|
|
the mapping array size) and it already has something
|
||
|
|
attached, then attach the field to a new object and make
|
||
|
|
that new object a child of the previous one. */
|
||
|
|
if(fieldObject < objectAttachmentCount.size() && objectAttachmentCount[fieldObject]) {
|
||
|
4 years ago
|
/* Go through all fields to copy and copy each entry that
|
||
|
|
was assigned to the original object */
|
||
|
|
for(std::size_t i = 0; i != fieldsToCopy.size(); ++i) {
|
||
|
|
const Containers::Optional<UnsignedInt> fieldToCopyId = scene.findFieldId(fieldsToCopy[i]);
|
||
|
|
if(!fieldToCopyId) continue;
|
||
|
|
|
||
|
|
/* View to copy the data from */
|
||
|
|
const Containers::StridedArrayView2D<const char> fieldToCopyDataSrc = scene.field(*fieldToCopyId);
|
||
|
|
|
||
|
|
/* Views to put the mapping to and copy the data to */
|
||
|
|
const std::size_t newFieldToCopyOffset = scene.fieldSize(*fieldToCopyId);
|
||
|
|
const Containers::StridedArrayView1D<UnsignedInt> newFieldToCopyMapping = out.mutableMapping<UnsignedInt>(*fieldToCopyId).suffix(newFieldToCopyOffset);
|
||
|
|
const Containers::StridedArrayView2D<char> newFieldToCopy = out.mutableField(*fieldToCopyId).suffix(newFieldToCopyOffset);
|
||
|
|
|
||
|
|
/* As long as there are entries attached to the
|
||
|
|
original objects, copy them */
|
||
|
|
std::size_t offset = 0;
|
||
|
|
while(Containers::Optional<std::size_t> found = scene.findFieldObjectOffset(*fieldToCopyId, fieldObject, offset)) {
|
||
|
|
/* Assgn a new field entry to the new object */
|
||
|
|
newFieldToCopyMapping[fieldsToCopyAdditionCount[i]] = newParentMapping[newParentIndex];
|
||
|
|
|
||
|
|
/* Copy the data from the old entry to it */
|
||
|
|
Utility::copy(fieldToCopyDataSrc[*found], newFieldToCopy[fieldsToCopyAdditionCount[i]]);
|
||
|
|
|
||
|
|
++fieldsToCopyAdditionCount[i];
|
||
|
|
offset = *found + 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
/* Use the old object as a parent of the new object */
|
||
|
|
newParents[newParentIndex] = fieldObject;
|
||
|
5 years ago
|
/* Assign the field to the new object */
|
||
|
5 years ago
|
fieldObject = newParentMapping[newParentIndex];
|
||
|
5 years ago
|
/* Move to the next reserved object */
|
||
|
|
++newParentIndex;
|
||
|
4 years ago
|
|
||
|
|
/** @todo mark this field as touched here and the restore
|
||
|
|
the original flags for all fields that didn't have
|
||
|
|
their object mapping touched */
|
||
|
|
|
||
|
5 years ago
|
} else ++objectAttachmentCount[fieldObject];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CORRADE_INTERNAL_ASSERT(newParentIndex == objectsToAdd);
|
||
|
|
}
|
||
|
|
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
}}}
|
||
|
|
|
||
|
|
#endif
|