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