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
62QT_BEGIN_NAMESPACE
63
64namespace Qt3DAnimation {
65class QAnimationCallback;
66namespace Animation {
67
68struct Channel;
69class BlendedClipAnimator;
70class Handler;
71class AnimationClip;
72class ChannelMapper;
73class ChannelMapping;
74
75typedef QVector<int> ComponentIndices;
76
77enum JointTransformComponent {
78 NoTransformComponent = 0,
79 Scale,
80 Rotation,
81 Translation
82};
83
84struct 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
98inline 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
110struct AnimatorEvaluationData
111{
112 double elapsedTime;
113 double currentTime;
114 int loopCount;
115 int currentLoop;
116 double playbackRate;
117 float normalizedLocalTime;
118};
119
120struct ClipEvaluationData
121{
122 int currentLoop;
123 float normalizedLocalTime;
124 double localTime;
125 bool isFinalFrame;
126};
127
128typedef QVector<float> ClipResults;
129
130struct 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
142 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)
157 : jointName()
158 , name(_name)
159 , type(_type)
160 , jointIndex(_jointIndex)
161 , mappingId(_mappingId)
162 , jointTransformComponent(NoTransformComponent)
163 , componentCount(componentCount)
164 {}
165
166 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
202inline 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
216struct ComponentValue
217{
218 int componentIndex;
219 float value;
220};
221QT3D_DECLARE_TYPEINFO_2(Qt3DAnimation, Animation, ComponentValue, Q_PRIMITIVE_TYPE)
222
223struct 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
236inline 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
261struct AnimationCallbackAndValue
262{
263 QAnimationCallback *callback;
264 QAnimationCallback::Flags flags;
265 QVariant value;
266};
267
268struct AnimationRecord {
269 struct TargetChange {
270 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
287Q_AUTOTEST_EXPORT
288AnimationRecord prepareAnimationRecord(Qt3DCore::QNodeId animatorId,
289 const QVector<MappingData> &mappingDataVec,
290 const QVector<float> &channelResults,
291 bool finalFrame,
292 float normalizedLocalTime);
293
294inline constexpr double toSecs(qint64 nsecs) { return nsecs / 1.0e9; }
295inline qint64 toNsecs(double seconds) { return qRound64(d: seconds * 1.0e9); }
296
297template<typename Animator>
298AnimatorEvaluationData 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(nsecs: 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
318inline 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
332inline bool isValidNormalizedTime(float t)
333{
334 return !(t < 0.0f) && !(t > 1.0f);
335}
336
337Q_AUTOTEST_EXPORT
338ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
339 const AnimatorEvaluationData &animatorData);
340
341Q_AUTOTEST_EXPORT
342ComponentIndices channelComponentsToIndices(const Channel &channel,
343 int dataType,
344 int expectedComponentCount,
345 int offset);
346
347Q_AUTOTEST_EXPORT
348ComponentIndices channelComponentsToIndicesHelper(const Channel &channelGroup,
349 int expectedComponentCount,
350 int offset,
351 const QVector<char> &suffixes);
352
353Q_AUTOTEST_EXPORT
354ClipResults evaluateClipAtLocalTime(AnimationClip *clip,
355 float localTime);
356
357Q_AUTOTEST_EXPORT
358ClipResults evaluateClipAtPhase(AnimationClip *clip,
359 float phase);
360
361Q_AUTOTEST_EXPORT
362QVector<AnimationCallbackAndValue> prepareCallbacks(const QVector<MappingData> &mappingDataVec,
363 const QVector<float> &channelResults);
364
365Q_AUTOTEST_EXPORT
366QVector<MappingData> buildPropertyMappings(const QVector<ChannelMapping *> &channelMappings,
367 const QVector<ChannelNameAndType> &channelNamesAndTypes,
368 const QVector<ComponentIndices> &channelComponentIndices,
369 const QVector<QBitArray> &sourceClipMask);
370
371Q_AUTOTEST_EXPORT
372QVector<ChannelNameAndType> buildRequiredChannelsAndTypes(Handler *handler,
373 const ChannelMapper *mapper);
374
375Q_AUTOTEST_EXPORT
376QVector<ComponentIndices> assignChannelComponentIndices(const QVector<ChannelNameAndType> &namesAndTypes);
377
378Q_AUTOTEST_EXPORT
379double localTimeFromElapsedTime(double t_current_local, double t_elapsed_global,
380 double playbackRate, double duration,
381 int loopCount, int &currentLoop);
382
383Q_AUTOTEST_EXPORT
384double phaseFromElapsedTime(double t_current_local, double t_elapsed_global,
385 double playbackRate, double duration,
386 int loopCount, int &currentLoop);
387
388Q_AUTOTEST_EXPORT
389QVector<Qt3DCore::QNodeId> gatherValueNodesToEvaluate(Handler *handler,
390 Qt3DCore::QNodeId blendTreeRootId);
391
392Q_AUTOTEST_EXPORT
393ClipFormat generateClipFormatIndices(const QVector<ChannelNameAndType> &targetChannels,
394 const QVector<ComponentIndices> &targetIndices,
395 const AnimationClip *clip);
396
397Q_AUTOTEST_EXPORT
398ClipResults formatClipResults(const ClipResults &rawClipResults,
399 const ComponentIndices &format);
400
401Q_AUTOTEST_EXPORT
402ClipResults evaluateBlendTree(Handler *handler,
403 BlendedClipAnimator *animator,
404 Qt3DCore::QNodeId blendNodeId);
405
406Q_AUTOTEST_EXPORT
407QVector<float> defaultValueForChannel(Handler *handler, const ChannelNameAndType &channelDescription);
408
409Q_AUTOTEST_EXPORT
410void applyComponentDefaultValues(const QVector<ComponentValue> &componentDefaults,
411 ClipResults &formattedClipResults);
412
413} // Animation
414} // Qt3DAnimation
415
416QT_END_NAMESPACE
417
418Q_DECLARE_METATYPE(Qt3DAnimation::Animation::AnimationRecord) // LCOV_EXCL_LINE
419
420
421#endif // QT3DANIMATION_ANIMATION_ANIMATIONUTILS_P_H
422

source code of qt3d/src/animation/backend/animationutils_p.h