1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt3D module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36
37 #ifndef QT3DANIMATION_ANIMATION_ANIMATIONUTILS_P_H
38 #define QT3DANIMATION_ANIMATION_ANIMATIONUTILS_P_H
39
40 //
41 // W A R N I N G
42 // -------------
43 //
44 // This file is not part of the Qt API. It exists for the convenience
45 // of other Qt classes. This header file may change from version to
46 // version without notice, or even be removed.
47 //
48 // We mean it.
49 //
50
51 #include <Qt3DAnimation/private/qt3danimation_global_p.h>
52 #include <Qt3DAnimation/private/clock_p.h>
53 #include <Qt3DAnimation/qanimationcallback.h>
54 #include <Qt3DCore/qnodeid.h>
55 #include <Qt3DCore/qscenechange.h>
56 #include <Qt3DCore/private/sqt_p.h>
57
58 #include <QtCore/qbitarray.h>
59 #include <QtCore/qdebug.h>
60 #include <qmath.h>
61
62 QT_BEGIN_NAMESPACE
63
64 namespace Qt3DAnimation {
65 class QAnimationCallback;
66 namespace Animation {
67
68 struct Channel;
69 class BlendedClipAnimator;
70 class Handler;
71 class AnimationClip;
72 class ChannelMapper;
73 class ChannelMapping;
74
75 typedef QVector<int> ComponentIndices;
76
77 enum JointTransformComponent {
78 NoTransformComponent = 0,
79 Scale,
80 Rotation,
81 Translation
82 };
83
84 struct MappingData
85 {
86 Qt3DCore::QNodeId targetId;
87 Skeleton *skeleton = nullptr;
88 int jointIndex = -1;
89 JointTransformComponent jointTransformComponent = NoTransformComponent;
90 const char *propertyName;
91 QAnimationCallback *callback = nullptr;
92 QAnimationCallback::Flags callbackFlags;
93 int type;
94 ComponentIndices channelIndices;
95 };
96
97 #ifndef QT_NO_DEBUG_STREAM
98 inline QDebug operator<<(QDebug dbg, const MappingData &mapping)
99 {
100 QDebugStateSaver saver(dbg);
101 dbg << "targetId =" << mapping.targetId << Qt::endl
102 << "jointIndex =" << mapping.jointIndex << Qt::endl
103 << "jointTransformComponent: " << mapping.jointTransformComponent << Qt::endl
104 << "propertyName:" << mapping.propertyName << Qt::endl
105 << "channelIndices:" << mapping.channelIndices;
106 return dbg;
107 }
108 #endif
109
110 struct AnimatorEvaluationData
111 {
112 double elapsedTime;
113 double currentTime;
114 int loopCount;
115 int currentLoop;
116 double playbackRate;
117 float normalizedLocalTime;
118 };
119
120 struct ClipEvaluationData
121 {
122 int currentLoop;
123 float normalizedLocalTime;
124 double localTime;
125 bool isFinalFrame;
126 };
127
128 typedef QVector<float> ClipResults;
129
130 struct ChannelNameAndType
131 {
132 QString jointName;
133 QString name;
134 int type;
135 int jointIndex;
136 Qt3DCore::QNodeId mappingId;
137 JointTransformComponent jointTransformComponent;
138 int componentCount;
139
140 static const int invalidIndex = -1;
141
ChannelNameAndTypeChannelNameAndType142 ChannelNameAndType()
143 : jointName()
144 , name()
145 , type(-1)
146 , jointIndex(-1)
147 , mappingId()
148 , jointTransformComponent(NoTransformComponent)
149 , componentCount(-1)
150 {}
151
152 ChannelNameAndType(const QString &_name,
153 int _type,
154 int componentCount,
155 Qt3DCore::QNodeId _mappingId = Qt3DCore::QNodeId(),
156 int _jointIndex = invalidIndex)
jointNameChannelNameAndType157 : jointName()
158 , name(_name)
159 , type(_type)
160 , jointIndex(_jointIndex)
161 , mappingId(_mappingId)
162 , jointTransformComponent(NoTransformComponent)
163 , componentCount(componentCount)
164 {}
165
ChannelNameAndTypeChannelNameAndType166 ChannelNameAndType(const QString &_name,
167 int _type,
168 JointTransformComponent _jointTransformComponent)
169 : jointName()
170 , name(_name)
171 , type(_type)
172 , jointIndex(invalidIndex)
173 , mappingId()
174 , jointTransformComponent(_jointTransformComponent)
175 , componentCount(-1)
176 {
177 switch (_jointTransformComponent) {
178 case NoTransformComponent:
179 break;
180 case Scale:
181 case Translation:
182 componentCount = 3;
183 break;
184 case Rotation:
185 componentCount = 4;
186 break;
187 }
188 }
189
190 bool operator==(const ChannelNameAndType &rhs) const
191 {
192 return name == rhs.name
193 && type == rhs.type
194 && jointIndex == rhs.jointIndex
195 && mappingId == rhs.mappingId
196 && jointTransformComponent == rhs.jointTransformComponent
197 && componentCount == rhs.componentCount;
198 }
199 };
200
201 #ifndef QT_NO_DEBUG_STREAM
202 inline QDebug operator<<(QDebug dbg, const ChannelNameAndType &nameAndType)
203 {
204 QDebugStateSaver saver(dbg);
205 dbg << "name =" << nameAndType.name
206 << "type =" << nameAndType.type
207 << "mappingId =" << nameAndType.mappingId
208 << "jointIndex =" << nameAndType.jointIndex
209 << "jointName =" << nameAndType.jointName
210 << "jointTransformComponent =" << nameAndType.jointTransformComponent
211 << "componentCount =" << nameAndType.componentCount;
212 return dbg;
213 }
214 #endif
215
216 struct ComponentValue
217 {
218 int componentIndex;
219 float value;
220 };
221 QT3D_DECLARE_TYPEINFO_2(Qt3DAnimation, Animation, ComponentValue, Q_PRIMITIVE_TYPE)
222
223 struct ClipFormat
224 {
225 // TODO: Remove the mask and store both the sourceClipIndices and
226 // formattedComponentIndices in flat vectors. This will require a
227 // way to look up the offset and number of elements for each channel.
228 ComponentIndices sourceClipIndices;
229 QVector<QBitArray> sourceClipMask;
230 QVector<ComponentIndices> formattedComponentIndices;
231 QVector<ChannelNameAndType> namesAndTypes;
232 QVector<ComponentValue> defaultComponentValues;
233 };
234
235 #ifndef QT_NO_DEBUG_STREAM
236 inline QDebug operator<<(QDebug dbg, const ClipFormat &format)
237 {
238 QDebugStateSaver saver(dbg);
239 int sourceIndex = 0;
240 for (int i = 0; i < format.namesAndTypes.size(); ++i) {
241 dbg << i
242 << format.namesAndTypes[i].jointIndex
243 << format.namesAndTypes[i].jointName
244 << format.namesAndTypes[i].name
245 << format.namesAndTypes[i].type
246 << "formatted results dst indices =" << format.formattedComponentIndices[i];
247 const int componentCount = format.formattedComponentIndices[i].size();
248
249 dbg << "clip src indices =";
250 for (int j = sourceIndex; j < sourceIndex + componentCount; ++j)
251 dbg << format.sourceClipIndices[j] << "";
252
253 dbg << "src clip mask =" << format.sourceClipMask[i];
254 dbg << Qt::endl;
255 sourceIndex += componentCount;
256 }
257 return dbg;
258 }
259 #endif
260
261 struct AnimationCallbackAndValue
262 {
263 QAnimationCallback *callback;
264 QAnimationCallback::Flags flags;
265 QVariant value;
266 };
267
268 struct AnimationRecord {
269 struct TargetChange {
TargetChangeAnimationRecord::TargetChange270 TargetChange(Qt3DCore::QNodeId id, const char *name, QVariant v)
271 : targetId(id), propertyName(name), value(v) {
272
273 }
274
275 Qt3DCore::QNodeId targetId;
276 const char *propertyName = nullptr;
277 QVariant value;
278 };
279
280 Qt3DCore::QNodeId animatorId;
281 QVector<TargetChange> targetChanges;
282 QVector<QPair<Qt3DCore::QNodeId, QVector<Qt3DCore::Sqt>>> skeletonChanges;
283 float normalizedTime = -1.f;
284 bool finalFrame = false;
285 };
286
287 Q_AUTOTEST_EXPORT
288 AnimationRecord prepareAnimationRecord(Qt3DCore::QNodeId animatorId,
289 const QVector<MappingData> &mappingDataVec,
290 const QVector<float> &channelResults,
291 bool finalFrame,
292 float normalizedLocalTime);
293
toSecs(qint64 nsecs)294 inline constexpr double toSecs(qint64 nsecs) { return nsecs / 1.0e9; }
toNsecs(double seconds)295 inline qint64 toNsecs(double seconds) { return qRound64(seconds * 1.0e9); }
296
297 template<typename Animator>
evaluationDataForAnimator(Animator animator,Clock * clock,qint64 nsSincePreviousFrame)298 AnimatorEvaluationData evaluationDataForAnimator(Animator animator,
299 Clock* clock,
300 qint64 nsSincePreviousFrame)
301 {
302 const bool seeking = animator->isSeeking();
303 AnimatorEvaluationData data;
304 data.loopCount = animator->loops();
305 data.currentLoop = animator->currentLoop();
306 // The playback-rate is always 1.0 when seeking
307 data.playbackRate = ((clock != nullptr) && !seeking) ? clock->playbackRate() : 1.0;
308 // Convert global time from nsec to sec
309 data.elapsedTime = toSecs(nsSincePreviousFrame);
310 // When seeking we base it on the current time being at the start of the clip
311 data.currentTime = seeking ? 0.0 : animator->lastLocalTime();
312 // If we're not seeking the local normalized time will be calculate in
313 // evaluationDataForClip().
314 data.normalizedLocalTime = seeking ? animator->normalizedLocalTime() : -1.0;
315 return data;
316 }
317
isFinalFrame(double localTime,double duration,int currentLoop,int loopCount,double playbackRate)318 inline bool isFinalFrame(double localTime,
319 double duration,
320 int currentLoop,
321 int loopCount,
322 double playbackRate)
323 {
324 // We must be on the final loop and
325 // - if playing forward, localTime must be equal or above the duration
326 // - if playing backward, localTime must be equal or below 0
327 if (playbackRate >= 0.0)
328 return (loopCount != 0 && currentLoop >= loopCount - 1 && localTime >= duration);
329 return (loopCount != 0 && currentLoop <= 0 && localTime <= 0);
330 }
331
isValidNormalizedTime(float t)332 inline bool isValidNormalizedTime(float t)
333 {
334 return !(t < 0.0f) && !(t > 1.0f);
335 }
336
337 Q_AUTOTEST_EXPORT
338 ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
339 const AnimatorEvaluationData &animatorData);
340
341 Q_AUTOTEST_EXPORT
342 ComponentIndices channelComponentsToIndices(const Channel &channel,
343 int dataType,
344 int expectedComponentCount,
345 int offset);
346
347 Q_AUTOTEST_EXPORT
348 ComponentIndices channelComponentsToIndicesHelper(const Channel &channelGroup,
349 int expectedComponentCount,
350 int offset,
351 const QVector<char> &suffixes);
352
353 Q_AUTOTEST_EXPORT
354 ClipResults evaluateClipAtLocalTime(AnimationClip *clip,
355 float localTime);
356
357 Q_AUTOTEST_EXPORT
358 ClipResults evaluateClipAtPhase(AnimationClip *clip,
359 float phase);
360
361 Q_AUTOTEST_EXPORT
362 QVector<AnimationCallbackAndValue> prepareCallbacks(const QVector<MappingData> &mappingDataVec,
363 const QVector<float> &channelResults);
364
365 Q_AUTOTEST_EXPORT
366 QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping *> &channelMappings,
367 const QVector<ChannelNameAndType> &channelNamesAndTypes,
368 const QVector<ComponentIndices> &channelComponentIndices,
369 const QVector<QBitArray> &sourceClipMask);
370
371 Q_AUTOTEST_EXPORT
372 QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler,
373 const ChannelMapper *mapper);
374
375 Q_AUTOTEST_EXPORT
376 QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNameAndType> &namesAndTypes);
377
378 Q_AUTOTEST_EXPORT
379 double localTimeFromElapsedTime(double t_current_local, double t_elapsed_global,
380 double playbackRate, double duration,
381 int loopCount, int ¤tLoop);
382
383 Q_AUTOTEST_EXPORT
384 double phaseFromElapsedTime(double t_current_local, double t_elapsed_global,
385 double playbackRate, double duration,
386 int loopCount, int ¤tLoop);
387
388 Q_AUTOTEST_EXPORT
389 QVector<Qt3DCore::QNodeId> gatherValueNodesToEvaluate(Handler *handler,
390 Qt3DCore::QNodeId blendTreeRootId);
391
392 Q_AUTOTEST_EXPORT
393 ClipFormat generateClipFormatIndices(const QVector<ChannelNameAndType> &targetChannels,
394 const QVector<ComponentIndices> &targetIndices,
395 const AnimationClip *clip);
396
397 Q_AUTOTEST_EXPORT
398 ClipResults formatClipResults(const ClipResults &rawClipResults,
399 const ComponentIndices &format);
400
401 Q_AUTOTEST_EXPORT
402 ClipResults evaluateBlendTree(Handler *handler,
403 BlendedClipAnimator *animator,
404 Qt3DCore::QNodeId blendNodeId);
405
406 Q_AUTOTEST_EXPORT
407 QVector<float> defaultValueForChannel(Handler *handler, const ChannelNameAndType &channelDescription);
408
409 Q_AUTOTEST_EXPORT
410 void applyComponentDefaultValues(const QVector<ComponentValue> &componentDefaults,
411 ClipResults &formattedClipResults);
412
413 } // Animation
414 } // Qt3DAnimation
415
416 QT_END_NAMESPACE
417
418 Q_DECLARE_METATYPE(Qt3DAnimation::Animation::AnimationRecord) // LCOV_EXCL_LINE
419
420
421 #endif // QT3DANIMATION_ANIMATION_ANIMATIONUTILS_P_H
422