1/****************************************************************************
2**
3** Copyright (C) 2017 Paul Lemire <paul.lemire350@gmail.com>
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
30#include <QtTest/QTest>
31#include <Qt3DAnimation/qadditiveclipblend.h>
32#include <Qt3DAnimation/qanimationcliploader.h>
33#include <Qt3DAnimation/private/qadditiveclipblend_p.h>
34#include <Qt3DAnimation/private/additiveclipblend_p.h>
35#include <Qt3DCore/qpropertyupdatedchange.h>
36#include "qbackendnodetester.h"
37
38using namespace Qt3DAnimation::Animation;
39
40Q_DECLARE_METATYPE(Handler *)
41Q_DECLARE_METATYPE(AdditiveClipBlend *)
42
43namespace {
44
45class TestClipBlendNode : public ClipBlendNode
46{
47public:
48 TestClipBlendNode(double duration)
49 : ClipBlendNode(ClipBlendNode::LerpBlendType)
50 , m_duration(duration)
51 {}
52
53 inline QVector<Qt3DCore::QNodeId> allDependencyIds() const override
54 {
55 return currentDependencyIds();
56 }
57
58 QVector<Qt3DCore::QNodeId> currentDependencyIds() const final
59 {
60 return QVector<Qt3DCore::QNodeId>();
61 }
62
63 using ClipBlendNode::setClipResults;
64
65 double duration() const final { return m_duration; }
66
67protected:
68 ClipResults doBlend(const QVector<ClipResults> &) const final { return ClipResults(); }
69
70private:
71 double m_duration;
72};
73
74} // anonymous
75
76class tst_AdditiveClipBlend : public Qt3DCore::QBackendNodeTester
77{
78 Q_OBJECT
79public:
80 TestClipBlendNode *createTestBlendNode(Handler *handler,
81 double duration)
82 {
83 auto id = Qt3DCore::QNodeId::createId();
84 TestClipBlendNode *node = new TestClipBlendNode(duration);
85 setPeerId(node, id);
86 node->setHandler(handler);
87 node->setClipBlendNodeManager(handler->clipBlendNodeManager());
88 handler->clipBlendNodeManager()->appendNode(id, node);
89 return node;
90 }
91
92 AdditiveClipBlend *createAdditiveClipBlendNode(Handler *handler, const float &blendFactor)
93 {
94 auto id = Qt3DCore::QNodeId::createId();
95 AdditiveClipBlend *node = new AdditiveClipBlend();
96 node->setAdditiveFactor(blendFactor);
97 setPeerId(node, id);
98 node->setHandler(handler);
99 node->setClipBlendNodeManager(handler->clipBlendNodeManager());
100 handler->clipBlendNodeManager()->appendNode(id, node);
101 return node;
102 }
103
104 BlendedClipAnimator *createBlendedClipAnimator(Handler *handler,
105 qint64 globalStartTimeNS,
106 int loops)
107 {
108 auto animatorId = Qt3DCore::QNodeId::createId();
109 BlendedClipAnimator *animator = handler->blendedClipAnimatorManager()->getOrCreateResource(animatorId);
110 setPeerId(animator, animatorId);
111 animator->setStartTime(globalStartTimeNS);
112 animator->setLoops(loops);
113 return animator;
114 }
115
116private Q_SLOTS:
117
118 void checkInitialState()
119 {
120 // GIVEN
121 AdditiveClipBlend backendAdditiveBlend;
122
123 // THEN
124 QCOMPARE(backendAdditiveBlend.isEnabled(), false);
125 QVERIFY(backendAdditiveBlend.peerId().isNull());
126 QCOMPARE(backendAdditiveBlend.baseClipId(), Qt3DCore::QNodeId());
127 QCOMPARE(backendAdditiveBlend.additiveClipId(), Qt3DCore::QNodeId());
128 QCOMPARE(backendAdditiveBlend.additiveFactor(), 0.0f);
129 QCOMPARE(backendAdditiveBlend.blendType(), ClipBlendNode::AdditiveBlendType);
130 }
131
132 void checkInitializeFromPeer()
133 {
134 // GIVEN
135 Qt3DAnimation::QAdditiveClipBlend additiveBlend;
136 Qt3DAnimation::QAdditiveClipBlend baseClip;
137 Qt3DAnimation::QAdditiveClipBlend additiveClip;
138 Qt3DAnimation::QAnimationClipLoader clip;
139 additiveBlend.setBaseClip(&baseClip);
140 additiveBlend.setAdditiveClip(&additiveClip);
141 additiveBlend.setAdditiveFactor(0.8f);
142
143 {
144 // WHEN
145 AdditiveClipBlend backendAdditiveBlend;
146 simulateInitialization(&additiveBlend, &backendAdditiveBlend);
147
148 // THEN
149 QCOMPARE(backendAdditiveBlend.isEnabled(), true);
150 QCOMPARE(backendAdditiveBlend.peerId(), additiveBlend.id());
151 QCOMPARE(backendAdditiveBlend.baseClipId(), baseClip.id());
152 QCOMPARE(backendAdditiveBlend.additiveClipId(), additiveClip.id());
153 QCOMPARE(backendAdditiveBlend.additiveFactor(), 0.8f);
154 }
155 {
156 // WHEN
157 AdditiveClipBlend backendAdditiveBlend;
158 additiveBlend.setEnabled(false);
159 simulateInitialization(&additiveBlend, &backendAdditiveBlend);
160
161 // THEN
162 QCOMPARE(backendAdditiveBlend.peerId(), additiveBlend.id());
163 QCOMPARE(backendAdditiveBlend.isEnabled(), false);
164 }
165 }
166
167 void checkSceneChangeEvents()
168 {
169 // GIVEN
170 AdditiveClipBlend backendAdditiveBlend;
171 {
172 // WHEN
173 const bool newValue = false;
174 const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
175 change->setPropertyName("enabled");
176 change->setValue(newValue);
177 backendAdditiveBlend.sceneChangeEvent(change);
178
179 // THEN
180 QCOMPARE(backendAdditiveBlend.isEnabled(), newValue);
181 }
182 {
183 // WHEN
184 const float newValue = 0.883f;
185 const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
186 change->setPropertyName("additiveFactor");
187 change->setValue(QVariant::fromValue(newValue));
188 backendAdditiveBlend.sceneChangeEvent(change);
189
190 // THEN
191 QCOMPARE(backendAdditiveBlend.additiveFactor(), newValue);
192 }
193 {
194 // WHEN
195 const Qt3DAnimation::QAdditiveClipBlend newValue;
196 const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
197 change->setPropertyName("baseClip");
198 change->setValue(QVariant::fromValue(newValue.id()));
199 backendAdditiveBlend.sceneChangeEvent(change);
200
201 // THEN
202 QCOMPARE(backendAdditiveBlend.baseClipId(), newValue.id());
203 }
204 {
205 // WHEN
206 const Qt3DAnimation::QAdditiveClipBlend newValue;
207 const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
208 change->setPropertyName("additiveClip");
209 change->setValue(QVariant::fromValue(newValue.id()));
210 backendAdditiveBlend.sceneChangeEvent(change);
211
212 // THEN
213 QCOMPARE(backendAdditiveBlend.additiveClipId(), newValue.id());
214 }
215 }
216
217 void checkDependencyIds()
218 {
219 // GIVEN
220 AdditiveClipBlend addBlend;
221 auto baseClipId = Qt3DCore::QNodeId::createId();
222 auto additiveClipId = Qt3DCore::QNodeId::createId();
223
224 // WHEN
225 addBlend.setBaseClipId(baseClipId);
226 addBlend.setAdditiveClipId(additiveClipId);
227 QVector<Qt3DCore::QNodeId> actualIds = addBlend.currentDependencyIds();
228
229 // THEN
230 QCOMPARE(actualIds.size(), 2);
231 QCOMPARE(actualIds[0], baseClipId);
232 QCOMPARE(actualIds[1], additiveClipId);
233
234 // WHEN
235 auto anotherAdditiveClipId = Qt3DCore::QNodeId::createId();
236 addBlend.setAdditiveClipId(anotherAdditiveClipId);
237 actualIds = addBlend.currentDependencyIds();
238
239 // THEN
240 QCOMPARE(actualIds.size(), 2);
241 QCOMPARE(actualIds[0], baseClipId);
242 QCOMPARE(actualIds[1], anotherAdditiveClipId);
243 }
244
245 void checkDuration()
246 {
247 // GIVEN
248 auto handler = new Handler();
249 const double expectedDuration = 123.5;
250 const double baseNodeDuration = expectedDuration;
251 const double additiveNodeDuration = 5.0;
252
253 auto baseNode = createTestBlendNode(handler, baseNodeDuration);
254 auto additiveNode = createTestBlendNode(handler, additiveNodeDuration);
255
256 AdditiveClipBlend blendNode;
257 blendNode.setHandler(handler);
258 blendNode.setClipBlendNodeManager(handler->clipBlendNodeManager());
259 blendNode.setBaseClipId(baseNode->peerId());
260 blendNode.setAdditiveClipId(additiveNode->peerId());
261
262 // WHEN
263 double actualDuration = blendNode.duration();
264
265 // THEN
266 QCOMPARE(actualDuration, expectedDuration);
267 }
268
269 void checkDoBlend_data()
270 {
271 QTest::addColumn<Handler *>("handler");
272 QTest::addColumn<AdditiveClipBlend *>("blendNode");
273 QTest::addColumn<Qt3DCore::QNodeId>("animatorId");
274 QTest::addColumn<ClipResults>("expectedResults");
275
276 {
277 auto handler = new Handler();
278
279 const qint64 globalStartTimeNS = 0;
280 const int loopCount = 1;
281 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount);
282
283 const double duration = 1.0;
284 auto baseNode = createTestBlendNode(handler, duration);
285 baseNode->setClipResults(animator->peerId(), { 0.0f, 0.0f, 0.0f });
286 auto additiveNode = createTestBlendNode(handler, duration);
287 additiveNode->setClipResults(animator->peerId(), { 1.0f, 1.0f, 1.0f });
288
289 const float additiveFactor = 0.0f;
290 auto blendNode = createAdditiveClipBlendNode(handler, additiveFactor);
291 blendNode->setBaseClipId(baseNode->peerId());
292 blendNode->setAdditiveClipId(additiveNode->peerId());
293 blendNode->setAdditiveFactor(additiveFactor);
294
295 ClipResults expectedResults = { 0.0f, 0.0f, 0.0f };
296
297 QTest::addRow("unit additive, beta = 0.0")
298 << handler << blendNode << animator->peerId() << expectedResults;
299 }
300
301 {
302 auto handler = new Handler();
303
304 const qint64 globalStartTimeNS = 0;
305 const int loopCount = 1;
306 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount);
307
308 const double duration = 1.0;
309 auto baseNode = createTestBlendNode(handler, duration);
310 baseNode->setClipResults(animator->peerId(), { 0.0f, 0.0f, 0.0f });
311 auto additiveNode = createTestBlendNode(handler, duration);
312 additiveNode->setClipResults(animator->peerId(), { 1.0f, 1.0f, 1.0f });
313
314 const float additiveFactor = 0.5f;
315 auto blendNode = createAdditiveClipBlendNode(handler, additiveFactor);
316 blendNode->setBaseClipId(baseNode->peerId());
317 blendNode->setAdditiveClipId(additiveNode->peerId());
318 blendNode->setAdditiveFactor(additiveFactor);
319
320 ClipResults expectedResults = { 0.5f, 0.5f, 0.5f };
321
322 QTest::addRow("unit additive, beta = 0.5")
323 << handler << blendNode << animator->peerId() << expectedResults;
324 }
325
326 {
327 auto handler = new Handler();
328
329 const qint64 globalStartTimeNS = 0;
330 const int loopCount = 1;
331 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount);
332
333 const double duration = 1.0;
334 auto baseNode = createTestBlendNode(handler, duration);
335 baseNode->setClipResults(animator->peerId(), { 0.0f, 0.0f, 0.0f });
336 auto additiveNode = createTestBlendNode(handler, duration);
337 additiveNode->setClipResults(animator->peerId(), { 1.0f, 1.0f, 1.0f });
338
339 const float additiveFactor = 1.0f;
340 auto blendNode = createAdditiveClipBlendNode(handler, additiveFactor);
341 blendNode->setBaseClipId(baseNode->peerId());
342 blendNode->setAdditiveClipId(additiveNode->peerId());
343 blendNode->setAdditiveFactor(additiveFactor);
344
345 ClipResults expectedResults = { 1.0f, 1.0f, 1.0f };
346
347 QTest::addRow("unit additive, beta = 1.0")
348 << handler << blendNode << animator->peerId() << expectedResults;
349 }
350
351 {
352 auto handler = new Handler();
353
354 const qint64 globalStartTimeNS = 0;
355 const int loopCount = 1;
356 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount);
357
358 const double duration = 1.0;
359 auto baseNode = createTestBlendNode(handler, duration);
360 baseNode->setClipResults(animator->peerId(), { 0.0f, 1.0f, 2.0f });
361 auto additiveNode = createTestBlendNode(handler, duration);
362 additiveNode->setClipResults(animator->peerId(), { 1.0f, 2.0f, 3.0f });
363
364 const float blendFactor = 0.5f;
365 auto blendNode = createAdditiveClipBlendNode(handler, blendFactor);
366 blendNode->setBaseClipId(baseNode->peerId());
367 blendNode->setAdditiveClipId(additiveNode->peerId());
368 blendNode->setAdditiveFactor(blendFactor);
369
370 ClipResults expectedResults = { 0.5f, 2.0f, 3.5f };
371
372 QTest::addRow("lerp varying data, beta = 0.5")
373 << handler << blendNode << animator->peerId() << expectedResults;
374 }
375
376 {
377 auto handler = new Handler();
378
379 const qint64 globalStartTimeNS = 0;
380 const int loopCount = 1;
381 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loopCount);
382
383 const double duration = 1.0;
384 const int dataCount = 1000;
385 ClipResults baseData(dataCount);
386 ClipResults additiveData(dataCount);
387 ClipResults expectedResults(dataCount);
388 for (int i = 0; i < dataCount; ++i) {
389 baseData[i] = float(i);
390 additiveData[i] = 2.0f * float(i);
391 expectedResults[i] = 2.0f * float(i);
392 }
393 auto baseNode = createTestBlendNode(handler, duration);
394 baseNode->setClipResults(animator->peerId(), baseData);
395 auto additiveNode = createTestBlendNode(handler, duration);
396 additiveNode->setClipResults(animator->peerId(), additiveData);
397
398 const float blendFactor = 0.5f;
399 auto blendNode = createAdditiveClipBlendNode(handler, blendFactor);
400 blendNode->setBaseClipId(baseNode->peerId());
401 blendNode->setAdditiveClipId(additiveNode->peerId());
402 blendNode->setAdditiveFactor(blendFactor);
403
404 QTest::addRow("lerp lots of data, beta = 0.5")
405 << handler << blendNode << animator->peerId() << expectedResults;
406 }
407 }
408
409 void checkDoBlend()
410 {
411 // GIVEN
412 QFETCH(Handler *, handler);
413 QFETCH(AdditiveClipBlend *, blendNode);
414 QFETCH(Qt3DCore::QNodeId, animatorId);
415 QFETCH(ClipResults, expectedResults);
416
417 // WHEN
418 blendNode->blend(animatorId);
419
420 // THEN
421 const ClipResults actualResults = blendNode->clipResults(animatorId);
422 QCOMPARE(actualResults.size(), expectedResults.size());
423 for (int i = 0; i < actualResults.size(); ++i)
424 QCOMPARE(actualResults[i], expectedResults[i]);
425
426 // Cleanup
427 delete handler;
428 }
429};
430
431QTEST_MAIN(tst_AdditiveClipBlend)
432
433#include "tst_additiveclipblend.moc"
434