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/qlerpclipblend.h>
32#include <Qt3DAnimation/qanimationcliploader.h>
33#include <Qt3DAnimation/private/qabstractclipblendnode_p.h>
34#include <Qt3DAnimation/private/clipblendnode_p.h>
35#include <Qt3DAnimation/private/managers_p.h>
36#include "qbackendnodetester.h"
37
38#include <random>
39#include <algorithm>
40
41using namespace Qt3DAnimation::Animation;
42
43namespace {
44
45class TestClipBlendNode : public ClipBlendNode
46{
47public:
48 TestClipBlendNode(const ClipResults &clipResults = ClipResults())
49 : ClipBlendNode(ClipBlendNode::LerpBlendType)
50 , m_clipResults(clipResults)
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 0.0f; }
66
67protected:
68 ClipResults doBlend(const QVector<ClipResults> &) const final
69 {
70 return m_clipResults;
71 }
72
73private:
74 ClipResults m_clipResults;
75};
76
77} // anonymous
78
79Q_DECLARE_METATYPE(TestClipBlendNode *)
80
81class tst_ClipBlendNode : public Qt3DCore::QBackendNodeTester
82{
83 Q_OBJECT
84public:
85 TestClipBlendNode *createTestClipBlendNode(Handler *handler, const ClipResults &clipResults)
86 {
87 auto id = Qt3DCore::QNodeId::createId();
88 TestClipBlendNode *node = new TestClipBlendNode(clipResults);
89 setPeerId(backend: node, id);
90 handler->clipBlendNodeManager()->appendNode(id, node);
91 return node;
92 }
93
94 BlendedClipAnimator *createBlendedClipAnimator(Handler *handler,
95 qint64 globalStartTimeNS,
96 int loops)
97 {
98 auto animatorId = Qt3DCore::QNodeId::createId();
99 BlendedClipAnimator *animator = handler->blendedClipAnimatorManager()->getOrCreateResource(id: animatorId);
100 setPeerId(backend: animator, id: animatorId);
101 animator->setStartTime(globalStartTimeNS);
102 animator->setLoops(loops);
103 return animator;
104 }
105
106private Q_SLOTS:
107
108 void checkInitialState()
109 {
110 // GIVEN
111 TestClipBlendNode backendClipBlendNode;
112
113 // THEN
114 QCOMPARE(backendClipBlendNode.isEnabled(), false);
115 QVERIFY(backendClipBlendNode.peerId().isNull());
116 QVERIFY(backendClipBlendNode.clipBlendNodeManager() == nullptr);
117 QCOMPARE(backendClipBlendNode.blendType(), ClipBlendNode::LerpBlendType);
118 QCOMPARE(backendClipBlendNode.clipResults(Qt3DCore::QNodeId()), ClipResults());
119 }
120
121 void checkInitializeFromPeer()
122 {
123 // GIVEN
124 Qt3DAnimation::QLerpClipBlend clipBlendNode;
125 Qt3DAnimation::QAnimationClipLoader clip;
126
127 QCoreApplication::processEvents();
128
129 {
130 // WHEN
131 ClipBlendNodeManager manager;
132 TestClipBlendNode backendClipBlendNode;
133 backendClipBlendNode.setClipBlendNodeManager(&manager);
134 simulateInitialization(frontend: &clipBlendNode, backend: &backendClipBlendNode);
135
136 // THEN
137 QCOMPARE(backendClipBlendNode.isEnabled(), true);
138 QCOMPARE(backendClipBlendNode.peerId(), clipBlendNode.id());
139 QCOMPARE(backendClipBlendNode.clipBlendNodeManager(), &manager);
140 QCOMPARE(backendClipBlendNode.blendType(), ClipBlendNode::LerpBlendType);
141 QCOMPARE(backendClipBlendNode.clipResults(Qt3DCore::QNodeId()), ClipResults());
142 }
143 {
144 // WHEN
145 ClipBlendNodeManager manager;
146 TestClipBlendNode backendClipBlendNode;
147 clipBlendNode.setEnabled(false);
148 backendClipBlendNode.setClipBlendNodeManager(&manager);
149 simulateInitialization(frontend: &clipBlendNode, backend: &backendClipBlendNode);
150
151 // THEN
152 QCOMPARE(backendClipBlendNode.peerId(), clipBlendNode.id());
153 QCOMPARE(backendClipBlendNode.isEnabled(), false);
154 }
155 }
156
157 void checkClipResults_data()
158 {
159 QTest::addColumn<TestClipBlendNode *>(name: "blendNode");
160 QTest::addColumn<QVector<int>>(name: "indexes");
161 QTest::addColumn<QVector<Qt3DCore::QNodeId>>(name: "animatorIds");
162 QTest::addColumn<QVector<ClipResults>>(name: "expectedClipResults");
163
164 // Single entry
165 {
166 auto blendNode = new TestClipBlendNode;
167 QVector<Qt3DCore::QNodeId> animatorIds;
168 QVector<ClipResults> expectedClipResults;
169
170 const auto animatorId = Qt3DCore::QNodeId::createId();
171 animatorIds.push_back(t: animatorId);
172
173 ClipResults clipResults = { 0.0f, 1.0f, 2.0f };
174 for (int i = 0; i < 3; ++i)
175 clipResults.push_back(t: float(i));
176 expectedClipResults.push_back(t: clipResults);
177
178 // Set data and indexes
179 blendNode->setClipResults(animatorId, clipResults);
180 QVector<int> indexes = QVector<int>() << 0;
181
182 QTest::newRow(dataTag: "single entry")
183 << blendNode << indexes << animatorIds << expectedClipResults;
184 }
185
186 // No data
187 {
188 auto blendNode = new TestClipBlendNode;
189 QVector<Qt3DCore::QNodeId> animatorIds;
190 QVector<ClipResults> expectedClipResults;
191
192 auto animatorId = Qt3DCore::QNodeId::createId();
193 animatorIds.push_back(t: animatorId);
194
195 ClipResults clipResults;
196 expectedClipResults.push_back(t: clipResults);
197
198 // Don't set any data
199 QVector<int> indexes = QVector<int>() << 0;
200
201 QTest::newRow(dataTag: "no entries")
202 << blendNode << indexes << animatorIds << expectedClipResults;
203 }
204
205 // Multiple entries, ordered
206 {
207 auto blendNode = new TestClipBlendNode;
208 QVector<Qt3DCore::QNodeId> animatorIds;
209 QVector<ClipResults> expectedClipResults;
210
211 const int animatorCount = 10;
212 for (int j = 0; j < animatorCount; ++j) {
213 auto animatorId = Qt3DCore::QNodeId::createId();
214 animatorIds.push_back(t: animatorId);
215
216 ClipResults clipResults;
217 for (int i = 0; i < j + 5; ++i)
218 clipResults.push_back(t: float(i + j));
219 expectedClipResults.push_back(t: clipResults);
220
221 blendNode->setClipResults(animatorId, clipResults);
222 }
223
224 QVector<int> indexes(animatorCount);
225 std::iota(first: indexes.begin(), last: indexes.end(), value: 0);
226
227 QTest::newRow(dataTag: "multiple entries, ordered")
228 << blendNode << indexes << animatorIds << expectedClipResults;
229 }
230
231 // Multiple entries, unordered
232 {
233 auto blendNode = new TestClipBlendNode;
234 QVector<Qt3DCore::QNodeId> animatorIds;
235 QVector<ClipResults> expectedClipResults;
236
237 const int animatorCount = 10;
238 for (int j = 0; j < animatorCount; ++j) {
239 auto animatorId = Qt3DCore::QNodeId::createId();
240 animatorIds.push_back(t: animatorId);
241
242 ClipResults clipResults;
243 for (int i = 0; i < j + 5; ++i)
244 clipResults.push_back(t: float(i + j));
245 expectedClipResults.push_back(t: clipResults);
246
247 blendNode->setClipResults(animatorId, clipResults);
248 }
249
250 // Shuffle the animatorIds to randomise the lookups
251 QVector<int> indexes(animatorCount);
252 std::iota(first: indexes.begin(), last: indexes.end(), value: 0);
253 std::random_device rd;
254 std::mt19937 generator(rd());
255 std::shuffle(first: indexes.begin(), last: indexes.end(), g&: generator);
256
257 QTest::newRow(dataTag: "multiple entries, unordered")
258 << blendNode << indexes << animatorIds << expectedClipResults;
259 }
260 }
261
262 void checkClipResults()
263 {
264 // GIVEN
265 QFETCH(TestClipBlendNode *, blendNode);
266 QFETCH(QVector<int>, indexes);
267 QFETCH(QVector<Qt3DCore::QNodeId>, animatorIds);
268 QFETCH(QVector<ClipResults>, expectedClipResults);
269
270 for (int i = 0; i < indexes.size(); ++i) {
271 // WHEN
272 const int index = indexes[i];
273 const ClipResults actualClipResults = blendNode->clipResults(animatorId: animatorIds[index]);
274
275 // THEN
276 QCOMPARE(actualClipResults.size(), expectedClipResults[index].size());
277 for (int j = 0; j < actualClipResults.size(); ++j)
278 QCOMPARE(actualClipResults[j], expectedClipResults[index][j]);
279 }
280
281 delete blendNode;
282 }
283
284 void checkPerformBlend()
285 {
286 // GIVEN
287 auto handler = new Handler();
288 ClipResults expectedResults = { 1.0f, 2.0f, 3.0f };
289 auto blendNode = createTestClipBlendNode(handler, clipResults: expectedResults);
290 const qint64 globalStartTimeNS = 0;
291 const int loopCount = 1;
292 auto animator = createBlendedClipAnimator(handler, globalStartTimeNS, loops: loopCount);
293
294 // WHEN
295 blendNode->blend(animatorId: animator->peerId());
296
297 // THEN
298 const ClipResults actualResults = blendNode->clipResults(animatorId: animator->peerId());
299 QCOMPARE(actualResults.size(), expectedResults.size());
300 for (int i = 0; i < actualResults.size(); ++i)
301 QCOMPARE(actualResults[i], expectedResults[i]);
302 }
303};
304
305QTEST_MAIN(tst_ClipBlendNode)
306
307#include "tst_clipblendnode.moc"
308

source code of qt3d/tests/auto/animation/clipblendnode/tst_clipblendnode.cpp