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 | |
51 | using namespace Qt3DAnimation::Animation; |
52 | |
53 | Q_DECLARE_METATYPE(Qt3DAnimation::Animation::Handler*) |
54 | Q_DECLARE_METATYPE(QVector<ChannelMapping *>) |
55 | Q_DECLARE_METATYPE(Clock *) |
56 | Q_DECLARE_METATYPE(ChannelMapper *) |
57 | Q_DECLARE_METATYPE(AnimationClip *) |
58 | Q_DECLARE_METATYPE(QVector<MappingData>) |
59 | Q_DECLARE_METATYPE(Channel) |
60 | Q_DECLARE_METATYPE(AnimatorEvaluationData) |
61 | Q_DECLARE_METATYPE(ClipEvaluationData) |
62 | Q_DECLARE_METATYPE(ClipAnimator *) |
63 | Q_DECLARE_METATYPE(BlendedClipAnimator *) |
64 | Q_DECLARE_METATYPE(QVector<ChannelNameAndType>) |
65 | Q_DECLARE_METATYPE(QVector<AnimationCallbackAndValue>) |
66 | Q_DECLARE_METATYPE(ClipFormat) |
67 | Q_DECLARE_METATYPE(ChannelNameAndType) |
68 | |
69 | namespace { |
70 | |
71 | class MeanBlendNode : public ClipBlendNode |
72 | { |
73 | public: |
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 | |
99 | protected: |
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 | |
112 | private: |
113 | Qt3DCore::QNodeId m_value1Id; |
114 | Qt3DCore::QNodeId m_value2Id; |
115 | }; |
116 | |
117 | bool 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 | |
129 | class DummyCallback : public Qt3DAnimation::QAnimationCallback |
130 | { |
131 | public: |
132 | void valueChanged(const QVariant &) override { } |
133 | }; |
134 | |
135 | } // anonymous |
136 | |
137 | |
138 | class tst_AnimationUtils : public Qt3DCore::QBackendNodeTester |
139 | { |
140 | Q_OBJECT |
141 | |
142 | public: |
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 | |
274 | private 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 | |
3382 | QTEST_MAIN(tst_AnimationUtils) |
3383 | |
3384 | #include "tst_animationutils.moc" |
3385 | |