/**************************************************************************** ** ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Qt3DAnimation::Animation; Q_DECLARE_METATYPE(Qt3DAnimation::Animation::Handler*) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(Clock *) Q_DECLARE_METATYPE(ChannelMapper *) Q_DECLARE_METATYPE(AnimationClip *) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(Channel) Q_DECLARE_METATYPE(AnimatorEvaluationData) Q_DECLARE_METATYPE(ClipEvaluationData) Q_DECLARE_METATYPE(ClipAnimator *) Q_DECLARE_METATYPE(BlendedClipAnimator *) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(ClipFormat) Q_DECLARE_METATYPE(ChannelNameAndType) namespace { class MeanBlendNode : public ClipBlendNode { public: MeanBlendNode() : ClipBlendNode(ClipBlendNode::LerpBlendType) {} void setValueNodeIds(Qt3DCore::QNodeId value1Id, Qt3DCore::QNodeId value2Id) { m_value1Id = value1Id; m_value2Id = value2Id; } inline QVector allDependencyIds() const override { return currentDependencyIds(); } QVector currentDependencyIds() const final { return QVector() << m_value1Id << m_value2Id; } using ClipBlendNode::setClipResults; double duration() const final { return 0.0f; } protected: ClipResults doBlend(const QVector &blendData) const final { Q_ASSERT(blendData.size() == 2); const int elementCount = blendData.first().size(); ClipResults blendResults(elementCount); for (int i = 0; i < elementCount; ++i) blendResults[i] = 0.5f * (blendData[0][i] + blendData[1][i]); return blendResults; } private: Qt3DCore::QNodeId m_value1Id; Qt3DCore::QNodeId m_value2Id; }; bool fuzzyCompare(float x1, float x2) { if (qFuzzyIsNull(x1) && qFuzzyIsNull(x2)) { return true; } else if ((qFuzzyIsNull(x1) && !qFuzzyIsNull(x2)) || (!qFuzzyIsNull(x1) && qFuzzyIsNull(x2))) { return false; } else { return qFuzzyCompare(x1, x2); } } class DummyCallback : public Qt3DAnimation::QAnimationCallback { public: void valueChanged(const QVariant &) override { } }; } // anonymous class tst_AnimationUtils : public Qt3DCore::QBackendNodeTester { Q_OBJECT public: ChannelMapping *createChannelMapping(Handler *handler, const QString &channelName, const Qt3DCore::QNodeId targetId, const char *propertyName, int type, int componentCount) { auto channelMappingId = Qt3DCore::QNodeId::createId(); ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(channelMappingId); setPeerId(channelMapping, channelMappingId); channelMapping->setHandler(handler); channelMapping->setTargetId(targetId); channelMapping->setPropertyName(propertyName); channelMapping->setChannelName(channelName); channelMapping->setType(type); channelMapping->setMappingType(ChannelMapping::ChannelMappingType); channelMapping->setComponentCount(componentCount); return channelMapping; } ChannelMapping *createChannelMapping(Handler *handler, const Qt3DCore::QNodeId skeletonId) { auto channelMappingId = Qt3DCore::QNodeId::createId(); ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(channelMappingId); setPeerId(channelMapping, channelMappingId); channelMapping->setHandler(handler); channelMapping->setSkeletonId(skeletonId); channelMapping->setMappingType(ChannelMapping::SkeletonMappingType); return channelMapping; } ChannelMapper *createChannelMapper(Handler *handler, const QVector &mappingIds) { auto channelMapperId = Qt3DCore::QNodeId::createId(); ChannelMapper *channelMapper = handler->channelMapperManager()->getOrCreateResource(channelMapperId); setPeerId(channelMapper, channelMapperId); channelMapper->setMappingIds(mappingIds); return channelMapper; } AnimationClip *createAnimationClipLoader(Handler *handler, const QUrl &source) { auto clipId = Qt3DCore::QNodeId::createId(); AnimationClip *clip = handler->animationClipLoaderManager()->getOrCreateResource(clipId); setPeerId(clip, clipId); clip->setDataType(AnimationClip::File); clip->setSource(source); clip->loadAnimation(); return clip; } ClipAnimator *createClipAnimator(Handler *handler, qint64 globalStartTimeNS, int loops) { auto animatorId = Qt3DCore::QNodeId::createId(); ClipAnimator *animator = handler->clipAnimatorManager()->getOrCreateResource(animatorId); setPeerId(animator, animatorId); animator->setStartTime(globalStartTimeNS); animator->setLoops(loops); return animator; } BlendedClipAnimator *createBlendedClipAnimator(Handler *handler, qint64 globalStartTimeNS, int loops) { auto animatorId = Qt3DCore::QNodeId::createId(); BlendedClipAnimator *animator = handler->blendedClipAnimatorManager()->getOrCreateResource(animatorId); setPeerId(animator, animatorId); animator->setStartTime(globalStartTimeNS); animator->setLoops(loops); return animator; } LerpClipBlend *createLerpClipBlend(Handler *handler) { auto lerpId = Qt3DCore::QNodeId::createId(); LerpClipBlend *lerp = new LerpClipBlend(); setPeerId(lerp, lerpId); lerp->setClipBlendNodeManager(handler->clipBlendNodeManager()); lerp->setHandler(handler); handler->clipBlendNodeManager()->appendNode(lerpId, lerp); return lerp; } AdditiveClipBlend *createAdditiveClipBlend(Handler *handler) { auto additiveId = Qt3DCore::QNodeId::createId(); AdditiveClipBlend *additive = new AdditiveClipBlend(); setPeerId(additive, additiveId); additive->setClipBlendNodeManager(handler->clipBlendNodeManager()); additive->setHandler(handler); handler->clipBlendNodeManager()->appendNode(additiveId, additive); return additive; } ClipBlendValue *createClipBlendValue(Handler *handler) { auto valueId = Qt3DCore::QNodeId::createId(); ClipBlendValue *value = new ClipBlendValue(); setPeerId(value, valueId); value->setClipBlendNodeManager(handler->clipBlendNodeManager()); value->setHandler(handler); handler->clipBlendNodeManager()->appendNode(valueId, value); return value; } MeanBlendNode *createMeanBlendNode(Handler *handler) { auto id = Qt3DCore::QNodeId::createId(); MeanBlendNode *node = new MeanBlendNode(); setPeerId(node, id); node->setClipBlendNodeManager(handler->clipBlendNodeManager()); node->setHandler(handler); handler->clipBlendNodeManager()->appendNode(id, node); return node; } Skeleton *createSkeleton(Handler *handler, int jointCount) { auto skeletonId = Qt3DCore::QNodeId::createId(); Skeleton *skeleton = handler->skeletonManager()->getOrCreateResource(skeletonId); setPeerId(skeleton, skeletonId); skeleton->setJointCount(jointCount); return skeleton; } private Q_SLOTS: void checkBuildPropertyMappings_data() { QTest::addColumn("handler"); QTest::addColumn>("channelMappings"); QTest::addColumn>("channelNamesAndTypes"); QTest::addColumn>("channelComponentIndices"); QTest::addColumn>("sourceClipMask"); QTest::addColumn>("expectedResults"); // Single ChannelMapping { Handler *handler = new Handler(); auto channelMapping = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); QVector channelMappings = { channelMapping }; // Create a few channels in the format description ChannelNameAndType rotation = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, channelMapping->peerId() }; ChannelNameAndType location = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; ChannelNameAndType baseColor = { QLatin1String("BaseColor"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; ChannelNameAndType metalness = { QLatin1String("Metalness"), static_cast(QVariant::Double), 1, channelMapping->peerId() }; ChannelNameAndType roughness = { QLatin1String("Roughness"), static_cast(QVariant::Double), 1, channelMapping->peerId() }; ChannelNameAndType morphTargetWeightsList = { QLatin1String("MorphTargetWeightsList"), static_cast(QVariant::List), 5, channelMapping->peerId() }; ChannelNameAndType morphTargetWeightsVec = { QLatin1String("MorphTargetWeightsVec"), qMetaTypeId>(), 6, channelMapping->peerId() }; QVector channelNamesAndTypes = { rotation, location, baseColor, metalness, roughness, morphTargetWeightsList, morphTargetWeightsVec }; // And the matching indices ComponentIndices rotationIndices = { 0, 1, 2, 3 }; ComponentIndices locationIndices = { 4, 5, 6 }; ComponentIndices baseColorIndices = { 7, 8, 9 }; ComponentIndices metalnessIndices = { 10 }; ComponentIndices roughnessIndices = { 11 }; ComponentIndices morphTargetListIndices = { 12, 13, 14, 15, 16 }; ComponentIndices morphTargetVecIndices = { 17, 18, 19, 20, 21, 22 }; QVector channelComponentIndices = { rotationIndices, locationIndices, baseColorIndices, metalnessIndices, roughnessIndices, morphTargetListIndices, morphTargetVecIndices }; QVector sourceClipMask = { QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(1, true), QBitArray(1, true), QBitArray(5, true), QBitArray(6, true) }; MappingData expectedMapping; expectedMapping.targetId = channelMapping->targetId(); expectedMapping.propertyName = channelMapping->propertyName(); expectedMapping.type = channelMapping->type(); expectedMapping.channelIndices = locationIndices; QVector expectedResults = { expectedMapping }; QTest::newRow("single mapping") << handler << channelMappings << channelNamesAndTypes << channelComponentIndices << sourceClipMask << expectedResults; } // Multiple ChannelMappings { Handler *handler = new Handler(); auto locationMapping = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); auto metalnessMapping = createChannelMapping(handler, QLatin1String("Metalness"), Qt3DCore::QNodeId::createId(), "metalness", static_cast(QVariant::Double), 1); auto baseColorMapping = createChannelMapping(handler, QLatin1String("BaseColor"), Qt3DCore::QNodeId::createId(), "baseColor", static_cast(QVariant::Vector3D), 3); auto roughnessMapping = createChannelMapping(handler, QLatin1String("Roughness"), Qt3DCore::QNodeId::createId(), "roughness", static_cast(QVariant::Double), 1); auto rotationMapping = createChannelMapping(handler, QLatin1String("Rotation"), Qt3DCore::QNodeId::createId(), "rotation", static_cast(QVariant::Quaternion), 4); auto morphTargetMapping = createChannelMapping(handler, QLatin1String("MorphTargetWeights"), Qt3DCore::QNodeId::createId(), "weights", static_cast(QVariant::List), 5); QVector channelMappings = { locationMapping, metalnessMapping, baseColorMapping, roughnessMapping, rotationMapping, morphTargetMapping }; // Create a few channels in the format description ChannelNameAndType rotation = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, rotationMapping->peerId() }; ChannelNameAndType location = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, locationMapping->peerId() }; ChannelNameAndType baseColor = { QLatin1String("BaseColor"), static_cast(QVariant::Vector3D), 3, baseColorMapping->peerId() }; ChannelNameAndType metalness = { QLatin1String("Metalness"), static_cast(QVariant::Double), 1, metalnessMapping->peerId() }; ChannelNameAndType roughness = { QLatin1String("Roughness"), static_cast(QVariant::Double), 1, roughnessMapping->peerId() }; ChannelNameAndType morphTarget = { QLatin1String("MorphTargetWeights"), static_cast(QVariant::List), 5, morphTargetMapping->peerId() }; QVector channelNamesAndTypes = { rotation, location, baseColor, metalness, roughness, morphTarget }; // And the matching indices ComponentIndices rotationIndices = { 0, 1, 2, 3 }; ComponentIndices locationIndices = { 4, 5, 6 }; ComponentIndices baseColorIndices = { 7, 8, 9 }; ComponentIndices metalnessIndices = { 10 }; ComponentIndices roughnessIndices = { 11 }; ComponentIndices morphTargetIndices = { 12, 13, 14, 15, 16 }; QVector channelComponentIndices = { rotationIndices, locationIndices, baseColorIndices, metalnessIndices, roughnessIndices, morphTargetIndices }; QVector sourceClipMask = { QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(1, true), QBitArray(1, true), QBitArray(5, true) }; MappingData expectedLocationMapping; expectedLocationMapping.targetId = locationMapping->targetId(); expectedLocationMapping.propertyName = locationMapping->propertyName(); expectedLocationMapping.type = locationMapping->type(); expectedLocationMapping.channelIndices = locationIndices; MappingData expectedMetalnessMapping; expectedMetalnessMapping.targetId = metalnessMapping->targetId(); expectedMetalnessMapping.propertyName = metalnessMapping->propertyName(); expectedMetalnessMapping.type = metalnessMapping->type(); expectedMetalnessMapping.channelIndices = metalnessIndices; MappingData expectedBaseColorMapping; expectedBaseColorMapping.targetId = baseColorMapping->targetId(); expectedBaseColorMapping.propertyName = baseColorMapping->propertyName(); expectedBaseColorMapping.type = baseColorMapping->type(); expectedBaseColorMapping.channelIndices = baseColorIndices; MappingData expectedRoughnessMapping; expectedRoughnessMapping.targetId = roughnessMapping->targetId(); expectedRoughnessMapping.propertyName = roughnessMapping->propertyName(); expectedRoughnessMapping.type = roughnessMapping->type(); expectedRoughnessMapping.channelIndices = roughnessIndices; MappingData expectedRotationMapping; expectedRotationMapping.targetId = rotationMapping->targetId(); expectedRotationMapping.propertyName = rotationMapping->propertyName(); expectedRotationMapping.type = rotationMapping->type(); expectedRotationMapping.channelIndices = rotationIndices; MappingData expectedMorphTargetMapping; expectedMorphTargetMapping.targetId = morphTargetMapping->targetId(); expectedMorphTargetMapping.propertyName = morphTargetMapping->propertyName(); expectedMorphTargetMapping.type = morphTargetMapping->type(); expectedMorphTargetMapping.channelIndices = morphTargetIndices; QVector expectedResults = { expectedLocationMapping, expectedMetalnessMapping, expectedBaseColorMapping, expectedRoughnessMapping, expectedRotationMapping, expectedMorphTargetMapping }; QTest::newRow("multiple mappings") << handler << channelMappings << channelNamesAndTypes << channelComponentIndices << sourceClipMask << expectedResults; } // Single skeleton mapping { Handler *handler = new Handler(); const int jointCount = 4; auto skeleton = createSkeleton(handler, jointCount); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); QVector channelMappings = { channelMapping }; // Create a few channels in the format description QVector channelNamesAndTypes; for (int i = 0; i < jointCount; ++i) { ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; locationDescription.jointIndex = i; locationDescription.jointTransformComponent = Translation; channelNamesAndTypes.push_back(locationDescription); ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, channelMapping->peerId() }; rotationDescription.jointIndex = i; rotationDescription.jointTransformComponent = Rotation; channelNamesAndTypes.push_back(rotationDescription); ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; scaleDescription.jointIndex = i; scaleDescription.jointTransformComponent = Scale; channelNamesAndTypes.push_back(scaleDescription); } // And the matching indices QVector channelComponentIndices; channelComponentIndices.push_back({ 0, 1, 2 }); channelComponentIndices.push_back({ 3, 4, 5, 6 }); channelComponentIndices.push_back({ 7, 8, 9 }); channelComponentIndices.push_back({ 10, 11, 12 }); channelComponentIndices.push_back({ 13, 14, 15, 16 }); channelComponentIndices.push_back({ 17, 18, 19 }); channelComponentIndices.push_back({ 20, 21, 22 }); channelComponentIndices.push_back({ 23, 24, 25, 26 }); channelComponentIndices.push_back({ 27, 28, 29 }); channelComponentIndices.push_back({ 30, 31, 32 }); channelComponentIndices.push_back({ 33, 34, 35, 36 }); channelComponentIndices.push_back({ 37, 38, 39 }); QVector sourceClipMask = { QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true) }; QVector expectedResults; int componentIndicesIndex = 0; for (int i = 0; i < jointCount; ++i) { MappingData locationMapping; locationMapping.targetId = channelMapping->skeletonId(); locationMapping.propertyName = "translation"; locationMapping.type = static_cast(QVariant::Vector3D); locationMapping.channelIndices = channelComponentIndices[componentIndicesIndex++]; locationMapping.jointIndex = i; MappingData rotationMapping; rotationMapping.targetId = channelMapping->skeletonId(); rotationMapping.propertyName = "rotation"; rotationMapping.type = static_cast(QVariant::Quaternion); rotationMapping.channelIndices = channelComponentIndices[componentIndicesIndex++]; rotationMapping.jointIndex = i; MappingData scaleMapping; scaleMapping.targetId = channelMapping->skeletonId(); scaleMapping.propertyName = "scale"; scaleMapping.type = static_cast(QVariant::Vector3D); scaleMapping.channelIndices = channelComponentIndices[componentIndicesIndex++]; scaleMapping.jointIndex = i; expectedResults << locationMapping << rotationMapping << scaleMapping; } QTest::newRow("single skeleton mapping") << handler << channelMappings << channelNamesAndTypes << channelComponentIndices << sourceClipMask << expectedResults; } } void checkBuildPropertyMappings() { // GIVEN QFETCH(Handler *, handler); QFETCH(QVector, channelMappings); QFETCH(QVector, channelNamesAndTypes); QFETCH(QVector, channelComponentIndices); QFETCH(QVector, sourceClipMask); QFETCH(QVector, expectedResults); // WHEN const QVector actualResults = buildPropertyMappings(channelMappings, channelNamesAndTypes, channelComponentIndices, sourceClipMask); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { const auto actualMapping = actualResults[i]; const auto expectedMapping = expectedResults[i]; QCOMPARE(actualMapping.targetId, expectedMapping.targetId); QCOMPARE(actualMapping.jointIndex, expectedMapping.jointIndex); QCOMPARE(actualMapping.propertyName, expectedMapping.propertyName); QCOMPARE(actualMapping.type, expectedMapping.type); QCOMPARE(actualMapping.channelIndices.size(), expectedMapping.channelIndices.size()); for (int j = 0; j < actualMapping.channelIndices.size(); ++j) { QCOMPARE(actualMapping.channelIndices[j], expectedMapping.channelIndices[j]); } } // Cleanup delete handler; } void checkLocalTimeFromElapsedTime_data() { QTest::addColumn("elapsedTime"); QTest::addColumn("currentTime"); QTest::addColumn("playbackRate"); QTest::addColumn("duration"); QTest::addColumn("loopCount"); QTest::addColumn("currentLoop"); QTest::addColumn("expectedLocalTime"); QTest::addColumn("expectedCurrentLoop"); double elapsedTime; double currentTime; double playbackRate; double duration; int loopCount; int currentLoop; double expectedLocalTime; int expectedCurrentLoop; elapsedTime = 0.0; currentTime = 0.0; playbackRate = 1.0; duration = 1.0; loopCount = 1; currentLoop = 0; expectedLocalTime = 0.0; expectedCurrentLoop = 0; QTest::newRow("simple, t_current = 0, t_elapsed = 0, loop_current = 0") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 0.5; currentTime = 0.0; playbackRate = 1.0; duration = 1.0; loopCount = 1; currentLoop = 0; expectedLocalTime = 0.5; expectedCurrentLoop = 0; QTest::newRow("simple, t_current = 0, t_elapsed = 0.5, loop_current = 0") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 1.5; currentTime = 0.0; playbackRate = 1.0; duration = 1.0; loopCount = 1; currentLoop = 0; expectedLocalTime = 1.0; expectedCurrentLoop = 0; QTest::newRow("simple, t_current = 0, t_elapsed = 1.5, loop_current = 0") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 0.5; currentTime = 0.6; playbackRate = 1.0; duration = 1.0; loopCount = 1; currentLoop = 0; expectedLocalTime = 1.0; expectedCurrentLoop = 0; QTest::newRow("simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 0") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 0.5; currentTime = 0.6; playbackRate = 1.0; duration = 1.0; loopCount = 2; currentLoop = 0; expectedLocalTime = 0.1; expectedCurrentLoop = 1; QTest::newRow("simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 0, loop_count = 2") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 0.5; currentTime = 0.6; playbackRate = 1.0; duration = 1.0; loopCount = 2; currentLoop = 1; expectedLocalTime = 1.0; expectedCurrentLoop = 1; // We clamp at end of final loop QTest::newRow("simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 1, loop_count = 2") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; elapsedTime = 0.5; currentTime = 0.6; playbackRate = 0.1; duration = 1.0; loopCount = 2; currentLoop = 1; expectedLocalTime = 0.65; expectedCurrentLoop = 1; QTest::newRow("simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 1, loop_count = 2") << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop << expectedLocalTime << expectedCurrentLoop; } void checkLocalTimeFromElapsedTime() { // GIVEN QFETCH(double, elapsedTime); QFETCH(double, currentTime); QFETCH(double, playbackRate); QFETCH(double, duration); QFETCH(int, loopCount); QFETCH(int, currentLoop); QFETCH(double, expectedLocalTime); QFETCH(int, expectedCurrentLoop); // WHEN int actualCurrentLoop = currentLoop; double actualLocalTime = localTimeFromElapsedTime(currentTime, elapsedTime, playbackRate, duration, loopCount, actualCurrentLoop); // THEN QCOMPARE(actualCurrentLoop, expectedCurrentLoop); QCOMPARE(actualLocalTime, expectedLocalTime); } void checkPreparePropertyChanges_data() { QTest::addColumn("animatorId"); QTest::addColumn>("mappingData"); QTest::addColumn>("channelResults"); QTest::addColumn("expectedChanges"); Qt3DCore::QNodeId animatorId; QVector mappingData; QVector channelResults; AnimationRecord expectedChanges; // Single property, vec3 { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "translation"; mapping.type = static_cast(QVariant::Vector3D); mapping.channelIndices = QVector() << 0 << 1 << 2; mappingData.push_back(mapping); channelResults = QVector() << 1.0f << 2.0f << 3.0f; expectedChanges.normalizedTime = 1.1f; // Invalid expectedChanges.finalFrame = false; expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QVector3D(1.0f, 2.0f, 3.0f))}); QTest::newRow("vec3 translation, final = false") << animatorId << mappingData << channelResults << expectedChanges; expectedChanges.normalizedTime = 1.0f; expectedChanges.finalFrame = true; QTest::newRow("vec3 translation, final = true, normalizedTime = 1.0f") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Multiple properties, all vec3 { animatorId = Qt3DCore::QNodeId::createId(); MappingData translationMapping; translationMapping.targetId = Qt3DCore::QNodeId::createId(); translationMapping.propertyName = "translation"; translationMapping.type = static_cast(QVariant::Vector3D); translationMapping.channelIndices = QVector() << 0 << 1 << 2; mappingData.push_back(translationMapping); MappingData scaleMapping; scaleMapping.targetId = Qt3DCore::QNodeId::createId(); scaleMapping.propertyName = "scale"; scaleMapping.type = static_cast(QVariant::Vector3D); scaleMapping.channelIndices = QVector() << 3 << 4 << 5; mappingData.push_back(scaleMapping); channelResults = QVector() << 1.0f << 2.0f << 3.0f << 4.0f << 5.0f << 6.0f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = -0.1f; // Invalid expectedChanges.targetChanges.push_back({translationMapping.targetId, translationMapping.propertyName, QVariant::fromValue(QVector3D(1.0f, 2.0f, 3.0f))}); expectedChanges.targetChanges.push_back({scaleMapping.targetId, scaleMapping.propertyName, QVariant::fromValue(QVector3D(4.0f, 5.0f, 6.0f))}); QTest::newRow("vec3 translation, vec3 scale, final = false") << animatorId << mappingData << channelResults << expectedChanges; expectedChanges.normalizedTime = 0.5f; expectedChanges.finalFrame = true; QTest::newRow("vec3 translation, vec3 scale, final = true") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, double { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "mass"; mapping.type = static_cast(QVariant::Double); mapping.channelIndices = QVector() << 0; mappingData.push_back(mapping); channelResults = QVector() << 3.5f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = -1.0f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(3.5f)}); QTest::newRow("double mass") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, vec2 { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "pos"; mapping.type = static_cast(QVariant::Vector2D); mapping.channelIndices = QVector() << 0 << 1; mappingData.push_back(mapping); channelResults = QVector() << 2.0f << 1.0f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = 1.1f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QVector2D(2.0f, 1.0f))}); QTest::newRow("vec2 pos") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, vec4 { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "foo"; mapping.type = static_cast(QVariant::Vector4D); mapping.channelIndices = QVector() << 0 << 1 << 2 << 3; mappingData.push_back(mapping); channelResults = QVector() << 4.0f << 3.0f << 2.0f << 1.0f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = 1.1f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QVector4D(4.0f, 3.0f, 2.0f, 1.0f))}); QTest::newRow("vec4 foo") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, quaternion { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "rotation"; mapping.type = static_cast(QVariant::Quaternion); mapping.channelIndices = QVector() << 0 << 1 << 2 << 3; mappingData.push_back(mapping); channelResults = QVector() << 1.0f << 0.0f << 0.0f << 1.0f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = -0.1f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QQuaternion(1.0f, 0.0f, 0.0f, 1.0f).normalized())}); QTest::newRow("quaternion rotation") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, QColor { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "color"; mapping.type = static_cast(QVariant::Color); mapping.channelIndices = QVector() << 0 << 1 << 2; mappingData.push_back(mapping); channelResults = QVector() << 0.5f << 0.4f << 0.3f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = 1.1f; // Invalid expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(QColor::fromRgbF(0.5f, 0.4f, 0.3f))}); QTest::newRow("QColor color") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } // Single property, QVariantList { animatorId = Qt3DCore::QNodeId::createId(); MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "weights"; mapping.type = static_cast(QVariant::List); mapping.channelIndices = QVector() << 0 << 1 << 2 << 3 << 4 << 5 << 6; mappingData.push_back(mapping); channelResults = QVector() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f; expectedChanges.finalFrame = false; expectedChanges.normalizedTime = 1.1f; // Invalid QVariantList expectedValue = QVariantList() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f; expectedChanges.targetChanges.push_back({mapping.targetId, mapping.propertyName, QVariant::fromValue(expectedValue)}); QTest::newRow("QVariantList weights") << animatorId << mappingData << channelResults << expectedChanges; mappingData.clear(); channelResults.clear(); expectedChanges.targetChanges.clear(); } } void checkPreparePropertyChanges() { // GIVEN QFETCH(Qt3DCore::QNodeId, animatorId); QFETCH(QVector, mappingData); QFETCH(QVector, channelResults); QFETCH(AnimationRecord, expectedChanges); // WHEN AnimationRecord actualChanges = prepareAnimationRecord(animatorId, mappingData, channelResults, expectedChanges.finalFrame, expectedChanges.normalizedTime); // THEN QCOMPARE(actualChanges.targetChanges.size(), expectedChanges.targetChanges.size()); for (int i = 0; i < actualChanges.targetChanges.size(); ++i) { const auto &expectedChange = expectedChanges.targetChanges[i]; const auto &actualChange = actualChanges.targetChanges[i]; QCOMPARE(actualChange.targetId, expectedChange.targetId); QCOMPARE(actualChange.propertyName, expectedChange.propertyName); QCOMPARE(actualChange.value, expectedChange.value); } } void checkPrepareCallbacks_data() { QTest::addColumn>("mappingData"); QTest::addColumn>("channelResults"); QTest::addColumn >("expectedValues"); QVector mappingData; QVector channelResults; QVector expectedValues; // vec3 { DummyCallback callback; // safe since the object is never used, just the address MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "translation"; mapping.type = static_cast(QVariant::Vector3D); mapping.channelIndices = QVector() << 0 << 1 << 2; mapping.callback = &callback; mapping.callbackFlags = {}; mappingData.push_back(mapping); channelResults = QVector() << 1.0f << 2.0f << 3.0f; AnimationCallbackAndValue cbv; cbv.callback = mapping.callback; cbv.flags = mapping.callbackFlags; cbv.value = QVariant::fromValue(QVector3D(1.0f, 2.0f, 3.0f)); expectedValues.push_back(cbv); QTest::newRow("vec3 translation, no flags") << mappingData << channelResults << expectedValues; mappingData.clear(); channelResults.clear(); expectedValues.clear(); } // double { DummyCallback callback; MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "something"; mapping.type = static_cast(QVariant::Double); mapping.channelIndices = QVector() << 0; mapping.callback = &callback; mapping.callbackFlags = {}; mappingData.push_back(mapping); channelResults = QVector() << 1.0f; AnimationCallbackAndValue cbv; cbv.callback = mapping.callback; cbv.flags = mapping.callbackFlags; cbv.value = QVariant(double(1.0)); expectedValues.push_back(cbv); QTest::newRow("double, no flags") << mappingData << channelResults << expectedValues; mappingData.clear(); channelResults.clear(); expectedValues.clear(); } // float, set a flag { DummyCallback callback; MappingData mapping; mapping.targetId = Qt3DCore::QNodeId::createId(); mapping.propertyName = "opacity"; mapping.type = static_cast(QMetaType::Float); mapping.channelIndices = QVector() << 0; mapping.callback = &callback; mapping.callbackFlags = Qt3DAnimation::QAnimationCallback::OnThreadPool; mappingData.push_back(mapping); channelResults = QVector() << 0.5f; AnimationCallbackAndValue cbv; cbv.callback = mapping.callback; cbv.flags = mapping.callbackFlags; cbv.value = QVariant(float(0.5f)); expectedValues.push_back(cbv); QTest::newRow("float, OnThreadPool") << mappingData << channelResults << expectedValues; mappingData.clear(); channelResults.clear(); expectedValues.clear(); } } void checkPrepareCallbacks() { // GIVEN QFETCH(QVector, mappingData); QFETCH(QVector, channelResults); QFETCH(QVector, expectedValues); // WHEN QVector callbacks = prepareCallbacks(mappingData, channelResults); // THEN QCOMPARE(callbacks.size(), expectedValues.size()); for (int i = 0; i < callbacks.size(); ++i) { auto expected = expectedValues[i]; auto actual = callbacks[i]; QCOMPARE(actual.callback, expected.callback); QCOMPARE(actual.flags, expected.flags); QCOMPARE(actual.value, expected.value); } } void checkEvaluateClipAtLocalTime_data() { QTest::addColumn("handler"); QTest::addColumn("clip"); QTest::addColumn("localTime"); QTest::addColumn("expectedResults"); Handler *handler; AnimationClip *clip; float localTime; ClipResults expectedResults; { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); localTime = 0.0f; expectedResults = QVector() << 0.0f << 0.0f << 0.0f; QTest::newRow("clip1.json, t = 0.0") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); localTime = clip->duration(); expectedResults = QVector() << 5.0f << 0.0f << 0.0f; QTest::newRow("clip1.json, t = duration") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); localTime = clip->duration() / 2.0f; expectedResults = QVector() << 2.5f << 0.0f << 0.0f; QTest::newRow("clip1.json, t = duration/2") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); localTime = 0.0f; expectedResults = QVector() << 0.0f << 0.0f << 0.0f // Translation << 1.0f << 0.0f << 0.0f << 0.0f; // Rotation QTest::newRow("clip2.json, t = 0.0") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); localTime = clip->duration(); expectedResults = QVector() << 5.0f << 0.0f << 0.0f // Translation << 0.0f << 0.0f << -1.0f << 0.0f; // Rotation QTest::newRow("clip2.json, t = duration") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); localTime = clip->duration() / 2.0f; expectedResults = QVector() << 2.5f << 0.0f << 0.0f // Translation << 0.5f << 0.0f << -0.5f << 0.0f; // Rotation QTest::newRow("clip2.json, t = duration/2") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { // a clip with linear interpolation handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip4.json")); localTime = clip->duration(); expectedResults = QVector() << 5.0 << -2.0f << 6.0f; QTest::newRow("clip4.json, linear, t = duration") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { // a clip with linear interpolation handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip4.json")); localTime = clip->duration() / 2.0f; expectedResults = QVector() << 2.5f << -1.0f << 3.0f; QTest::newRow("clip4.json, linear, t = duration/2") << handler << clip << localTime << expectedResults; expectedResults.clear(); } { // a clip with slerp interpolation handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip6.json")); localTime = clip->duration() / 2.0f; expectedResults = QVector() << 0.923822f << 0.382626f << 0.0f << 0.0f; QTest::newRow("clip6.json, slerp, t = duration/2") << handler << clip << localTime << expectedResults; expectedResults.clear(); } } void checkEvaluateClipAtLocalTime() { // GIVEN QFETCH(Handler *, handler); QFETCH(AnimationClip *, clip); QFETCH(float, localTime); QFETCH(ClipResults, expectedResults); // WHEN ClipResults actualResults = evaluateClipAtLocalTime(clip, localTime); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { auto actual = actualResults[i]; auto expected = expectedResults[i]; QVERIFY(fuzzyCompare(actual, expected) == true); } // Cleanup delete handler; } void checkEvaluateClipAtPhase_data() { QTest::addColumn("handler"); QTest::addColumn("clip"); QTest::addColumn("phase"); QTest::addColumn("expectedResults"); Handler *handler; AnimationClip *clip; float phase; ClipResults expectedResults; { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); phase = 0.0f; expectedResults = QVector() << 0.0f << 0.0f << 0.0f; QTest::newRow("clip1.json, phi = 0.0") << handler << clip << phase << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); phase = 1.0f; expectedResults = QVector() << 5.0f << 0.0f << 0.0f; QTest::newRow("clip1.json, phi = 1.0") << handler << clip << phase << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); phase = 0.5f; expectedResults = QVector() << 2.5f << 0.0f << 0.0f; QTest::newRow("clip1.json, phi = 0.5") << handler << clip << phase << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); phase = 0.0f; expectedResults = QVector() << 0.0f << 0.0f << 0.0f // Translation << 1.0f << 0.0f << 0.0f << 0.0f; // Rotation QTest::newRow("clip2.json, phi = 0.0") << handler << clip << phase << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); phase = 1.0f; expectedResults = QVector() << 5.0f << 0.0f << 0.0f // Translation << 0.0f << 0.0f << -1.0f << 0.0f; // Rotation QTest::newRow("clip2.json, t = 1.0") << handler << clip << phase << expectedResults; expectedResults.clear(); } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip2.json")); phase = 0.5f; expectedResults = QVector() << 2.5f << 0.0f << 0.0f // Translation << 0.5f << 0.0f << -0.5f << 0.0f; // Rotation QTest::newRow("clip2.json, phi = 0.5") << handler << clip << phase << expectedResults; expectedResults.clear(); } } void checkEvaluateClipAtPhase() { // GIVEN QFETCH(Handler *, handler); QFETCH(AnimationClip *, clip); QFETCH(float, phase); QFETCH(ClipResults, expectedResults); // WHEN ClipResults actualResults = evaluateClipAtPhase(clip, phase); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { auto actual = actualResults[i]; auto expected = expectedResults[i]; QVERIFY(fuzzyCompare(actual, expected) == true); } // Cleanup delete handler; } void checkChannelComponentsToIndicesHelper_data() { QTest::addColumn("channel"); QTest::addColumn("dataType"); QTest::addColumn("expectedChannelComponentCount"); QTest::addColumn("offset"); QTest::addColumn>("suffixes"); QTest::addColumn>("expectedResults"); Channel channel; int dataType; int expectedChannelComponentCount; int offset; QVector suffixes; QVector expectedResults; // already sorted vec3, no component names, with and without offset { channel = Channel(); channel.name = QLatin1String("Location"); channel.channelComponents.resize(3); // leave 'name' empty dataType = static_cast(QVariant::Vector3D); expectedChannelComponentCount = 3; offset = 0; // suffixes expected to be ignored expectedResults = (QVector() << 0 << 1 << 2); QTest::newRow("vec3 location, pre-sorted, no component names, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 4; expectedResults = (QVector() << 4 << 5 << 6); QTest::newRow("vec3 location, pre-sorted, no component names, offset = 4") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); } // vec3 with and without offset { channel = Channel(); channel.name = QLatin1String("Location"); channel.channelComponents.resize(3); channel.channelComponents[0].name = QLatin1String("Location X"); channel.channelComponents[1].name = QLatin1String("Location Y"); channel.channelComponents[2].name = QLatin1String("Location Z"); dataType = static_cast(QVariant::Vector3D); expectedChannelComponentCount = 3; offset = 0; suffixes = (QVector() << 'X' << 'Y' << 'Z' << 'W'); expectedResults = (QVector() << 0 << 1 << 2); QTest::newRow("vec3 location, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 4; expectedResults = (QVector() << 4 << 5 << 6); QTest::newRow("vec3 location, offset = 4") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // vec2 with and without offset { channel = Channel(); channel.name = QLatin1String("pos"); channel.channelComponents.resize(2); channel.channelComponents[0].name = QLatin1String("pos X"); channel.channelComponents[1].name = QLatin1String("pos Y"); dataType = static_cast(QVariant::Vector2D); expectedChannelComponentCount = 2; offset = 0; suffixes = (QVector() << 'X' << 'Y' << 'Z' << 'W'); expectedResults = (QVector() << 0 << 1); QTest::newRow("vec2 pos, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 2; expectedResults = (QVector() << 2 << 3); QTest::newRow("vec2 pos, offset = 2") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // vec4 with and without offset { channel = Channel(); channel.name = QLatin1String("foo"); channel.channelComponents.resize(4); channel.channelComponents[0].name = QLatin1String("foo X"); channel.channelComponents[1].name = QLatin1String("foo Y"); channel.channelComponents[2].name = QLatin1String("foo Z"); channel.channelComponents[3].name = QLatin1String("foo W"); dataType = static_cast(QVariant::Vector4D); expectedChannelComponentCount = 4; offset = 0; suffixes = (QVector() << 'X' << 'Y' << 'Z' << 'W'); expectedResults = (QVector() << 0 << 1 << 2 << 3); QTest::newRow("vec4 foo, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12 << 13); QTest::newRow("vec4 foo, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // double with and without offset { channel = Channel(); channel.name = QLatin1String("foo"); channel.channelComponents.resize(1); channel.channelComponents[0].name = QLatin1String("Mass X"); dataType = static_cast(QVariant::Double); expectedChannelComponentCount = 1; offset = 0; suffixes = (QVector() << 'X' << 'Y' << 'Z' << 'W'); expectedResults = (QVector() << 0); QTest::newRow("double Mass, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 5; expectedResults = (QVector() << 5); QTest::newRow("double Mass, offset = 5") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // quaternion with and without offset { channel = Channel(); channel.name = QLatin1String("Rotation"); channel.channelComponents.resize(4); channel.channelComponents[0].name = QLatin1String("Rotation W"); channel.channelComponents[1].name = QLatin1String("Rotation X"); channel.channelComponents[2].name = QLatin1String("Rotation Y"); channel.channelComponents[3].name = QLatin1String("Rotation Z"); dataType = static_cast(QVariant::Quaternion); expectedChannelComponentCount = 4; offset = 0; suffixes = (QVector() << 'W' << 'X' << 'Y' << 'Z'); expectedResults = (QVector() << 0 << 1 << 2 << 3); QTest::newRow("quaternion Rotation, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12 << 13); QTest::newRow("quaternion Rotation, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // quaternion with and without offset, randomized { channel = Channel(); channel.name = QLatin1String("Rotation"); channel.channelComponents.resize(4); channel.channelComponents[0].name = QLatin1String("Rotation X"); channel.channelComponents[1].name = QLatin1String("Rotation W"); channel.channelComponents[2].name = QLatin1String("Rotation Z"); channel.channelComponents[3].name = QLatin1String("Rotation Y"); dataType = static_cast(QVariant::Quaternion); expectedChannelComponentCount = 4; offset = 0; suffixes = (QVector() << 'W' << 'X' << 'Y' << 'Z'); expectedResults = (QVector() << 1 << 0 << 3 << 2); QTest::newRow("quaternion Rotation, offset = 0, randomized") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 11 << 10 << 13 << 12); QTest::newRow("quaternion Rotation, offset = 10, randomized") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // color with and without offset { channel = Channel(); channel.name = QLatin1String("Color"); channel.channelComponents.resize(3); channel.channelComponents[0].name = QLatin1String("Color R"); channel.channelComponents[1].name = QLatin1String("Color G"); channel.channelComponents[2].name = QLatin1String("Color B"); dataType = static_cast(QVariant::Color); expectedChannelComponentCount = 3; offset = 0; suffixes = (QVector() << 'R' << 'G' << 'B'); expectedResults = (QVector() << 0 << 1 << 2); QTest::newRow("QColor Color, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12); QTest::newRow("QColor Color, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // weights as list with and without offset { channel = Channel(); channel.name = QLatin1String("MorphWeights"); channel.channelComponents.resize(6); // leave channel component names empty dataType = static_cast(QVariant::List); expectedChannelComponentCount = 6; offset = 0; // suffixes expected to be ignored expectedResults = (QVector() << 0 << 1 << 2 << 3 << 4 << 5); QTest::newRow("MorphWeights List count = 6, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12 << 13 << 14 << 15); QTest::newRow("MorphWeights List count = 6, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } // weights as vec of float with and without offset { channel = Channel(); channel.name = QLatin1String("MorphWeights"); channel.channelComponents.resize(6); // leave channel component names empty dataType = qMetaTypeId>(); expectedChannelComponentCount = 6; offset = 0; // suffixes expected to be ignored expectedResults = (QVector() << 0 << 1 << 2 << 3 << 4 << 5); QTest::newRow("MorphWeights Vec count = 6, offset = 0") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12 << 13 << 14 << 15); QTest::newRow("MorphWeights Vec count = 6, offset = 10") << channel << dataType << expectedChannelComponentCount << offset << suffixes << expectedResults; suffixes.clear(); expectedResults.clear(); } } void checkChannelComponentsToIndicesHelper() { // GIVEN QFETCH(Channel, channel); QFETCH(int, offset); QFETCH(int, expectedChannelComponentCount); QFETCH(QVector, suffixes); QFETCH(QVector, expectedResults); // WHEN QVector actualResults = channelComponentsToIndicesHelper(channel, expectedChannelComponentCount, offset, suffixes); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { QCOMPARE(actualResults[i], expectedResults[i]); } } void checkChannelComponentsToIndices_data() { QTest::addColumn("channel"); QTest::addColumn("dataType"); QTest::addColumn("componentCount"); QTest::addColumn("offset"); QTest::addColumn>("expectedResults"); Channel channel; int dataType; int componentCount; int offset; QVector expectedResults; // Quaternion { channel = Channel(); channel.name = QLatin1String("Rotation"); channel.channelComponents.resize(4); channel.channelComponents[0].name = QLatin1String("Rotation W"); channel.channelComponents[1].name = QLatin1String("Rotation X"); channel.channelComponents[2].name = QLatin1String("Rotation Y"); channel.channelComponents[3].name = QLatin1String("Rotation Z"); dataType = static_cast(QVariant::Quaternion); componentCount = 4; offset = 0; expectedResults = (QVector() << 0 << 1 << 2 << 3); QTest::newRow("quaternion Rotation, offset = 0") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12 << 13); QTest::newRow("quaternion Rotation, offset = 10") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); } // vec3 with and without offset { channel = Channel(); channel.name = QLatin1String("Location"); channel.channelComponents.resize(3); channel.channelComponents[0].name = QLatin1String("Location X"); channel.channelComponents[1].name = QLatin1String("Location Y"); channel.channelComponents[2].name = QLatin1String("Location Z"); dataType = static_cast(QVariant::Vector3D); componentCount = 3; offset = 0; expectedResults = (QVector() << 0 << 1 << 2); QTest::newRow("vec3 location, offset = 0") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); offset = 4; expectedResults = (QVector() << 4 << 5 << 6); QTest::newRow("vec3 location, offset = 4") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); } // QColor { channel = Channel(); channel.name = QLatin1String("Color"); channel.channelComponents.resize(3); channel.channelComponents[0].name = QLatin1String("Color R"); channel.channelComponents[1].name = QLatin1String("Color G"); channel.channelComponents[2].name = QLatin1String("Color B"); dataType = static_cast(QVariant::Color); componentCount = 3; offset = 0; expectedResults = (QVector() << 0 << 1 << 2); QTest::newRow("QColor Color, offset = 0") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); offset = 10; expectedResults = (QVector() << 10 << 11 << 12); QTest::newRow("QColor Color, offset = 10") << channel << dataType << componentCount << offset << expectedResults; expectedResults.clear(); } } void checkChannelComponentsToIndices() { QFETCH(Channel, channel); QFETCH(int, dataType); QFETCH(int, componentCount); QFETCH(int, offset); QFETCH(QVector, expectedResults); // WHEN QVector actualResults = channelComponentsToIndices(channel, dataType, componentCount, offset); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { QCOMPARE(actualResults[i], expectedResults[i]); } } void checkEvaluationDataForClip_data() { QTest::addColumn("handler"); QTest::addColumn("clip"); QTest::addColumn("animatorData"); QTest::addColumn("expectedClipData"); Handler *handler; AnimationClip *clip; AnimatorEvaluationData animatorData; ClipEvaluationData clipData; auto* clock = new Clock; { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); const qint64 globalStartTimeNS = 0; const int loops = 1; auto animator = createClipAnimator(handler, globalStartTimeNS, loops); animator->setCurrentLoop(0); clipData.currentLoop = animator->currentLoop(); const qint64 elapsedTimeNS = 0; animatorData = evaluationDataForAnimator(animator, clock, elapsedTimeNS); // Tested elsewhere clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime, animatorData.elapsedTime, animatorData.playbackRate, clip->duration(), animatorData.loopCount, clipData.currentLoop); // Tested elsewhere clipData.isFinalFrame = false; QTest::newRow("clip1.json, globalTime = 0") << handler << clip << animatorData << clipData; } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); const qint64 globalStartTimeNS = 0; const int loops = 1; auto animator = createClipAnimator(handler, globalStartTimeNS, loops); animator->setCurrentLoop(0); clipData.currentLoop = animator->currentLoop(); const qint64 elapsedTimeNS = toNsecs(clip->duration()+1); // +1 to ensure beyond end animatorData = evaluationDataForAnimator(animator, nullptr, elapsedTimeNS); // Tested elsewhere clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime, animatorData.elapsedTime, animatorData.playbackRate, clip->duration(), animatorData.loopCount, clipData.currentLoop); // Tested elsewhere clipData.isFinalFrame = true; QTest::newRow("clip1.json, elapsedTime = duration + 1") << handler << clip << animatorData << clipData; } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); const qint64 globalStartTimeNS = 0; const int loops = 0; // Infinite loops auto animator = createClipAnimator(handler, globalStartTimeNS, loops); animator->setCurrentLoop(0); clipData.currentLoop = animator->currentLoop(); const qint64 elapsedTimeNS = toNsecs(2.0 * clip->duration()); animatorData = evaluationDataForAnimator(animator, clock, elapsedTimeNS); // Tested elsewhere clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime, animatorData.elapsedTime, animatorData.playbackRate, clip->duration(), animatorData.loopCount, clipData.currentLoop); // Tested elsewhere clipData.isFinalFrame = false; QTest::newRow("clip1.json, elapsedTime = 2 * duration, loops = infinite") << handler << clip << animatorData << clipData; } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); const qint64 globalStartTimeNS = 0; const int loops = 2; auto animator = createClipAnimator(handler, globalStartTimeNS, loops); animator->setCurrentLoop(0); clipData.currentLoop = animator->currentLoop(); const qint64 elapsedTimeNS = toNsecs(2.0 * clip->duration() + 1.0); // +1 to ensure beyond end of clip animatorData = evaluationDataForAnimator(animator, nullptr, elapsedTimeNS); // Tested elsewhere clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime, animatorData.elapsedTime, animatorData.playbackRate, clip->duration(), animatorData.loopCount, clipData.currentLoop); // Tested elsewhere clipData.isFinalFrame = true; QTest::newRow("clip1.json, elapsedTime = 2 * duration + 1, loops = 2") << handler << clip << animatorData << clipData; } { handler = new Handler(); clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json")); const qint64 globalStartTimeNS = 0; const int loops = 2; auto animator = createClipAnimator(handler, globalStartTimeNS, loops); animator->setCurrentLoop(1); clipData.currentLoop = animator->currentLoop(); const qint64 elapsedTimeNS = toNsecs(clip->duration() + 1.0); // +1 to ensure beyond end of clip animatorData = evaluationDataForAnimator(animator, nullptr, elapsedTimeNS); // Tested elsewhere clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime, animatorData.elapsedTime, animatorData.playbackRate, clip->duration(), animatorData.loopCount, clipData.currentLoop); // Tested elsewhere clipData.isFinalFrame = true; QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1") << handler << clip << animatorData << clipData; } } void checkEvaluationDataForClip() { // GIVEN QFETCH(Handler *, handler); QFETCH(AnimationClip *, clip); QFETCH(AnimatorEvaluationData, animatorData); QFETCH(ClipEvaluationData, expectedClipData); // WHEN ClipEvaluationData actualClipData = evaluationDataForClip(clip, animatorData); // THEN QCOMPARE(actualClipData.currentLoop, expectedClipData.currentLoop); QVERIFY(fuzzyCompare(actualClipData.localTime, expectedClipData.localTime) == true); QCOMPARE(actualClipData.isFinalFrame, expectedClipData.isFinalFrame); // Cleanup delete handler; } void checkEvaluationDataForAnimator_data() { QTest::addColumn("handler"); QTest::addColumn("animator"); QTest::addColumn("elapsedTime"); QTest::addColumn("expectedAnimatorData"); Handler *handler; ClipAnimator *animator; qint64 elapsedTimeNS; AnimatorEvaluationData expectedAnimatorData; { handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loops = 1; animator = createClipAnimator(handler, globalStartTimeNS, loops); elapsedTimeNS = 0; expectedAnimatorData.loopCount = loops; expectedAnimatorData.playbackRate = 1.0; // hard-wired for now expectedAnimatorData.elapsedTime = 0.0; QTest::newRow("globalStartTime = 0, elapsedTime = 0, loops = 1") << handler << animator << elapsedTimeNS << expectedAnimatorData; } { handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loops = 5; animator = createClipAnimator(handler, globalStartTimeNS, loops); elapsedTimeNS = 0; expectedAnimatorData.loopCount = loops; expectedAnimatorData.playbackRate = 1.0; // hard-wired for now expectedAnimatorData.elapsedTime = 0.0; QTest::newRow("globalStartTime = 0, elapsedTime = 0, loops = 5") << handler << animator << elapsedTimeNS << expectedAnimatorData; } { handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loops = 1; animator = createClipAnimator(handler, globalStartTimeNS, loops); elapsedTimeNS = 5000000000; expectedAnimatorData.loopCount = loops; expectedAnimatorData.playbackRate = 1.0; // hard-wired for now expectedAnimatorData.elapsedTime = 5.0; QTest::newRow("globalStartTime = 0, elapsedTime = 5, loops = 1") << handler << animator << elapsedTimeNS << expectedAnimatorData; } { handler = new Handler(); const qint64 globalStartTimeNS = 3000000000; const int loops = 1; animator = createClipAnimator(handler, globalStartTimeNS, loops); elapsedTimeNS = 2000000000; expectedAnimatorData.loopCount = loops; expectedAnimatorData.playbackRate = 1.0; // hard-wired for now expectedAnimatorData.elapsedTime = 2.0; QTest::newRow("globalStartTime = 3, elapsedTime = 2, loops = 1") << handler << animator << elapsedTimeNS << expectedAnimatorData; } } void checkEvaluationDataForAnimator() { // GIVEN QFETCH(Handler *, handler); QFETCH(ClipAnimator *, animator); QFETCH(qint64, elapsedTime); QFETCH(AnimatorEvaluationData, expectedAnimatorData); // WHEN AnimatorEvaluationData actualAnimatorData = evaluationDataForAnimator(animator, nullptr, elapsedTime); // THEN QCOMPARE(actualAnimatorData.loopCount, expectedAnimatorData.loopCount); QVERIFY(fuzzyCompare(actualAnimatorData.playbackRate, expectedAnimatorData.playbackRate) == true); QVERIFY(fuzzyCompare(actualAnimatorData.elapsedTime, expectedAnimatorData.elapsedTime) == true); // Cleanup delete handler; } void checkGatherValueNodesToEvaluate_data() { QTest::addColumn("handler"); QTest::addColumn("blendTreeRootId"); QTest::addColumn>("expectedIds"); { Handler *handler = new Handler; const auto lerp = createLerpClipBlend(handler); const auto value1 = createClipBlendValue(handler); const auto clip1Id = Qt3DCore::QNodeId::createId(); value1->setClipId(clip1Id); lerp->setStartClipId(value1->peerId()); const auto value2 = createClipBlendValue(handler); const auto clip2Id = Qt3DCore::QNodeId::createId(); value2->setClipId(clip2Id); lerp->setEndClipId(value2->peerId()); QVector expectedIds = { value1->peerId(), value2->peerId() }; QTest::newRow("simple lerp") << handler << lerp->peerId() << expectedIds; } { Handler *handler = new Handler; const auto value1 = createClipBlendValue(handler); const auto clip1Id = Qt3DCore::QNodeId::createId(); value1->setClipId(clip1Id); QVector expectedIds = { value1->peerId() }; QTest::newRow("value only") << handler << value1->peerId() << expectedIds; } } void checkGatherValueNodesToEvaluate() { // GIVEN QFETCH(Handler *, handler); QFETCH(Qt3DCore::QNodeId, blendTreeRootId); QFETCH(QVector, expectedIds); // WHEN QVector actualIds = gatherValueNodesToEvaluate(handler, blendTreeRootId); // THEN QCOMPARE(actualIds.size(), expectedIds.size()); for (int i = 0; i < actualIds.size(); ++i) QCOMPARE(actualIds[i], expectedIds[i]); // Cleanup delete handler; } void checkEvaluateBlendTree_data() { QTest::addColumn("handler"); QTest::addColumn("animator"); QTest::addColumn("blendNodeId"); QTest::addColumn("expectedResults"); { /* ValueNode1---- | MeanBlendNode | ValueNode2---- */ auto handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loopCount = 1; auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount); // Set up the blend node and dependencies (evaluated clip results of the // dependent nodes in the animator indexed by their ids). MeanBlendNode *blendNode = createMeanBlendNode(handler); // First clip to use in the mean auto valueNode1 = createClipBlendValue(handler); ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f }; valueNode1->setClipResults(animator->peerId(), valueNode1Results); // Second clip to use in the mean auto valueNode2 = createClipBlendValue(handler); ClipResults valueNode2Results = { 1.0f, 1.0f, 1.0f }; valueNode2->setClipResults(animator->peerId(), valueNode2Results); blendNode->setValueNodeIds(valueNode1->peerId(), valueNode2->peerId()); ClipResults expectedResults = { 0.5f, 0.5f, 0.5f }; QTest::newRow("mean node, 1 channel") << handler << animator << blendNode->peerId() << expectedResults; } { /* ValueNode1---- | MeanBlendNode | ValueNode2---- */ auto handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loopCount = 1; auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount); // Set up the blend node and dependencies (evaluated clip results of the // dependent nodes in the animator indexed by their ids). MeanBlendNode *blendNode = createMeanBlendNode(handler); // First clip to use in the mean auto valueNode1 = createClipBlendValue(handler); ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 3.0f }; valueNode1->setClipResults(animator->peerId(), valueNode1Results); // Second clip to use in the mean auto valueNode2 = createClipBlendValue(handler); ClipResults valueNode2Results = { 1.0f, 1.0f, 1.0f, 2.0f, 4.0f, 6.0f }; valueNode2->setClipResults(animator->peerId(), valueNode2Results); blendNode->setValueNodeIds(valueNode1->peerId(), valueNode2->peerId()); ClipResults expectedResults = { 0.5f, 0.5f, 0.5f, 1.5f, 3.0f, 4.5f }; QTest::newRow("mean node, 2 channels") << handler << animator << blendNode->peerId() << expectedResults; } { /* ValueNode1---- | MeanBlendNode1------ | | ValueNode2---- | MeanBlendNode3 ValueNode3---- | | | MeanBlendNode2------ | ValueNode4---- */ auto handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loopCount = 1; auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount); // Set up the blend node and dependencies (evaluated clip results of the // dependent nodes in the animator indexed by their ids). // MeanBlendNode1 MeanBlendNode *meanNode1 = createMeanBlendNode(handler); // First clip to use in mean1 auto valueNode1 = createClipBlendValue(handler); ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f }; valueNode1->setClipResults(animator->peerId(), valueNode1Results); // Second clip to use in mean1 auto valueNode2 = createClipBlendValue(handler); ClipResults valueNode2Results = { 2.0f, 2.0f, 2.0f }; valueNode2->setClipResults(animator->peerId(), valueNode2Results); meanNode1->setValueNodeIds(valueNode1->peerId(), valueNode2->peerId()); // MeanBlendNode2 MeanBlendNode *meanNode2 = createMeanBlendNode(handler); // First clip to use in mean1 auto valueNode3 = createClipBlendValue(handler); ClipResults valueNode3Results = { 10.0f, 10.0f, 10.0f }; valueNode3->setClipResults(animator->peerId(), valueNode3Results); // Second clip to use in mean1 auto valueNode4 = createClipBlendValue(handler); ClipResults valueNode4Results = { 20.0f, 20.0f, 20.0f }; valueNode4->setClipResults(animator->peerId(), valueNode4Results); meanNode2->setValueNodeIds(valueNode3->peerId(), valueNode4->peerId()); // MeanBlendNode3 MeanBlendNode *meanNode3 = createMeanBlendNode(handler); meanNode3->setValueNodeIds(meanNode1->peerId(), meanNode2->peerId()); // Mean1 = 1 // Mean2 = 15 // Mean3 = (1 + 15 ) / 2 = 8 ClipResults expectedResults = { 8.0f, 8.0f, 8.0f }; QTest::newRow("3 mean nodes, 1 channel") << handler << animator << meanNode3->peerId() << expectedResults; } { /* ValueNode1---- | MeanBlendNode1------ | | ValueNode2---- | MeanBlendNode3--- ValueNode3---- | | | | | MeanBlendNode2------ AdditiveBlendNode1 | | ValueNode4---- | ValueNode5------- */ auto handler = new Handler(); const qint64 globalStartTimeNS = 0; const int loopCount = 1; auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount); // Set up the blend node and dependencies (evaluated clip results of the // dependent nodes in the animator indexed by their ids). // MeanBlendNode1 MeanBlendNode *meanNode1 = createMeanBlendNode(handler); // First clip to use in mean1 auto valueNode1 = createClipBlendValue(handler); ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f }; valueNode1->setClipResults(animator->peerId(), valueNode1Results); // Second clip to use in mean1 auto valueNode2 = createClipBlendValue(handler); ClipResults valueNode2Results = { 2.0f, 2.0f, 2.0f }; valueNode2->setClipResults(animator->peerId(), valueNode2Results); meanNode1->setValueNodeIds(valueNode1->peerId(), valueNode2->peerId()); // MeanBlendNode2 MeanBlendNode *meanNode2 = createMeanBlendNode(handler); // First clip to use in mean2 auto valueNode3 = createClipBlendValue(handler); ClipResults valueNode3Results = { 10.0f, 10.0f, 10.0f }; valueNode3->setClipResults(animator->peerId(), valueNode3Results); // Second clip to use in mean2 auto valueNode4 = createClipBlendValue(handler); ClipResults valueNode4Results = { 20.0f, 20.0f, 20.0f }; valueNode4->setClipResults(animator->peerId(), valueNode4Results); meanNode2->setValueNodeIds(valueNode3->peerId(), valueNode4->peerId()); // MeanBlendNode3 MeanBlendNode *meanNode3 = createMeanBlendNode(handler); meanNode3->setValueNodeIds(meanNode1->peerId(), meanNode2->peerId()); // AdditiveBlendNode1 AdditiveClipBlend *additiveBlendNode1 = createAdditiveClipBlend(handler); auto valueNode5 = createClipBlendValue(handler); ClipResults valueNode5Results = { 1.0f, 2.0f, 3.0f }; valueNode5->setClipResults(animator->peerId(), valueNode5Results); additiveBlendNode1->setBaseClipId(meanNode3->peerId()); additiveBlendNode1->setAdditiveClipId(valueNode5->peerId()); additiveBlendNode1->setAdditiveFactor(0.5); // Mean1 = 1 // Mean2 = 15 // Mean3 = (1 + 15 ) / 2 = 8 // Additive1 = 8 + 0.5 * (1, 2, 3) = (8.5, 9, 9.5) ClipResults expectedResults = { 8.5f, 9.0f, 9.5f }; QTest::newRow("3 mean nodes + additive, 1 channel") << handler << animator << additiveBlendNode1->peerId() << expectedResults; } } void checkEvaluateBlendTree() { // GIVEN QFETCH(Handler *, handler); QFETCH(BlendedClipAnimator *, animator); QFETCH(Qt3DCore::QNodeId, blendNodeId); QFETCH(ClipResults, expectedResults); // WHEN const ClipResults actualResults = evaluateBlendTree(handler, animator, blendNodeId); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) QCOMPARE(actualResults[i], expectedResults[i]); // Cleanup delete handler; } void checkFormatClipResults_data() { QTest::addColumn("rawClipResults"); QTest::addColumn("format"); QTest::addColumn("expectedResults"); { ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f }; ComponentIndices format = { 0, 1, 2 }; ClipResults expectedResults = { 1.0f, 2.0f, 3.0f }; QTest::newRow("identity") << rawClipResults << format << expectedResults; } { ClipResults rawClipResults = { 1.0f, 2.0f }; ComponentIndices format = { 1, 0 }; ClipResults expectedResults = { 2.0f, 1.0f }; QTest::newRow("swap") << rawClipResults << format << expectedResults; } { ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; ComponentIndices format = { 0, 2, 1, 3, 4 }; ClipResults expectedResults = { 1.0f, 3.0f, 2.0f, 4.0f, 5.0f }; QTest::newRow("swap subset") << rawClipResults << format << expectedResults; } { ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; ComponentIndices format = { 4, 3, 2, 1, 0 }; ClipResults expectedResults = { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f }; QTest::newRow("reverse") << rawClipResults << format << expectedResults; } { ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; ComponentIndices format = { 0, 1, -1, 3, 4 }; ClipResults expectedResults = { 1.0f, 2.0f, 0.0f, 4.0f, 5.0f }; QTest::newRow("include missing") << rawClipResults << format << expectedResults; } } void checkFormatClipResults() { // GIVEN QFETCH(ClipResults, rawClipResults); QFETCH(ComponentIndices, format); QFETCH(ClipResults, expectedResults); // WHEN const ClipResults actualResults = formatClipResults(rawClipResults, format); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) QCOMPARE(actualResults[i], expectedResults[i]); } void checkBuildRequiredChannelsAndTypes_data() { QTest::addColumn("handler"); QTest::addColumn("mapper"); QTest::addColumn>("expectedResults"); { auto handler = new Handler(); auto channelMapping = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); QVector channelMappings; channelMappings.push_back(channelMapping); auto channelMapper = createChannelMapper(handler, QVector() << channelMapping->peerId()); QVector expectedResults; expectedResults.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }); QTest::addRow("Location, vec3") << handler << channelMapper << expectedResults; } { auto handler = new Handler(); auto channelMapping1 = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); auto channelMapping2 = createChannelMapping(handler, QLatin1String("Rotation"), Qt3DCore::QNodeId::createId(), "rotation", static_cast(QVariant::Quaternion), 4); QVector channelMappings; channelMappings.push_back(channelMapping1); channelMappings.push_back(channelMapping2); QVector channelMappingIds = (QVector() << channelMapping1->peerId() << channelMapping2->peerId()); auto channelMapper = createChannelMapper(handler, channelMappingIds); QVector expectedResults; expectedResults.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping1->peerId() }); expectedResults.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, channelMapping2->peerId() }); QTest::addRow("Multiple unique channels") << handler << channelMapper << expectedResults; } { auto handler = new Handler(); auto channelMapping1 = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); auto channelMapping2 = createChannelMapping(handler, QLatin1String("Rotation"), Qt3DCore::QNodeId::createId(), "rotation", static_cast(QVariant::Quaternion), 4); auto channelMapping3 = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); auto channelMapping4 = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); QVector channelMappings; channelMappings.push_back(channelMapping1); channelMappings.push_back(channelMapping2); channelMappings.push_back(channelMapping3); channelMappings.push_back(channelMapping4); QVector channelMappingIds = (QVector() << channelMapping1->peerId() << channelMapping2->peerId() << channelMapping3->peerId() << channelMapping4->peerId()); auto channelMapper = createChannelMapper(handler, channelMappingIds); QVector expectedResults; expectedResults.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping1->peerId() }); expectedResults.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, channelMapping2->peerId() }); expectedResults.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping3->peerId() }); expectedResults.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping4->peerId() }); QTest::addRow("Multiple channels with repeats") << handler << channelMapper << expectedResults; } { auto handler = new Handler(); const int jointCount = 10; auto skeleton = createSkeleton(handler, jointCount); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); QVector channelMappings; channelMappings.push_back(channelMapping); auto channelMapper = createChannelMapper(handler, QVector() << channelMapping->peerId()); QVector expectedResults; for (int i = 0; i < jointCount; ++i) { ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; locationDescription.jointIndex = i; locationDescription.jointTransformComponent = Translation; expectedResults.push_back(locationDescription); ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4, channelMapping->peerId() }; rotationDescription.jointIndex = i; rotationDescription.jointTransformComponent = Rotation; expectedResults.push_back(rotationDescription); ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast(QVariant::Vector3D), 3, channelMapping->peerId() }; scaleDescription.jointIndex = i; scaleDescription.jointTransformComponent = Scale; expectedResults.push_back(scaleDescription); } QTest::addRow("Skeleton, 10 joints") << handler << channelMapper << expectedResults; } } void checkBuildRequiredChannelsAndTypes() { // GIVEN QFETCH(Handler *, handler); QFETCH(ChannelMapper *, mapper); QFETCH(QVector, expectedResults); // WHEN const QVector actualResults = buildRequiredChannelsAndTypes(handler, mapper); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) QCOMPARE(actualResults[i], expectedResults[i]); // Cleanup delete handler; } void checkAssignChannelComponentIndices_data() { QTest::addColumn>("allChannels"); QTest::addColumn>("expectedResults"); { QVector allChannels; allChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); QVector expectedResults; expectedResults.push_back({ 0, 1, 2 }); QTest::newRow("vec3 location") << allChannels << expectedResults; } { QVector allChannels; allChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); allChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); QVector expectedResults; expectedResults.push_back({ 0, 1, 2 }); expectedResults.push_back({ 3, 4, 5, 6 }); QTest::newRow("vec3 location, quaterion rotation") << allChannels << expectedResults; } { QVector allChannels; allChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); allChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); allChannels.push_back({ QLatin1String("BaseColor"), static_cast(QVariant::Vector3D), 3 }); allChannels.push_back({ QLatin1String("Metalness"), static_cast(QVariant::Double), 1 }); allChannels.push_back({ QLatin1String("Roughness"), static_cast(QVariant::Double), 1 }); allChannels.push_back({ QLatin1String("MorphWeights"), static_cast(QVariant::List), 6 }); QVector expectedResults; expectedResults.push_back({ 0, 1, 2 }); expectedResults.push_back({ 3, 4, 5, 6 }); expectedResults.push_back({ 7, 8, 9 }); expectedResults.push_back({ 10 }); expectedResults.push_back({ 11 }); expectedResults.push_back({ 12, 13, 14, 15, 16, 17 }); QTest::newRow("vec3 location, quaterion rotation, pbr metal-rough morphweights") << allChannels << expectedResults; } { QVector allChannels; const int jointCount = 4; for (int i = 0; i < jointCount; ++i) { ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }; locationDescription.jointIndex = i; allChannels.push_back(locationDescription); ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }; rotationDescription.jointIndex = i; allChannels.push_back(rotationDescription); ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast(QVariant::Vector3D), 3 }; scaleDescription.jointIndex = i; allChannels.push_back(scaleDescription); } QVector expectedResults; expectedResults.push_back({ 0, 1, 2 }); expectedResults.push_back({ 3, 4, 5, 6 }); expectedResults.push_back({ 7, 8, 9 }); expectedResults.push_back({ 10, 11, 12 }); expectedResults.push_back({ 13, 14, 15, 16 }); expectedResults.push_back({ 17, 18, 19 }); expectedResults.push_back({ 20, 21, 22 }); expectedResults.push_back({ 23, 24, 25, 26 }); expectedResults.push_back({ 27, 28, 29 }); expectedResults.push_back({ 30, 31, 32 }); expectedResults.push_back({ 33, 34, 35, 36 }); expectedResults.push_back({ 37, 38, 39 }); QTest::newRow("skeleton, 4 joints") << allChannels << expectedResults; } } void checkAssignChannelComponentIndices() { // GIVEN QFETCH(QVector, allChannels); QFETCH(QVector, expectedResults); // WHEN const QVector actualResults = assignChannelComponentIndices(allChannels); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { const ComponentIndices &actualResult = actualResults[i]; const ComponentIndices &expectedResult = expectedResults[i]; for (int j = 0; j < actualResult.size(); ++j) QCOMPARE(actualResult[j], expectedResult[j]); } } void checkGenerateClipFormatIndices_data() { QTest::addColumn>("targetChannels"); QTest::addColumn>("targetIndices"); QTest::addColumn("clip"); QTest::addColumn("expectedResults"); { QVector targetChannels; targetChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); targetChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Base Color"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Metalness"), static_cast(QVariant::Double), 1 }); targetChannels.push_back({ QLatin1String("Roughness"), static_cast(QVariant::Double), 1 }); QVector targetIndices; targetIndices.push_back({ 0, 1, 2, 3 }); targetIndices.push_back({ 4, 5, 6 }); targetIndices.push_back({ 7, 8, 9 }); targetIndices.push_back({ 10 }); targetIndices.push_back({ 11 }); auto *clip = new AnimationClip(); clip->setDataType(AnimationClip::File); clip->setSource(QUrl("qrc:/clip3.json")); clip->loadAnimation(); ClipFormat expectedResults; expectedResults.sourceClipIndices = { 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json) 4, 6, 5, // Location (y/z swapped in clip3.json) 7, 8, 9, // Base Color 10, // Metalness 11 }; // Roughness expectedResults.sourceClipMask = { QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(1, true), QBitArray(1, true) }; expectedResults.namesAndTypes = targetChannels; expectedResults.formattedComponentIndices = targetIndices; QTest::newRow("rotation, location, pbr metal-rough") << targetChannels << targetIndices << clip << expectedResults; } { QVector targetChannels; targetChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); targetChannels.push_back({ QLatin1String("Base Color"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Metalness"), static_cast(QVariant::Double), 1 }); targetChannels.push_back({ QLatin1String("Roughness"), static_cast(QVariant::Double), 1 }); QVector targetIndices; targetIndices.push_back({ 0, 1, 2 }); targetIndices.push_back({ 3, 4, 5, 6 }); targetIndices.push_back({ 7, 8, 9 }); targetIndices.push_back({ 10 }); targetIndices.push_back({ 11 }); auto *clip = new AnimationClip(); clip->setDataType(AnimationClip::File); clip->setSource(QUrl("qrc:/clip3.json")); clip->loadAnimation(); ClipFormat expectedResults; expectedResults.sourceClipIndices = { 4, 6, 5, // Location (y/z swapped in clip3.json) 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json) 7, 8, 9, // Base Color 10, // Metalness 11 }; // Roughness expectedResults.sourceClipMask = { QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(1, true), QBitArray(1, true) }; expectedResults.namesAndTypes = targetChannels; expectedResults.formattedComponentIndices = targetIndices; QTest::newRow("location, rotation, pbr metal-rough") << targetChannels << targetIndices << clip << expectedResults; } { QVector targetChannels; targetChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); targetChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Albedo"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Metalness"), static_cast(QVariant::Double), 1 }); targetChannels.push_back({ QLatin1String("Roughness"), static_cast(QVariant::Double), 1 }); QVector targetIndices; targetIndices.push_back({ 0, 1, 2, 3 }); targetIndices.push_back({ 4, 5, 6 }); targetIndices.push_back({ 7, 8, 9 }); targetIndices.push_back({ 10 }); targetIndices.push_back({ 11 }); auto *clip = new AnimationClip(); clip->setDataType(AnimationClip::File); clip->setSource(QUrl("qrc:/clip3.json")); clip->loadAnimation(); ClipFormat expectedResults; expectedResults.sourceClipIndices = { 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json) 4, 6, 5, // Location (y/z swapped in clip3.json) -1, -1, -1, // Albedo (missing from clip) 10, // Metalness 11 }; // Roughness expectedResults.sourceClipMask = { QBitArray(4, true), QBitArray(3, true), QBitArray(3, false), QBitArray(1, true), QBitArray(1, true) }; expectedResults.namesAndTypes = targetChannels; expectedResults.formattedComponentIndices = targetIndices; QTest::newRow("rotation, location, albedo (missing), metal-rough") << targetChannels << targetIndices << clip << expectedResults; } { QVector targetChannels; targetChannels.push_back({ QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }); targetChannels.push_back({ QLatin1String("Albedo"), static_cast(QVariant::Vector3D), 3 }); targetChannels.push_back({ QLatin1String("Metalness"), static_cast(QVariant::Double), 1 }); targetChannels.push_back({ QLatin1String("Roughness"), static_cast(QVariant::Double), 1 }); QVector targetIndices; targetIndices.push_back({ 0, 1, 2 }); targetIndices.push_back({ 3, 4, 5, 6 }); targetIndices.push_back({ 7, 8, 9 }); targetIndices.push_back({ 10 }); targetIndices.push_back({ 11 }); auto *clip = new AnimationClip(); clip->setDataType(AnimationClip::File); clip->setSource(QUrl("qrc:/clip3.json")); clip->loadAnimation(); ClipFormat expectedResults; expectedResults.sourceClipIndices = { 4, 6, 5, // Location (y/z swapped in clip3.json) 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json) -1, -1, -1, // Albedo (missing from clip) 10, // Metalness 11 }; // Roughness expectedResults.sourceClipMask = { QBitArray(3, true), QBitArray(4, true), QBitArray(3, false), QBitArray(1, true), QBitArray(1, true) }; expectedResults.namesAndTypes = targetChannels; expectedResults.formattedComponentIndices = targetIndices; QTest::newRow("location, rotation, albedo (missing), metal-rough") << targetChannels << targetIndices << clip << expectedResults; } { QVector targetChannels; const int jointCount = 4; for (int i = 0; i < jointCount; ++i) { ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast(QVariant::Vector3D), 3 }; locationDescription.jointIndex = i; targetChannels.push_back(locationDescription); ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast(QVariant::Quaternion), 4 }; rotationDescription.jointIndex = i; targetChannels.push_back(rotationDescription); ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast(QVariant::Vector3D), 3 }; scaleDescription.jointIndex = i; targetChannels.push_back(scaleDescription); } QVector targetIndices; targetIndices.push_back({ 0, 1, 2 }); targetIndices.push_back({ 3, 4, 5, 6 }); targetIndices.push_back({ 7, 8, 9 }); targetIndices.push_back({ 10, 11, 12 }); targetIndices.push_back({ 13, 14, 15, 16 }); targetIndices.push_back({ 17, 18, 19 }); targetIndices.push_back({ 20, 21, 22 }); targetIndices.push_back({ 23, 24, 25, 26 }); targetIndices.push_back({ 27, 28, 29 }); targetIndices.push_back({ 30, 31, 32 }); targetIndices.push_back({ 33, 34, 35, 36 }); targetIndices.push_back({ 37, 38, 39 }); auto *clip = new AnimationClip(); clip->setDataType(AnimationClip::File); clip->setSource(QUrl("qrc:/clip5.json")); clip->loadAnimation(); ClipFormat expectedResults; expectedResults.sourceClipIndices = { 4, 6, 5, // Location, joint 0 (y/z swapped in clip5.json) 0, 1, 3, 2, // Rotation, joint 0 (y/z swapped in clip5.json) 7, 8, 9, // Scale, joint 0 14, 16, 15, // Location, joint 1 (y/z swapped in clip5.json) 10, 11, 13, 12, // Rotation, joint 1 (y/z swapped in clip5.json) 17, 18, 19, // Scale, joint 1 24, 26, 25, // Location, joint 2 (y/z swapped in clip5.json) 20, 21, 23, 22, // Rotation, joint 2 (y/z swapped in clip5.json) 27, 28, 29, // Scale, joint 2 34, 36, 35, // Location, joint 3 (y/z swapped in clip5.json) 30, 31, 33, 32, // Rotation, joint 3 (y/z swapped in clip5.json) 37, 38, 39 }; // Scale, joint 3 expectedResults.sourceClipMask = { QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true), QBitArray(3, true), QBitArray(4, true), QBitArray(3, true) }; expectedResults.namesAndTypes = targetChannels; expectedResults.formattedComponentIndices = targetIndices; QTest::newRow("skeleton (SQT), 4 joints") << targetChannels << targetIndices << clip << expectedResults; } } void checkGenerateClipFormatIndices() { // GIVEN QFETCH(QVector, targetChannels); QFETCH(QVector, targetIndices); QFETCH(AnimationClip *, clip); QFETCH(ClipFormat, expectedResults); // WHEN const ClipFormat actualResults = generateClipFormatIndices(targetChannels, targetIndices, clip); // THEN QCOMPARE(actualResults.sourceClipIndices.size(), expectedResults.sourceClipIndices.size()); for (int i = 0; i < actualResults.sourceClipIndices.size(); ++i) QCOMPARE(actualResults.sourceClipIndices[i], expectedResults.sourceClipIndices[i]); QCOMPARE(actualResults.sourceClipMask.size(), expectedResults.sourceClipMask.size()); for (int i = 0; i < actualResults.sourceClipMask.size(); ++i) QCOMPARE(actualResults.sourceClipMask[i], expectedResults.sourceClipMask[i]); QCOMPARE(actualResults.formattedComponentIndices.size(), expectedResults.formattedComponentIndices.size()); for (int i = 0; i < actualResults.formattedComponentIndices.size(); ++i) QCOMPARE(actualResults.formattedComponentIndices[i], expectedResults.formattedComponentIndices[i]); QCOMPARE(actualResults.namesAndTypes.size(), expectedResults.namesAndTypes.size()); for (int i = 0; i < actualResults.namesAndTypes.size(); ++i) QCOMPARE(actualResults.namesAndTypes[i], expectedResults.namesAndTypes[i]); // Cleanup delete clip; } void checkDefaultValueForChannel_data() { QTest::addColumn("handler"); QTest::addColumn("channelDescription"); QTest::addColumn>("expectedResults"); { auto handler = new Handler(); auto channelMapping = createChannelMapping(handler, QLatin1String("Location"), Qt3DCore::QNodeId::createId(), "translation", static_cast(QVariant::Vector3D), 3); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.name = QLatin1String("translation"); const QVector expectedResults = { 0.0f, 0.0f, 0.0f }; QTest::newRow("translation") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto channelMapping = createChannelMapping(handler, QLatin1String("Rotation"), Qt3DCore::QNodeId::createId(), "rotation", static_cast(QVariant::Quaternion), 4); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Quaternion); channelDescription.name = QLatin1String("rotation"); const QVector expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f }; QTest::newRow("rotation") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto channelMapping = createChannelMapping(handler, QLatin1String("Scale"), Qt3DCore::QNodeId::createId(), "scale", static_cast(QVariant::Vector3D), 3); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.name = QLatin1String("scale"); const QVector expectedResults = { 1.0f, 1.0f, 1.0f }; QTest::newRow("scale") << handler << channelDescription << expectedResults; } // Test skeleton cases { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointScale(0, QVector3D(2.0f, 3.0f, 4.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 0; channelDescription.jointTransformComponent = Scale; const QVector expectedResults = { 2.0f, 3.0f, 4.0f }; QTest::newRow("joint 0 scale") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointRotation(0, QQuaternion(1.0f, 0.0f, 0.0f, 0.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 0; channelDescription.jointTransformComponent = Rotation; const QVector expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f }; QTest::newRow("joint 0 rotation") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointTranslation(0, QVector3D(2.0f, 3.0f, 4.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 0; channelDescription.jointTransformComponent = Translation; const QVector expectedResults = { 2.0f, 3.0f, 4.0f }; QTest::newRow("joint 0 translation") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointScale(1, QVector3D(20.0f, 30.0f, 40.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 1; channelDescription.jointTransformComponent = Scale; const QVector expectedResults = { 20.0f, 30.0f, 40.0f }; QTest::newRow("joint 1 scale") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointRotation(1, QQuaternion(1.0f, 0.0f, 0.0f, 0.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 1; channelDescription.jointTransformComponent = Rotation; const QVector expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f }; QTest::newRow("joint 1 rotation") << handler << channelDescription << expectedResults; } { auto handler = new Handler(); auto skeleton = createSkeleton(handler, 2); skeleton->setJointTranslation(1, QVector3D(4.0f, 5.0f, 6.0f)); auto channelMapping = createChannelMapping(handler, skeleton->peerId()); ChannelNameAndType channelDescription; channelDescription.mappingId = channelMapping->peerId(); channelDescription.type = static_cast(QVariant::Vector3D); channelDescription.jointIndex = 1; channelDescription.jointTransformComponent = Translation; const QVector expectedResults = { 4.0f, 5.0f, 6.0f }; QTest::newRow("joint 1 translation") << handler << channelDescription << expectedResults; } } void checkDefaultValueForChannel() { // GIVEN QFETCH(Handler *, handler); QFETCH(ChannelNameAndType, channelDescription); QFETCH(QVector, expectedResults); // WHEN auto actualResults = defaultValueForChannel(handler, channelDescription); // THEN QCOMPARE(actualResults.size(), expectedResults.size()); for (int i = 0; i < actualResults.size(); ++i) { QCOMPARE(actualResults[i], expectedResults[i]); } } }; QTEST_MAIN(tst_AnimationUtils) #include "tst_animationutils.moc"