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#include "fcurve_p.h"
38#include <private/bezierevaluator_p.h>
39
40#include <QtCore/qjsonarray.h>
41#include <QtCore/qjsonobject.h>
42#include <QtCore/QLatin1String>
43
44QT_BEGIN_NAMESPACE
45
46namespace Qt3DAnimation {
47namespace Animation {
48
49FCurve::FCurve()
50 : m_rangeFinder(m_localTimes)
51{
52}
53
54float FCurve::evaluateAtTime(float localTime) const
55{
56 return evaluateAtTime(localTime, lowerBound: lowerKeyframeBound(localTime));
57}
58
59
60float FCurve::evaluateAtTime(float localTime, int lowerBound) const
61{
62 // TODO: Implement extrapolation beyond first/last keyframes
63 if (localTime < m_localTimes.first()) {
64 return m_keyframes.first().value;
65 } else if (localTime > m_localTimes.last()) {
66 return m_keyframes.last().value;
67 } else {
68 // Find keyframes that sandwich the requested localTime
69 if (lowerBound < 0) // only one keyframe
70 return m_keyframes.first().value;
71
72 const float t0 = m_localTimes[lowerBound];
73 const float t1 = m_localTimes[lowerBound + 1];
74 const Keyframe &keyframe0(m_keyframes[lowerBound]);
75 const Keyframe &keyframe1(m_keyframes[lowerBound + 1]);
76
77 switch (keyframe0.interpolation) {
78 case QKeyFrame::ConstantInterpolation:
79 return keyframe0.value;
80 case QKeyFrame::LinearInterpolation:
81 if (localTime >= t0 && localTime <= t1 && t1 > t0) {
82 float t = (localTime - t0) / (t1 - t0);
83 return (1 - t) * keyframe0.value + t * keyframe1.value;
84 }
85 break;
86 case QKeyFrame::BezierInterpolation:
87 {
88 BezierEvaluator evaluator(t0, keyframe0, t1, keyframe1);
89 return evaluator.valueForTime(time: localTime);
90 }
91 default:
92 qWarning(msg: "Unknown interpolation type %d", keyframe0.interpolation);
93 break;
94 }
95 }
96
97 return m_keyframes.first().value;
98}
99
100float FCurve::evaluateAtTimeAsSlerp(float localTime, int lowerBound, float halfTheta, float sinHalfTheta, float reverseQ1) const
101{
102 // TODO: Implement extrapolation beyond first/last keyframes
103 if (localTime < m_localTimes.first())
104 return m_keyframes.first().value;
105
106 if (localTime > m_localTimes.last())
107 return m_keyframes.last().value;
108 // Find keyframes that sandwich the requested localTime
109 if (lowerBound < 0) // only one keyframe
110 return m_keyframes.first().value;
111
112 const float t0 = m_localTimes[lowerBound];
113 const float t1 = m_localTimes[lowerBound + 1];
114 const Keyframe &keyframe0(m_keyframes[lowerBound]);
115 const Keyframe &keyframe1(m_keyframes[lowerBound + 1]);
116
117 switch (keyframe0.interpolation) {
118 case QKeyFrame::ConstantInterpolation:
119 return keyframe0.value;
120 case QKeyFrame::LinearInterpolation:
121 if (localTime >= t0 && localTime <= t1 && t1 > t0) {
122 const auto t = (localTime - t0) / (t1 - t0);
123
124 const auto A = std::sin(x: (1.0f-t) * halfTheta) / sinHalfTheta;
125 const auto B = std::sin(x: t * halfTheta) / sinHalfTheta;
126 return A * keyframe0.value + reverseQ1 * B * keyframe1.value;
127 }
128 break;
129 case QKeyFrame::BezierInterpolation:
130 // TODO implement a proper slerp bezier interpolation
131 BezierEvaluator evaluator(t0, keyframe0, t1, keyframe1);
132 return evaluator.valueForTime(time: localTime);
133 }
134
135 return m_keyframes.first().value;
136}
137
138int FCurve::lowerKeyframeBound(float localTime) const
139{
140 if (localTime < m_localTimes.first())
141 return 0;
142 if (localTime > m_localTimes.last())
143 return 0;
144 return m_rangeFinder.findLowerBound(x: localTime);
145}
146
147float FCurve::startTime() const
148{
149 if (!m_localTimes.isEmpty())
150 return m_localTimes.first();
151 return 0.0f;
152}
153
154float FCurve::endTime() const
155{
156 if (!m_localTimes.isEmpty())
157 return m_localTimes.last();
158 return 0.0f;
159}
160
161void FCurve::appendKeyframe(float localTime, const Keyframe &keyframe)
162{
163 m_localTimes.append(t: localTime);
164 m_keyframes.append(t: keyframe);
165}
166
167void FCurve::read(const QJsonObject &json)
168{
169 clearKeyframes();
170
171 const QJsonArray keyframeArray = json[QLatin1String("keyFrames")].toArray();
172 const int keyframeCount = keyframeArray.size();
173
174 for (int i = 0; i < keyframeCount; ++i) {
175 const QJsonObject keyframeData = keyframeArray.at(i).toObject();
176
177 // Extract the keyframe local time and value
178 const QJsonArray keyframeCoords = keyframeData[QLatin1String("coords")].toArray();
179 float localTime = keyframeCoords.at(i: 0).toDouble();
180
181 Keyframe keyframe;
182 keyframe.value = keyframeCoords.at(i: 1).toDouble();
183
184 if (keyframeData.contains(key: QLatin1String("leftHandle"))) {
185 keyframe.interpolation = QKeyFrame::BezierInterpolation;
186
187 const QJsonArray leftHandle = keyframeData[QLatin1String("leftHandle")].toArray();
188 keyframe.leftControlPoint[0] = leftHandle.at(i: 0).toDouble();
189 keyframe.leftControlPoint[1] = leftHandle.at(i: 1).toDouble();
190
191 const QJsonArray rightHandle = keyframeData[QLatin1String("rightHandle")].toArray();
192 keyframe.rightControlPoint[0] = rightHandle.at(i: 0).toDouble();
193 keyframe.rightControlPoint[1] = rightHandle.at(i: 1).toDouble();
194 } else {
195 keyframe.interpolation = QKeyFrame::LinearInterpolation;
196 }
197
198 appendKeyframe(localTime, keyframe);
199 }
200
201 // TODO: Ensure beziers have no loops or cusps by scaling the control points
202 // back so they do not interset.
203}
204
205void FCurve::setFromQChannelComponent(const QChannelComponent &qcc)
206{
207 clearKeyframes();
208
209 for (const auto &frontendKeyFrame : qcc) {
210 // Extract the keyframe local time and value
211 const float localTime = frontendKeyFrame.coordinates()[0];
212
213 Keyframe keyFrame;
214 keyFrame.interpolation = frontendKeyFrame.interpolationType();
215 keyFrame.value = frontendKeyFrame.coordinates()[1];
216 keyFrame.leftControlPoint = frontendKeyFrame.leftControlPoint();
217 keyFrame.rightControlPoint = frontendKeyFrame.rightControlPoint();
218 appendKeyframe(localTime, keyframe: keyFrame);
219 }
220
221 // TODO: Ensure beziers have no loops or cusps by scaling the control points
222 // back so they do not interset.
223}
224
225void ChannelComponent::read(const QJsonObject &json)
226{
227 name = json[QLatin1String("channelComponentName")].toString();
228 fcurve.read(json);
229}
230
231void ChannelComponent::setFromQChannelComponent(const QChannelComponent &qcc)
232{
233 name = qcc.name();
234 fcurve.setFromQChannelComponent(qcc);
235}
236
237void Channel::read(const QJsonObject &json)
238{
239 name = json[QLatin1String("channelName")].toString();
240 const auto jointIndexValue = json[QLatin1String("jointIndex")];
241 if (!jointIndexValue.isUndefined())
242 jointIndex = jointIndexValue.toInt();
243 const QJsonArray channelComponentsArray = json[QLatin1String("channelComponents")].toArray();
244 const int channelCount = channelComponentsArray.size();
245 channelComponents.resize(asize: channelCount);
246
247 for (int i = 0; i < channelCount; ++i) {
248 const QJsonObject channel = channelComponentsArray.at(i).toObject();
249 channelComponents[i].read(json: channel);
250 }
251}
252
253void Channel::setFromQChannel(const QChannel &qch)
254{
255 name = qch.name();
256 jointIndex = qch.jointIndex();
257 channelComponents.resize(asize: qch.channelComponentCount());
258 int i = 0;
259 for (const auto &frontendChannelComponent : qch)
260 channelComponents[i++].setFromQChannelComponent(frontendChannelComponent);
261}
262
263} // namespace Animation
264} // namespace Qt3DAnimation
265
266QT_END_NAMESPACE
267

source code of qt3d/src/animation/backend/fcurve.cpp