1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QTest>
30#include <Qt3DAnimation/private/animationclip_p.h>
31#include <Qt3DAnimation/private/animationutils_p.h>
32#include <Qt3DAnimation/private/blendedclipanimator_p.h>
33#include <Qt3DAnimation/private/clock_p.h>
34#include <Qt3DAnimation/private/channelmapper_p.h>
35#include <Qt3DAnimation/private/channelmapping_p.h>
36#include <Qt3DAnimation/private/clipblendvalue_p.h>
37#include <Qt3DAnimation/private/handler_p.h>
38#include <Qt3DAnimation/private/additiveclipblend_p.h>
39#include <Qt3DAnimation/private/lerpclipblend_p.h>
40#include <Qt3DAnimation/private/managers_p.h>
41#include <QtGui/qvector2d.h>
42#include <QtGui/qvector3d.h>
43#include <QtGui/qvector4d.h>
44#include <QtGui/qquaternion.h>
45#include <QtGui/qcolor.h>
46#include <QtCore/qbitarray.h>
47
48#include <qbackendnodetester.h>
49#include <testpostmanarbiter.h>
50
51using namespace Qt3DAnimation::Animation;
52
53Q_DECLARE_METATYPE(Qt3DAnimation::Animation::Handler*)
54Q_DECLARE_METATYPE(QVector<ChannelMapping *>)
55Q_DECLARE_METATYPE(Clock *)
56Q_DECLARE_METATYPE(ChannelMapper *)
57Q_DECLARE_METATYPE(AnimationClip *)
58Q_DECLARE_METATYPE(QVector<MappingData>)
59Q_DECLARE_METATYPE(Channel)
60Q_DECLARE_METATYPE(AnimatorEvaluationData)
61Q_DECLARE_METATYPE(ClipEvaluationData)
62Q_DECLARE_METATYPE(ClipAnimator *)
63Q_DECLARE_METATYPE(BlendedClipAnimator *)
64Q_DECLARE_METATYPE(QVector<ChannelNameAndType>)
65Q_DECLARE_METATYPE(QVector<AnimationCallbackAndValue>)
66Q_DECLARE_METATYPE(ClipFormat)
67Q_DECLARE_METATYPE(ChannelNameAndType)
68
69namespace {
70
71class MeanBlendNode : public ClipBlendNode
72{
73public:
74 MeanBlendNode()
75 : ClipBlendNode(ClipBlendNode::LerpBlendType)
76 {}
77
78 void setValueNodeIds(Qt3DCore::QNodeId value1Id,
79 Qt3DCore::QNodeId value2Id)
80 {
81 m_value1Id = value1Id;
82 m_value2Id = value2Id;
83 }
84
85 inline QVector<Qt3DCore::QNodeId> allDependencyIds() const override
86 {
87 return currentDependencyIds();
88 }
89
90 QVector<Qt3DCore::QNodeId> currentDependencyIds() const final
91 {
92 return QVector<Qt3DCore::QNodeId>() << m_value1Id << m_value2Id;
93 }
94
95 using ClipBlendNode::setClipResults;
96
97 double duration() const final { return 0.0f; }
98
99protected:
100 ClipResults doBlend(const QVector<ClipResults> &blendData) const final
101 {
102 Q_ASSERT(blendData.size() == 2);
103 const int elementCount = blendData.first().size();
104 ClipResults blendResults(elementCount);
105
106 for (int i = 0; i < elementCount; ++i)
107 blendResults[i] = 0.5f * (blendData[0][i] + blendData[1][i]);
108
109 return blendResults;
110 }
111
112private:
113 Qt3DCore::QNodeId m_value1Id;
114 Qt3DCore::QNodeId m_value2Id;
115};
116
117bool fuzzyCompare(float x1, float x2)
118{
119 if (qFuzzyIsNull(f: x1) && qFuzzyIsNull(f: x2)) {
120 return true;
121 } else if ((qFuzzyIsNull(f: x1) && !qFuzzyIsNull(f: x2)) ||
122 (!qFuzzyIsNull(f: x1) && qFuzzyIsNull(f: x2))) {
123 return false;
124 } else {
125 return qFuzzyCompare(p1: x1, p2: x2);
126 }
127}
128
129class DummyCallback : public Qt3DAnimation::QAnimationCallback
130{
131public:
132 void valueChanged(const QVariant &) override { }
133};
134
135} // anonymous
136
137
138class tst_AnimationUtils : public Qt3DCore::QBackendNodeTester
139{
140 Q_OBJECT
141
142public:
143 ChannelMapping *createChannelMapping(Handler *handler,
144 const QString &channelName,
145 const Qt3DCore::QNodeId targetId,
146 const char *propertyName,
147 int type,
148 int componentCount)
149 {
150 auto channelMappingId = Qt3DCore::QNodeId::createId();
151 ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(id: channelMappingId);
152 setPeerId(backend: channelMapping, id: channelMappingId);
153 channelMapping->setHandler(handler);
154 channelMapping->setTargetId(targetId);
155 channelMapping->setPropertyName(propertyName);
156 channelMapping->setChannelName(channelName);
157 channelMapping->setType(type);
158 channelMapping->setMappingType(ChannelMapping::ChannelMappingType);
159 channelMapping->setComponentCount(componentCount);
160 return channelMapping;
161 }
162
163 ChannelMapping *createChannelMapping(Handler *handler,
164 const Qt3DCore::QNodeId skeletonId)
165 {
166 auto channelMappingId = Qt3DCore::QNodeId::createId();
167 ChannelMapping *channelMapping = handler->channelMappingManager()->getOrCreateResource(id: channelMappingId);
168 setPeerId(backend: channelMapping, id: channelMappingId);
169 channelMapping->setHandler(handler);
170 channelMapping->setSkeletonId(skeletonId);
171 channelMapping->setMappingType(ChannelMapping::SkeletonMappingType);
172 return channelMapping;
173 }
174
175 ChannelMapper *createChannelMapper(Handler *handler,
176 const QVector<Qt3DCore::QNodeId> &mappingIds)
177 {
178 auto channelMapperId = Qt3DCore::QNodeId::createId();
179 ChannelMapper *channelMapper = handler->channelMapperManager()->getOrCreateResource(id: channelMapperId);
180 setPeerId(backend: channelMapper, id: channelMapperId);
181 channelMapper->setMappingIds(mappingIds);
182 return channelMapper;
183 }
184
185 AnimationClip *createAnimationClipLoader(Handler *handler,
186 const QUrl &source)
187 {
188 auto clipId = Qt3DCore::QNodeId::createId();
189 AnimationClip *clip = handler->animationClipLoaderManager()->getOrCreateResource(id: clipId);
190 setPeerId(backend: clip, id: clipId);
191 clip->setDataType(AnimationClip::File);
192 clip->setSource(source);
193 clip->loadAnimation();
194 return clip;
195 }
196
197 ClipAnimator *createClipAnimator(Handler *handler,
198 qint64 globalStartTimeNS,
199 int loops)
200 {
201 auto animatorId = Qt3DCore::QNodeId::createId();
202 ClipAnimator *animator = handler->clipAnimatorManager()->getOrCreateResource(id: animatorId);
203 setPeerId(backend: animator, id: animatorId);
204 animator->setStartTime(globalStartTimeNS);
205 animator->setLoops(loops);
206 return animator;
207 }
208
209 BlendedClipAnimator *createBlendedClipAnimator(Handler *handler,
210 qint64 globalStartTimeNS,
211 int loops)
212 {
213 auto animatorId = Qt3DCore::QNodeId::createId();
214 BlendedClipAnimator *animator = handler->blendedClipAnimatorManager()->getOrCreateResource(id: animatorId);
215 setPeerId(backend: animator, id: animatorId);
216 animator->setStartTime(globalStartTimeNS);
217 animator->setLoops(loops);
218 return animator;
219 }
220
221 LerpClipBlend *createLerpClipBlend(Handler *handler)
222 {
223 auto lerpId = Qt3DCore::QNodeId::createId();
224 LerpClipBlend *lerp = new LerpClipBlend();
225 setPeerId(backend: lerp, id: lerpId);
226 lerp->setClipBlendNodeManager(handler->clipBlendNodeManager());
227 lerp->setHandler(handler);
228 handler->clipBlendNodeManager()->appendNode(id: lerpId, node: lerp);
229 return lerp;
230 }
231
232 AdditiveClipBlend *createAdditiveClipBlend(Handler *handler)
233 {
234 auto additiveId = Qt3DCore::QNodeId::createId();
235 AdditiveClipBlend *additive = new AdditiveClipBlend();
236 setPeerId(backend: additive, id: additiveId);
237 additive->setClipBlendNodeManager(handler->clipBlendNodeManager());
238 additive->setHandler(handler);
239 handler->clipBlendNodeManager()->appendNode(id: additiveId, node: additive);
240 return additive;
241 }
242
243 ClipBlendValue *createClipBlendValue(Handler *handler)
244 {
245 auto valueId = Qt3DCore::QNodeId::createId();
246 ClipBlendValue *value = new ClipBlendValue();
247 setPeerId(backend: value, id: valueId);
248 value->setClipBlendNodeManager(handler->clipBlendNodeManager());
249 value->setHandler(handler);
250 handler->clipBlendNodeManager()->appendNode(id: valueId, node: value);
251 return value;
252 }
253
254 MeanBlendNode *createMeanBlendNode(Handler *handler)
255 {
256 auto id = Qt3DCore::QNodeId::createId();
257 MeanBlendNode *node = new MeanBlendNode();
258 setPeerId(backend: node, id);
259 node->setClipBlendNodeManager(handler->clipBlendNodeManager());
260 node->setHandler(handler);
261 handler->clipBlendNodeManager()->appendNode(id, node);
262 return node;
263 }
264
265 Skeleton *createSkeleton(Handler *handler, int jointCount)
266 {
267 auto skeletonId = Qt3DCore::QNodeId::createId();
268 Skeleton *skeleton = handler->skeletonManager()->getOrCreateResource(id: skeletonId);
269 setPeerId(backend: skeleton, id: skeletonId);
270 skeleton->setJointCount(jointCount);
271 return skeleton;
272 }
273
274private Q_SLOTS:
275 void checkBuildPropertyMappings_data()
276 {
277 QTest::addColumn<Handler *>(name: "handler");
278 QTest::addColumn<QVector<ChannelMapping *>>(name: "channelMappings");
279 QTest::addColumn<QVector<ChannelNameAndType>>(name: "channelNamesAndTypes");
280 QTest::addColumn<QVector<ComponentIndices>>(name: "channelComponentIndices");
281 QTest::addColumn<QVector<QBitArray>>(name: "sourceClipMask");
282 QTest::addColumn<QVector<MappingData>>(name: "expectedResults");
283
284 // Single ChannelMapping
285 {
286 Handler *handler = new Handler();
287
288 auto channelMapping = createChannelMapping(handler,
289 channelName: QLatin1String("Location"),
290 targetId: Qt3DCore::QNodeId::createId(),
291 propertyName: "translation",
292 type: static_cast<int>(QVariant::Vector3D),
293 componentCount: 3);
294
295 QVector<ChannelMapping *> channelMappings = { channelMapping };
296
297 // Create a few channels in the format description
298 ChannelNameAndType rotation = { QLatin1String("Rotation"),
299 static_cast<int>(QVariant::Quaternion),
300 4,
301 channelMapping->peerId() };
302 ChannelNameAndType location = { QLatin1String("Location"),
303 static_cast<int>(QVariant::Vector3D),
304 3,
305 channelMapping->peerId() };
306 ChannelNameAndType baseColor = { QLatin1String("BaseColor"),
307 static_cast<int>(QVariant::Vector3D),
308 3,
309 channelMapping->peerId() };
310 ChannelNameAndType metalness = { QLatin1String("Metalness"),
311 static_cast<int>(QVariant::Double),
312 1,
313 channelMapping->peerId() };
314 ChannelNameAndType roughness = { QLatin1String("Roughness"),
315 static_cast<int>(QVariant::Double),
316 1,
317 channelMapping->peerId() };
318 ChannelNameAndType morphTargetWeightsList = { QLatin1String("MorphTargetWeightsList"),
319 static_cast<int>(QVariant::List),
320 5,
321 channelMapping->peerId() };
322 ChannelNameAndType morphTargetWeightsVec = { QLatin1String("MorphTargetWeightsVec"),
323 qMetaTypeId<QVector<float>>(),
324 6,
325 channelMapping->peerId() };
326 ChannelNameAndType rgbColor = { QLatin1String("rgbColor"),
327 static_cast<int>(QVariant::Color),
328 3,
329 channelMapping->peerId() };
330
331 ChannelNameAndType rgbaColor = { QLatin1String("rgbaColor"),
332 static_cast<int>(QVariant::Color),
333 4,
334 channelMapping->peerId() };
335
336 QVector<ChannelNameAndType> channelNamesAndTypes
337 = { rotation, location, baseColor, metalness, roughness,
338 morphTargetWeightsList, morphTargetWeightsVec, rgbColor, rgbaColor };
339
340 // And the matching indices
341 ComponentIndices rotationIndices = { 0, 1, 2, 3 };
342 ComponentIndices locationIndices = { 4, 5, 6 };
343 ComponentIndices baseColorIndices = { 7, 8, 9 };
344 ComponentIndices metalnessIndices = { 10 };
345 ComponentIndices roughnessIndices = { 11 };
346 ComponentIndices morphTargetListIndices = { 12, 13, 14, 15, 16 };
347 ComponentIndices morphTargetVecIndices = { 17, 18, 19, 20, 21, 22 };
348 ComponentIndices rgbColorIndices = { 23, 24, 25 };
349 ComponentIndices rgbaColorIndices = { 26, 27, 28, 29 };
350 QVector<ComponentIndices> channelComponentIndices
351 = { rotationIndices, locationIndices, baseColorIndices,
352 metalnessIndices, roughnessIndices, morphTargetListIndices,
353 morphTargetVecIndices, rgbColorIndices, rgbaColorIndices };
354
355 QVector<QBitArray> sourceClipMask = { QBitArray(4, true),
356 QBitArray(3, true),
357 QBitArray(3, true),
358 QBitArray(1, true),
359 QBitArray(1, true),
360 QBitArray(5, true),
361 QBitArray(6, true),
362 QBitArray(3, true),
363 QBitArray(4, true),
364 };
365
366 MappingData expectedMapping;
367 expectedMapping.targetId = channelMapping->targetId();
368 expectedMapping.propertyName = channelMapping->propertyName();
369 expectedMapping.type = channelMapping->type();
370 expectedMapping.channelIndices = locationIndices;
371 QVector<MappingData> expectedResults = { expectedMapping };
372
373 QTest::newRow(dataTag: "single mapping")
374 << handler
375 << channelMappings
376 << channelNamesAndTypes
377 << channelComponentIndices
378 << sourceClipMask
379 << expectedResults;
380 }
381
382 // Multiple ChannelMappings
383 {
384 Handler *handler = new Handler();
385
386 auto locationMapping = createChannelMapping(handler,
387 channelName: QLatin1String("Location"),
388 targetId: Qt3DCore::QNodeId::createId(),
389 propertyName: "translation",
390 type: static_cast<int>(QVariant::Vector3D),
391 componentCount: 3);
392
393 auto metalnessMapping = createChannelMapping(handler,
394 channelName: QLatin1String("Metalness"),
395 targetId: Qt3DCore::QNodeId::createId(),
396 propertyName: "metalness",
397 type: static_cast<int>(QVariant::Double),
398 componentCount: 1);
399
400 auto baseColorMapping = createChannelMapping(handler,
401 channelName: QLatin1String("BaseColor"),
402 targetId: Qt3DCore::QNodeId::createId(),
403 propertyName: "baseColor",
404 type: static_cast<int>(QVariant::Vector3D),
405 componentCount: 3);
406
407 auto roughnessMapping = createChannelMapping(handler,
408 channelName: QLatin1String("Roughness"),
409 targetId: Qt3DCore::QNodeId::createId(),
410 propertyName: "roughness",
411 type: static_cast<int>(QVariant::Double),
412 componentCount: 1);
413
414 auto rotationMapping = createChannelMapping(handler,
415 channelName: QLatin1String("Rotation"),
416 targetId: Qt3DCore::QNodeId::createId(),
417 propertyName: "rotation",
418 type: static_cast<int>(QVariant::Quaternion),
419 componentCount: 4);
420
421 auto morphTargetMapping = createChannelMapping(handler,
422 channelName: QLatin1String("MorphTargetWeights"),
423 targetId: Qt3DCore::QNodeId::createId(),
424 propertyName: "weights",
425 type: static_cast<int>(QVariant::List),
426 componentCount: 5);
427
428
429 QVector<ChannelMapping *> channelMappings
430 = { locationMapping, metalnessMapping,
431 baseColorMapping, roughnessMapping,
432 rotationMapping, morphTargetMapping };
433
434 // Create a few channels in the format description
435 ChannelNameAndType rotation = { QLatin1String("Rotation"),
436 static_cast<int>(QVariant::Quaternion),
437 4,
438 rotationMapping->peerId() };
439 ChannelNameAndType location = { QLatin1String("Location"),
440 static_cast<int>(QVariant::Vector3D),
441 3,
442 locationMapping->peerId() };
443 ChannelNameAndType baseColor = { QLatin1String("BaseColor"),
444 static_cast<int>(QVariant::Vector3D),
445 3,
446 baseColorMapping->peerId() };
447 ChannelNameAndType metalness = { QLatin1String("Metalness"),
448 static_cast<int>(QVariant::Double),
449 1,
450 metalnessMapping->peerId() };
451 ChannelNameAndType roughness = { QLatin1String("Roughness"),
452 static_cast<int>(QVariant::Double),
453 1,
454 roughnessMapping->peerId() };
455 ChannelNameAndType morphTarget = { QLatin1String("MorphTargetWeights"),
456 static_cast<int>(QVariant::List),
457 5,
458 morphTargetMapping->peerId() };
459 QVector<ChannelNameAndType> channelNamesAndTypes
460 = { rotation, location, baseColor, metalness, roughness,
461 morphTarget };
462
463 // And the matching indices
464 ComponentIndices rotationIndices = { 0, 1, 2, 3 };
465 ComponentIndices locationIndices = { 4, 5, 6 };
466 ComponentIndices baseColorIndices = { 7, 8, 9 };
467 ComponentIndices metalnessIndices = { 10 };
468 ComponentIndices roughnessIndices = { 11 };
469 ComponentIndices morphTargetIndices = { 12, 13, 14, 15, 16 };
470 QVector<ComponentIndices> channelComponentIndices
471 = { rotationIndices, locationIndices, baseColorIndices,
472 metalnessIndices, roughnessIndices, morphTargetIndices };
473
474 QVector<QBitArray> sourceClipMask = { QBitArray(4, true),
475 QBitArray(3, true),
476 QBitArray(3, true),
477 QBitArray(1, true),
478 QBitArray(1, true),
479 QBitArray(5, true) };
480
481 MappingData expectedLocationMapping;
482 expectedLocationMapping.targetId = locationMapping->targetId();
483 expectedLocationMapping.propertyName = locationMapping->propertyName();
484 expectedLocationMapping.type = locationMapping->type();
485 expectedLocationMapping.channelIndices = locationIndices;
486
487 MappingData expectedMetalnessMapping;
488 expectedMetalnessMapping.targetId = metalnessMapping->targetId();
489 expectedMetalnessMapping.propertyName = metalnessMapping->propertyName();
490 expectedMetalnessMapping.type = metalnessMapping->type();
491 expectedMetalnessMapping.channelIndices = metalnessIndices;
492
493 MappingData expectedBaseColorMapping;
494 expectedBaseColorMapping.targetId = baseColorMapping->targetId();
495 expectedBaseColorMapping.propertyName = baseColorMapping->propertyName();
496 expectedBaseColorMapping.type = baseColorMapping->type();
497 expectedBaseColorMapping.channelIndices = baseColorIndices;
498
499 MappingData expectedRoughnessMapping;
500 expectedRoughnessMapping.targetId = roughnessMapping->targetId();
501 expectedRoughnessMapping.propertyName = roughnessMapping->propertyName();
502 expectedRoughnessMapping.type = roughnessMapping->type();
503 expectedRoughnessMapping.channelIndices = roughnessIndices;
504
505 MappingData expectedRotationMapping;
506 expectedRotationMapping.targetId = rotationMapping->targetId();
507 expectedRotationMapping.propertyName = rotationMapping->propertyName();
508 expectedRotationMapping.type = rotationMapping->type();
509 expectedRotationMapping.channelIndices = rotationIndices;
510
511 MappingData expectedMorphTargetMapping;
512 expectedMorphTargetMapping.targetId = morphTargetMapping->targetId();
513 expectedMorphTargetMapping.propertyName = morphTargetMapping->propertyName();
514 expectedMorphTargetMapping.type = morphTargetMapping->type();
515 expectedMorphTargetMapping.channelIndices = morphTargetIndices;
516
517 QVector<MappingData> expectedResults
518 = { expectedLocationMapping,
519 expectedMetalnessMapping,
520 expectedBaseColorMapping,
521 expectedRoughnessMapping,
522 expectedRotationMapping,
523 expectedMorphTargetMapping };
524
525 QTest::newRow(dataTag: "multiple mappings")
526 << handler
527 << channelMappings
528 << channelNamesAndTypes
529 << channelComponentIndices
530 << sourceClipMask
531 << expectedResults;
532 }
533
534 // Single skeleton mapping
535 {
536 Handler *handler = new Handler();
537 const int jointCount = 4;
538 auto skeleton = createSkeleton(handler, jointCount);
539 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
540
541 QVector<ChannelMapping *> channelMappings = { channelMapping };
542
543 // Create a few channels in the format description
544 QVector<ChannelNameAndType> channelNamesAndTypes;
545 for (int i = 0; i < jointCount; ++i) {
546 ChannelNameAndType locationDescription = { QLatin1String("Location"),
547 static_cast<int>(QVariant::Vector3D),
548 3,
549 channelMapping->peerId() };
550 locationDescription.jointIndex = i;
551 locationDescription.jointTransformComponent = Translation;
552 channelNamesAndTypes.push_back(t: locationDescription);
553
554 ChannelNameAndType rotationDescription = { QLatin1String("Rotation"),
555 static_cast<int>(QVariant::Quaternion),
556 4,
557 channelMapping->peerId() };
558 rotationDescription.jointIndex = i;
559 rotationDescription.jointTransformComponent = Rotation;
560 channelNamesAndTypes.push_back(t: rotationDescription);
561
562 ChannelNameAndType scaleDescription = { QLatin1String("Scale"),
563 static_cast<int>(QVariant::Vector3D),
564 3,
565 channelMapping->peerId() };
566 scaleDescription.jointIndex = i;
567 scaleDescription.jointTransformComponent = Scale;
568 channelNamesAndTypes.push_back(t: scaleDescription);
569 }
570
571 // And the matching indices
572 QVector<ComponentIndices> channelComponentIndices;
573 channelComponentIndices.push_back(t: { 0, 1, 2 });
574 channelComponentIndices.push_back(t: { 3, 4, 5, 6 });
575 channelComponentIndices.push_back(t: { 7, 8, 9 });
576
577 channelComponentIndices.push_back(t: { 10, 11, 12 });
578 channelComponentIndices.push_back(t: { 13, 14, 15, 16 });
579 channelComponentIndices.push_back(t: { 17, 18, 19 });
580
581 channelComponentIndices.push_back(t: { 20, 21, 22 });
582 channelComponentIndices.push_back(t: { 23, 24, 25, 26 });
583 channelComponentIndices.push_back(t: { 27, 28, 29 });
584
585 channelComponentIndices.push_back(t: { 30, 31, 32 });
586 channelComponentIndices.push_back(t: { 33, 34, 35, 36 });
587 channelComponentIndices.push_back(t: { 37, 38, 39 });
588
589 QVector<QBitArray> sourceClipMask = { QBitArray(3, true),
590 QBitArray(4, true),
591 QBitArray(3, true),
592 QBitArray(3, true),
593 QBitArray(4, true),
594 QBitArray(3, true),
595 QBitArray(3, true),
596 QBitArray(4, true),
597 QBitArray(3, true),
598 QBitArray(3, true),
599 QBitArray(4, true),
600 QBitArray(3, true) };
601
602 QVector<MappingData> expectedResults;
603 int componentIndicesIndex = 0;
604 for (int i = 0; i < jointCount; ++i) {
605 MappingData locationMapping;
606 locationMapping.targetId = channelMapping->skeletonId();
607 locationMapping.propertyName = "translation";
608 locationMapping.type = static_cast<int>(QVariant::Vector3D);
609 locationMapping.channelIndices = channelComponentIndices[componentIndicesIndex++];
610 locationMapping.jointIndex = i;
611
612 MappingData rotationMapping;
613 rotationMapping.targetId = channelMapping->skeletonId();
614 rotationMapping.propertyName = "rotation";
615 rotationMapping.type = static_cast<int>(QVariant::Quaternion);
616 rotationMapping.channelIndices = channelComponentIndices[componentIndicesIndex++];
617 rotationMapping.jointIndex = i;
618
619 MappingData scaleMapping;
620 scaleMapping.targetId = channelMapping->skeletonId();
621 scaleMapping.propertyName = "scale";
622 scaleMapping.type = static_cast<int>(QVariant::Vector3D);
623 scaleMapping.channelIndices = channelComponentIndices[componentIndicesIndex++];
624 scaleMapping.jointIndex = i;
625
626 expectedResults << locationMapping << rotationMapping << scaleMapping;
627 }
628
629 QTest::newRow(dataTag: "single skeleton mapping")
630 << handler
631 << channelMappings
632 << channelNamesAndTypes
633 << channelComponentIndices
634 << sourceClipMask
635 << expectedResults;
636 }
637 }
638
639 void checkBuildPropertyMappings()
640 {
641 // GIVEN
642 QFETCH(Handler *, handler);
643 QFETCH(QVector<ChannelMapping *>, channelMappings);
644 QFETCH(QVector<ChannelNameAndType>, channelNamesAndTypes);
645 QFETCH(QVector<ComponentIndices>, channelComponentIndices);
646 QFETCH(QVector<QBitArray>, sourceClipMask);
647 QFETCH(QVector<MappingData>, expectedResults);
648
649 // WHEN
650 const QVector<MappingData> actualResults = buildPropertyMappings(channelMappings,
651 channelNamesAndTypes,
652 channelComponentIndices,
653 sourceClipMask);
654
655 // THEN
656 QCOMPARE(actualResults.size(), expectedResults.size());
657 for (int i = 0; i < actualResults.size(); ++i) {
658 const auto actualMapping = actualResults[i];
659 const auto expectedMapping = expectedResults[i];
660
661 QCOMPARE(actualMapping.targetId, expectedMapping.targetId);
662 QCOMPARE(actualMapping.jointIndex, expectedMapping.jointIndex);
663 QCOMPARE(actualMapping.propertyName, expectedMapping.propertyName);
664 QCOMPARE(actualMapping.type, expectedMapping.type);
665 QCOMPARE(actualMapping.channelIndices.size(), expectedMapping.channelIndices.size());
666 for (int j = 0; j < actualMapping.channelIndices.size(); ++j) {
667 QCOMPARE(actualMapping.channelIndices[j], expectedMapping.channelIndices[j]);
668 }
669 }
670
671 // Cleanup
672 delete handler;
673 }
674
675 void checkLocalTimeFromElapsedTime_data()
676 {
677 QTest::addColumn<double>(name: "elapsedTime");
678 QTest::addColumn<double>(name: "currentTime");
679 QTest::addColumn<double>(name: "playbackRate");
680 QTest::addColumn<double>(name: "duration");
681 QTest::addColumn<int>(name: "loopCount");
682 QTest::addColumn<int>(name: "currentLoop");
683 QTest::addColumn<double>(name: "expectedLocalTime");
684 QTest::addColumn<int>(name: "expectedCurrentLoop");
685
686 double elapsedTime;
687 double currentTime;
688 double playbackRate;
689 double duration;
690 int loopCount;
691 int currentLoop;
692 double expectedLocalTime;
693 int expectedCurrentLoop;
694
695 elapsedTime = 0.0;
696 currentTime = 0.0;
697 playbackRate = 1.0;
698 duration = 1.0;
699 loopCount = 1;
700 currentLoop = 0;
701 expectedLocalTime = 0.0;
702 expectedCurrentLoop = 0;
703 QTest::newRow(dataTag: "simple, t_current = 0, t_elapsed = 0, loop_current = 0")
704 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
705 << expectedLocalTime << expectedCurrentLoop;
706
707 elapsedTime = 0.5;
708 currentTime = 0.0;
709 playbackRate = 1.0;
710 duration = 1.0;
711 loopCount = 1;
712 currentLoop = 0;
713 expectedLocalTime = 0.5;
714 expectedCurrentLoop = 0;
715 QTest::newRow(dataTag: "simple, t_current = 0, t_elapsed = 0.5, loop_current = 0")
716 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
717 << expectedLocalTime << expectedCurrentLoop;
718
719 elapsedTime = 1.5;
720 currentTime = 0.0;
721 playbackRate = 1.0;
722 duration = 1.0;
723 loopCount = 1;
724 currentLoop = 0;
725 expectedLocalTime = 1.0;
726 expectedCurrentLoop = 0;
727 QTest::newRow(dataTag: "simple, t_current = 0, t_elapsed = 1.5, loop_current = 0")
728 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
729 << expectedLocalTime << expectedCurrentLoop;
730
731 elapsedTime = 0.5;
732 currentTime = 0.6;
733 playbackRate = 1.0;
734 duration = 1.0;
735 loopCount = 1;
736 currentLoop = 0;
737 expectedLocalTime = 1.0;
738 expectedCurrentLoop = 0;
739 QTest::newRow(dataTag: "simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 0")
740 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
741 << expectedLocalTime << expectedCurrentLoop;
742
743 elapsedTime = 0.5;
744 currentTime = 0.6;
745 playbackRate = 1.0;
746 duration = 1.0;
747 loopCount = 2;
748 currentLoop = 0;
749 expectedLocalTime = 0.1;
750 expectedCurrentLoop = 1;
751 QTest::newRow(dataTag: "simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 0, loop_count = 2")
752 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
753 << expectedLocalTime << expectedCurrentLoop;
754
755 elapsedTime = 0.5;
756 currentTime = 0.6;
757 playbackRate = 1.0;
758 duration = 1.0;
759 loopCount = 2;
760 currentLoop = 1;
761 expectedLocalTime = 1.0;
762 expectedCurrentLoop = 1; // We clamp at end of final loop
763 QTest::newRow(dataTag: "simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 1, loop_count = 2")
764 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
765 << expectedLocalTime << expectedCurrentLoop;
766
767 elapsedTime = 0.5;
768 currentTime = 0.6;
769 playbackRate = 0.1;
770 duration = 1.0;
771 loopCount = 2;
772 currentLoop = 1;
773 expectedLocalTime = 0.65;
774 expectedCurrentLoop = 1;
775 QTest::newRow(dataTag: "simple, t_current = 0.5, t_elapsed = 0.6, loop_current = 1, loop_count = 2")
776 << elapsedTime << currentTime << playbackRate << duration << loopCount << currentLoop
777 << expectedLocalTime << expectedCurrentLoop;
778 }
779
780 void checkLocalTimeFromElapsedTime()
781 {
782 // GIVEN
783 QFETCH(double, elapsedTime);
784 QFETCH(double, currentTime);
785 QFETCH(double, playbackRate);
786 QFETCH(double, duration);
787 QFETCH(int, loopCount);
788 QFETCH(int, currentLoop);
789 QFETCH(double, expectedLocalTime);
790 QFETCH(int, expectedCurrentLoop);
791
792 // WHEN
793 int actualCurrentLoop = currentLoop;
794 double actualLocalTime = localTimeFromElapsedTime(t_current_local: currentTime,
795 t_elapsed_global: elapsedTime,
796 playbackRate,
797 duration,
798 loopCount,
799 currentLoop&: actualCurrentLoop);
800
801 // THEN
802 QCOMPARE(actualCurrentLoop, expectedCurrentLoop);
803 QCOMPARE(actualLocalTime, expectedLocalTime);
804 }
805
806 void checkPreparePropertyChanges_data()
807 {
808 QTest::addColumn<Qt3DCore::QNodeId>(name: "animatorId");
809 QTest::addColumn<QVector<MappingData>>(name: "mappingData");
810 QTest::addColumn<QVector<float>>(name: "channelResults");
811 QTest::addColumn<AnimationRecord>(name: "expectedChanges");
812
813 Qt3DCore::QNodeId animatorId;
814 QVector<MappingData> mappingData;
815 QVector<float> channelResults;
816 AnimationRecord expectedChanges;
817
818 // Single property, vec3
819 {
820 animatorId = Qt3DCore::QNodeId::createId();
821 MappingData mapping;
822 mapping.targetId = Qt3DCore::QNodeId::createId();
823 mapping.propertyName = "translation";
824 mapping.type = static_cast<int>(QVariant::Vector3D);
825 mapping.channelIndices = QVector<int>() << 0 << 1 << 2;
826 mappingData.push_back(t: mapping);
827 channelResults = QVector<float>() << 1.0f << 2.0f << 3.0f;
828 expectedChanges.normalizedTime = 1.1f; // Invalid
829 expectedChanges.finalFrame = false;
830 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QVector3D(1.0f, 2.0f, 3.0f))});
831
832 QTest::newRow(dataTag: "vec3 translation, final = false")
833 << animatorId << mappingData << channelResults << expectedChanges;
834
835 expectedChanges.normalizedTime = 1.0f;
836 expectedChanges.finalFrame = true;
837
838 QTest::newRow(dataTag: "vec3 translation, final = true, normalizedTime = 1.0f")
839 << animatorId << mappingData << channelResults << expectedChanges;
840
841 mappingData.clear();
842 channelResults.clear();
843 expectedChanges.targetChanges.clear();
844 }
845
846 // Multiple properties, all vec3
847 {
848 animatorId = Qt3DCore::QNodeId::createId();
849 MappingData translationMapping;
850 translationMapping.targetId = Qt3DCore::QNodeId::createId();
851 translationMapping.propertyName = "translation";
852 translationMapping.type = static_cast<int>(QVariant::Vector3D);
853 translationMapping.channelIndices = QVector<int>() << 0 << 1 << 2;
854 mappingData.push_back(t: translationMapping);
855
856 MappingData scaleMapping;
857 scaleMapping.targetId = Qt3DCore::QNodeId::createId();
858 scaleMapping.propertyName = "scale";
859 scaleMapping.type = static_cast<int>(QVariant::Vector3D);
860 scaleMapping.channelIndices = QVector<int>() << 3 << 4 << 5;
861 mappingData.push_back(t: scaleMapping);
862
863 channelResults = QVector<float>() << 1.0f << 2.0f << 3.0f
864 << 4.0f << 5.0f << 6.0f;
865 expectedChanges.finalFrame = false;
866 expectedChanges.normalizedTime = -0.1f; // Invalid
867
868 expectedChanges.targetChanges.push_back(t: {translationMapping.targetId, translationMapping.propertyName, QVariant::fromValue(value: QVector3D(1.0f, 2.0f, 3.0f))});
869 expectedChanges.targetChanges.push_back(t: {scaleMapping.targetId, scaleMapping.propertyName, QVariant::fromValue(value: QVector3D(4.0f, 5.0f, 6.0f))});
870
871 QTest::newRow(dataTag: "vec3 translation, vec3 scale, final = false")
872 << animatorId << mappingData << channelResults << expectedChanges;
873
874 expectedChanges.normalizedTime = 0.5f;
875 expectedChanges.finalFrame = true;
876
877 QTest::newRow(dataTag: "vec3 translation, vec3 scale, final = true")
878 << animatorId << mappingData << channelResults << expectedChanges;
879
880 mappingData.clear();
881 channelResults.clear();
882 expectedChanges.targetChanges.clear();
883 }
884
885 // Single property, double
886 {
887 animatorId = Qt3DCore::QNodeId::createId();
888 MappingData mapping;
889 mapping.targetId = Qt3DCore::QNodeId::createId();
890 mapping.propertyName = "mass";
891 mapping.type = static_cast<int>(QVariant::Double);
892 mapping.channelIndices = QVector<int>() << 0;
893 mappingData.push_back(t: mapping);
894 channelResults = QVector<float>() << 3.5f;
895 expectedChanges.finalFrame = false;
896 expectedChanges.normalizedTime = -1.0f; // Invalid
897 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: 3.5f)});
898
899 QTest::newRow(dataTag: "double mass")
900 << animatorId << mappingData << channelResults << expectedChanges;
901
902 mappingData.clear();
903 channelResults.clear();
904 expectedChanges.targetChanges.clear();
905 }
906
907 // Single property, vec2
908 {
909 animatorId = Qt3DCore::QNodeId::createId();
910 MappingData mapping;
911 mapping.targetId = Qt3DCore::QNodeId::createId();
912 mapping.propertyName = "pos";
913 mapping.type = static_cast<int>(QVariant::Vector2D);
914 mapping.channelIndices = QVector<int>() << 0 << 1;
915 mappingData.push_back(t: mapping);
916 channelResults = QVector<float>() << 2.0f << 1.0f;
917 expectedChanges.finalFrame = false;
918 expectedChanges.normalizedTime = 1.1f; // Invalid
919 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QVector2D(2.0f, 1.0f))});
920
921 QTest::newRow(dataTag: "vec2 pos")
922 << animatorId << mappingData << channelResults << expectedChanges;
923
924 mappingData.clear();
925 channelResults.clear();
926 expectedChanges.targetChanges.clear();
927 }
928
929 // Single property, vec4
930 {
931 animatorId = Qt3DCore::QNodeId::createId();
932 MappingData mapping;
933 mapping.targetId = Qt3DCore::QNodeId::createId();
934 mapping.propertyName = "foo";
935 mapping.type = static_cast<int>(QVariant::Vector4D);
936 mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3;
937 mappingData.push_back(t: mapping);
938 channelResults = QVector<float>() << 4.0f << 3.0f << 2.0f << 1.0f;
939 expectedChanges.finalFrame = false;
940 expectedChanges.normalizedTime = 1.1f; // Invalid
941 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QVector4D(4.0f, 3.0f, 2.0f, 1.0f))});
942
943 QTest::newRow(dataTag: "vec4 foo")
944 << animatorId << mappingData << channelResults << expectedChanges;
945
946 mappingData.clear();
947 channelResults.clear();
948 expectedChanges.targetChanges.clear();
949 }
950
951 // Single property, quaternion
952 {
953 animatorId = Qt3DCore::QNodeId::createId();
954 MappingData mapping;
955 mapping.targetId = Qt3DCore::QNodeId::createId();
956 mapping.propertyName = "rotation";
957 mapping.type = static_cast<int>(QVariant::Quaternion);
958 mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3;
959 mappingData.push_back(t: mapping);
960 channelResults = QVector<float>() << 1.0f << 0.0f << 0.0f << 1.0f;
961 expectedChanges.finalFrame = false;
962 expectedChanges.normalizedTime = -0.1f; // Invalid
963 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QQuaternion(1.0f, 0.0f, 0.0f, 1.0f).normalized())});
964
965 QTest::newRow(dataTag: "quaternion rotation")
966 << animatorId << mappingData << channelResults << expectedChanges;
967
968 mappingData.clear();
969 channelResults.clear();
970 expectedChanges.targetChanges.clear();
971 }
972
973 // Single property, QColor
974 {
975 animatorId = Qt3DCore::QNodeId::createId();
976 MappingData mapping;
977 mapping.targetId = Qt3DCore::QNodeId::createId();
978 mapping.propertyName = "color";
979 mapping.type = static_cast<int>(QVariant::Color);
980 mapping.channelIndices = QVector<int>() << 0 << 1 << 2;
981 mappingData.push_back(t: mapping);
982 channelResults = QVector<float>() << 0.5f << 0.4f << 0.3f;
983 expectedChanges.finalFrame = false;
984 expectedChanges.normalizedTime = 1.1f; // Invalid
985 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QColor::fromRgbF(r: 0.5f, g: 0.4f, b: 0.3f))});
986
987 QTest::newRow(dataTag: "QColor rgb color")
988 << animatorId << mappingData << channelResults << expectedChanges;
989
990 mappingData.clear();
991 channelResults.clear();
992 expectedChanges.targetChanges.clear();
993 }
994
995 // Single property, QColor
996 {
997 animatorId = Qt3DCore::QNodeId::createId();
998 MappingData mapping;
999 mapping.targetId = Qt3DCore::QNodeId::createId();
1000 mapping.propertyName = "color";
1001 mapping.type = static_cast<int>(QVariant::Color);
1002 mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3;
1003 mappingData.push_back(t: mapping);
1004 channelResults = QVector<float>() << 0.5f << 0.4f << 0.3f << 0.2f;
1005 expectedChanges.finalFrame = false;
1006 expectedChanges.normalizedTime = 1.1f; // Invalid
1007 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: QColor::fromRgbF(r: 0.5f, g: 0.4f, b: 0.3f, a: 0.2f))});
1008
1009 QTest::newRow(dataTag: "QColor rgba color")
1010 << animatorId << mappingData << channelResults << expectedChanges;
1011
1012 mappingData.clear();
1013 channelResults.clear();
1014 expectedChanges.targetChanges.clear();
1015 }
1016
1017 // Single property, QVariantList
1018 {
1019 animatorId = Qt3DCore::QNodeId::createId();
1020 MappingData mapping;
1021 mapping.targetId = Qt3DCore::QNodeId::createId();
1022 mapping.propertyName = "weights";
1023 mapping.type = static_cast<int>(QVariant::List);
1024 mapping.channelIndices = QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6;
1025 mappingData.push_back(t: mapping);
1026 channelResults = QVector<float>() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f;
1027 expectedChanges.finalFrame = false;
1028 expectedChanges.normalizedTime = 1.1f; // Invalid
1029 QVariantList expectedValue = QVariantList() << 0.5f << 0.4f << 0.3f << 0.0f << 1.0f << 0.6f << 0.9f;
1030 expectedChanges.targetChanges.push_back(t: {mapping.targetId, mapping.propertyName, QVariant::fromValue(value: expectedValue)});
1031
1032 QTest::newRow(dataTag: "QVariantList weights")
1033 << animatorId << mappingData << channelResults << expectedChanges;
1034
1035 mappingData.clear();
1036 channelResults.clear();
1037 expectedChanges.targetChanges.clear();
1038 }
1039
1040 }
1041
1042 void checkPreparePropertyChanges()
1043 {
1044 // GIVEN
1045 QFETCH(Qt3DCore::QNodeId, animatorId);
1046 QFETCH(QVector<MappingData>, mappingData);
1047 QFETCH(QVector<float>, channelResults);
1048 QFETCH(AnimationRecord, expectedChanges);
1049
1050 // WHEN
1051 AnimationRecord actualChanges = prepareAnimationRecord(animatorId, mappingDataVec: mappingData, channelResults,
1052 finalFrame: expectedChanges.finalFrame, normalizedLocalTime: expectedChanges.normalizedTime);
1053
1054 // THEN
1055 QCOMPARE(actualChanges.targetChanges.size(), expectedChanges.targetChanges.size());
1056 for (int i = 0; i < actualChanges.targetChanges.size(); ++i) {
1057 const auto &expectedChange = expectedChanges.targetChanges[i];
1058 const auto &actualChange = actualChanges.targetChanges[i];
1059
1060 QCOMPARE(actualChange.targetId, expectedChange.targetId);
1061 QCOMPARE(actualChange.propertyName, expectedChange.propertyName);
1062 QCOMPARE(actualChange.value, expectedChange.value);
1063 }
1064 }
1065
1066 void checkPrepareCallbacks_data()
1067 {
1068 QTest::addColumn<QVector<MappingData>>(name: "mappingData");
1069 QTest::addColumn<QVector<float>>(name: "channelResults");
1070 QTest::addColumn<QVector<AnimationCallbackAndValue> >(name: "expectedValues");
1071
1072 QVector<MappingData> mappingData;
1073 QVector<float> channelResults;
1074 QVector<AnimationCallbackAndValue> expectedValues;
1075
1076 // vec3
1077 {
1078 DummyCallback callback; // safe since the object is never used, just the address
1079 MappingData mapping;
1080 mapping.targetId = Qt3DCore::QNodeId::createId();
1081 mapping.propertyName = "translation";
1082 mapping.type = static_cast<int>(QVariant::Vector3D);
1083 mapping.channelIndices = QVector<int>() << 0 << 1 << 2;
1084 mapping.callback = &callback;
1085 mapping.callbackFlags = {};
1086 mappingData.push_back(t: mapping);
1087 channelResults = QVector<float>() << 1.0f << 2.0f << 3.0f;
1088
1089 AnimationCallbackAndValue cbv;
1090 cbv.callback = mapping.callback;
1091 cbv.flags = mapping.callbackFlags;
1092 cbv.value = QVariant::fromValue<QVector3D>(value: QVector3D(1.0f, 2.0f, 3.0f));
1093 expectedValues.push_back(t: cbv);
1094
1095 QTest::newRow(dataTag: "vec3 translation, no flags") << mappingData << channelResults << expectedValues;
1096
1097 mappingData.clear();
1098 channelResults.clear();
1099 expectedValues.clear();
1100 }
1101
1102 // double
1103 {
1104 DummyCallback callback;
1105 MappingData mapping;
1106 mapping.targetId = Qt3DCore::QNodeId::createId();
1107 mapping.propertyName = "something";
1108 mapping.type = static_cast<int>(QVariant::Double);
1109 mapping.channelIndices = QVector<int>() << 0;
1110 mapping.callback = &callback;
1111 mapping.callbackFlags = {};
1112 mappingData.push_back(t: mapping);
1113 channelResults = QVector<float>() << 1.0f;
1114
1115 AnimationCallbackAndValue cbv;
1116 cbv.callback = mapping.callback;
1117 cbv.flags = mapping.callbackFlags;
1118 cbv.value = QVariant(double(1.0));
1119 expectedValues.push_back(t: cbv);
1120
1121 QTest::newRow(dataTag: "double, no flags") << mappingData << channelResults << expectedValues;
1122
1123 mappingData.clear();
1124 channelResults.clear();
1125 expectedValues.clear();
1126 }
1127
1128 // float, set a flag
1129 {
1130 DummyCallback callback;
1131 MappingData mapping;
1132 mapping.targetId = Qt3DCore::QNodeId::createId();
1133 mapping.propertyName = "opacity";
1134 mapping.type = static_cast<int>(QMetaType::Float);
1135 mapping.channelIndices = QVector<int>() << 0;
1136 mapping.callback = &callback;
1137 mapping.callbackFlags = Qt3DAnimation::QAnimationCallback::OnThreadPool;
1138 mappingData.push_back(t: mapping);
1139 channelResults = QVector<float>() << 0.5f;
1140
1141 AnimationCallbackAndValue cbv;
1142 cbv.callback = mapping.callback;
1143 cbv.flags = mapping.callbackFlags;
1144 cbv.value = QVariant(float(0.5f));
1145 expectedValues.push_back(t: cbv);
1146
1147 QTest::newRow(dataTag: "float, OnThreadPool") << mappingData << channelResults << expectedValues;
1148
1149 mappingData.clear();
1150 channelResults.clear();
1151 expectedValues.clear();
1152 }
1153 }
1154
1155 void checkPrepareCallbacks()
1156 {
1157 // GIVEN
1158 QFETCH(QVector<MappingData>, mappingData);
1159 QFETCH(QVector<float>, channelResults);
1160 QFETCH(QVector<AnimationCallbackAndValue>, expectedValues);
1161
1162 // WHEN
1163 QVector<AnimationCallbackAndValue> callbacks = prepareCallbacks(mappingDataVec: mappingData, channelResults);
1164
1165 // THEN
1166 QCOMPARE(callbacks.size(), expectedValues.size());
1167 for (int i = 0; i < callbacks.size(); ++i) {
1168 auto expected = expectedValues[i];
1169 auto actual = callbacks[i];
1170
1171 QCOMPARE(actual.callback, expected.callback);
1172 QCOMPARE(actual.flags, expected.flags);
1173 QCOMPARE(actual.value, expected.value);
1174 }
1175 }
1176
1177 void checkEvaluateClipAtLocalTime_data()
1178 {
1179 QTest::addColumn<Handler *>(name: "handler");
1180 QTest::addColumn<AnimationClip *>(name: "clip");
1181 QTest::addColumn<float>(name: "localTime");
1182 QTest::addColumn<ClipResults>(name: "expectedResults");
1183
1184 Handler *handler;
1185 AnimationClip *clip;
1186 float localTime;
1187 ClipResults expectedResults;
1188
1189 {
1190 handler = new Handler();
1191 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1192 localTime = 0.0f;
1193 expectedResults = QVector<float>() << 0.0f << 0.0f << 0.0f;
1194
1195 QTest::newRow(dataTag: "clip1.json, t = 0.0")
1196 << handler << clip << localTime << expectedResults;
1197 expectedResults.clear();
1198 }
1199
1200 {
1201 handler = new Handler();
1202 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1203 localTime = clip->duration();
1204 expectedResults = QVector<float>() << 5.0f << 0.0f << 0.0f;
1205
1206 QTest::newRow(dataTag: "clip1.json, t = duration")
1207 << handler << clip << localTime << expectedResults;
1208 expectedResults.clear();
1209 }
1210
1211 {
1212 handler = new Handler();
1213 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1214 localTime = clip->duration() / 2.0f;
1215 expectedResults = QVector<float>() << 2.5f << 0.0f << 0.0f;
1216
1217 QTest::newRow(dataTag: "clip1.json, t = duration/2")
1218 << handler << clip << localTime << expectedResults;
1219 expectedResults.clear();
1220 }
1221
1222 {
1223 handler = new Handler();
1224 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1225 localTime = 0.0f;
1226 expectedResults = QVector<float>()
1227 << 0.0f << 0.0f << 0.0f // Translation
1228 << 1.0f << 0.0f << 0.0f << 0.0f; // Rotation
1229
1230 QTest::newRow(dataTag: "clip2.json, t = 0.0")
1231 << handler << clip << localTime << expectedResults;
1232 expectedResults.clear();
1233 }
1234 {
1235 handler = new Handler();
1236 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1237 localTime = clip->duration();
1238 expectedResults = QVector<float>()
1239 << 5.0f << 0.0f << 0.0f // Translation
1240 << 0.0f << 0.0f << -1.0f << 0.0f; // Rotation
1241
1242 QTest::newRow(dataTag: "clip2.json, t = duration")
1243 << handler << clip << localTime << expectedResults;
1244 expectedResults.clear();
1245 }
1246 {
1247 handler = new Handler();
1248 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1249 localTime = clip->duration() / 2.0f;
1250 expectedResults = QVector<float>()
1251 << 2.5f << 0.0f << 0.0f // Translation
1252 << 0.5f << 0.0f << -0.5f << 0.0f; // Rotation
1253
1254 QTest::newRow(dataTag: "clip2.json, t = duration/2")
1255 << handler << clip << localTime << expectedResults;
1256 expectedResults.clear();
1257 }
1258 {
1259 // a clip with linear interpolation
1260 handler = new Handler();
1261 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip4.json"));
1262 localTime = clip->duration();
1263 expectedResults = QVector<float>() << 5.0 << -2.0f << 6.0f;
1264
1265 QTest::newRow(dataTag: "clip4.json, linear, t = duration")
1266 << handler << clip << localTime << expectedResults;
1267 expectedResults.clear();
1268 }
1269 {
1270 // a clip with linear interpolation
1271 handler = new Handler();
1272 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip4.json"));
1273 localTime = clip->duration() / 2.0f;
1274 expectedResults = QVector<float>() << 2.5f << -1.0f << 3.0f;
1275
1276 QTest::newRow(dataTag: "clip4.json, linear, t = duration/2")
1277 << handler << clip << localTime << expectedResults;
1278 expectedResults.clear();
1279 }
1280 {
1281 // a clip with slerp interpolation
1282 handler = new Handler();
1283 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip6.json"));
1284 localTime = clip->duration() / 2.0f;
1285 expectedResults = QVector<float>() << 0.923822f << 0.382626f << 0.0f << 0.0f;
1286
1287 QTest::newRow(dataTag: "clip6.json, slerp, t = duration/2")
1288 << handler << clip << localTime << expectedResults;
1289 expectedResults.clear();
1290 }
1291 }
1292
1293 void checkEvaluateClipAtLocalTime()
1294 {
1295 // GIVEN
1296 QFETCH(Handler *, handler);
1297 QFETCH(AnimationClip *, clip);
1298 QFETCH(float, localTime);
1299 QFETCH(ClipResults, expectedResults);
1300
1301 // WHEN
1302 ClipResults actualResults = evaluateClipAtLocalTime(clip, localTime);
1303
1304 // THEN
1305 QCOMPARE(actualResults.size(), expectedResults.size());
1306 for (int i = 0; i < actualResults.size(); ++i) {
1307 auto actual = actualResults[i];
1308 auto expected = expectedResults[i];
1309
1310 QVERIFY(fuzzyCompare(actual, expected) == true);
1311 }
1312
1313 // Cleanup
1314 delete handler;
1315 }
1316
1317 void checkEvaluateClipAtPhase_data()
1318 {
1319 QTest::addColumn<Handler *>(name: "handler");
1320 QTest::addColumn<AnimationClip *>(name: "clip");
1321 QTest::addColumn<float>(name: "phase");
1322 QTest::addColumn<ClipResults>(name: "expectedResults");
1323
1324 Handler *handler;
1325 AnimationClip *clip;
1326 float phase;
1327 ClipResults expectedResults;
1328
1329 {
1330 handler = new Handler();
1331 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1332 phase = 0.0f;
1333 expectedResults = QVector<float>() << 0.0f << 0.0f << 0.0f;
1334
1335 QTest::newRow(dataTag: "clip1.json, phi = 0.0")
1336 << handler << clip << phase << expectedResults;
1337 expectedResults.clear();
1338 }
1339
1340 {
1341 handler = new Handler();
1342 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1343 phase = 1.0f;
1344 expectedResults = QVector<float>() << 5.0f << 0.0f << 0.0f;
1345
1346 QTest::newRow(dataTag: "clip1.json, phi = 1.0")
1347 << handler << clip << phase << expectedResults;
1348 expectedResults.clear();
1349 }
1350
1351 {
1352 handler = new Handler();
1353 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1354 phase = 0.5f;
1355 expectedResults = QVector<float>() << 2.5f << 0.0f << 0.0f;
1356
1357 QTest::newRow(dataTag: "clip1.json, phi = 0.5")
1358 << handler << clip << phase << expectedResults;
1359 expectedResults.clear();
1360 }
1361
1362 {
1363 handler = new Handler();
1364 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1365 phase = 0.0f;
1366 expectedResults = QVector<float>()
1367 << 0.0f << 0.0f << 0.0f // Translation
1368 << 1.0f << 0.0f << 0.0f << 0.0f; // Rotation
1369
1370 QTest::newRow(dataTag: "clip2.json, phi = 0.0")
1371 << handler << clip << phase << expectedResults;
1372 expectedResults.clear();
1373 }
1374 {
1375 handler = new Handler();
1376 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1377 phase = 1.0f;
1378 expectedResults = QVector<float>()
1379 << 5.0f << 0.0f << 0.0f // Translation
1380 << 0.0f << 0.0f << -1.0f << 0.0f; // Rotation
1381
1382 QTest::newRow(dataTag: "clip2.json, t = 1.0")
1383 << handler << clip << phase << expectedResults;
1384 expectedResults.clear();
1385 }
1386 {
1387 handler = new Handler();
1388 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip2.json"));
1389 phase = 0.5f;
1390 expectedResults = QVector<float>()
1391 << 2.5f << 0.0f << 0.0f // Translation
1392 << 0.5f << 0.0f << -0.5f << 0.0f; // Rotation
1393
1394 QTest::newRow(dataTag: "clip2.json, phi = 0.5")
1395 << handler << clip << phase << expectedResults;
1396 expectedResults.clear();
1397 }
1398 }
1399
1400 void checkEvaluateClipAtPhase()
1401 {
1402 // GIVEN
1403 QFETCH(Handler *, handler);
1404 QFETCH(AnimationClip *, clip);
1405 QFETCH(float, phase);
1406 QFETCH(ClipResults, expectedResults);
1407
1408 // WHEN
1409 ClipResults actualResults = evaluateClipAtPhase(clip, phase);
1410
1411 // THEN
1412 QCOMPARE(actualResults.size(), expectedResults.size());
1413 for (int i = 0; i < actualResults.size(); ++i) {
1414 auto actual = actualResults[i];
1415 auto expected = expectedResults[i];
1416
1417 QVERIFY(fuzzyCompare(actual, expected) == true);
1418 }
1419
1420 // Cleanup
1421 delete handler;
1422 }
1423
1424 void checkChannelComponentsToIndicesHelper_data()
1425 {
1426 QTest::addColumn<Channel>(name: "channel");
1427 QTest::addColumn<int>(name: "dataType");
1428 QTest::addColumn<int>(name: "expectedChannelComponentCount");
1429 QTest::addColumn<int>(name: "offset");
1430 QTest::addColumn<QVector<char>>(name: "suffixes");
1431 QTest::addColumn<QVector<int>>(name: "expectedResults");
1432
1433 Channel channel;
1434 int dataType;
1435 int expectedChannelComponentCount;
1436 int offset;
1437 QVector<char> suffixes;
1438 QVector<int> expectedResults;
1439
1440 // already sorted vec3, no component names, with and without offset
1441 {
1442 channel = Channel();
1443 channel.name = QLatin1String("Location");
1444 channel.channelComponents.resize(asize: 3);
1445 // leave 'name' empty
1446
1447 dataType = static_cast<int>(QVariant::Vector3D);
1448 expectedChannelComponentCount = 3;
1449 offset = 0;
1450 // suffixes expected to be ignored
1451 expectedResults = (QVector<int>() << 0 << 1 << 2);
1452
1453 QTest::newRow(dataTag: "vec3 location, pre-sorted, no component names, offset = 0")
1454 << channel << dataType << expectedChannelComponentCount
1455 << offset << suffixes << expectedResults;
1456
1457 expectedResults.clear();
1458
1459 offset = 4;
1460 expectedResults = (QVector<int>() << 4 << 5 << 6);
1461 QTest::newRow(dataTag: "vec3 location, pre-sorted, no component names, offset = 4")
1462 << channel << dataType << expectedChannelComponentCount
1463 << offset << suffixes << expectedResults;
1464 expectedResults.clear();
1465 }
1466
1467 // vec3 with and without offset
1468 {
1469 channel = Channel();
1470 channel.name = QLatin1String("Location");
1471 channel.channelComponents.resize(asize: 3);
1472 channel.channelComponents[0].name = QLatin1String("Location X");
1473 channel.channelComponents[1].name = QLatin1String("Location Y");
1474 channel.channelComponents[2].name = QLatin1String("Location Z");
1475
1476 dataType = static_cast<int>(QVariant::Vector3D);
1477 expectedChannelComponentCount = 3;
1478 offset = 0;
1479 suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
1480 expectedResults = (QVector<int>() << 0 << 1 << 2);
1481
1482 QTest::newRow(dataTag: "vec3 location, offset = 0")
1483 << channel << dataType << expectedChannelComponentCount
1484 << offset << suffixes << expectedResults;
1485
1486 expectedResults.clear();
1487
1488 offset = 4;
1489 expectedResults = (QVector<int>() << 4 << 5 << 6);
1490 QTest::newRow(dataTag: "vec3 location, offset = 4")
1491 << channel << dataType << expectedChannelComponentCount
1492 << offset << suffixes << expectedResults;
1493
1494 suffixes.clear();
1495 expectedResults.clear();
1496 }
1497
1498 // vec2 with and without offset
1499 {
1500 channel = Channel();
1501 channel.name = QLatin1String("pos");
1502 channel.channelComponents.resize(asize: 2);
1503 channel.channelComponents[0].name = QLatin1String("pos X");
1504 channel.channelComponents[1].name = QLatin1String("pos Y");
1505
1506 dataType = static_cast<int>(QVariant::Vector2D);
1507 expectedChannelComponentCount = 2;
1508 offset = 0;
1509 suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
1510 expectedResults = (QVector<int>() << 0 << 1);
1511
1512 QTest::newRow(dataTag: "vec2 pos, offset = 0")
1513 << channel << dataType << expectedChannelComponentCount
1514 << offset << suffixes << expectedResults;
1515
1516 expectedResults.clear();
1517
1518 offset = 2;
1519 expectedResults = (QVector<int>() << 2 << 3);
1520 QTest::newRow(dataTag: "vec2 pos, offset = 2")
1521 << channel << dataType << expectedChannelComponentCount
1522 << offset << suffixes << expectedResults;
1523
1524 suffixes.clear();
1525 expectedResults.clear();
1526 }
1527
1528 // vec4 with and without offset
1529 {
1530 channel = Channel();
1531 channel.name = QLatin1String("foo");
1532 channel.channelComponents.resize(asize: 4);
1533 channel.channelComponents[0].name = QLatin1String("foo X");
1534 channel.channelComponents[1].name = QLatin1String("foo Y");
1535 channel.channelComponents[2].name = QLatin1String("foo Z");
1536 channel.channelComponents[3].name = QLatin1String("foo W");
1537
1538 dataType = static_cast<int>(QVariant::Vector4D);
1539 expectedChannelComponentCount = 4;
1540 offset = 0;
1541 suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
1542 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
1543
1544 QTest::newRow(dataTag: "vec4 foo, offset = 0")
1545 << channel << dataType << expectedChannelComponentCount
1546 << offset << suffixes << expectedResults;
1547
1548 expectedResults.clear();
1549
1550 offset = 10;
1551 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
1552 QTest::newRow(dataTag: "vec4 foo, offset = 10")
1553 << channel << dataType << expectedChannelComponentCount
1554 << offset << suffixes << expectedResults;
1555
1556 suffixes.clear();
1557 expectedResults.clear();
1558 }
1559
1560 // double with and without offset
1561 {
1562 channel = Channel();
1563 channel.name = QLatin1String("foo");
1564 channel.channelComponents.resize(asize: 1);
1565 channel.channelComponents[0].name = QLatin1String("Mass X");
1566
1567 dataType = static_cast<int>(QVariant::Double);
1568 expectedChannelComponentCount = 1;
1569 offset = 0;
1570 suffixes = (QVector<char>() << 'X' << 'Y' << 'Z' << 'W');
1571 expectedResults = (QVector<int>() << 0);
1572
1573 QTest::newRow(dataTag: "double Mass, offset = 0")
1574 << channel << dataType << expectedChannelComponentCount
1575 << offset << suffixes << expectedResults;
1576
1577 expectedResults.clear();
1578
1579 offset = 5;
1580 expectedResults = (QVector<int>() << 5);
1581 QTest::newRow(dataTag: "double Mass, offset = 5")
1582 << channel << dataType << expectedChannelComponentCount
1583 << offset << suffixes << expectedResults;
1584
1585 suffixes.clear();
1586 expectedResults.clear();
1587 }
1588
1589 // quaternion with and without offset
1590 {
1591 channel = Channel();
1592 channel.name = QLatin1String("Rotation");
1593 channel.channelComponents.resize(asize: 4);
1594 channel.channelComponents[0].name = QLatin1String("Rotation W");
1595 channel.channelComponents[1].name = QLatin1String("Rotation X");
1596 channel.channelComponents[2].name = QLatin1String("Rotation Y");
1597 channel.channelComponents[3].name = QLatin1String("Rotation Z");
1598
1599 dataType = static_cast<int>(QVariant::Quaternion);
1600 expectedChannelComponentCount = 4;
1601 offset = 0;
1602 suffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z');
1603 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
1604
1605 QTest::newRow(dataTag: "quaternion Rotation, offset = 0")
1606 << channel << dataType << expectedChannelComponentCount
1607 << offset << suffixes << expectedResults;
1608
1609 expectedResults.clear();
1610
1611 offset = 10;
1612 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
1613 QTest::newRow(dataTag: "quaternion Rotation, offset = 10")
1614 << channel << dataType << expectedChannelComponentCount
1615 << offset << suffixes << expectedResults;
1616
1617 suffixes.clear();
1618 expectedResults.clear();
1619 }
1620
1621 // quaternion with and without offset, randomized
1622 {
1623 channel = Channel();
1624 channel.name = QLatin1String("Rotation");
1625 channel.channelComponents.resize(asize: 4);
1626 channel.channelComponents[0].name = QLatin1String("Rotation X");
1627 channel.channelComponents[1].name = QLatin1String("Rotation W");
1628 channel.channelComponents[2].name = QLatin1String("Rotation Z");
1629 channel.channelComponents[3].name = QLatin1String("Rotation Y");
1630
1631 dataType = static_cast<int>(QVariant::Quaternion);
1632 expectedChannelComponentCount = 4;
1633 offset = 0;
1634 suffixes = (QVector<char>() << 'W' << 'X' << 'Y' << 'Z');
1635 expectedResults = (QVector<int>() << 1 << 0 << 3 << 2);
1636
1637 QTest::newRow(dataTag: "quaternion Rotation, offset = 0, randomized")
1638 << channel << dataType << expectedChannelComponentCount
1639 << offset << suffixes << expectedResults;
1640
1641 expectedResults.clear();
1642
1643 offset = 10;
1644 expectedResults = (QVector<int>() << 11 << 10 << 13 << 12);
1645 QTest::newRow(dataTag: "quaternion Rotation, offset = 10, randomized")
1646 << channel << dataType << expectedChannelComponentCount
1647 << offset << suffixes << expectedResults;
1648
1649 suffixes.clear();
1650 expectedResults.clear();
1651 }
1652
1653 // color with and without offset 3 components
1654 {
1655 channel = Channel();
1656 channel.name = QLatin1String("Color");
1657 channel.channelComponents.resize(asize: 3);
1658 channel.channelComponents[0].name = QLatin1String("Color R");
1659 channel.channelComponents[1].name = QLatin1String("Color G");
1660 channel.channelComponents[2].name = QLatin1String("Color B");
1661
1662 dataType = static_cast<int>(QVariant::Color);
1663 expectedChannelComponentCount = 3;
1664 offset = 0;
1665 suffixes = (QVector<char>() << 'R' << 'G' << 'B');
1666 expectedResults = (QVector<int>() << 0 << 1 << 2);
1667
1668 QTest::newRow(dataTag: "QColor RGB Color, offset = 0")
1669 << channel << dataType << expectedChannelComponentCount
1670 << offset << suffixes << expectedResults;
1671
1672 expectedResults.clear();
1673
1674 offset = 10;
1675 expectedResults = (QVector<int>() << 10 << 11 << 12);
1676 QTest::newRow(dataTag: "QColor RGB Color, offset = 10")
1677 << channel << dataType << expectedChannelComponentCount
1678 << offset << suffixes << expectedResults;
1679
1680 suffixes.clear();
1681 expectedResults.clear();
1682 }
1683
1684 // color with and without offset 4 components
1685 {
1686 channel = Channel();
1687 channel.name = QLatin1String("Color");
1688 channel.channelComponents.resize(asize: 4);
1689 channel.channelComponents[0].name = QLatin1String("Color R");
1690 channel.channelComponents[1].name = QLatin1String("Color G");
1691 channel.channelComponents[2].name = QLatin1String("Color B");
1692 channel.channelComponents[3].name = QLatin1String("Color A");
1693
1694 dataType = static_cast<int>(QVariant::Color);
1695 expectedChannelComponentCount = 4;
1696 offset = 0;
1697 suffixes = (QVector<char>() << 'R' << 'G' << 'B' << 'A');
1698 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
1699
1700 QTest::newRow(dataTag: "QColor RGBA Color, offset = 0")
1701 << channel << dataType << expectedChannelComponentCount
1702 << offset << suffixes << expectedResults;
1703
1704 expectedResults.clear();
1705
1706 offset = 10;
1707 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
1708 QTest::newRow(dataTag: "QColor RGBA Color, offset = 10")
1709 << channel << dataType << expectedChannelComponentCount
1710 << offset << suffixes << expectedResults;
1711
1712 suffixes.clear();
1713 expectedResults.clear();
1714 }
1715
1716 // weights as list with and without offset
1717 {
1718 channel = Channel();
1719 channel.name = QLatin1String("MorphWeights");
1720 channel.channelComponents.resize(asize: 6);
1721 // leave channel component names empty
1722
1723 dataType = static_cast<int>(QVariant::List);
1724 expectedChannelComponentCount = 6;
1725 offset = 0;
1726 // suffixes expected to be ignored
1727 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5);
1728
1729 QTest::newRow(dataTag: "MorphWeights List count = 6, offset = 0")
1730 << channel << dataType << expectedChannelComponentCount
1731 << offset << suffixes << expectedResults;
1732 expectedResults.clear();
1733
1734 offset = 10;
1735 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13 << 14 << 15);
1736 QTest::newRow(dataTag: "MorphWeights List count = 6, offset = 10")
1737 << channel << dataType << expectedChannelComponentCount
1738 << offset << suffixes << expectedResults;
1739 suffixes.clear();
1740 expectedResults.clear();
1741 }
1742
1743 // weights as vec of float with and without offset
1744 {
1745 channel = Channel();
1746 channel.name = QLatin1String("MorphWeights");
1747 channel.channelComponents.resize(asize: 6);
1748 // leave channel component names empty
1749
1750 dataType = qMetaTypeId<QVector<float>>();
1751 expectedChannelComponentCount = 6;
1752 offset = 0;
1753 // suffixes expected to be ignored
1754 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3 << 4 << 5);
1755
1756 QTest::newRow(dataTag: "MorphWeights Vec count = 6, offset = 0")
1757 << channel << dataType << expectedChannelComponentCount
1758 << offset << suffixes << expectedResults;
1759 expectedResults.clear();
1760
1761 offset = 10;
1762 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13 << 14 << 15);
1763 QTest::newRow(dataTag: "MorphWeights Vec count = 6, offset = 10")
1764 << channel << dataType << expectedChannelComponentCount
1765 << offset << suffixes << expectedResults;
1766 suffixes.clear();
1767 expectedResults.clear();
1768 }
1769 }
1770
1771 void checkChannelComponentsToIndicesHelper()
1772 {
1773 // GIVEN
1774 QFETCH(Channel, channel);
1775 QFETCH(int, offset);
1776 QFETCH(int, expectedChannelComponentCount);
1777 QFETCH(QVector<char>, suffixes);
1778 QFETCH(QVector<int>, expectedResults);
1779
1780 // WHEN
1781 QVector<int> actualResults
1782 = channelComponentsToIndicesHelper(channelGroup: channel, expectedComponentCount: expectedChannelComponentCount,
1783 offset, suffixes);
1784
1785 // THEN
1786 QCOMPARE(actualResults.size(), expectedResults.size());
1787 for (int i = 0; i < actualResults.size(); ++i) {
1788 QCOMPARE(actualResults[i], expectedResults[i]);
1789 }
1790 }
1791
1792 void checkChannelComponentsToIndices_data()
1793 {
1794 QTest::addColumn<Channel>(name: "channel");
1795 QTest::addColumn<int>(name: "dataType");
1796 QTest::addColumn<int>(name: "componentCount");
1797 QTest::addColumn<int>(name: "offset");
1798 QTest::addColumn<QVector<int>>(name: "expectedResults");
1799
1800 Channel channel;
1801 int dataType;
1802 int componentCount;
1803 int offset;
1804 QVector<int> expectedResults;
1805
1806 // Quaternion
1807 {
1808 channel = Channel();
1809 channel.name = QLatin1String("Rotation");
1810 channel.channelComponents.resize(asize: 4);
1811 channel.channelComponents[0].name = QLatin1String("Rotation W");
1812 channel.channelComponents[1].name = QLatin1String("Rotation X");
1813 channel.channelComponents[2].name = QLatin1String("Rotation Y");
1814 channel.channelComponents[3].name = QLatin1String("Rotation Z");
1815
1816 dataType = static_cast<int>(QVariant::Quaternion);
1817 componentCount = 4;
1818 offset = 0;
1819 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
1820
1821 QTest::newRow(dataTag: "quaternion Rotation, offset = 0")
1822 << channel << dataType << componentCount << offset << expectedResults;
1823
1824 expectedResults.clear();
1825
1826 offset = 10;
1827 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
1828 QTest::newRow(dataTag: "quaternion Rotation, offset = 10")
1829 << channel << dataType << componentCount << offset << expectedResults;
1830
1831 expectedResults.clear();
1832 }
1833
1834 // vec3 with and without offset
1835 {
1836 channel = Channel();
1837 channel.name = QLatin1String("Location");
1838 channel.channelComponents.resize(asize: 3);
1839 channel.channelComponents[0].name = QLatin1String("Location X");
1840 channel.channelComponents[1].name = QLatin1String("Location Y");
1841 channel.channelComponents[2].name = QLatin1String("Location Z");
1842
1843 dataType = static_cast<int>(QVariant::Vector3D);
1844 componentCount = 3;
1845 offset = 0;
1846 expectedResults = (QVector<int>() << 0 << 1 << 2);
1847
1848 QTest::newRow(dataTag: "vec3 location, offset = 0")
1849 << channel << dataType << componentCount << offset << expectedResults;
1850
1851 expectedResults.clear();
1852
1853 offset = 4;
1854 expectedResults = (QVector<int>() << 4 << 5 << 6);
1855 QTest::newRow(dataTag: "vec3 location, offset = 4")
1856 << channel << dataType << componentCount << offset << expectedResults;
1857
1858 expectedResults.clear();
1859 }
1860
1861 // QColor
1862 {
1863 channel = Channel();
1864 channel.name = QLatin1String("Color");
1865 channel.channelComponents.resize(asize: 3);
1866 channel.channelComponents[0].name = QLatin1String("Color R");
1867 channel.channelComponents[1].name = QLatin1String("Color G");
1868 channel.channelComponents[2].name = QLatin1String("Color B");
1869
1870 dataType = static_cast<int>(QVariant::Color);
1871 componentCount = 3;
1872 offset = 0;
1873 expectedResults = (QVector<int>() << 0 << 1 << 2);
1874
1875 QTest::newRow(dataTag: "QColor RGB Color, offset = 0")
1876 << channel << dataType << componentCount << offset << expectedResults;
1877
1878 expectedResults.clear();
1879
1880 offset = 10;
1881 expectedResults = (QVector<int>() << 10 << 11 << 12);
1882 QTest::newRow(dataTag: "QColor RGB Color, offset = 10")
1883 << channel << dataType << componentCount << offset << expectedResults;
1884
1885 expectedResults.clear();
1886 }
1887
1888 {
1889 channel = Channel();
1890 channel.name = QLatin1String("Color");
1891 channel.channelComponents.resize(asize: 4);
1892 channel.channelComponents[0].name = QLatin1String("Color R");
1893 channel.channelComponents[1].name = QLatin1String("Color G");
1894 channel.channelComponents[2].name = QLatin1String("Color B");
1895 channel.channelComponents[3].name = QLatin1String("Color A");
1896
1897 dataType = static_cast<int>(QVariant::Color);
1898 componentCount = 4;
1899 offset = 0;
1900 expectedResults = (QVector<int>() << 0 << 1 << 2 << 3);
1901
1902 QTest::newRow(dataTag: "QColor RGBA Color, offset = 0")
1903 << channel << dataType << componentCount << offset << expectedResults;
1904
1905 expectedResults.clear();
1906
1907 offset = 10;
1908 expectedResults = (QVector<int>() << 10 << 11 << 12 << 13);
1909 QTest::newRow(dataTag: "QColor RGBA Color, offset = 10")
1910 << channel << dataType << componentCount << offset << expectedResults;
1911
1912 expectedResults.clear();
1913 }
1914 }
1915
1916 void checkChannelComponentsToIndices()
1917 {
1918 QFETCH(Channel, channel);
1919 QFETCH(int, dataType);
1920 QFETCH(int, componentCount);
1921 QFETCH(int, offset);
1922 QFETCH(QVector<int>, expectedResults);
1923
1924 // WHEN
1925 QVector<int> actualResults
1926 = channelComponentsToIndices(channel, dataType, expectedComponentCount: componentCount, offset);
1927
1928 // THEN
1929 QCOMPARE(actualResults.size(), expectedResults.size());
1930 for (int i = 0; i < actualResults.size(); ++i) {
1931 QCOMPARE(actualResults[i], expectedResults[i]);
1932 }
1933 }
1934
1935 void checkEvaluationDataForClip_data()
1936 {
1937 QTest::addColumn<Handler *>(name: "handler");
1938 QTest::addColumn<AnimationClip *>(name: "clip");
1939 QTest::addColumn<AnimatorEvaluationData>(name: "animatorData");
1940 QTest::addColumn<ClipEvaluationData>(name: "expectedClipData");
1941
1942 Handler *handler;
1943 AnimationClip *clip;
1944 AnimatorEvaluationData animatorData;
1945 ClipEvaluationData clipData;
1946 auto* clock = new Clock;
1947
1948 {
1949 handler = new Handler();
1950 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1951 const qint64 globalStartTimeNS = 0;
1952 const int loops = 1;
1953 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
1954 animator->setCurrentLoop(0);
1955 clipData.currentLoop = animator->currentLoop();
1956 const qint64 elapsedTimeNS = 0;
1957 animatorData = evaluationDataForAnimator(animator, clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
1958
1959 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
1960 t_elapsed_global: animatorData.elapsedTime,
1961 playbackRate: animatorData.playbackRate,
1962 duration: clip->duration(),
1963 loopCount: animatorData.loopCount,
1964 currentLoop&: clipData.currentLoop); // Tested elsewhere
1965 clipData.isFinalFrame = false;
1966
1967 QTest::newRow(dataTag: "clip1.json, globalTime = 0")
1968 << handler << clip << animatorData << clipData;
1969 }
1970
1971 {
1972 handler = new Handler();
1973 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1974 const qint64 globalStartTimeNS = 0;
1975 const int loops = 1;
1976 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
1977 animator->setCurrentLoop(0);
1978 clipData.currentLoop = animator->currentLoop();
1979 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration()+1); // +1 to ensure beyond end
1980 animatorData = evaluationDataForAnimator(animator, clock: nullptr, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
1981
1982 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
1983 t_elapsed_global: animatorData.elapsedTime,
1984 playbackRate: animatorData.playbackRate,
1985 duration: clip->duration(),
1986 loopCount: animatorData.loopCount,
1987 currentLoop&: clipData.currentLoop); // Tested elsewhere
1988 clipData.isFinalFrame = true;
1989
1990 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration + 1")
1991 << handler << clip << animatorData << clipData;
1992 }
1993
1994 {
1995 handler = new Handler();
1996 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
1997 const qint64 globalStartTimeNS = 0;
1998 const int loops = 0; // Infinite loops
1999 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2000 animator->setCurrentLoop(0);
2001 clipData.currentLoop = animator->currentLoop();
2002 const qint64 elapsedTimeNS = toNsecs(seconds: 2.0 * clip->duration());
2003 animatorData = evaluationDataForAnimator(animator, clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2004
2005 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2006 t_elapsed_global: animatorData.elapsedTime,
2007 playbackRate: animatorData.playbackRate,
2008 duration: clip->duration(),
2009 loopCount: animatorData.loopCount,
2010 currentLoop&: clipData.currentLoop); // Tested elsewhere
2011 clipData.isFinalFrame = false;
2012
2013 QTest::newRow(dataTag: "clip1.json, elapsedTime = 2 * duration, loops = infinite")
2014 << handler << clip << animatorData << clipData;
2015 }
2016
2017 {
2018 handler = new Handler();
2019 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2020 const qint64 globalStartTimeNS = 0;
2021 const int loops = 2;
2022 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2023 animator->setCurrentLoop(0);
2024 clipData.currentLoop = animator->currentLoop();
2025 const qint64 elapsedTimeNS = toNsecs(seconds: 2.0 * clip->duration() + 1.0); // +1 to ensure beyond end of clip
2026 animatorData = evaluationDataForAnimator(animator, clock: nullptr, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2027
2028 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2029 t_elapsed_global: animatorData.elapsedTime,
2030 playbackRate: animatorData.playbackRate,
2031 duration: clip->duration(),
2032 loopCount: animatorData.loopCount,
2033 currentLoop&: clipData.currentLoop); // Tested elsewhere
2034 clipData.isFinalFrame = true;
2035
2036 QTest::newRow(dataTag: "clip1.json, elapsedTime = 2 * duration + 1, loops = 2")
2037 << handler << clip << animatorData << clipData;
2038 }
2039
2040 {
2041 handler = new Handler();
2042 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2043 const qint64 globalStartTimeNS = 0;
2044 const int loops = 2;
2045 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2046 animator->setCurrentLoop(1);
2047 clipData.currentLoop = animator->currentLoop();
2048 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration() + 1.0); // +1 to ensure beyond end of clip
2049 animatorData = evaluationDataForAnimator(animator, clock: nullptr, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2050
2051 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2052 t_elapsed_global: animatorData.elapsedTime,
2053 playbackRate: animatorData.playbackRate,
2054 duration: clip->duration(),
2055 loopCount: animatorData.loopCount,
2056 currentLoop&: clipData.currentLoop); // Tested elsewhere
2057 clipData.isFinalFrame = true;
2058
2059 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1")
2060 << handler << clip << animatorData << clipData;
2061 }
2062
2063 {
2064 handler = new Handler();
2065 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2066 const qint64 globalStartTimeNS = clip->duration();
2067 const int loops = 1;
2068 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2069 animator->setCurrentLoop(1);
2070 clipData.currentLoop = animator->currentLoop();
2071 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration() * 0.5); // +1 to ensure beyond end of clip
2072
2073 Clock clock;
2074 clock.setPlaybackRate(-1.0);
2075
2076 animatorData = evaluationDataForAnimator(animator, clock: &clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2077
2078 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2079 t_elapsed_global: animatorData.elapsedTime,
2080 playbackRate: animatorData.playbackRate,
2081 duration: clip->duration(),
2082 loopCount: animatorData.loopCount,
2083 currentLoop&: clipData.currentLoop); // Tested elsewhere
2084 clipData.isFinalFrame = false;
2085
2086 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration / 2, loops = 1, current_loop = 1, playback_rate = -1")
2087 << handler << clip << animatorData << clipData;
2088 }
2089
2090 {
2091 handler = new Handler();
2092 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2093 const qint64 globalStartTimeNS = clip->duration();
2094 const int loops = 1;
2095 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2096 animator->setCurrentLoop(1);
2097 clipData.currentLoop = animator->currentLoop();
2098 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration() + 1); // +1 to ensure beyond end of clip
2099
2100 Clock clock;
2101 clock.setPlaybackRate(-1.0);
2102
2103 animatorData = evaluationDataForAnimator(animator, clock: &clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2104
2105 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2106 t_elapsed_global: animatorData.elapsedTime,
2107 playbackRate: animatorData.playbackRate,
2108 duration: clip->duration(),
2109 loopCount: animatorData.loopCount,
2110 currentLoop&: clipData.currentLoop); // Tested elsewhere
2111 clipData.isFinalFrame = true;
2112
2113 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration + 1, loops = 1, current_loop = 1, playback_rate = -1")
2114 << handler << clip << animatorData << clipData;
2115 }
2116
2117 {
2118 handler = new Handler();
2119 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2120 const qint64 globalStartTimeNS = clip->duration();
2121 const int loops = 2;
2122 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2123 animator->setCurrentLoop(0);
2124 clipData.currentLoop = animator->currentLoop();
2125 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration() + 1); // +1 to ensure beyond end of clip
2126
2127 Clock clock;
2128 clock.setPlaybackRate(-1.0);
2129
2130 animatorData = evaluationDataForAnimator(animator, clock: &clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2131
2132 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2133 t_elapsed_global: animatorData.elapsedTime,
2134 playbackRate: animatorData.playbackRate,
2135 duration: clip->duration(),
2136 loopCount: animatorData.loopCount,
2137 currentLoop&: clipData.currentLoop); // Tested elsewhere
2138 clipData.isFinalFrame = true;
2139
2140 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 0, playback_rate = -1")
2141 << handler << clip << animatorData << clipData;
2142 }
2143
2144 {
2145 handler = new Handler();
2146 clip = createAnimationClipLoader(handler, source: QUrl("qrc:/clip1.json"));
2147 const qint64 globalStartTimeNS = clip->duration();
2148 const int loops = 2;
2149 auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
2150 animator->setCurrentLoop(1);
2151 clipData.currentLoop = animator->currentLoop();
2152 const qint64 elapsedTimeNS = toNsecs(seconds: clip->duration() * 2.0 + 1); // +1 to ensure beyond end of clip
2153
2154 Clock clock;
2155 clock.setPlaybackRate(-1.0);
2156
2157 animatorData = evaluationDataForAnimator(animator, clock: &clock, nsSincePreviousFrame: elapsedTimeNS); // Tested elsewhere
2158
2159 clipData.localTime = localTimeFromElapsedTime(t_current_local: animatorData.currentTime,
2160 t_elapsed_global: animatorData.elapsedTime,
2161 playbackRate: animatorData.playbackRate,
2162 duration: clip->duration(),
2163 loopCount: animatorData.loopCount,
2164 currentLoop&: clipData.currentLoop); // Tested elsewhere
2165 clipData.isFinalFrame = true;
2166
2167 QTest::newRow(dataTag: "clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1, playback_rate = -1")
2168 << handler << clip << animatorData << clipData;
2169 }
2170 }
2171
2172 void checkEvaluationDataForClip()
2173 {
2174 // GIVEN
2175 QFETCH(Handler *, handler);
2176 QFETCH(AnimationClip *, clip);
2177 QFETCH(AnimatorEvaluationData, animatorData);
2178 QFETCH(ClipEvaluationData, expectedClipData);
2179
2180 // WHEN
2181 ClipEvaluationData actualClipData = evaluationDataForClip(clip, animatorData);
2182
2183 // THEN
2184 QCOMPARE(actualClipData.currentLoop, expectedClipData.currentLoop);
2185 QVERIFY(fuzzyCompare(actualClipData.localTime, expectedClipData.localTime) == true);
2186 QCOMPARE(actualClipData.isFinalFrame, expectedClipData.isFinalFrame);
2187
2188 // Cleanup
2189 delete handler;
2190 }
2191
2192 void checkEvaluationDataForAnimator_data()
2193 {
2194 QTest::addColumn<Handler *>(name: "handler");
2195 QTest::addColumn<ClipAnimator *>(name: "animator");
2196 QTest::addColumn<qint64>(name: "elapsedTime");
2197 QTest::addColumn<AnimatorEvaluationData>(name: "expectedAnimatorData");
2198
2199 Handler *handler;
2200 ClipAnimator *animator;
2201 qint64 elapsedTimeNS;
2202 AnimatorEvaluationData expectedAnimatorData;
2203
2204 {
2205 handler = new Handler();
2206 const qint64 globalStartTimeNS = 0;
2207 const int loops = 1;
2208 animator = createClipAnimator(handler, globalStartTimeNS, loops);
2209 elapsedTimeNS = 0;
2210
2211 expectedAnimatorData.loopCount = loops;
2212 expectedAnimatorData.playbackRate = 1.0; // hard-wired for now
2213 expectedAnimatorData.elapsedTime = 0.0;
2214
2215 QTest::newRow(dataTag: "globalStartTime = 0, elapsedTime = 0, loops = 1")
2216 << handler << animator << elapsedTimeNS << expectedAnimatorData;
2217 }
2218
2219 {
2220 handler = new Handler();
2221 const qint64 globalStartTimeNS = 0;
2222 const int loops = 5;
2223 animator = createClipAnimator(handler, globalStartTimeNS, loops);
2224 elapsedTimeNS = 0;
2225
2226 expectedAnimatorData.loopCount = loops;
2227 expectedAnimatorData.playbackRate = 1.0; // hard-wired for now
2228 expectedAnimatorData.elapsedTime = 0.0;
2229
2230 QTest::newRow(dataTag: "globalStartTime = 0, elapsedTime = 0, loops = 5")
2231 << handler << animator << elapsedTimeNS << expectedAnimatorData;
2232 }
2233
2234 {
2235 handler = new Handler();
2236 const qint64 globalStartTimeNS = 0;
2237 const int loops = 1;
2238 animator = createClipAnimator(handler, globalStartTimeNS, loops);
2239 elapsedTimeNS = 5000000000;
2240
2241 expectedAnimatorData.loopCount = loops;
2242 expectedAnimatorData.playbackRate = 1.0; // hard-wired for now
2243 expectedAnimatorData.elapsedTime = 5.0;
2244
2245 QTest::newRow(dataTag: "globalStartTime = 0, elapsedTime = 5, loops = 1")
2246 << handler << animator << elapsedTimeNS << expectedAnimatorData;
2247 }
2248
2249 {
2250 handler = new Handler();
2251 const qint64 globalStartTimeNS = 3000000000;
2252 const int loops = 1;
2253 animator = createClipAnimator(handler, globalStartTimeNS, loops);
2254 elapsedTimeNS = 2000000000;
2255
2256 expectedAnimatorData.loopCount = loops;
2257 expectedAnimatorData.playbackRate = 1.0; // hard-wired for now
2258 expectedAnimatorData.elapsedTime = 2.0;
2259
2260 QTest::newRow(dataTag: "globalStartTime = 3, elapsedTime = 2, loops = 1")
2261 << handler << animator << elapsedTimeNS << expectedAnimatorData;
2262 }
2263 }
2264
2265 void checkEvaluationDataForAnimator()
2266 {
2267 // GIVEN
2268 QFETCH(Handler *, handler);
2269 QFETCH(ClipAnimator *, animator);
2270 QFETCH(qint64, elapsedTime);
2271 QFETCH(AnimatorEvaluationData, expectedAnimatorData);
2272
2273 // WHEN
2274 AnimatorEvaluationData actualAnimatorData = evaluationDataForAnimator(animator, clock: nullptr, nsSincePreviousFrame: elapsedTime);
2275
2276 // THEN
2277 QCOMPARE(actualAnimatorData.loopCount, expectedAnimatorData.loopCount);
2278 QVERIFY(fuzzyCompare(actualAnimatorData.playbackRate, expectedAnimatorData.playbackRate) == true);
2279 QVERIFY(fuzzyCompare(actualAnimatorData.elapsedTime, expectedAnimatorData.elapsedTime) == true);
2280
2281 // Cleanup
2282 delete handler;
2283 }
2284
2285 void checkGatherValueNodesToEvaluate_data()
2286 {
2287 QTest::addColumn<Handler *>(name: "handler");
2288 QTest::addColumn<Qt3DCore::QNodeId>(name: "blendTreeRootId");
2289 QTest::addColumn<QVector<Qt3DCore::QNodeId>>(name: "expectedIds");
2290
2291 {
2292 Handler *handler = new Handler;
2293
2294 const auto lerp = createLerpClipBlend(handler);
2295 const auto value1 = createClipBlendValue(handler);
2296 const auto clip1Id = Qt3DCore::QNodeId::createId();
2297 value1->setClipId(clip1Id);
2298 lerp->setStartClipId(value1->peerId());
2299
2300 const auto value2 = createClipBlendValue(handler);
2301 const auto clip2Id = Qt3DCore::QNodeId::createId();
2302 value2->setClipId(clip2Id);
2303 lerp->setEndClipId(value2->peerId());
2304
2305 QVector<Qt3DCore::QNodeId> expectedIds = { value1->peerId(), value2->peerId() };
2306
2307 QTest::newRow(dataTag: "simple lerp") << handler << lerp->peerId() << expectedIds;
2308 }
2309
2310 {
2311 Handler *handler = new Handler;
2312
2313 const auto value1 = createClipBlendValue(handler);
2314 const auto clip1Id = Qt3DCore::QNodeId::createId();
2315 value1->setClipId(clip1Id);
2316
2317 QVector<Qt3DCore::QNodeId> expectedIds = { value1->peerId() };
2318
2319 QTest::newRow(dataTag: "value only") << handler << value1->peerId() << expectedIds;
2320 }
2321 }
2322
2323 void checkGatherValueNodesToEvaluate()
2324 {
2325 // GIVEN
2326 QFETCH(Handler *, handler);
2327 QFETCH(Qt3DCore::QNodeId, blendTreeRootId);
2328 QFETCH(QVector<Qt3DCore::QNodeId>, expectedIds);
2329
2330 // WHEN
2331 QVector<Qt3DCore::QNodeId> actualIds = gatherValueNodesToEvaluate(handler, blendTreeRootId);
2332
2333 // THEN
2334 QCOMPARE(actualIds.size(), expectedIds.size());
2335 for (int i = 0; i < actualIds.size(); ++i)
2336 QCOMPARE(actualIds[i], expectedIds[i]);
2337
2338 // Cleanup
2339 delete handler;
2340 }
2341
2342 void checkEvaluateBlendTree_data()
2343 {
2344 QTest::addColumn<Handler *>(name: "handler");
2345 QTest::addColumn<BlendedClipAnimator *>(name: "animator");
2346 QTest::addColumn<Qt3DCore::QNodeId>(name: "blendNodeId");
2347 QTest::addColumn<ClipResults>(name: "expectedResults");
2348
2349 {
2350 /*
2351 ValueNode1----
2352 |
2353 MeanBlendNode
2354 |
2355 ValueNode2----
2356 */
2357
2358 auto handler = new Handler();
2359 const qint64 globalStartTimeNS = 0;
2360 const int loopCount = 1;
2361 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loops: loopCount);
2362
2363 // Set up the blend node and dependencies (evaluated clip results of the
2364 // dependent nodes in the animator indexed by their ids).
2365 MeanBlendNode *blendNode = createMeanBlendNode(handler);
2366
2367 // First clip to use in the mean
2368 auto valueNode1 = createClipBlendValue(handler);
2369 ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f };
2370 valueNode1->setClipResults(animatorId: animator->peerId(), clipResults: valueNode1Results);
2371
2372 // Second clip to use in the mean
2373 auto valueNode2 = createClipBlendValue(handler);
2374 ClipResults valueNode2Results = { 1.0f, 1.0f, 1.0f };
2375 valueNode2->setClipResults(animatorId: animator->peerId(), clipResults: valueNode2Results);
2376
2377 blendNode->setValueNodeIds(value1Id: valueNode1->peerId(), value2Id: valueNode2->peerId());
2378
2379 ClipResults expectedResults = { 0.5f, 0.5f, 0.5f };
2380
2381 QTest::newRow(dataTag: "mean node, 1 channel")
2382 << handler << animator << blendNode->peerId() << expectedResults;
2383 }
2384
2385 {
2386 /*
2387 ValueNode1----
2388 |
2389 MeanBlendNode
2390 |
2391 ValueNode2----
2392 */
2393
2394 auto handler = new Handler();
2395 const qint64 globalStartTimeNS = 0;
2396 const int loopCount = 1;
2397 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loops: loopCount);
2398
2399 // Set up the blend node and dependencies (evaluated clip results of the
2400 // dependent nodes in the animator indexed by their ids).
2401 MeanBlendNode *blendNode = createMeanBlendNode(handler);
2402
2403 // First clip to use in the mean
2404 auto valueNode1 = createClipBlendValue(handler);
2405 ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 3.0f };
2406 valueNode1->setClipResults(animatorId: animator->peerId(), clipResults: valueNode1Results);
2407
2408 // Second clip to use in the mean
2409 auto valueNode2 = createClipBlendValue(handler);
2410 ClipResults valueNode2Results = { 1.0f, 1.0f, 1.0f, 2.0f, 4.0f, 6.0f };
2411 valueNode2->setClipResults(animatorId: animator->peerId(), clipResults: valueNode2Results);
2412
2413 blendNode->setValueNodeIds(value1Id: valueNode1->peerId(), value2Id: valueNode2->peerId());
2414
2415 ClipResults expectedResults = { 0.5f, 0.5f, 0.5f, 1.5f, 3.0f, 4.5f };
2416
2417 QTest::newRow(dataTag: "mean node, 2 channels")
2418 << handler << animator << blendNode->peerId() << expectedResults;
2419 }
2420
2421 {
2422 /*
2423 ValueNode1----
2424 |
2425 MeanBlendNode1------
2426 | |
2427 ValueNode2---- |
2428 MeanBlendNode3
2429 ValueNode3---- |
2430 | |
2431 MeanBlendNode2------
2432 |
2433 ValueNode4----
2434 */
2435
2436 auto handler = new Handler();
2437 const qint64 globalStartTimeNS = 0;
2438 const int loopCount = 1;
2439 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loops: loopCount);
2440
2441 // Set up the blend node and dependencies (evaluated clip results of the
2442 // dependent nodes in the animator indexed by their ids).
2443
2444 // MeanBlendNode1
2445 MeanBlendNode *meanNode1 = createMeanBlendNode(handler);
2446
2447 // First clip to use in mean1
2448 auto valueNode1 = createClipBlendValue(handler);
2449 ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f };
2450 valueNode1->setClipResults(animatorId: animator->peerId(), clipResults: valueNode1Results);
2451
2452 // Second clip to use in mean1
2453 auto valueNode2 = createClipBlendValue(handler);
2454 ClipResults valueNode2Results = { 2.0f, 2.0f, 2.0f };
2455 valueNode2->setClipResults(animatorId: animator->peerId(), clipResults: valueNode2Results);
2456
2457 meanNode1->setValueNodeIds(value1Id: valueNode1->peerId(), value2Id: valueNode2->peerId());
2458
2459
2460 // MeanBlendNode2
2461 MeanBlendNode *meanNode2 = createMeanBlendNode(handler);
2462
2463 // First clip to use in mean1
2464 auto valueNode3 = createClipBlendValue(handler);
2465 ClipResults valueNode3Results = { 10.0f, 10.0f, 10.0f };
2466 valueNode3->setClipResults(animatorId: animator->peerId(), clipResults: valueNode3Results);
2467
2468 // Second clip to use in mean1
2469 auto valueNode4 = createClipBlendValue(handler);
2470 ClipResults valueNode4Results = { 20.0f, 20.0f, 20.0f };
2471 valueNode4->setClipResults(animatorId: animator->peerId(), clipResults: valueNode4Results);
2472
2473 meanNode2->setValueNodeIds(value1Id: valueNode3->peerId(), value2Id: valueNode4->peerId());
2474
2475
2476 // MeanBlendNode3
2477 MeanBlendNode *meanNode3 = createMeanBlendNode(handler);
2478 meanNode3->setValueNodeIds(value1Id: meanNode1->peerId(), value2Id: meanNode2->peerId());
2479
2480 // Mean1 = 1
2481 // Mean2 = 15
2482 // Mean3 = (1 + 15 ) / 2 = 8
2483 ClipResults expectedResults = { 8.0f, 8.0f, 8.0f };
2484
2485 QTest::newRow(dataTag: "3 mean nodes, 1 channel")
2486 << handler << animator << meanNode3->peerId() << expectedResults;
2487 }
2488
2489 {
2490 /*
2491 ValueNode1----
2492 |
2493 MeanBlendNode1------
2494 | |
2495 ValueNode2---- |
2496 MeanBlendNode3---
2497 ValueNode3---- | |
2498 | | |
2499 MeanBlendNode2------ AdditiveBlendNode1
2500 | |
2501 ValueNode4---- |
2502 ValueNode5-------
2503 */
2504
2505 auto handler = new Handler();
2506 const qint64 globalStartTimeNS = 0;
2507 const int loopCount = 1;
2508 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loops: loopCount);
2509
2510 // Set up the blend node and dependencies (evaluated clip results of the
2511 // dependent nodes in the animator indexed by their ids).
2512
2513 // MeanBlendNode1
2514 MeanBlendNode *meanNode1 = createMeanBlendNode(handler);
2515
2516 // First clip to use in mean1
2517 auto valueNode1 = createClipBlendValue(handler);
2518 ClipResults valueNode1Results = { 0.0f, 0.0f, 0.0f };
2519 valueNode1->setClipResults(animatorId: animator->peerId(), clipResults: valueNode1Results);
2520
2521 // Second clip to use in mean1
2522 auto valueNode2 = createClipBlendValue(handler);
2523 ClipResults valueNode2Results = { 2.0f, 2.0f, 2.0f };
2524 valueNode2->setClipResults(animatorId: animator->peerId(), clipResults: valueNode2Results);
2525
2526 meanNode1->setValueNodeIds(value1Id: valueNode1->peerId(), value2Id: valueNode2->peerId());
2527
2528
2529 // MeanBlendNode2
2530 MeanBlendNode *meanNode2 = createMeanBlendNode(handler);
2531
2532 // First clip to use in mean2
2533 auto valueNode3 = createClipBlendValue(handler);
2534 ClipResults valueNode3Results = { 10.0f, 10.0f, 10.0f };
2535 valueNode3->setClipResults(animatorId: animator->peerId(), clipResults: valueNode3Results);
2536
2537 // Second clip to use in mean2
2538 auto valueNode4 = createClipBlendValue(handler);
2539 ClipResults valueNode4Results = { 20.0f, 20.0f, 20.0f };
2540 valueNode4->setClipResults(animatorId: animator->peerId(), clipResults: valueNode4Results);
2541
2542 meanNode2->setValueNodeIds(value1Id: valueNode3->peerId(), value2Id: valueNode4->peerId());
2543
2544
2545 // MeanBlendNode3
2546 MeanBlendNode *meanNode3 = createMeanBlendNode(handler);
2547 meanNode3->setValueNodeIds(value1Id: meanNode1->peerId(), value2Id: meanNode2->peerId());
2548
2549
2550 // AdditiveBlendNode1
2551 AdditiveClipBlend *additiveBlendNode1 = createAdditiveClipBlend(handler);
2552 auto valueNode5 = createClipBlendValue(handler);
2553 ClipResults valueNode5Results = { 1.0f, 2.0f, 3.0f };
2554 valueNode5->setClipResults(animatorId: animator->peerId(), clipResults: valueNode5Results);
2555
2556 additiveBlendNode1->setBaseClipId(meanNode3->peerId());
2557 additiveBlendNode1->setAdditiveClipId(valueNode5->peerId());
2558 additiveBlendNode1->setAdditiveFactor(0.5);
2559
2560 // Mean1 = 1
2561 // Mean2 = 15
2562 // Mean3 = (1 + 15 ) / 2 = 8
2563 // Additive1 = 8 + 0.5 * (1, 2, 3) = (8.5, 9, 9.5)
2564 ClipResults expectedResults = { 8.5f, 9.0f, 9.5f };
2565
2566 QTest::newRow(dataTag: "3 mean nodes + additive, 1 channel")
2567 << handler << animator << additiveBlendNode1->peerId() << expectedResults;
2568 }
2569 }
2570
2571 void checkEvaluateBlendTree()
2572 {
2573 // GIVEN
2574 QFETCH(Handler *, handler);
2575 QFETCH(BlendedClipAnimator *, animator);
2576 QFETCH(Qt3DCore::QNodeId, blendNodeId);
2577 QFETCH(ClipResults, expectedResults);
2578
2579 // WHEN
2580 const ClipResults actualResults = evaluateBlendTree(handler, animator, blendNodeId);
2581
2582 // THEN
2583 QCOMPARE(actualResults.size(), expectedResults.size());
2584 for (int i = 0; i < actualResults.size(); ++i)
2585 QCOMPARE(actualResults[i], expectedResults[i]);
2586
2587 // Cleanup
2588 delete handler;
2589 }
2590
2591 void checkFormatClipResults_data()
2592 {
2593 QTest::addColumn<ClipResults>(name: "rawClipResults");
2594 QTest::addColumn<ComponentIndices>(name: "format");
2595 QTest::addColumn<ClipResults>(name: "expectedResults");
2596
2597 {
2598 ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f };
2599 ComponentIndices format = { 0, 1, 2 };
2600 ClipResults expectedResults = { 1.0f, 2.0f, 3.0f };
2601
2602 QTest::newRow(dataTag: "identity")
2603 << rawClipResults << format << expectedResults;
2604 }
2605
2606 {
2607 ClipResults rawClipResults = { 1.0f, 2.0f };
2608 ComponentIndices format = { 1, 0 };
2609 ClipResults expectedResults = { 2.0f, 1.0f };
2610
2611 QTest::newRow(dataTag: "swap")
2612 << rawClipResults << format << expectedResults;
2613 }
2614
2615 {
2616 ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
2617 ComponentIndices format = { 0, 2, 1, 3, 4 };
2618 ClipResults expectedResults = { 1.0f, 3.0f, 2.0f, 4.0f, 5.0f };
2619
2620 QTest::newRow(dataTag: "swap subset")
2621 << rawClipResults << format << expectedResults;
2622 }
2623
2624 {
2625 ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
2626 ComponentIndices format = { 4, 3, 2, 1, 0 };
2627 ClipResults expectedResults = { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
2628
2629 QTest::newRow(dataTag: "reverse")
2630 << rawClipResults << format << expectedResults;
2631 }
2632
2633 {
2634 ClipResults rawClipResults = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
2635 ComponentIndices format = { 0, 1, -1, 3, 4 };
2636 ClipResults expectedResults = { 1.0f, 2.0f, 0.0f, 4.0f, 5.0f };
2637
2638 QTest::newRow(dataTag: "include missing")
2639 << rawClipResults << format << expectedResults;
2640 }
2641 }
2642
2643 void checkFormatClipResults()
2644 {
2645 // GIVEN
2646 QFETCH(ClipResults, rawClipResults);
2647 QFETCH(ComponentIndices, format);
2648 QFETCH(ClipResults, expectedResults);
2649
2650 // WHEN
2651 const ClipResults actualResults = formatClipResults(rawClipResults, format);
2652
2653 // THEN
2654 QCOMPARE(actualResults.size(), expectedResults.size());
2655 for (int i = 0; i < actualResults.size(); ++i)
2656 QCOMPARE(actualResults[i], expectedResults[i]);
2657 }
2658
2659 void checkBuildRequiredChannelsAndTypes_data()
2660 {
2661 QTest::addColumn<Handler *>(name: "handler");
2662 QTest::addColumn<ChannelMapper *>(name: "mapper");
2663 QTest::addColumn<QVector<ChannelNameAndType>>(name: "expectedResults");
2664
2665 {
2666 auto handler = new Handler();
2667 auto channelMapping = createChannelMapping(handler,
2668 channelName: QLatin1String("Location"),
2669 targetId: Qt3DCore::QNodeId::createId(),
2670 propertyName: "translation",
2671 type: static_cast<int>(QVariant::Vector3D),
2672 componentCount: 3);
2673 QVector<ChannelMapping *> channelMappings;
2674 channelMappings.push_back(t: channelMapping);
2675
2676 auto channelMapper = createChannelMapper(handler,
2677 mappingIds: QVector<Qt3DCore::QNodeId>() << channelMapping->peerId());
2678
2679 QVector<ChannelNameAndType> expectedResults;
2680 expectedResults.push_back(t: { QLatin1String("Location"),
2681 static_cast<int>(QVariant::Vector3D),
2682 3,
2683 channelMapping->peerId() });
2684
2685 QTest::addRow(format: "Location, vec3") << handler << channelMapper << expectedResults;
2686 }
2687
2688 {
2689 auto handler = new Handler();
2690 auto channelMapping1 = createChannelMapping(handler,
2691 channelName: QLatin1String("Location"),
2692 targetId: Qt3DCore::QNodeId::createId(),
2693 propertyName: "translation",
2694 type: static_cast<int>(QVariant::Vector3D),
2695 componentCount: 3);
2696 auto channelMapping2 = createChannelMapping(handler,
2697 channelName: QLatin1String("Rotation"),
2698 targetId: Qt3DCore::QNodeId::createId(),
2699 propertyName: "rotation",
2700 type: static_cast<int>(QVariant::Quaternion),
2701 componentCount: 4);
2702 QVector<ChannelMapping *> channelMappings;
2703 channelMappings.push_back(t: channelMapping1);
2704 channelMappings.push_back(t: channelMapping2);
2705
2706 QVector<Qt3DCore::QNodeId> channelMappingIds
2707 = (QVector<Qt3DCore::QNodeId>()
2708 << channelMapping1->peerId()
2709 << channelMapping2->peerId());
2710 auto channelMapper = createChannelMapper(handler, mappingIds: channelMappingIds);
2711
2712 QVector<ChannelNameAndType> expectedResults;
2713 expectedResults.push_back(t: { QLatin1String("Location"),
2714 static_cast<int>(QVariant::Vector3D),
2715 3,
2716 channelMapping1->peerId() });
2717 expectedResults.push_back(t: { QLatin1String("Rotation"),
2718 static_cast<int>(QVariant::Quaternion),
2719 4,
2720 channelMapping2->peerId() });
2721
2722 QTest::addRow(format: "Multiple unique channels") << handler << channelMapper << expectedResults;
2723 }
2724
2725 {
2726 auto handler = new Handler();
2727 auto channelMapping1 = createChannelMapping(handler,
2728 channelName: QLatin1String("Location"),
2729 targetId: Qt3DCore::QNodeId::createId(),
2730 propertyName: "translation",
2731 type: static_cast<int>(QVariant::Vector3D),
2732 componentCount: 3);
2733 auto channelMapping2 = createChannelMapping(handler,
2734 channelName: QLatin1String("Rotation"),
2735 targetId: Qt3DCore::QNodeId::createId(),
2736 propertyName: "rotation",
2737 type: static_cast<int>(QVariant::Quaternion),
2738 componentCount: 4);
2739 auto channelMapping3 = createChannelMapping(handler,
2740 channelName: QLatin1String("Location"),
2741 targetId: Qt3DCore::QNodeId::createId(),
2742 propertyName: "translation",
2743 type: static_cast<int>(QVariant::Vector3D),
2744 componentCount: 3);
2745 auto channelMapping4 = createChannelMapping(handler,
2746 channelName: QLatin1String("Location"),
2747 targetId: Qt3DCore::QNodeId::createId(),
2748 propertyName: "translation",
2749 type: static_cast<int>(QVariant::Vector3D),
2750 componentCount: 3);
2751
2752 QVector<ChannelMapping *> channelMappings;
2753 channelMappings.push_back(t: channelMapping1);
2754 channelMappings.push_back(t: channelMapping2);
2755 channelMappings.push_back(t: channelMapping3);
2756 channelMappings.push_back(t: channelMapping4);
2757
2758 QVector<Qt3DCore::QNodeId> channelMappingIds
2759 = (QVector<Qt3DCore::QNodeId>()
2760 << channelMapping1->peerId()
2761 << channelMapping2->peerId()
2762 << channelMapping3->peerId()
2763 << channelMapping4->peerId());
2764 auto channelMapper = createChannelMapper(handler, mappingIds: channelMappingIds);
2765
2766 QVector<ChannelNameAndType> expectedResults;
2767 expectedResults.push_back(t: { QLatin1String("Location"),
2768 static_cast<int>(QVariant::Vector3D),
2769 3,
2770 channelMapping1->peerId() });
2771 expectedResults.push_back(t: { QLatin1String("Rotation"),
2772 static_cast<int>(QVariant::Quaternion),
2773 4,
2774 channelMapping2->peerId() });
2775 expectedResults.push_back(t: { QLatin1String("Location"),
2776 static_cast<int>(QVariant::Vector3D),
2777 3,
2778 channelMapping3->peerId() });
2779 expectedResults.push_back(t: { QLatin1String("Location"),
2780 static_cast<int>(QVariant::Vector3D),
2781 3,
2782 channelMapping4->peerId() });
2783
2784 QTest::addRow(format: "Multiple channels with repeats") << handler << channelMapper << expectedResults;
2785 }
2786
2787 {
2788 auto handler = new Handler();
2789 const int jointCount = 10;
2790 auto skeleton = createSkeleton(handler, jointCount);
2791 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
2792 QVector<ChannelMapping *> channelMappings;
2793 channelMappings.push_back(t: channelMapping);
2794
2795 auto channelMapper = createChannelMapper(handler,
2796 mappingIds: QVector<Qt3DCore::QNodeId>() << channelMapping->peerId());
2797
2798 QVector<ChannelNameAndType> expectedResults;
2799 for (int i = 0; i < jointCount; ++i) {
2800 ChannelNameAndType locationDescription = { QLatin1String("Location"),
2801 static_cast<int>(QVariant::Vector3D),
2802 3,
2803 channelMapping->peerId() };
2804 locationDescription.jointIndex = i;
2805 locationDescription.jointTransformComponent = Translation;
2806 expectedResults.push_back(t: locationDescription);
2807
2808 ChannelNameAndType rotationDescription = { QLatin1String("Rotation"),
2809 static_cast<int>(QVariant::Quaternion),
2810 4,
2811 channelMapping->peerId() };
2812 rotationDescription.jointIndex = i;
2813 rotationDescription.jointTransformComponent = Rotation;
2814 expectedResults.push_back(t: rotationDescription);
2815
2816 ChannelNameAndType scaleDescription = { QLatin1String("Scale"),
2817 static_cast<int>(QVariant::Vector3D),
2818 3,
2819 channelMapping->peerId() };
2820 scaleDescription.jointIndex = i;
2821 scaleDescription.jointTransformComponent = Scale;
2822 expectedResults.push_back(t: scaleDescription);
2823 }
2824
2825 QTest::addRow(format: "Skeleton, 10 joints") << handler << channelMapper << expectedResults;
2826 }
2827 }
2828
2829 void checkBuildRequiredChannelsAndTypes()
2830 {
2831 // GIVEN
2832 QFETCH(Handler *, handler);
2833 QFETCH(ChannelMapper *, mapper);
2834 QFETCH(QVector<ChannelNameAndType>, expectedResults);
2835
2836 // WHEN
2837 const QVector<ChannelNameAndType> actualResults = buildRequiredChannelsAndTypes(handler, mapper);
2838
2839 // THEN
2840 QCOMPARE(actualResults.size(), expectedResults.size());
2841 for (int i = 0; i < actualResults.size(); ++i)
2842 QCOMPARE(actualResults[i], expectedResults[i]);
2843
2844 // Cleanup
2845 delete handler;
2846 }
2847
2848 void checkAssignChannelComponentIndices_data()
2849 {
2850 QTest::addColumn<QVector<ChannelNameAndType>>(name: "allChannels");
2851 QTest::addColumn<QVector<ComponentIndices>>(name: "expectedResults");
2852
2853 {
2854 QVector<ChannelNameAndType> allChannels;
2855 allChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
2856
2857 QVector<ComponentIndices> expectedResults;
2858 expectedResults.push_back(t: { 0, 1, 2 });
2859
2860 QTest::newRow(dataTag: "vec3 location") << allChannels << expectedResults;
2861 }
2862
2863 {
2864 QVector<ChannelNameAndType> allChannels;
2865 allChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
2866 allChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
2867
2868 QVector<ComponentIndices> expectedResults;
2869 expectedResults.push_back(t: { 0, 1, 2 });
2870 expectedResults.push_back(t: { 3, 4, 5, 6 });
2871
2872 QTest::newRow(dataTag: "vec3 location, quaterion rotation") << allChannels << expectedResults;
2873 }
2874
2875 {
2876 QVector<ChannelNameAndType> allChannels;
2877 allChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
2878 allChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
2879 allChannels.push_back(t: { QLatin1String("BaseColor"), static_cast<int>(QVariant::Vector3D), 3 });
2880 allChannels.push_back(t: { QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
2881 allChannels.push_back(t: { QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
2882 allChannels.push_back(t: { QLatin1String("MorphWeights"), static_cast<int>(QVariant::List), 6 });
2883
2884 QVector<ComponentIndices> expectedResults;
2885 expectedResults.push_back(t: { 0, 1, 2 });
2886 expectedResults.push_back(t: { 3, 4, 5, 6 });
2887 expectedResults.push_back(t: { 7, 8, 9 });
2888 expectedResults.push_back(t: { 10 });
2889 expectedResults.push_back(t: { 11 });
2890 expectedResults.push_back(t: { 12, 13, 14, 15, 16, 17 });
2891
2892 QTest::newRow(dataTag: "vec3 location, quaterion rotation, pbr metal-rough morphweights") << allChannels << expectedResults;
2893 }
2894
2895 {
2896 QVector<ChannelNameAndType> allChannels;
2897 const int jointCount = 4;
2898 for (int i = 0; i < jointCount; ++i) {
2899 ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 };
2900 locationDescription.jointIndex = i;
2901 allChannels.push_back(t: locationDescription);
2902
2903 ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 };
2904 rotationDescription.jointIndex = i;
2905 allChannels.push_back(t: rotationDescription);
2906
2907 ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D), 3 };
2908 scaleDescription.jointIndex = i;
2909 allChannels.push_back(t: scaleDescription);
2910 }
2911
2912 QVector<ComponentIndices> expectedResults;
2913 expectedResults.push_back(t: { 0, 1, 2 });
2914 expectedResults.push_back(t: { 3, 4, 5, 6 });
2915 expectedResults.push_back(t: { 7, 8, 9 });
2916
2917 expectedResults.push_back(t: { 10, 11, 12 });
2918 expectedResults.push_back(t: { 13, 14, 15, 16 });
2919 expectedResults.push_back(t: { 17, 18, 19 });
2920
2921 expectedResults.push_back(t: { 20, 21, 22 });
2922 expectedResults.push_back(t: { 23, 24, 25, 26 });
2923 expectedResults.push_back(t: { 27, 28, 29 });
2924
2925 expectedResults.push_back(t: { 30, 31, 32 });
2926 expectedResults.push_back(t: { 33, 34, 35, 36 });
2927 expectedResults.push_back(t: { 37, 38, 39 });
2928
2929 QTest::newRow(dataTag: "skeleton, 4 joints") << allChannels << expectedResults;
2930 }
2931 }
2932
2933 void checkAssignChannelComponentIndices()
2934 {
2935 // GIVEN
2936 QFETCH(QVector<ChannelNameAndType>, allChannels);
2937 QFETCH(QVector<ComponentIndices>, expectedResults);
2938
2939 // WHEN
2940 const QVector<ComponentIndices> actualResults = assignChannelComponentIndices(namesAndTypes: allChannels);
2941
2942 // THEN
2943 QCOMPARE(actualResults.size(), expectedResults.size());
2944 for (int i = 0; i < actualResults.size(); ++i) {
2945 const ComponentIndices &actualResult = actualResults[i];
2946 const ComponentIndices &expectedResult = expectedResults[i];
2947
2948 for (int j = 0; j < actualResult.size(); ++j)
2949 QCOMPARE(actualResult[j], expectedResult[j]);
2950 }
2951 }
2952
2953 void checkGenerateClipFormatIndices_data()
2954 {
2955 QTest::addColumn<QVector<ChannelNameAndType>>(name: "targetChannels");
2956 QTest::addColumn<QVector<ComponentIndices>>(name: "targetIndices");
2957 QTest::addColumn<AnimationClip *>(name: "clip");
2958 QTest::addColumn<ClipFormat>(name: "expectedResults");
2959
2960 {
2961 QVector<ChannelNameAndType> targetChannels;
2962 targetChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
2963 targetChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
2964 targetChannels.push_back(t: { QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D), 3 });
2965 targetChannels.push_back(t: { QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
2966 targetChannels.push_back(t: { QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
2967
2968 QVector<ComponentIndices> targetIndices;
2969 targetIndices.push_back(t: { 0, 1, 2, 3 });
2970 targetIndices.push_back(t: { 4, 5, 6 });
2971 targetIndices.push_back(t: { 7, 8, 9 });
2972 targetIndices.push_back(t: { 10 });
2973 targetIndices.push_back(t: { 11 });
2974
2975 auto *clip = new AnimationClip();
2976 clip->setDataType(AnimationClip::File);
2977 clip->setSource(QUrl("qrc:/clip3.json"));
2978 clip->loadAnimation();
2979
2980 ClipFormat expectedResults;
2981 expectedResults.sourceClipIndices = { 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json)
2982 4, 6, 5, // Location (y/z swapped in clip3.json)
2983 7, 8, 9, // Base Color
2984 10, // Metalness
2985 11 }; // Roughness
2986 expectedResults.sourceClipMask = { QBitArray(4, true),
2987 QBitArray(3, true),
2988 QBitArray(3, true),
2989 QBitArray(1, true),
2990 QBitArray(1, true) };
2991 expectedResults.namesAndTypes = targetChannels;
2992 expectedResults.formattedComponentIndices = targetIndices;
2993
2994 QTest::newRow(dataTag: "rotation, location, pbr metal-rough")
2995 << targetChannels << targetIndices << clip << expectedResults;
2996 }
2997
2998 {
2999 QVector<ChannelNameAndType> targetChannels;
3000 targetChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
3001 targetChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
3002 targetChannels.push_back(t: { QLatin1String("Base Color"), static_cast<int>(QVariant::Vector3D), 3 });
3003 targetChannels.push_back(t: { QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
3004 targetChannels.push_back(t: { QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
3005
3006 QVector<ComponentIndices> targetIndices;
3007 targetIndices.push_back(t: { 0, 1, 2 });
3008 targetIndices.push_back(t: { 3, 4, 5, 6 });
3009 targetIndices.push_back(t: { 7, 8, 9 });
3010 targetIndices.push_back(t: { 10 });
3011 targetIndices.push_back(t: { 11 });
3012
3013 auto *clip = new AnimationClip();
3014 clip->setDataType(AnimationClip::File);
3015 clip->setSource(QUrl("qrc:/clip3.json"));
3016 clip->loadAnimation();
3017
3018 ClipFormat expectedResults;
3019 expectedResults.sourceClipIndices = { 4, 6, 5, // Location (y/z swapped in clip3.json)
3020 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json)
3021 7, 8, 9, // Base Color
3022 10, // Metalness
3023 11 }; // Roughness
3024 expectedResults.sourceClipMask = { QBitArray(3, true),
3025 QBitArray(4, true),
3026 QBitArray(3, true),
3027 QBitArray(1, true),
3028 QBitArray(1, true) };
3029 expectedResults.namesAndTypes = targetChannels;
3030 expectedResults.formattedComponentIndices = targetIndices;
3031
3032 QTest::newRow(dataTag: "location, rotation, pbr metal-rough")
3033 << targetChannels << targetIndices << clip << expectedResults;
3034 }
3035
3036 {
3037 QVector<ChannelNameAndType> targetChannels;
3038 targetChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
3039 targetChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
3040 targetChannels.push_back(t: { QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D), 3 });
3041 targetChannels.push_back(t: { QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
3042 targetChannels.push_back(t: { QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
3043
3044 QVector<ComponentIndices> targetIndices;
3045 targetIndices.push_back(t: { 0, 1, 2, 3 });
3046 targetIndices.push_back(t: { 4, 5, 6 });
3047 targetIndices.push_back(t: { 7, 8, 9 });
3048 targetIndices.push_back(t: { 10 });
3049 targetIndices.push_back(t: { 11 });
3050
3051 auto *clip = new AnimationClip();
3052 clip->setDataType(AnimationClip::File);
3053 clip->setSource(QUrl("qrc:/clip3.json"));
3054 clip->loadAnimation();
3055
3056 ClipFormat expectedResults;
3057 expectedResults.sourceClipIndices = { 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json)
3058 4, 6, 5, // Location (y/z swapped in clip3.json)
3059 -1, -1, -1, // Albedo (missing from clip)
3060 10, // Metalness
3061 11 }; // Roughness
3062 expectedResults.sourceClipMask = { QBitArray(4, true),
3063 QBitArray(3, true),
3064 QBitArray(3, false),
3065 QBitArray(1, true),
3066 QBitArray(1, true) };
3067 expectedResults.namesAndTypes = targetChannels;
3068 expectedResults.formattedComponentIndices = targetIndices;
3069
3070 QTest::newRow(dataTag: "rotation, location, albedo (missing), metal-rough")
3071 << targetChannels << targetIndices << clip << expectedResults;
3072 }
3073
3074 {
3075 QVector<ChannelNameAndType> targetChannels;
3076 targetChannels.push_back(t: { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 });
3077 targetChannels.push_back(t: { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 });
3078 targetChannels.push_back(t: { QLatin1String("Albedo"), static_cast<int>(QVariant::Vector3D), 3 });
3079 targetChannels.push_back(t: { QLatin1String("Metalness"), static_cast<int>(QVariant::Double), 1 });
3080 targetChannels.push_back(t: { QLatin1String("Roughness"), static_cast<int>(QVariant::Double), 1 });
3081
3082 QVector<ComponentIndices> targetIndices;
3083 targetIndices.push_back(t: { 0, 1, 2 });
3084 targetIndices.push_back(t: { 3, 4, 5, 6 });
3085 targetIndices.push_back(t: { 7, 8, 9 });
3086 targetIndices.push_back(t: { 10 });
3087 targetIndices.push_back(t: { 11 });
3088
3089 auto *clip = new AnimationClip();
3090 clip->setDataType(AnimationClip::File);
3091 clip->setSource(QUrl("qrc:/clip3.json"));
3092 clip->loadAnimation();
3093
3094 ClipFormat expectedResults;
3095 expectedResults.sourceClipIndices = { 4, 6, 5, // Location (y/z swapped in clip3.json)
3096 0, 1, 3, 2, // Rotation (y/z swapped in clip3.json)
3097 -1, -1, -1, // Albedo (missing from clip)
3098 10, // Metalness
3099 11 }; // Roughness
3100 expectedResults.sourceClipMask = { QBitArray(3, true),
3101 QBitArray(4, true),
3102 QBitArray(3, false),
3103 QBitArray(1, true),
3104 QBitArray(1, true) };
3105 expectedResults.namesAndTypes = targetChannels;
3106 expectedResults.formattedComponentIndices = targetIndices;
3107
3108 QTest::newRow(dataTag: "location, rotation, albedo (missing), metal-rough")
3109 << targetChannels << targetIndices << clip << expectedResults;
3110 }
3111
3112 {
3113 QVector<ChannelNameAndType> targetChannels;
3114 const int jointCount = 4;
3115 for (int i = 0; i < jointCount; ++i) {
3116 ChannelNameAndType locationDescription = { QLatin1String("Location"), static_cast<int>(QVariant::Vector3D), 3 };
3117 locationDescription.jointIndex = i;
3118 targetChannels.push_back(t: locationDescription);
3119
3120 ChannelNameAndType rotationDescription = { QLatin1String("Rotation"), static_cast<int>(QVariant::Quaternion), 4 };
3121 rotationDescription.jointIndex = i;
3122 targetChannels.push_back(t: rotationDescription);
3123
3124 ChannelNameAndType scaleDescription = { QLatin1String("Scale"), static_cast<int>(QVariant::Vector3D), 3 };
3125 scaleDescription.jointIndex = i;
3126 targetChannels.push_back(t: scaleDescription);
3127 }
3128
3129 QVector<ComponentIndices> targetIndices;
3130 targetIndices.push_back(t: { 0, 1, 2 });
3131 targetIndices.push_back(t: { 3, 4, 5, 6 });
3132 targetIndices.push_back(t: { 7, 8, 9 });
3133
3134 targetIndices.push_back(t: { 10, 11, 12 });
3135 targetIndices.push_back(t: { 13, 14, 15, 16 });
3136 targetIndices.push_back(t: { 17, 18, 19 });
3137
3138 targetIndices.push_back(t: { 20, 21, 22 });
3139 targetIndices.push_back(t: { 23, 24, 25, 26 });
3140 targetIndices.push_back(t: { 27, 28, 29 });
3141
3142 targetIndices.push_back(t: { 30, 31, 32 });
3143 targetIndices.push_back(t: { 33, 34, 35, 36 });
3144 targetIndices.push_back(t: { 37, 38, 39 });
3145
3146 auto *clip = new AnimationClip();
3147 clip->setDataType(AnimationClip::File);
3148 clip->setSource(QUrl("qrc:/clip5.json"));
3149 clip->loadAnimation();
3150
3151 ClipFormat expectedResults;
3152 expectedResults.sourceClipIndices = { 4, 6, 5, // Location, joint 0 (y/z swapped in clip5.json)
3153 0, 1, 3, 2, // Rotation, joint 0 (y/z swapped in clip5.json)
3154 7, 8, 9, // Scale, joint 0
3155 14, 16, 15, // Location, joint 1 (y/z swapped in clip5.json)
3156 10, 11, 13, 12, // Rotation, joint 1 (y/z swapped in clip5.json)
3157 17, 18, 19, // Scale, joint 1
3158 24, 26, 25, // Location, joint 2 (y/z swapped in clip5.json)
3159 20, 21, 23, 22, // Rotation, joint 2 (y/z swapped in clip5.json)
3160 27, 28, 29, // Scale, joint 2
3161 34, 36, 35, // Location, joint 3 (y/z swapped in clip5.json)
3162 30, 31, 33, 32, // Rotation, joint 3 (y/z swapped in clip5.json)
3163 37, 38, 39 }; // Scale, joint 3
3164 expectedResults.sourceClipMask = { QBitArray(3, true),
3165 QBitArray(4, true),
3166 QBitArray(3, true),
3167 QBitArray(3, true),
3168 QBitArray(4, true),
3169 QBitArray(3, true),
3170 QBitArray(3, true),
3171 QBitArray(4, true),
3172 QBitArray(3, true),
3173 QBitArray(3, true),
3174 QBitArray(4, true),
3175 QBitArray(3, true) };
3176 expectedResults.namesAndTypes = targetChannels;
3177 expectedResults.formattedComponentIndices = targetIndices;
3178
3179 QTest::newRow(dataTag: "skeleton (SQT), 4 joints")
3180 << targetChannels << targetIndices << clip << expectedResults;
3181 }
3182 }
3183
3184 void checkGenerateClipFormatIndices()
3185 {
3186 // GIVEN
3187 QFETCH(QVector<ChannelNameAndType>, targetChannels);
3188 QFETCH(QVector<ComponentIndices>, targetIndices);
3189 QFETCH(AnimationClip *, clip);
3190 QFETCH(ClipFormat, expectedResults);
3191
3192 // WHEN
3193 const ClipFormat actualResults = generateClipFormatIndices(targetChannels,
3194 targetIndices,
3195 clip);
3196
3197 // THEN
3198 QCOMPARE(actualResults.sourceClipIndices.size(), expectedResults.sourceClipIndices.size());
3199 for (int i = 0; i < actualResults.sourceClipIndices.size(); ++i)
3200 QCOMPARE(actualResults.sourceClipIndices[i], expectedResults.sourceClipIndices[i]);
3201
3202 QCOMPARE(actualResults.sourceClipMask.size(), expectedResults.sourceClipMask.size());
3203 for (int i = 0; i < actualResults.sourceClipMask.size(); ++i)
3204 QCOMPARE(actualResults.sourceClipMask[i], expectedResults.sourceClipMask[i]);
3205
3206 QCOMPARE(actualResults.formattedComponentIndices.size(), expectedResults.formattedComponentIndices.size());
3207 for (int i = 0; i < actualResults.formattedComponentIndices.size(); ++i)
3208 QCOMPARE(actualResults.formattedComponentIndices[i], expectedResults.formattedComponentIndices[i]);
3209
3210 QCOMPARE(actualResults.namesAndTypes.size(), expectedResults.namesAndTypes.size());
3211 for (int i = 0; i < actualResults.namesAndTypes.size(); ++i)
3212 QCOMPARE(actualResults.namesAndTypes[i], expectedResults.namesAndTypes[i]);
3213
3214 // Cleanup
3215 delete clip;
3216 }
3217
3218 void checkDefaultValueForChannel_data()
3219 {
3220 QTest::addColumn<Handler *>(name: "handler");
3221 QTest::addColumn<ChannelNameAndType>(name: "channelDescription");
3222 QTest::addColumn<QVector<float>>(name: "expectedResults");
3223
3224 {
3225 auto handler = new Handler();
3226 auto channelMapping = createChannelMapping(handler,
3227 channelName: QLatin1String("Location"),
3228 targetId: Qt3DCore::QNodeId::createId(),
3229 propertyName: "translation",
3230 type: static_cast<int>(QVariant::Vector3D),
3231 componentCount: 3);
3232 ChannelNameAndType channelDescription;
3233 channelDescription.mappingId = channelMapping->peerId();
3234 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3235 channelDescription.name = QLatin1String("translation");
3236 const QVector<float> expectedResults = { 0.0f, 0.0f, 0.0f };
3237 QTest::newRow(dataTag: "translation") << handler << channelDescription << expectedResults;
3238 }
3239
3240 {
3241 auto handler = new Handler();
3242 auto channelMapping = createChannelMapping(handler,
3243 channelName: QLatin1String("Rotation"),
3244 targetId: Qt3DCore::QNodeId::createId(),
3245 propertyName: "rotation",
3246 type: static_cast<int>(QVariant::Quaternion),
3247 componentCount: 4);
3248 ChannelNameAndType channelDescription;
3249 channelDescription.mappingId = channelMapping->peerId();
3250 channelDescription.type = static_cast<int>(QVariant::Quaternion);
3251 channelDescription.name = QLatin1String("rotation");
3252 const QVector<float> expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f };
3253 QTest::newRow(dataTag: "rotation") << handler << channelDescription << expectedResults;
3254 }
3255
3256 {
3257 auto handler = new Handler();
3258 auto channelMapping = createChannelMapping(handler,
3259 channelName: QLatin1String("Scale"),
3260 targetId: Qt3DCore::QNodeId::createId(),
3261 propertyName: "scale",
3262 type: static_cast<int>(QVariant::Vector3D),
3263 componentCount: 3);
3264 ChannelNameAndType channelDescription;
3265 channelDescription.mappingId = channelMapping->peerId();
3266 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3267 channelDescription.name = QLatin1String("scale");
3268 const QVector<float> expectedResults = { 1.0f, 1.0f, 1.0f };
3269 QTest::newRow(dataTag: "scale") << handler << channelDescription << expectedResults;
3270 }
3271
3272 // Test skeleton cases
3273 {
3274 auto handler = new Handler();
3275 auto skeleton = createSkeleton(handler, jointCount: 2);
3276 skeleton->setJointScale(jointIndex: 0, scale: QVector3D(2.0f, 3.0f, 4.0f));
3277
3278 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3279 ChannelNameAndType channelDescription;
3280 channelDescription.mappingId = channelMapping->peerId();
3281 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3282 channelDescription.jointIndex = 0;
3283 channelDescription.jointTransformComponent = Scale;
3284 const QVector<float> expectedResults = { 2.0f, 3.0f, 4.0f };
3285 QTest::newRow(dataTag: "joint 0 scale") << handler << channelDescription << expectedResults;
3286 }
3287
3288 {
3289 auto handler = new Handler();
3290 auto skeleton = createSkeleton(handler, jointCount: 2);
3291 skeleton->setJointRotation(jointIndex: 0, rotation: QQuaternion(1.0f, 0.0f, 0.0f, 0.0f));
3292
3293 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3294 ChannelNameAndType channelDescription;
3295 channelDescription.mappingId = channelMapping->peerId();
3296 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3297 channelDescription.jointIndex = 0;
3298 channelDescription.jointTransformComponent = Rotation;
3299 const QVector<float> expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f };
3300 QTest::newRow(dataTag: "joint 0 rotation") << handler << channelDescription << expectedResults;
3301 }
3302
3303 {
3304 auto handler = new Handler();
3305 auto skeleton = createSkeleton(handler, jointCount: 2);
3306 skeleton->setJointTranslation(jointIndex: 0, translation: QVector3D(2.0f, 3.0f, 4.0f));
3307
3308 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3309 ChannelNameAndType channelDescription;
3310 channelDescription.mappingId = channelMapping->peerId();
3311 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3312 channelDescription.jointIndex = 0;
3313 channelDescription.jointTransformComponent = Translation;
3314 const QVector<float> expectedResults = { 2.0f, 3.0f, 4.0f };
3315 QTest::newRow(dataTag: "joint 0 translation") << handler << channelDescription << expectedResults;
3316 }
3317
3318 {
3319 auto handler = new Handler();
3320 auto skeleton = createSkeleton(handler, jointCount: 2);
3321 skeleton->setJointScale(jointIndex: 1, scale: QVector3D(20.0f, 30.0f, 40.0f));
3322
3323 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3324 ChannelNameAndType channelDescription;
3325 channelDescription.mappingId = channelMapping->peerId();
3326 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3327 channelDescription.jointIndex = 1;
3328 channelDescription.jointTransformComponent = Scale;
3329 const QVector<float> expectedResults = { 20.0f, 30.0f, 40.0f };
3330 QTest::newRow(dataTag: "joint 1 scale") << handler << channelDescription << expectedResults;
3331 }
3332
3333 {
3334 auto handler = new Handler();
3335 auto skeleton = createSkeleton(handler, jointCount: 2);
3336 skeleton->setJointRotation(jointIndex: 1, rotation: QQuaternion(1.0f, 0.0f, 0.0f, 0.0f));
3337
3338 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3339 ChannelNameAndType channelDescription;
3340 channelDescription.mappingId = channelMapping->peerId();
3341 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3342 channelDescription.jointIndex = 1;
3343 channelDescription.jointTransformComponent = Rotation;
3344 const QVector<float> expectedResults = { 1.0f, 0.0f, 0.0f, 0.0f };
3345 QTest::newRow(dataTag: "joint 1 rotation") << handler << channelDescription << expectedResults;
3346 }
3347
3348 {
3349 auto handler = new Handler();
3350 auto skeleton = createSkeleton(handler, jointCount: 2);
3351 skeleton->setJointTranslation(jointIndex: 1, translation: QVector3D(4.0f, 5.0f, 6.0f));
3352
3353 auto channelMapping = createChannelMapping(handler, skeletonId: skeleton->peerId());
3354 ChannelNameAndType channelDescription;
3355 channelDescription.mappingId = channelMapping->peerId();
3356 channelDescription.type = static_cast<int>(QVariant::Vector3D);
3357 channelDescription.jointIndex = 1;
3358 channelDescription.jointTransformComponent = Translation;
3359 const QVector<float> expectedResults = { 4.0f, 5.0f, 6.0f };
3360 QTest::newRow(dataTag: "joint 1 translation") << handler << channelDescription << expectedResults;
3361 }
3362 }
3363
3364 void checkDefaultValueForChannel()
3365 {
3366 // GIVEN
3367 QFETCH(Handler *, handler);
3368 QFETCH(ChannelNameAndType, channelDescription);
3369 QFETCH(QVector<float>, expectedResults);
3370
3371 // WHEN
3372 auto actualResults = defaultValueForChannel(handler, channelDescription);
3373
3374 // THEN
3375 QCOMPARE(actualResults.size(), expectedResults.size());
3376 for (int i = 0; i < actualResults.size(); ++i) {
3377 QCOMPARE(actualResults[i], expectedResults[i]);
3378 }
3379 }
3380};
3381
3382QTEST_MAIN(tst_AnimationUtils)
3383
3384#include "tst_animationutils.moc"
3385

source code of qt3d/tests/auto/animation/animationutils/tst_animationutils.cpp