diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index cc6a9d7e9..028e8fb23 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -539,6 +539,7 @@ Debug& operator<<(Debug& debug, const SceneFieldFlag value) { _c(OffsetOnly) _c(ImplicitMapping) _c(OrderedMapping) + _c(MultiEntry) _c(NullTerminatedString) #undef _c /* LCOV_EXCL_STOP */ @@ -553,6 +554,7 @@ Debug& operator<<(Debug& debug, const SceneFieldFlags value) { SceneFieldFlag::ImplicitMapping, /* This one is implied by ImplicitMapping, so has to be after */ SceneFieldFlag::OrderedMapping, + SceneFieldFlag::MultiEntry, SceneFieldFlag::NullTerminatedString }); } @@ -624,6 +626,8 @@ SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedA SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, const char* const stringData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const SceneFieldFlags flags) noexcept: _size{mappingData.size()}, _name{name}, + /** @todo check allowed flags once disallowedSceneFieldFlagsFor() includes + string fields too */ _flags{flags|Implementation::implicitSceneFieldFlagsFor(fieldType)}, _mappingTypeStringType{UnsignedByte(UnsignedByte(mappingType)|(UnsignedShort(fieldType) << 3))}, _mappingStride{Short(mappingData.stride())}, diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index e514d7f14..836f1762a 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -758,6 +758,23 @@ enum class SceneFieldFlag: UnsignedByte { */ ImplicitMapping = (1 << 2)|OrderedMapping, + /** + * The field may have multiple entries for the same object. Meant to be + * used as a hint to distinguish between fields that are expected to have + * at most one entry for an object and fields that can have multiple + * entries for an object but sometimes have just one. + * + * Note that presence of this flag isn't enforced in any way, fields + * without this flag may still have multiple entries for the same object. + * + * Can't be set for @ref SceneField::Parent, + * @relativeref{SceneField,Transformation}, + * @relativeref{SceneField,Translation}, @relativeref{SceneField,Rotation} + * or @relativeref{SceneField,Scaling}; however mapping uniqueness for + * those fields isn't enforced in any way either. + */ + MultiEntry = 1 << 4, + /** * The string field is null-terminated, i.e. string views returned from * @ref SceneData::fieldStrings() will always have @@ -4021,6 +4038,16 @@ namespace Implementation { type == SceneFieldType::StringRangeNullTerminated64 ? SceneFieldFlag::NullTerminatedString : SceneFieldFlags{}; } + + constexpr SceneFieldFlags disallowedSceneFieldFlagsFor(SceneField name) { + return + name == SceneField::Parent || + name == SceneField::Transformation || + name == SceneField::Translation || + name == SceneField::Rotation || + name == SceneField::Scaling ? + SceneFieldFlag::MultiEntry : SceneFieldFlags{}; + } } constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView1D& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: @@ -4028,8 +4055,8 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi "Trade::SceneFieldData: expected" << name << "mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldData.size()), mappingData.size())}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, - _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)), - "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)) << "for a view of" << fieldType), flags)}, + _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))), + "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))) << "for a" << name << "view of" << fieldType), flags)}, /* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType' set but not used" */ _mappingTypeStringType(UnsignedByte(mappingType)), @@ -4068,8 +4095,8 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi "Trade::SceneFieldData: expected" << name << "mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldSize), mappingData.size())}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, SceneFieldType::Bit), "Trade::SceneFieldData:" << SceneFieldType::Bit << "is not a valid type for" << name), name)}, - _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)), - "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)) << "for a view of" << SceneFieldType::Bit), flags)}, + _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))), + "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))) << "for a" << name << "view of" << SceneFieldType::Bit), flags)}, /* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType' set but not used" */ _mappingTypeStringType(UnsignedByte(mappingType)), @@ -4105,8 +4132,8 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_ _size{size}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, - _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::NullTerminatedString), - "Trade::SceneFieldData: can't pass" << (flags & SceneFieldFlag::NullTerminatedString) << "for" << fieldType), flags|SceneFieldFlag::OffsetOnly)}, + _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))), + "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))) << "for" << name << "of" << fieldType), flags|SceneFieldFlag::OffsetOnly)}, /* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType' set but not used" */ _mappingTypeStringType(UnsignedByte(mappingType)), @@ -4129,8 +4156,8 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_ "Trade::SceneFieldData: size expected to be smaller than 2^" << Debug::nospace << (sizeof(std::size_t)*8 - 3) << "bits, got" << size), size)}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, SceneFieldType::Bit), "Trade::SceneFieldData:" << SceneFieldType::Bit << "is not a valid type for" << name), name)}, - _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::NullTerminatedString), - "Trade::SceneFieldData: can't pass" << (flags & SceneFieldFlag::NullTerminatedString) << "for" << SceneFieldType::Bit), flags|SceneFieldFlag::OffsetOnly)}, + _flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))), + "Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::NullTerminatedString|Implementation::disallowedSceneFieldFlagsFor(name))) << "for" << name << "of" << SceneFieldType::Bit), flags|SceneFieldFlag::OffsetOnly)}, /* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType' set but not used" */ _mappingTypeStringType(UnsignedByte(mappingType)), @@ -4149,6 +4176,8 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_ _size{size}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), "Trade::SceneFieldData:" << fieldType << "is not a valid type for" << name), name)}, + /** @todo check allowed flags once disallowedSceneFieldFlagsFor() includes + string fields too */ _flags{flags|SceneFieldFlag::OffsetOnly|Implementation::implicitSceneFieldFlagsFor(fieldType)}, _mappingTypeStringType{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeString(fieldType), "Trade::SceneFieldData: can't use a string constructor for" << fieldType), UnsignedByte(UnsignedByte(mappingType)|(UnsignedShort(fieldType) << 3)))}, diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index a397598c9..d086cabdc 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -2097,16 +2097,21 @@ void SceneDataTest::constructFieldFlagNotAllowed() { /* These are fine */ SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), SceneFieldType::Quaternion, 0, sizeof(Quaternion), SceneFieldFlag::OffsetOnly}; - SceneFieldData{sceneFieldCustom(773), 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), 0, 0, sizeof(Quaternion), SceneFieldFlag::OffsetOnly}; - SceneFieldData{sceneFieldCustom(24), Containers::arrayView(mappingData), helloStringData, SceneFieldType::StringOffset32, helloFieldData, SceneFieldFlag::NullTerminatedString}; - SceneFieldData{sceneFieldCustom(24), 3, SceneMappingType::UnsignedShort, 0, 2, 0, SceneFieldType::StringOffset32, 0, 4, SceneFieldFlag::NullTerminatedString}; + SceneFieldData{SceneField::Mesh, Containers::arrayView(mappingData), Containers::arrayView(helloFieldData), SceneFieldFlag::MultiEntry}; + SceneFieldData{sceneFieldCustom(773), 3, SceneMappingType::UnsignedShort, 0, sizeof(UnsignedShort), 0, 0, sizeof(Quaternion), SceneFieldFlag::OffsetOnly|SceneFieldFlag::MultiEntry}; + SceneFieldData{sceneFieldCustom(773), Containers::arrayView(mappingData), Containers::BitArrayView{hiddenFieldData, 0, 3}, SceneFieldFlag::MultiEntry}; + SceneFieldData{sceneFieldCustom(24), Containers::arrayView(mappingData), helloStringData, SceneFieldType::StringOffset32, helloFieldData, SceneFieldFlag::NullTerminatedString|SceneFieldFlag::MultiEntry}; + SceneFieldData{sceneFieldCustom(24), 3, SceneMappingType::UnsignedShort, 0, 2, 0, SceneFieldType::StringOffset32, 0, 4, SceneFieldFlag::NullTerminatedString|SceneFieldFlag::MultiEntry}; std::ostringstream out; Error redirectError{&out}; SceneFieldData{SceneField::Rotation, Containers::arrayView(mappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OffsetOnly}; + SceneFieldData{SceneField::Rotation, Containers::arrayView(mappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::MultiEntry}; SceneFieldData{SceneField::Rotation, Containers::arrayView(mappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::NullTerminatedString}; - SceneFieldData{SceneField::Rotation, Containers::arrayView(mappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString}; + SceneFieldData{SceneField::Rotation, Containers::arrayView(mappingData), Containers::arrayView(rotationFieldData), SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString|SceneFieldFlag::MultiEntry}; + SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, 2, SceneFieldType::Quaternion, 0, 16, SceneFieldFlag::MultiEntry}; SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, 2, SceneFieldType::Quaternion, 0, 16, SceneFieldFlag::NullTerminatedString}; + SceneFieldData{SceneField::Rotation, 3, SceneMappingType::UnsignedShort, 0, 2, SceneFieldType::Quaternion, 0, 16, SceneFieldFlag::MultiEntry|SceneFieldFlag::NullTerminatedString}; SceneFieldData{sceneFieldCustom(773), Containers::arrayView(mappingData), Containers::BitArrayView{hiddenFieldData, 0, 3}, SceneFieldFlag::OffsetOnly}; SceneFieldData{sceneFieldCustom(773), Containers::arrayView(mappingData), Containers::BitArrayView{hiddenFieldData, 0, 3}, SceneFieldFlag::NullTerminatedString}; @@ -2114,18 +2119,22 @@ void SceneDataTest::constructFieldFlagNotAllowed() { SceneFieldData{sceneFieldCustom(773), 3, SceneMappingType::UnsignedShort, 0, 2, 0, 0, 16, SceneFieldFlag::NullTerminatedString}; SceneFieldData{sceneFieldCustom(24), Containers::arrayView(mappingData), helloStringData, SceneFieldType::StringOffset32, helloFieldData, SceneFieldFlag::OffsetOnly}; - CORRADE_COMPARE(out.str(), - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view of Trade::SceneFieldType::Quaternion\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for a view of Trade::SceneFieldType::Quaternion\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag::NullTerminatedString for a view of Trade::SceneFieldType::Quaternion\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for Trade::SceneFieldType::Quaternion\n" - - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view of Trade::SceneFieldType::Bit\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for a view of Trade::SceneFieldType::Bit\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag::NullTerminatedString for a view of Trade::SceneFieldType::Bit\n" - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for Trade::SceneFieldType::Bit\n" - - "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view\n"); + CORRADE_COMPARE_AS(out.str(), + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a Trade::SceneField::Rotation view of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::MultiEntry for a Trade::SceneField::Rotation view of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for a Trade::SceneField::Rotation view of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag::MultiEntry|Trade::SceneFieldFlag::NullTerminatedString for a Trade::SceneField::Rotation view of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::MultiEntry for Trade::SceneField::Rotation of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for Trade::SceneField::Rotation of Trade::SceneFieldType::Quaternion\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::MultiEntry|Trade::SceneFieldFlag::NullTerminatedString for Trade::SceneField::Rotation of Trade::SceneFieldType::Quaternion\n" + + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a Trade::SceneField::Custom(773) view of Trade::SceneFieldType::Bit\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for a Trade::SceneField::Custom(773) view of Trade::SceneFieldType::Bit\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag::NullTerminatedString for a Trade::SceneField::Custom(773) view of Trade::SceneFieldType::Bit\n" + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::NullTerminatedString for Trade::SceneField::Custom(773) of Trade::SceneFieldType::Bit\n" + + "Trade::SceneFieldData: can't pass Trade::SceneFieldFlag::OffsetOnly for a view\n", + TestSuite::Compare::String); } void SceneDataTest::constructFieldWrongOffsetOnlyDataAccess() {