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 | |
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 |
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 { |
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 | |
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 | |
294 | inline constexpr double toSecs(qint64 nsecs) { return nsecs / 1.0e9; } |
295 | inline qint64 toNsecs(double seconds) { return qRound64(d: seconds * 1.0e9); } |
296 | |
297 | template<typename Animator> |
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(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 | |
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 | |
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 | |