diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index 91e4fd6bb..81cde6bb0 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -79,6 +79,7 @@ set(MagnumTrade_HEADERS set(MagnumTrade_PRIVATE_HEADERS Implementation/arrayUtilities.h + Implementation/checkSharedSceneFieldMapping.h Implementation/converterUtilities.h Implementation/materialAttributeProperties.hpp) diff --git a/src/Magnum/Trade/Implementation/checkSharedSceneFieldMapping.h b/src/Magnum/Trade/Implementation/checkSharedSceneFieldMapping.h new file mode 100644 index 000000000..3e199056d --- /dev/null +++ b/src/Magnum/Trade/Implementation/checkSharedSceneFieldMapping.h @@ -0,0 +1,119 @@ +#ifndef Magnum_Trade_Implementation_checkSharedSceneFieldMapping_h +#define Magnum_Trade_Implementation_checkSharedSceneFieldMapping_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + 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 + +#include "Magnum/Trade/SceneData.h" + +/* Used by SceneData internals */ + +namespace Magnum { namespace Trade { namespace Implementation { + +/* A prefix of the arrays get filled by findSharedSceneFields() in whatever + order the fields appear in SceneData. Position of the first ~UnsignedInt{} + element indicates how many were found, all elements after are ~UnsignedInt{} + as well. */ +struct SharedSceneFieldIds { + UnsignedInt trs[3]{ + ~UnsignedInt{}, + ~UnsignedInt{}, + ~UnsignedInt{} + }; + UnsignedInt meshMaterial[2]{ + ~UnsignedInt{}, + ~UnsignedInt{} + }; +}; + +inline SharedSceneFieldIds findSharedSceneFields(const Containers::ArrayView fields) { + std::size_t trsFieldOffset = 0; + std::size_t meshMaterialOffset = 0; + SharedSceneFieldIds out; + for(std::size_t i = 0; i != fields.size(); ++i) { + const SceneFieldData& field = fields[i]; + if(field.name() == SceneField::Translation) + out.trs[trsFieldOffset++] = i; + else if(field.name() == SceneField::Rotation) + out.trs[trsFieldOffset++] = i; + else if(field.name() == SceneField::Scaling) + out.trs[trsFieldOffset++] = i; + else if(field.name() == SceneField::Mesh) + out.meshMaterial[meshMaterialOffset++] = i; + else if(field.name() == SceneField::MeshMaterial) + out.meshMaterial[meshMaterialOffset++] = i; + } + + CORRADE_INTERNAL_ASSERT(trsFieldOffset <= Containers::arraySize(out.trs)); + CORRADE_INTERNAL_ASSERT(meshMaterialOffset <= Containers::arraySize(out.meshMaterial)); + + return out; +} + +#ifndef CORRADE_NO_ASSERT +inline bool checkFieldMappingDataMatch(const char* + #ifndef CORRADE_STANDARD_ASSERT + messagePrefix + #endif + , Containers::ArrayView fieldIds, Containers::ArrayView data, const Containers::ArrayView fields) +{ + /* Going through all fields and comparing to the first one, aborting if the + field ID is unset (as all other will be as well). If no fields are + present, the first is already unset, which means it'll break right at + the start, never accessing `fields[~UnsignedInt{}]`. */ + for(const UnsignedInt fieldId: fieldIds) { + if(fieldId == ~UnsignedInt{}) + break; + + const Trade::SceneFieldData& a = fields[fieldIds[0]]; + const Trade::SceneFieldData& b = fields[fieldId]; + const Containers::StridedArrayView1D aData = a.mappingData(data); + const Containers::StridedArrayView1D bData = b.mappingData(data); + CORRADE_ASSERT(aData.data() == bData.data() && aData.size() == bData.size() && aData.stride() == bData.stride(), + messagePrefix << b.name() << "mapping data {" << Debug::nospace << bData.data() << Debug::nospace << "," << bData.size() << Debug::nospace << "," << bData.stride() << Debug::nospace << "} is different from" << a.name() << "mapping data {" << Debug::nospace << aData.data() << Debug::nospace << "," << aData.size() << Debug::nospace << "," << aData.stride() << Debug::nospace << "}", false); + } + + return true; +} + +inline bool checkSharedSceneFieldMapping(const char* messagePrefix, const SharedSceneFieldIds& fieldIds, Containers::ArrayView data, const Containers::ArrayView fields) { + for(const Containers::ArrayView i: { + /* All present TRS fields should share the same object mapping */ + Containers::arrayView(fieldIds.trs), + /* Mesh and materials also */ + Containers::arrayView(fieldIds.meshMaterial) + }) { + if(!checkFieldMappingDataMatch(messagePrefix, i, data, fields)) + return false; + } + + return true; +} +#endif + +}}} + +#endif diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index d2f7f8ee1..6e1230b66 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -44,6 +44,7 @@ #include "Magnum/Math/DualQuaternion.h" #include "Magnum/Math/PackingBatch.h" #include "Magnum/Trade/Implementation/arrayUtilities.h" +#include "Magnum/Trade/Implementation/checkSharedSceneFieldMapping.h" #ifdef MAGNUM_BUILD_DEPRECATED #include @@ -798,8 +799,6 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp UnsignedInt rotationField = ~UnsignedInt{}; UnsignedInt scalingField = ~UnsignedInt{}; #ifndef CORRADE_NO_ASSERT - UnsignedInt meshField = ~UnsignedInt{}; - UnsignedInt meshMaterialField = ~UnsignedInt{}; UnsignedInt skinField = ~UnsignedInt{}; #endif for(std::size_t i = 0; i != _fields.size(); ++i) { @@ -923,11 +922,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp scalingField = i; } #ifndef CORRADE_NO_ASSERT - else if(field._name == SceneField::Mesh) { - meshField = i; - } else if(field._name == SceneField::MeshMaterial) { - meshMaterialField = i; - } else if(field._name == SceneField::Skin) { + else if(field._name == SceneField::Skin) { skinField = i; } #endif @@ -937,28 +932,7 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp /* Check that certain fields share the same object mapping, i.e. the same begin, size and stride. Cases where one of the two is offset-only and the other not are allowed if both have the same absolute begin pointer. */ - const auto checkFieldMappingDataMatch = [this](const SceneFieldData& a, const SceneFieldData& b) { - const void* const aBegin = a._flags & SceneFieldFlag::OffsetOnly ? - _data.begin() + a._mappingData.offset : a._mappingData.pointer; - const void* const bBegin = b._flags & SceneFieldFlag::OffsetOnly ? - _data.begin() + b._mappingData.offset : b._mappingData.pointer; - CORRADE_ASSERT(aBegin == bBegin && a._size == b._size && a._mappingStride == b._mappingStride, - "Trade::SceneData:" << b._name << "mapping data {" << Debug::nospace << bBegin << Debug::nospace << "," << b._size << Debug::nospace << "," << b._mappingStride << Debug::nospace << "} is different from" << a._name << "mapping data {" << Debug::nospace << aBegin << Debug::nospace << "," << a._size << Debug::nospace << "," << a._mappingStride << Debug::nospace << "}", ); - }; - - /* All present TRS fields should share the same object mapping */ - if(translationField != ~UnsignedInt{}) { - if(rotationField != ~UnsignedInt{}) - checkFieldMappingDataMatch(_fields[translationField], _fields[rotationField]); - if(scalingField != ~UnsignedInt{}) - checkFieldMappingDataMatch(_fields[translationField], _fields[scalingField]); - } - if(rotationField != ~UnsignedInt{} && scalingField != ~UnsignedInt{}) - checkFieldMappingDataMatch(_fields[rotationField], _fields[scalingField]); - - /* Mesh and materials also */ - if(meshField != ~UnsignedInt{} && meshMaterialField != ~UnsignedInt{}) - checkFieldMappingDataMatch(_fields[meshField], _fields[meshMaterialField]); + Implementation::checkSharedSceneFieldMapping("Trade::SceneData:", Implementation::findSharedSceneFields(_fields), _data, _fields); #endif /* Decide about dimensionality based on transformation type, if present */