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