summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristian Strømme <christian.stromme@qt.io>2018-12-05 17:01:15 +0100
committerChristian Stromme <christian.stromme@qt.io>2018-12-12 12:43:57 +0000
commit1eb1f09fcc36220dcb9030e4455b69435bfc9b94 (patch)
tree4725055b68cc8e634fb1501b3774fafd531305c1 /src
parentefd642d9221bcd1ddb80a58e12868f3c40648cc9 (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.cpp32
-rw-r--r--src/runtime/animator/q3dsanimator_p.h8
-rw-r--r--src/runtime/q3dsabstractslideplayer_p.h2
-rw-r--r--src/runtime/slideplayerng/q3dsanimationmanagerng.cpp112
-rw-r--r--src/runtime/slideplayerng/q3dsanimationmanagerng_p.h3
-rw-r--r--src/runtime/slideplayerng/q3dsslideplayerng.cpp34
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