diff options
author | Christian Strømme <christian.stromme@qt.io> | 2018-12-05 17:01:15 +0100 |
---|---|---|
committer | Christian Stromme <christian.stromme@qt.io> | 2018-12-12 12:43:57 +0000 |
commit | 1eb1f09fcc36220dcb9030e4455b69435bfc9b94 (patch) | |
tree | 4725055b68cc8e634fb1501b3774fafd531305c1 /src | |
parent | efd642d9221bcd1ddb80a58e12868f3c40648cc9 (diff) |
Add support for dynamic key frames
This is a fairly simple implementation for dynamic key frames where
we just go through all the slide properties and update the first key
frames that are marked as dynamic.
Change-Id: I239a6a94983e108ac6296484ca6d02f053204d2f
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/runtime/animator/q3dsanimator.cpp | 32 | ||||
-rw-r--r-- | src/runtime/animator/q3dsanimator_p.h | 8 | ||||
-rw-r--r-- | src/runtime/q3dsabstractslideplayer_p.h | 2 | ||||
-rw-r--r-- | src/runtime/slideplayerng/q3dsanimationmanagerng.cpp | 112 | ||||
-rw-r--r-- | src/runtime/slideplayerng/q3dsanimationmanagerng_p.h | 3 | ||||
-rw-r--r-- | src/runtime/slideplayerng/q3dsslideplayerng.cpp | 34 |
6 files changed, 111 insertions, 80 deletions
diff --git a/src/runtime/animator/q3dsanimator.cpp b/src/runtime/animator/q3dsanimator.cpp index 588d7f0..1b5bb54 100644 --- a/src/runtime/animator/q3dsanimator.cpp +++ b/src/runtime/animator/q3dsanimator.cpp @@ -113,13 +113,13 @@ void syncDirtyProperties(const QVector<Q3DSNodeAnimation::DirtyProperty> &dirtyP void evaluateNodesAt(const Q3DSAnimator::AnimationList::const_iterator &animBegin, const Q3DSAnimator::AnimationList::const_iterator &animEnd, float time, - Q3DSAnimator::DirtyPropertyList *dirtyList) + Q3DSAnimator::DirtyPropertyList *dirtyList, + bool sync) { using Components = Q3DSNodeAnimation::Property::Components; auto animIt = animBegin; Q3DSGraphObject *target = nullptr; - float value; while (animIt != animEnd) { target = animIt->target; Q_ASSERT(target); @@ -134,38 +134,28 @@ void evaluateNodesAt(const Q3DSAnimator::AnimationList::const_iterator &animBegi qWarning("Property animation without no component specified!"); continue; } - bool changed = false; + // If sync is set to true we mark all as dirty to make sure the target property and the + // animation node values are in sync. + bool changed = sync; if (components & Components::X) { const auto cbegin = keyFrames.cbegin() + propertiesIt->components[0].offset; const auto cend = cbegin + propertiesIt->components[0].size - 1; - if (evaluateAnimation(cbegin, cend, time, &(value = v.x()))) { - changed = true; - v.setX(value); - } + changed |= evaluateAnimation(cbegin, cend, time, &v[0]); } if (components & Components::Y) { const auto cbegin = keyFrames.cbegin() + propertiesIt->components[1].offset; const auto cend = cbegin + propertiesIt->components[1].size - 1; - if (evaluateAnimation(cbegin, cend, time, &(value = v.y()))) { - changed = true; - v.setY(value); - } + changed |= evaluateAnimation(cbegin, cend, time, &v[1]); } if (components & Components::Z) { const auto cbegin = keyFrames.cbegin() + propertiesIt->components[2].offset; const auto cend = cbegin + propertiesIt->components[2].size - 1; - if (evaluateAnimation(cbegin, cend, time, &(value = v.z()))) { - changed = true; - v.setZ(value); - } + changed |= evaluateAnimation(cbegin, cend, time, &v[2]); } if (components & Components::W) { const auto cbegin = keyFrames.cbegin() + propertiesIt->components[3].offset; const auto cend = cbegin + propertiesIt->components[3].size - 1; - if (evaluateAnimation(cbegin, cend, time, &(value = v.w()))) { - changed = true; - v.setW(value); - } + changed |= evaluateAnimation(cbegin, cend, time, &v[3]); } if (changed) @@ -177,7 +167,7 @@ void evaluateNodesAt(const Q3DSAnimator::AnimationList::const_iterator &animBegi } } -void Q3DSAnimator::goToTime(float time) +void Q3DSAnimator::goToTime(float time, bool sync) { if (time < 0.0f || time > duration) return; @@ -185,7 +175,7 @@ void Q3DSAnimator::goToTime(float time) localTime = time; dirtyList.clear(); - evaluateNodesAt(animationList.cbegin(), animationList.cend(), localTime, &dirtyList); + evaluateNodesAt(animationList.cbegin(), animationList.cend(), localTime, &dirtyList, sync); syncDirtyProperties(dirtyList); if (timeChangeCallback) diff --git a/src/runtime/animator/q3dsanimator_p.h b/src/runtime/animator/q3dsanimator_p.h index 78e4141..d7e933b 100644 --- a/src/runtime/animator/q3dsanimator_p.h +++ b/src/runtime/animator/q3dsanimator_p.h @@ -64,7 +64,8 @@ struct Q3DSNodeAnimation X = 0x1, Y = 0x2, Z = 0x4, - W = 0x8 + W = 0x8, + Dynamic = 0x10 }; struct Component { @@ -121,7 +122,7 @@ struct Q3DSAnimator using TimeChangeCallback = std::function<void(float /* seconds */)>; TimeChangeCallback timeChangeCallback; - void goToTime(float time); + void goToTime(float time, bool sync = false); // Note that the animator is not self-driven, so advance needs to be called periodically, // i.e., by attaching it to the frame updater... @@ -140,7 +141,8 @@ void syncDirtyProperties(const QVector<Q3DSNodeAnimation::DirtyProperty> &dirtyP void evaluateNodesAt(const Q3DSAnimator::AnimationList::const_iterator &animBegin, const Q3DSAnimator::AnimationList::const_iterator &animEnd, float time, - Q3DSAnimator::DirtyPropertyList *dirtyList); + Q3DSAnimator::DirtyPropertyList *dirtyList, + bool sync = false); // TODO: Declare movable diff --git a/src/runtime/q3dsabstractslideplayer_p.h b/src/runtime/q3dsabstractslideplayer_p.h index ea477d7..0ecf15f 100644 --- a/src/runtime/q3dsabstractslideplayer_p.h +++ b/src/runtime/q3dsabstractslideplayer_p.h @@ -197,7 +197,7 @@ public: return; if (m_player) - m_player->stop(); + m_player->pause(); m_previouslyActiveIndex = m_index; m_index = index; diff --git a/src/runtime/slideplayerng/q3dsanimationmanagerng.cpp b/src/runtime/slideplayerng/q3dsanimationmanagerng.cpp index 37d30ce..0ec2b0f 100644 --- a/src/runtime/slideplayerng/q3dsanimationmanagerng.cpp +++ b/src/runtime/slideplayerng/q3dsanimationmanagerng.cpp @@ -40,6 +40,30 @@ static constexpr qint8 componentSuffixToIndex(qint8 c) return (c >= 'w' && c <= 'z') ? (c == 'w') ? 3 : (c - 'x') : -1; } +static QVector4D toVector4D(const QVariant &qv) +{ + switch (int(qv.type())) { + case QVariant::Double: + Q_FALLTHROUGH(); + case QMetaType::Float: + return QVector4D{QVector2D{*reinterpret_cast<const float *>(qv.constData()), 0.0f}}; + case QVariant::Vector2D: + return QVector4D{*reinterpret_cast<const QVector2D *>(qv.constData())}; + case QVariant::Vector3D: + return QVector4D{*reinterpret_cast<const QVector3D *>(qv.constData())}; + case QVariant::Vector4D: + return *reinterpret_cast<const QVector4D *>(qv.constData()); + case QVariant::Color: + { + const auto c = qv.value<QColor>(); + return QVector4D{QVector3D{float(c.redF()), float(c.greenF()), float(c.blueF())}}; + } + default: + qCWarning(lcAnim, "The unsupported type %s attempted used!", qv.typeName()); + return QVector4D(); + } +}; + static Q3DSNodeAnimation::KeyFrameList buildKeyFramesForTrack(const Q3DSAnimationTrack &track) { const auto type = track.type(); @@ -104,45 +128,13 @@ static Q3DSNodeAnimation::KeyFrameList buildKeyFramesForTrack(const Q3DSAnimatio void Q3DSAnimationManagerNg::buildSlideAnimation(Q3DSSlide *slide, bool rebuild) { - static const auto toVector4D = [](const QVariant &qv) - { - switch (int(qv.type())) { - case QVariant::Double: - Q_FALLTHROUGH(); - case QMetaType::Float: - return QVector4D{QVector2D{*reinterpret_cast<const float *>(qv.constData()), 0.0f}}; - case QVariant::Vector2D: - return QVector4D{*reinterpret_cast<const QVector2D *>(qv.constData())}; - case QVariant::Vector3D: - return QVector4D{*reinterpret_cast<const QVector3D *>(qv.constData())}; - case QVariant::Vector4D: - return *reinterpret_cast<const QVector4D *>(qv.constData()); - case QVariant::Color: - { - const auto c = qv.value<QColor>(); - return QVector4D{QVector3D{float(c.redF()), float(c.greenF()), float(c.blueF())}}; - } - default: - qCWarning(lcAnim, "The unsupported type %s attempted used!", qv.typeName()); - return QVector4D(); - } - }; - - // TODO: Add support for dynamic keyframes!!! - // Do all the set-up: - // - Create the binding - // - Set initial value in the binding - // - make it cache friendly - pre-optimize (e.g, create a time table for the kf) - // - Dynamic keyframes - // - Create key for fast hashing. - // - ... - // This code is ridicules, but it will do for now Q3DSSlideAttached *data = slide->attached<Q3DSSlideAttached>(); Q_ASSERT(data); if (!data->animatorNg) data->animatorNg = new Q3DSAnimator; - else if (!data->animatorNg->animationList.isEmpty() && !rebuild) + + if (!rebuild && !data->animatorNg->animationList.isEmpty()) return; auto &propertyTracks = data->animatorNg->animationList; @@ -201,8 +193,7 @@ void Q3DSAnimationManagerNg::buildSlideAnimation(Q3DSSlide *slide, bool rebuild) const auto &atracks = propTrack.value(); const int pid = Q3DSGraphObject::indexOfProperty(target, property.toLatin1()); Q_ASSERT(pid != -1); - const QVariant value = Q3DSGraphObject::readProperty(target, pid); - Q_ASSERT(value.isValid()); + QVector4D vec4; quint8 compFlag = 0; for (int compPos = 0; compPos != 4; ++compPos) { const auto trackPtr = atracks.at(compPos); @@ -210,20 +201,26 @@ void Q3DSAnimationManagerNg::buildSlideAnimation(Q3DSSlide *slide, bool rebuild) continue; const auto componentKeyFrames = buildKeyFramesForTrack(*trackPtr); + // Get the start value, which is the value of the first kf. + if (componentKeyFrames.size() > 0) + vec4[compPos] = componentKeyFrames.at(0).value; compFlag |= (1 << compPos); const int offset = nodeAnimation.keyFrames.size(); const int size = componentKeyFrames.size(); nodeProperty.components[compPos].size = quint16(size); nodeProperty.components[compPos].offset = quint16(offset); nodeAnimation.keyFrames += componentKeyFrames; + if (trackPtr->isDynamic()) + compFlag |= static_cast<quint8>(Q3DSNodeAnimation::Property::Components::Dynamic); } const int changeFlag = targetIt.key()->mapChangeFlags({{property}}); - nodeProperty.vec4 = toVector4D(value); + nodeProperty.vec4 = vec4; nodeProperty.changeFlags = qint8(changeFlag); nodeProperty.componentFlags = static_cast<Q3DSNodeAnimation::Property::Components>(compFlag); nodeProperty.pid = qint16(pid); - nodeProperty.type = value.type(); + // TODO: Find a nice solution for getting the type... + nodeProperty.type = Q3DSGraphObject::readProperty(target, pid).type(); nodeAnimation.properties.push_back(nodeProperty); } @@ -231,16 +228,51 @@ void Q3DSAnimationManagerNg::buildSlideAnimation(Q3DSSlide *slide, bool rebuild) } } +void Q3DSAnimationManagerNg::updateDynamicKeyFrames(Q3DSSlide *slide) +{ + Q3DSSlideAttached *data = slide->attached<Q3DSSlideAttached>(); + Q_ASSERT(data); + + if (!data->animatorNg) + return; + + auto &animations = data->animatorNg->animationList; + for (auto &animNode : animations) { + using Component = Q3DSNodeAnimation::Property::Components; + for (const auto &property : qAsConst(animNode.properties)) { + const auto vec4 = toVector4D(Q3DSGraphObject::readProperty(animNode.target, property.pid)); + if ((property.componentFlags & Component::Dynamic) == 0) + continue; + if (property.componentFlags & Component::X) { + auto &kf = *(animNode.keyFrames.begin() + property.components[0].offset); + kf.value = kf.c1.value = kf.c2.value = vec4[0]; + } + if (property.componentFlags & Component::Y) { + auto &kf = *(animNode.keyFrames.begin() + property.components[1].offset); + kf.value = kf.c1.value = kf.c2.value = vec4[1]; + } + if (property.componentFlags & Component::Z) { + auto &kf = *(animNode.keyFrames.begin() + property.components[2].offset); + kf.value = kf.c1.value = kf.c2.value = vec4[2]; + } + if (property.componentFlags & Component::W) { + auto &kf = *(animNode.keyFrames.begin() + property.components[3].offset); + kf.value = kf.c1.value = kf.c2.value = vec4[3]; + } + } + } +} + void Q3DSAnimationManagerNg::setActive(Q3DSSlide *slide, bool active) { if (slide->attached<Q3DSSlideAttached>()->animatorNg) slide->attached<Q3DSSlideAttached>()->animatorNg->active = active; } -void Q3DSAnimationManagerNg::goToTime(Q3DSSlide *slide, float timeMs) +void Q3DSAnimationManagerNg::goToTime(Q3DSSlide *slide, float timeMs, bool sync) { if (slide->attached<Q3DSSlideAttached>()->animatorNg) - slide->attached<Q3DSSlideAttached>()->animatorNg->goToTime(timeMs / 1000.f); + slide->attached<Q3DSSlideAttached>()->animatorNg->goToTime(timeMs / 1000.f, sync); } void Q3DSAnimationManagerNg::setRate(Q3DSSlide *slide, float rate) diff --git a/src/runtime/slideplayerng/q3dsanimationmanagerng_p.h b/src/runtime/slideplayerng/q3dsanimationmanagerng_p.h index a8399e2..0621f9b 100644 --- a/src/runtime/slideplayerng/q3dsanimationmanagerng_p.h +++ b/src/runtime/slideplayerng/q3dsanimationmanagerng_p.h @@ -50,13 +50,14 @@ namespace Q3DSAnimationManagerNg { Q3DSV_PRIVATE_EXPORT void setActive(Q3DSSlide *slide, bool active); Q3DSV_PRIVATE_EXPORT void advance(Q3DSSlide *slide, float dtMs); - Q3DSV_PRIVATE_EXPORT void goToTime(Q3DSSlide *slide, float timeMs); + Q3DSV_PRIVATE_EXPORT void goToTime(Q3DSSlide *slide, float timeMs, bool sync = false); Q3DSV_PRIVATE_EXPORT void setRate(Q3DSSlide *slide, float rate); Q3DSV_PRIVATE_EXPORT void setDuration(Q3DSSlide *slide, float durationMs); Q3DSV_PRIVATE_EXPORT void setPlayMode(Q3DSSlide *slide, Q3DSSlide::PlayMode mode); Q3DSV_PRIVATE_EXPORT void setEosCallback(Q3DSSlide *slide, Q3DSAnimator::EosCallback cb); Q3DSV_PRIVATE_EXPORT void setTimeChangeCallback(Q3DSSlide *slide, Q3DSAnimator::TimeChangeCallback cb); Q3DSV_PRIVATE_EXPORT void buildSlideAnimation(Q3DSSlide *slide, bool rebuild = false); + Q3DSV_PRIVATE_EXPORT void updateDynamicKeyFrames(Q3DSSlide *slide); }; QT_END_NAMESPACE diff --git a/src/runtime/slideplayerng/q3dsslideplayerng.cpp b/src/runtime/slideplayerng/q3dsslideplayerng.cpp index 10daa33..2223185 100644 --- a/src/runtime/slideplayerng/q3dsslideplayerng.cpp +++ b/src/runtime/slideplayerng/q3dsslideplayerng.cpp @@ -504,17 +504,16 @@ void Q3DSSlidePlayerNg::changeState(Q3DSSlide *slide, Q3DSSlidePlayerNg::PlayerS break; case PlayerState::Paused: Q3DSAnimationManagerNg::setActive(slide, false); - Q3DSAnimationManagerNg::goToTime(slide, m_data.position); + Q3DSAnimationManagerNg::goToTime(slide, m_data.position, true); break; case PlayerState::Playing: - Q3DSAnimationManagerNg::goToTime(slide, m_data.position); + Q3DSAnimationManagerNg::goToTime(slide, m_data.position, true); forAllSlideComponents(static_cast<Q3DSSlide *>(slide->parent()), updateComponentState); forAllSlideComponents(slide, updateComponentState); Q3DSAnimationManagerNg::setActive(slide, true); break; case PlayerState::Ready: // Reset to the initial state Q3DSAnimationManagerNg::setActive(slide, false); - Q3DSAnimationManagerNg::goToTime(slide, 0.0f); // Hide all objects if (parentChanged) { setObjectVisibility(static_cast<Q3DSSlide *>(slide->parent()), -1.0f); @@ -526,7 +525,7 @@ void Q3DSSlidePlayerNg::changeState(Q3DSSlide *slide, Q3DSSlidePlayerNg::PlayerS case PlayerState::Stopped: // Make the slides animations inactive and reset the local time to 0 Q3DSAnimationManagerNg::setActive(slide, false); - Q3DSAnimationManagerNg::goToTime(slide, 0.0f); + Q3DSAnimationManagerNg::goToTime(slide, 0.0f, true); break; } } @@ -560,8 +559,10 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState // Slide changed or the current slide has not gotten its animator built yet. if (activeSlide != currentSlide) { - // If the slide changed, stop the previous slide first - changeState(activeSlide, PlayerState::Ready, parentChanged); + // If the slide changed, stop the previous slide first (skip if slide is already ready or stopped) + // This is usually the case when e.g., changing slides, as the old slide will be stopped first. + if (activeSlide && (m_data.state != PlayerState::Stopped && m_data.state != PlayerState::Ready)) + changeState(activeSlide, PlayerState::Ready, parentChanged); // TODO: // 1. Build all animation once (might be option to release them, but we'll get back to that). @@ -577,18 +578,24 @@ void Q3DSSlidePlayerNg::setInternalState(Q3DSSlidePlayerNg::PlayerState newState else m_component->setCurrentSlide(currentSlide); + // Ensure that the slide's animation tracks are built + Q3DSAnimationManagerNg::buildSlideAnimation(currentSlide); + // If this is a real slide change (previous slide != null and != current), then + // we need to update the dynamic key frames + if (activeSlide && activeSlide != currentSlide) + Q3DSAnimationManagerNg::updateDynamicKeyFrames(currentSlide); + // All set, now apply the property changes for this slide. Note that the values might + // be out of sync after this, but once the slide changes to playing/paused, the values + // will be synced to match the values from the animator (based on the first kf values). + processPropertyChanges(currentSlide); + Q_ASSERT(currentSlide->attached<Q3DSSlideAttached>()->animatorNg); + + qint32 endTime = 0; // ms Q3DSSlideUtils::getStartAndEndTime(currentSlide, nullptr, &endTime); onDurationChanged(endTime); - - processPropertyChanges(currentSlide); m_data.position = 0.0f; - // Ensure that the slide's animation tracks are built - // TODO: don't rebuild each time (dynamic kf needs to be updated though) - Q3DSAnimationManagerNg::buildSlideAnimation(currentSlide, true); - Q_ASSERT(currentSlide->attached<Q3DSSlideAttached>()->animatorNg); - // Update duration, rate, and playmode Q3DSAnimationManagerNg::setPlayMode(currentSlide, currentSlide->playMode()); Q3DSAnimationManagerNg::setDuration(currentSlide, m_data.duration); @@ -756,7 +763,6 @@ bool Q3DSSlidePlayerNg::isSlideVisible(Q3DSSlide *slide) void Q3DSSlidePlayerNg::processPropertyChanges(Q3DSSlide *currentSlide) { Q_ASSERT(currentSlide->attached()); - // Reset eyeball values // 1. Eyeball set to true on master and false on slideX -> eyeball updated by prop change for slideX // 2. Eyeball set to false on master and false on slideX -> eyeball update by prop change for slideX |