| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 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 "qkeyframeanimation.h" | 
| 38 | #include "Qt3DAnimation/private/qkeyframeanimation_p.h" | 
| 39 |  | 
| 40 | #include <cmath> | 
| 41 |  | 
| 42 | QT_BEGIN_NAMESPACE | 
| 43 |  | 
| 44 | namespace Qt3DAnimation { | 
| 45 |  | 
| 46 | /*! | 
| 47 |     \class Qt3DAnimation::QKeyframeAnimation | 
| 48 |     \brief A class implementing simple keyframe animation to a QTransform. | 
| 49 |     \inmodule Qt3DAnimation | 
| 50 |     \since 5.9 | 
| 51 |     \inherits Qt3DAnimation::QAbstractAnimation | 
| 52 |  | 
| 53 |     A Qt3DAnimation::QKeyframeAnimation class implements simple keyframe animation | 
| 54 |     that can be used to animate \l QTransform. The keyframes consists of multiple | 
| 55 |     timed QTransforms, which are interpolated and applied to the target \l QTransform. | 
| 56 |     \l QEasingCurve is used between keyframes to control the interpolator. RepeatMode | 
| 57 |     can be set for when the position set to the QKeyframeAnimation is below or above | 
| 58 |     the values defined in the keyframe positions. | 
| 59 | */ | 
| 60 |  | 
| 61 | /*! | 
| 62 |     \qmltype KeyframeAnimation | 
| 63 |     \brief A type implementing simple keyframe animation to a Transform. | 
| 64 |     \inqmlmodule Qt3D.Animation | 
| 65 |     \since 5.9 | 
| 66 |     \inherits AbstractAnimation | 
| 67 |     \instantiates Qt3DAnimation::QKeyframeAnimation | 
| 68 |  | 
| 69 |     A KeyframeAnimation type implements simple keyframe animation | 
| 70 |     that can be used to animate \l Transform. The keyframes consists of multiple | 
| 71 |     timed \l {Qt3D.Core::Transform}s, which are interpolated and applied | 
| 72 |     to the target Transform. EasingCurve is used between keyframes to control | 
| 73 |     the interpolator. RepeatMode can be set for when the position set to the | 
| 74 |     KeyframeAnimation is less or or greater than the values defined in the keyframe positions. | 
| 75 | */ | 
| 76 |  | 
| 77 | /*! | 
| 78 |     \property Qt3DAnimation::QKeyframeAnimation::framePositions | 
| 79 |     Holds the positions of the keyframes. Each position in the list specifies the position | 
| 80 |     of the corresponding keyframe with the same index. The values must be in an ascending order. | 
| 81 |     Values can be positive or negative and do not have any predefined unit. | 
| 82 | */ | 
| 83 | /*! | 
| 84 |     \property Qt3DAnimation::QKeyframeAnimation::target | 
| 85 |     Holds the target QTransform the animation is applied to. | 
| 86 | */ | 
| 87 | /*! | 
| 88 |     \property Qt3DAnimation::QKeyframeAnimation::easing | 
| 89 |     Holds the easing curve of the interpolator between keyframes. | 
| 90 | */ | 
| 91 | /*! | 
| 92 |     \property Qt3DAnimation::QKeyframeAnimation::targetName | 
| 93 |     Holds the name of the target transform. This is a convenience property making it | 
| 94 |     easier to match the target transform to the keyframe animation. The name | 
| 95 |     is usually same as the name of the parent entity of the target transform, but | 
| 96 |     does not have to be. | 
| 97 | */ | 
| 98 | /*! | 
| 99 |     \property Qt3DAnimation::QKeyframeAnimation::startMode | 
| 100 |     Holds the repeat mode for the position values less than the first frame position. | 
| 101 | */ | 
| 102 | /*! | 
| 103 |     \property Qt3DAnimation::QKeyframeAnimation::endMode | 
| 104 |     Holds the repeat mode for the position values greater than the last frame position. | 
| 105 | */ | 
| 106 | /*! | 
| 107 |     \enum QKeyframeAnimation::RepeatMode | 
| 108 |  | 
| 109 |     This enumeration specifies how position values outside keyframe values are handled. | 
| 110 |     \value None The animation is not applied to the target transform. | 
| 111 |     \value Constant The edge keyframe value is used. | 
| 112 |     \value Repeat The animation is repeated. | 
| 113 | */ | 
| 114 | /*! | 
| 115 |     \qmlproperty list<real> KeyframeAnimation::framePositions | 
| 116 |     Holds the positions of the keyframes. Each position in the list specifies the position | 
| 117 |     of the corresponding keyframe. The values must be in an ascending order. Values can | 
| 118 |     be positive or negative and do not have any predefined unit. | 
| 119 | */ | 
| 120 | /*! | 
| 121 |     \qmlproperty Transform KeyframeAnimation::target | 
| 122 |     Holds the target Transform the animation is applied to. | 
| 123 | */ | 
| 124 | /*! | 
| 125 |     \qmlproperty EasingCurve KeyframeAnimation::easing | 
| 126 |     Holds the easing curve of the interpolator between keyframes. | 
| 127 | */ | 
| 128 | /*! | 
| 129 |     \qmlproperty string KeyframeAnimation::targetName | 
| 130 |     Holds the name of the target transform. This is a convenience property making it | 
| 131 |     easier to match the target transform to the keyframe animation. The name | 
| 132 |     is usually same as the name of the parent entity of the target transform, but | 
| 133 |     does not have to be. | 
| 134 | */ | 
| 135 | /*! | 
| 136 |     \qmlproperty enumeration KeyframeAnimation::startMode | 
| 137 |     Holds the repeat mode for the position values less than the first frame position. | 
| 138 |     \list | 
| 139 |     \li None | 
| 140 |     \li Constant | 
| 141 |     \li Repeat | 
| 142 |     \endlist | 
| 143 | */ | 
| 144 | /*! | 
| 145 |     \qmlproperty enumeration KeyframeAnimation::endMode | 
| 146 |     Holds the repeat mode for the position values greater than the last frame position. | 
| 147 |     \list | 
| 148 |     \li None | 
| 149 |     \li Constant | 
| 150 |     \li Repeat | 
| 151 |     \endlist | 
| 152 | */ | 
| 153 | /*! | 
| 154 |     \qmlproperty list<Transform> KeyframeAnimation::keyframes | 
| 155 |     Holds the list of keyframes in the keyframe animation. | 
| 156 | */ | 
| 157 |  | 
| 158 | QKeyframeAnimationPrivate::QKeyframeAnimationPrivate() | 
| 159 |     : QAbstractAnimationPrivate(QAbstractAnimation::KeyframeAnimation) | 
| 160 |     , m_target(nullptr) | 
| 161 |     , m_minposition(0.0f) | 
| 162 |     , m_maxposition(0.0f) | 
| 163 |     , m_startMode(QKeyframeAnimation::Constant) | 
| 164 |     , m_endMode(QKeyframeAnimation::Constant) | 
| 165 | { | 
| 166 |  | 
| 167 | } | 
| 168 |  | 
| 169 | /*! | 
| 170 |     Constructs an QKeyframeAnimation with \a parent. | 
| 171 | */ | 
| 172 | QKeyframeAnimation::QKeyframeAnimation(QObject *parent) | 
| 173 |     : QAbstractAnimation(*new QKeyframeAnimationPrivate(), parent) | 
| 174 | { | 
| 175 |     Q_D(QKeyframeAnimation); | 
| 176 |     d->m_positionConnection = QObject::connect(sender: this, signal: &QAbstractAnimation::positionChanged, | 
| 177 |                                                receiver: this, slot: &QKeyframeAnimation::updateAnimation); | 
| 178 | } | 
| 179 |  | 
| 180 |  | 
| 181 | void QKeyframeAnimation::setFramePositions(const QVector<float> &positions) | 
| 182 | { | 
| 183 |     Q_D(QKeyframeAnimation); | 
| 184 |     d->m_framePositions = positions; | 
| 185 |     d->m_position = -1.0f; | 
| 186 |     if (d->m_framePositions.size() == 0) { | 
| 187 |         d->m_minposition = d->m_maxposition = 0.0f; | 
| 188 |         return; | 
| 189 |     } | 
| 190 |     d->m_minposition = d->m_framePositions.first(); | 
| 191 |     d->m_maxposition = d->m_framePositions.last(); | 
| 192 |     float lastPos = d->m_minposition; | 
| 193 |     for (float p : qAsConst(t&: d->m_framePositions)) { | 
| 194 |         if (p < lastPos || p > d->m_maxposition) | 
| 195 |             qWarning() << "positions not ordered correctly" ; | 
| 196 |         lastPos = p; | 
| 197 |     } | 
| 198 |     setDuration(d->m_maxposition); | 
| 199 | } | 
| 200 |  | 
| 201 | /*! | 
| 202 |     Sets the \a keyframes of the animation. Old keyframes are cleared. | 
| 203 |  */ | 
| 204 | void QKeyframeAnimation::setKeyframes(const QVector<Qt3DCore::QTransform *> &keyframes) | 
| 205 | { | 
| 206 |     Q_D(QKeyframeAnimation); | 
| 207 |     d->m_keyframes = keyframes; | 
| 208 | } | 
| 209 |  | 
| 210 | // slerp which allows long path | 
| 211 | QQuaternion lslerp(QQuaternion q1, QQuaternion q2, float t) | 
| 212 | { | 
| 213 |     QQuaternion ret; | 
| 214 |     // Handle the easy cases first. | 
| 215 |     if (t <= 0.0f) | 
| 216 |         return q1; | 
| 217 |     else if (t >= 1.0f) | 
| 218 |         return q2; | 
| 219 |  | 
| 220 |     float cos = qBound(min: -1.0f, val: QQuaternion::dotProduct(q1, q2), max: 1.0f); | 
| 221 |     float angle = std::acos(x: cos); | 
| 222 |     float sin = std::sin(x: angle); | 
| 223 |     if (!qFuzzyIsNull(f: sin)) { | 
| 224 |         float a = std::sin(x: (1.0 - t) * angle) / sin; | 
| 225 |         float b = std::sin(x: t * angle) / sin; | 
| 226 |         ret = (q1 * a + q2 * b).normalized(); | 
| 227 |     } else { | 
| 228 |         ret = q1 * (1.0f-t) + q2 * t; | 
| 229 |     } | 
| 230 |     return ret; | 
| 231 | } | 
| 232 |  | 
| 233 | void QKeyframeAnimationPrivate::calculateFrame(float position) | 
| 234 | { | 
| 235 |     if (m_target && m_framePositions.size() > 0 | 
| 236 |         && m_keyframes.size() == m_framePositions.size()) { | 
| 237 |         if (position < m_minposition) { | 
| 238 |             if (m_startMode == QKeyframeAnimation::None) { | 
| 239 |                 return; | 
| 240 |             } else if (m_startMode == QKeyframeAnimation::Constant) { | 
| 241 |                 m_target->setRotation(m_keyframes.first()->rotation()); | 
| 242 |                 m_target->setScale3D(m_keyframes.first()->scale3D()); | 
| 243 |                 m_target->setTranslation(m_keyframes.first()->translation()); | 
| 244 |                 return; | 
| 245 |             } else { | 
| 246 |                 // must be repeat | 
| 247 |                 position = std::fmod(x: -(position - m_minposition), y: m_maxposition - m_minposition) | 
| 248 |                                      + m_minposition; | 
| 249 |             } | 
| 250 |         } else if (position >= m_maxposition) { | 
| 251 |             if (m_endMode == QKeyframeAnimation::None) { | 
| 252 |                 return; | 
| 253 |             } else if (m_endMode == QKeyframeAnimation::Constant) { | 
| 254 |                 m_target->setRotation(m_keyframes.last()->rotation()); | 
| 255 |                 m_target->setScale3D(m_keyframes.last()->scale3D()); | 
| 256 |                 m_target->setTranslation(m_keyframes.last()->translation()); | 
| 257 |                 return; | 
| 258 |             } else { | 
| 259 |                 // must be repeat | 
| 260 |                 position = std::fmod(x: position - m_minposition, y: m_maxposition - m_minposition) | 
| 261 |                                      + m_minposition; | 
| 262 |             } | 
| 263 |         } | 
| 264 |         if (position >= m_minposition && position < m_maxposition) { | 
| 265 |             for (int i = 0; i < m_framePositions.size() - 1; i++) { | 
| 266 |                 if (position >= m_framePositions.at(i) | 
| 267 |                     && position < m_framePositions.at(i: i+1)) { | 
| 268 |                     float ip = (position - m_framePositions.at(i)) | 
| 269 |                                 / (m_framePositions.at(i: i+1) - m_framePositions.at(i)); | 
| 270 |                     float eIp = m_easing.valueForProgress(progress: ip); | 
| 271 |                     float eIip = 1.0f - eIp; | 
| 272 |  | 
| 273 |                     Qt3DCore::QTransform *a = m_keyframes.at(i); | 
| 274 |                     Qt3DCore::QTransform *b = m_keyframes.at(i: i+1); | 
| 275 |  | 
| 276 |                     QVector3D s = a->scale3D() * eIip + b->scale3D() * eIp; | 
| 277 |                     QVector3D t = a->translation() * eIip + b->translation() * eIp; | 
| 278 |                     QQuaternion r = QQuaternion::slerp(q1: a->rotation(), q2: b->rotation(), t: eIp); | 
| 279 |  | 
| 280 |                     m_target->setRotation(r); | 
| 281 |                     m_target->setScale3D(s); | 
| 282 |                     m_target->setTranslation(t); | 
| 283 |                     return; | 
| 284 |                 } | 
| 285 |             } | 
| 286 |         } | 
| 287 |     } | 
| 288 | } | 
| 289 |  | 
| 290 | void QKeyframeAnimation::updateAnimation(float position) | 
| 291 | { | 
| 292 |     Q_D(QKeyframeAnimation); | 
| 293 |     d->calculateFrame(position); | 
| 294 | } | 
| 295 |  | 
| 296 | QVector<float> QKeyframeAnimation::framePositions() const | 
| 297 | { | 
| 298 |     Q_D(const QKeyframeAnimation); | 
| 299 |     return d->m_framePositions; | 
| 300 | } | 
| 301 |  | 
| 302 | /*! | 
| 303 |     Returns the list of keyframes. | 
| 304 |  */ | 
| 305 | QVector<Qt3DCore::QTransform *> QKeyframeAnimation::keyframeList() const | 
| 306 | { | 
| 307 |     Q_D(const QKeyframeAnimation); | 
| 308 |     return d->m_keyframes; | 
| 309 | } | 
| 310 |  | 
| 311 | void QKeyframeAnimation::setTarget(Qt3DCore::QTransform *target) | 
| 312 | { | 
| 313 |     Q_D(QKeyframeAnimation); | 
| 314 |     if (d->m_target != target) { | 
| 315 |         d->m_target = target; | 
| 316 |         emit targetChanged(target: d->m_target); | 
| 317 |         d->m_position = -1.0f; | 
| 318 |  | 
| 319 |         if (target) { | 
| 320 |             d->m_baseScale = target->scale3D(); | 
| 321 |             d->m_baseTranslation = target->translation(); | 
| 322 |             d->m_baseRotation = target->rotation(); | 
| 323 |         } | 
| 324 |     } | 
| 325 | } | 
| 326 |  | 
| 327 | QKeyframeAnimation::RepeatMode QKeyframeAnimation::startMode() const | 
| 328 | { | 
| 329 |     Q_D(const QKeyframeAnimation); | 
| 330 |     return d->m_startMode; | 
| 331 | } | 
| 332 |  | 
| 333 | QKeyframeAnimation::RepeatMode QKeyframeAnimation::endMode() const | 
| 334 | { | 
| 335 |     Q_D(const QKeyframeAnimation); | 
| 336 |     return d->m_endMode; | 
| 337 | } | 
| 338 |  | 
| 339 | void QKeyframeAnimation::setEasing(const QEasingCurve &easing) | 
| 340 | { | 
| 341 |     Q_D(QKeyframeAnimation); | 
| 342 |     if (d->m_easing != easing) { | 
| 343 |         d->m_easing = easing; | 
| 344 |         emit easingChanged(easing); | 
| 345 |     } | 
| 346 | } | 
| 347 |  | 
| 348 | void QKeyframeAnimation::setTargetName(const QString &name) | 
| 349 | { | 
| 350 |     Q_D(QKeyframeAnimation); | 
| 351 |     if (d->m_targetName != name) { | 
| 352 |         d->m_targetName = name; | 
| 353 |         emit targetNameChanged(name); | 
| 354 |     } | 
| 355 | } | 
| 356 |  | 
| 357 | void QKeyframeAnimation::setStartMode(QKeyframeAnimation::RepeatMode mode) | 
| 358 | { | 
| 359 |     Q_D(QKeyframeAnimation); | 
| 360 |     if (d->m_startMode != mode) { | 
| 361 |         d->m_startMode = mode; | 
| 362 |         emit startModeChanged(startMode: mode); | 
| 363 |     } | 
| 364 | } | 
| 365 |  | 
| 366 | void QKeyframeAnimation::setEndMode(QKeyframeAnimation::RepeatMode mode) | 
| 367 | { | 
| 368 |     Q_D(QKeyframeAnimation); | 
| 369 |     if (mode != d->m_endMode) { | 
| 370 |         d->m_endMode = mode; | 
| 371 |         emit endModeChanged(endMode: mode); | 
| 372 |     } | 
| 373 | } | 
| 374 |  | 
| 375 | /*! | 
| 376 |     Adds new \a keyframe at the end of the animation. The QTransform can | 
| 377 |     be added to the animation multiple times. | 
| 378 |  */ | 
| 379 | void QKeyframeAnimation::addKeyframe(Qt3DCore::QTransform *keyframe) | 
| 380 | { | 
| 381 |     Q_D(QKeyframeAnimation); | 
| 382 |     d->m_keyframes.push_back(t: keyframe); | 
| 383 | } | 
| 384 |  | 
| 385 | /*! | 
| 386 |     Removes a \a keyframe from the animation. If the same QTransform | 
| 387 |     is set as keyframe multiple times, all occurrences are removed. | 
| 388 |  */ | 
| 389 | void QKeyframeAnimation::removeKeyframe(Qt3DCore::QTransform *keyframe) | 
| 390 | { | 
| 391 |     Q_D(QKeyframeAnimation); | 
| 392 |     d->m_keyframes.removeAll(t: keyframe); | 
| 393 | } | 
| 394 |  | 
| 395 | QString QKeyframeAnimation::targetName() const | 
| 396 | { | 
| 397 |     Q_D(const QKeyframeAnimation); | 
| 398 |     return d->m_targetName; | 
| 399 | } | 
| 400 |  | 
| 401 | QEasingCurve QKeyframeAnimation::easing() const | 
| 402 | { | 
| 403 |     Q_D(const QKeyframeAnimation); | 
| 404 |     return d->m_easing; | 
| 405 | } | 
| 406 |  | 
| 407 | Qt3DCore::QTransform *QKeyframeAnimation::target() const | 
| 408 | { | 
| 409 |     Q_D(const QKeyframeAnimation); | 
| 410 |     return d->m_target; | 
| 411 | } | 
| 412 |  | 
| 413 | } // Qt3DAnimation | 
| 414 |  | 
| 415 | QT_END_NAMESPACE | 
| 416 |  |