diff options
Diffstat (limited to 'src/runtime/q3dsslideplayer.cpp')
-rw-r--r-- | src/runtime/q3dsslideplayer.cpp | 283 |
1 files changed, 187 insertions, 96 deletions
diff --git a/src/runtime/q3dsslideplayer.cpp b/src/runtime/q3dsslideplayer.cpp index fee148c..f7c2ee2 100644 --- a/src/runtime/q3dsslideplayer.cpp +++ b/src/runtime/q3dsslideplayer.cpp @@ -44,45 +44,63 @@ QT_BEGIN_NAMESPACE -// NOTE: We assume that the properties for the layer has been applied before this is called -// if it hasn't it will either return the previous value or the odl value void Q3DSSlideUtils::getStartAndEndTime(Q3DSSlide *slide, qint32 *startTime, qint32 *endTime) { Q_ASSERT(startTime != nullptr || endTime != nullptr); - qint32 eTime = -1; + qint32 layerEndTime = -1; + qint32 nodesEndtime = -1; - // First go through the slide's parent (master slide) and look for layers which have - // endtime explicitly set by this slide's property changes + // Check if there are nodes from the parent slide that has property changes on this slide. if (Q3DSSlide *p = static_cast<Q3DSSlide *>(slide->parent())) { for (auto *obj : p->objects()) { - if (obj->type() != Q3DSGraphObject::Layer) + if (!obj->isNode()) continue; - const QHash<Q3DSGraphObject *, Q3DSPropertyChangeList *> props = - slide->propertyChanges(); - const QHash<Q3DSGraphObject *, Q3DSPropertyChangeList *>::const_iterator it = - props.find(obj); - if (it == props.constEnd()) + // Look for property updates on "this" slide. + const auto &props = slide->propertyChanges(); + const auto foundIt = props.constFind(obj); + if (foundIt == props.constEnd()) continue; - if ((*it)->keys().contains(QStringLiteral("endtime")) && obj->endTime() > eTime) - eTime = obj->endTime(); + + // If there are property changes for the object, check if it has a new endtime. + std::find_if(foundIt.value()->cbegin(), foundIt.value()->cend(), [&layerEndTime, &nodesEndtime, obj](const Q3DSPropertyChange &propChange) { + if (propChange.name() == QLatin1String("endtime")) { + bool ok = false; + const qint32 value = propChange.value().toInt(&ok); + if (ok) { + if (obj->type() == Q3DSGraphObject::Layer && (value > layerEndTime)) + layerEndTime = value; + else if (value > nodesEndtime) + nodesEndtime = value; + } + } + return false; + }); } } - // Now look for the endtime from the slides explicit layer objects + // Now look for the endtime on nodes on this slide. for (const auto obj : slide->objects()) { - if (obj->type() != Q3DSGraphObject::Layer) + // Skip non-node types. + if (!obj->isNode()) continue; - if (obj->endTime() > eTime) - eTime = obj->endTime(); + // We collect both layer endtimes (if any) and object endtimes in one go. + if (obj->type() == Q3DSGraphObject::Layer && (obj->endTime() > layerEndTime)) + layerEndTime = obj->endTime(); + else if (obj->endTime() > nodesEndtime) + nodesEndtime = obj->endTime(); } + // Final fallback, if neither was found use the value set by the slide. + if (layerEndTime == -1 && nodesEndtime == -1) + nodesEndtime = slide->endTime(); + if (startTime) *startTime = slide->startTime(); if (endTime) - *endTime = eTime != -1 ? eTime : slide->endTime(); + *endTime = layerEndTime != -1 ? layerEndTime : nodesEndtime; } static QString getSlideName(Q3DSSlide *slide) @@ -97,40 +115,28 @@ static Q3DSSlidePlayer::PlayerState getInitialSlideState(Q3DSSlide *slide) } -static void updatePosition(Q3DSSlide *slide, float pos, float duration) +static void updatePosition(Q3DSSlide *slide, float newTimeMs, float durationMs) { - const auto updateAnimator = [pos](Qt3DAnimation::QClipAnimator *animator, float duration) { + const auto updateAnimator = [newTimeMs](Qt3DAnimation::QClipAnimator *animator, float durationMs) { if (!animator) return; - const float nt = qBound(0.0f, pos / duration, 1.0f); + const float nt = qBound(0.0f, newTimeMs / durationMs, 1.0f); // NOTE!!!: This is a bit funky, but it means we can avoid tracking the normalized values in the // frontend node. This of course assumes that the limit in the fuzzy compare doesn't change! animator->setNormalizedTime(qFuzzyCompare(nt, animator->normalizedTime()) ? qAbs(nt - (1.0f / 100000.0f)) : nt); }; const auto updateAnimators = [&updateAnimator](const QVector<Qt3DAnimation::QClipAnimator *> &animators) { for (auto animator : animators) { - const float duration = animator->clip()->duration() * 1000.0f; - updateAnimator(animator, duration); - } - }; - - const auto updateAllComponentPlayers = [pos](Q3DSSlide *slide) { - for (auto obj : slide->objects()) { - if (obj->type() == Q3DSGraphObject::Component) { - Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); - const float slidePos = pos - obj->startTime(); - Q3DSSlidePlayer *player = comp->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer; - player->seek(slidePos); - } + const float durationMs = animator->clip()->duration() * 1000.0f; + updateAnimator(animator, durationMs); } }; Q3DSSlideAttached *data = slide->attached<Q3DSSlideAttached>(); Q_ASSERT(data); - updateAllComponentPlayers(slide); - updateAnimator(data->animator, duration); + updateAnimator(data->animator, durationMs); updateAnimators(data->animators); }; @@ -144,12 +150,11 @@ static void updateAnimators(Q3DSSlide *slide, bool running, bool restart, float // NOTE!!!: This is a bit funky, but it means we can avoid tracking the normalized values in the // frontend node. This of course assumes that the limit in the fuzzy compare doesn't change! animator->setNormalizedTime(qFuzzyCompare(animator->normalizedTime(), 0.0f) ? (0.0f + (1.0f / 100000.0f)) : 0.0f); - // NOTE: The running value might not have been updated in the frontend node yet, - // so force update this one as well! - if (animator->isRunning()) - animator->setRunning(false); } animator->clock()->setPlaybackRate(double(rate)); + // NOTE: The running value might not have been updated in the frontend node yet, + // so force update if the values are the same... + animator->setRunning(!running); animator->setRunning(running); }; const auto updateAnimators = [&updateAnimator](const QVector<Qt3DAnimation::QClipAnimator *> &animators) { @@ -157,24 +162,9 @@ static void updateAnimators(Q3DSSlide *slide, bool running, bool restart, float updateAnimator(animator); }; - const auto updateAllComponentPlayers = [running](Q3DSSlide *slide) { - for (auto obj : slide->objects()) { - if (obj->type() == Q3DSGraphObject::Component) { - Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); - Q3DSSlide *compSlide = comp->currentSlide(); - Q3DSSlidePlayer *player = comp->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer; - if (running && compSlide->initialPlayState() == Q3DSSlide::Play) - player->play(); - else - player->stop(); - } - } - }; - Q3DSSlideAttached *data = slide->attached<Q3DSSlideAttached>(); Q_ASSERT(data); - updateAllComponentPlayers(slide); updateAnimator(data->animator); updateAnimators(data->animators); } @@ -192,20 +182,9 @@ static void updatePlaybackRate(Q3DSSlide *slide, float rate) updateAnimator(animator); }; - const auto updateAllComponentPlayers = [rate](Q3DSSlide *slide) { - for (auto obj : slide->objects()) { - if (obj->type() == Q3DSGraphObject::Component) { - Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); - Q3DSSlidePlayer *player = comp->masterSlide()->attached<Q3DSSlideAttached>()->slidePlayer; - player->setPlaybackRate(rate); - } - } - }; - Q3DSSlideAttached *data = slide->attached<Q3DSSlideAttached>(); Q_ASSERT(data); - updateAllComponentPlayers(slide); updateAnimator(data->animator); updateAnimators(data->animators); } @@ -214,7 +193,7 @@ Q3DSSlidePlayer::Q3DSSlidePlayer(Q3DSSceneManager *sceneManager, QObject *parent) : QObject(parent) , m_sceneManager(sceneManager) - , m_animationManager(new Q3DSAnimationManager(this)) + , m_animationManager(new Q3DSAnimationManager) { } @@ -236,6 +215,43 @@ void Q3DSSlidePlayer::advanceFrame() m_animationManager->applyChanges(); } +void Q3DSSlidePlayer::sceneReady() +{ + Q3DSSlideDeck *slideDeck = m_data.slideDeck; + if (!slideDeck) + return; + + Q3DSSlide *currentSlide = slideDeck->currentSlide(); + Q_ASSERT(currentSlide); + + const bool viewerMode = (m_mode == PlayerMode::Viewer); + if (viewerMode && (currentSlide->initialPlayState() == Q3DSSlide::Play)) + play(); + else + pause(); + + // In viewer-mode we need to go through all components players as well + if (viewerMode) { + static const auto notifyComponentPlayers = [](Q3DSSlide *slide) { + if (!slide) + return; + + const auto &objects = slide->objects(); + std::find_if(objects.constBegin(), objects.constEnd(), [](Q3DSGraphObject *obj) { + if (obj->type() == Q3DSGraphObject::Component) { + Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); + Q3DSSlide *compSlide = comp->currentSlide(); + Q3DSSlidePlayer *player = compSlide->attached<Q3DSSlideAttached>()->slidePlayer; + player->sceneReady(); + } + return false; + }); + }; + notifyComponentPlayers(static_cast<Q3DSSlide *>(currentSlide->parent())); + notifyComponentPlayers(currentSlide); + } +} + float Q3DSSlidePlayer::duration() const { if (m_data.state == PlayerState::Idle) @@ -304,9 +320,14 @@ void Q3DSSlidePlayer::stop() void Q3DSSlidePlayer::pause() { - if (m_data.state != PlayerState::Playing) + if (m_data.state == PlayerState::Paused) return; + if (m_data.state == PlayerState::Idle) { + qCWarning(lcSlidePlayer) << "Pause called in Idle state (no content)"; + return; + } + setInternalState(PlayerState::Paused); } @@ -378,8 +399,6 @@ void Q3DSSlidePlayer::setSlideDeck(Q3DSSlideDeck *slideDeck) // Create a slide deck for this component compMasterData->slidePlayer->setSlideDeck(new Q3DSSlideDeck(compMasterSlide, slide)); } - - Q_ASSERT(compMasterData->slidePlayer->state() == PlayerState::Ready); }; const auto forAllComponentsOnSlide = [this, prepareComponentsOnSlide](Q3DSSlide *slide) { @@ -546,10 +565,28 @@ void Q3DSSlidePlayer::handleCurrentSlideChanged(Q3DSSlide *slide, qCDebug(lcSlidePlayer, "Handling current slide change: from slide \"%s\", to slide \"%s\"", qPrintable(getSlideName(previousSlide)), qPrintable(getSlideName(slide))); + static const auto cleanUpComponentPlayers = [](Q3DSSlide *slide) { + if (!slide) + return; + + const auto &objects = slide->objects(); + std::find_if(objects.constBegin(), objects.constEnd(), [](Q3DSGraphObject *obj) { + if (obj->type() == Q3DSGraphObject::Component) { + Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); + Q3DSSlide *compSlide = comp->currentSlide(); + Q3DSSlideAttached *data = compSlide->attached<Q3DSSlideAttached>(); + data->slidePlayer->handleCurrentSlideChanged(nullptr, compSlide); + } + return false; + }); + }; if (previousSlide && slideDidChange) { - if (parentChanged) + if (parentChanged) { + cleanUpComponentPlayers(static_cast<Q3DSSlide *>(previousSlide->parent())); setSlideTime(static_cast<Q3DSSlide *>(previousSlide->parent()), -1.0f); + } setSlideTime(previousSlide, -1.0f); + cleanUpComponentPlayers(previousSlide); Q3DSSlideAttached *data = previousSlide->attached<Q3DSSlideAttached>(); if (data && data->animator) { // TODO: We probably want to be a bit less brute. @@ -560,9 +597,8 @@ void Q3DSSlidePlayer::handleCurrentSlideChanged(Q3DSSlide *slide, } if (slide && slideDidChange && isSlideVisible(slide)) { - m_sceneManager->handleSlideChange(previousSlide, slide); + processPropertyChanges(slide, previousSlide); m_animationManager->updateAnimations(slide, (m_mode == PlayerMode::Editor)); - if (parentChanged) setSlideTime(static_cast<Q3DSSlide *>(slide->parent()), 0.0f); setSlideTime(slide, 0.0f); @@ -603,6 +639,26 @@ void Q3DSSlidePlayer::handleCurrentSlideChanged(Q3DSSlide *slide, qint32 endTime = 0; Q3DSSlideUtils::getStartAndEndTime(slide, &startTime, &endTime); onDurationChanged(endTime - startTime); + + static const auto updateComponentSlides = [](Q3DSSlide *slide) { + if (!slide) + return; + const auto &objects = slide->objects(); + std::find_if(objects.constBegin(), objects.constEnd(), [](Q3DSGraphObject *obj) { + if (obj->type() == Q3DSGraphObject::Component) { + Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); + Q3DSSlide *compSlide = comp->currentSlide(); + Q3DSSlideAttached *data = compSlide->attached<Q3DSSlideAttached>(); + data->slidePlayer->handleCurrentSlideChanged(compSlide, nullptr); + if (data->slidePlayer->m_mode == PlayerMode::Viewer) + data->slidePlayer->setInternalState(getInitialSlideState(compSlide)); + } + return false; + }); + }; + if (parentChanged) + updateComponentSlides(static_cast<Q3DSSlide *>(slide->parent())); + updateComponentSlides(slide); } if (previousSlide != slide) @@ -646,27 +702,6 @@ void Q3DSSlidePlayer::setSlideTime(Q3DSSlide *slide, float time, bool parentVisi && node->flags().testFlag(Q3DSNode::Active) && static_cast<Q3DSNodeAttached *>(node->attached())->globalVisibility; - if (obj->type() == Q3DSGraphObject::Component) { - Q3DSComponentNode *comp = static_cast<Q3DSComponentNode *>(obj); - Q3DSSlide *compMasterSlide = comp->masterSlide(); - Q_ASSERT(compMasterSlide); - Q3DSSlidePlayer *compPlayer = compMasterSlide->attached<Q3DSSlideAttached>()->slidePlayer; - Q_ASSERT(compPlayer); - - const float slideTime = time - obj->startTime(); - compPlayer->setSlideTime(compMasterSlide, slideTime, shouldBeVisible); - - if (!shouldBeVisible) { - Q3DSGraphObject *n = compMasterSlide->firstChild(); - while (n) { - compPlayer->setSlideTime(static_cast<Q3DSSlide *>(n), slideTime, shouldBeVisible); - n = n->nextSibling(); - } - } else { - compPlayer->setSlideTime(comp->currentSlide(), slideTime); - } - } - if (forceUpdate || shouldBeVisible != nodeHasVisibilityTag(node)) updateNodeVisibility(node, shouldBeVisible); } @@ -686,7 +721,7 @@ void Q3DSSlidePlayer::sendPositionChanged(Q3DSSlide *slide, float pos) if (!qFuzzyCompare(oldPosition, pos)) Q_EMIT positionChanged(pos); - const bool onEOS = (m_data.state == PlayerState::Playing) + const bool onEOS = (m_data.state == PlayerState::Playing && m_data.pendingState == PlayerState::Playing) && ((m_data.position == 0.0f && m_data.playbackRate < 0.0f) || (m_data.position == duration() && m_data.playbackRate > 0.0f)); @@ -713,7 +748,9 @@ bool Q3DSSlidePlayer::isSlideVisible(Q3DSSlide *slide) if (slide) { Q3DSSlidePlayer *player = slide->attached<Q3DSSlideAttached>()->slidePlayer; Q3DSSlideDeck *slideDeck = player->slideDeck(); - if (slideDeck->currentSlide() == slide) { + const bool isMasterSlide = (slideDeck->masterSlide() == slide); + const bool isCurrentSlide = (slideDeck->currentSlide() == slide); + if (isCurrentSlide || isMasterSlide) { Q3DSSlide *parentSlide = slideDeck->parentSlide(); if (parentSlide) { // We're a component and current, continue up the ladder... @@ -731,6 +768,60 @@ bool Q3DSSlidePlayer::isSlideVisible(Q3DSSlide *slide) return visible; } +void Q3DSSlidePlayer::processPropertyChanges(Q3DSSlide *currentSlide, + Q3DSSlide *previousSlide) +{ + Q_ASSERT(currentSlide->attached()); + + if (previousSlide) { + auto slideData = static_cast<Q3DSSlideAttached *>(previousSlide->attached()); + for (Q3DSNode *node : qAsConst(slideData->needsMasterRollback)) { + const Q3DSPropertyChangeList *changeList = node->masterRollbackList(); + if (changeList) { + qCDebug(lcScene, "Rolling back %d changes to master for %s", changeList->count(), node->id().constData()); + node->applyPropertyChanges(*changeList); + node->notifyPropertyChanges(*changeList); + m_sceneManager->updateSubTreeRecursive(node); + } + } + slideData->needsMasterRollback.clear(); + } + + // Find properties on targets that has dynamic properties. + // TODO: Find a better solution (e.g., there can be duplicate updates for e.g., xyz here). + QHash<Q3DSGraphObject *, Q3DSPropertyChangeList> dynamicPropertyChanges; + const auto &tracks = currentSlide->animations(); + std::find_if(tracks.cbegin(), tracks.cend(), [&dynamicPropertyChanges](const Q3DSAnimationTrack &track) { + if (track.isDynamic()) { + auto foundIt = dynamicPropertyChanges.constFind(track.target()); + Q3DSPropertyChangeList changeList; + if (foundIt != dynamicPropertyChanges.constEnd()) + changeList = *foundIt; + const QString property = track.property().split('.')[0]; + const auto value = track.target()->propertyValue(property); + changeList.append(Q3DSPropertyChange::fromVariant(property, value)); + dynamicPropertyChanges[track.target()] = changeList; + } + return false; + }); + + // Filter out properties that we needs to be marked dirty, i.e., eyeball changes. + const auto &propertyChanges = currentSlide->propertyChanges(); + for (auto it = propertyChanges.cbegin(); it != propertyChanges.cend(); ++it) { + std::find_if((*it)->cbegin(), (*it)->cend(), [it](const Q3DSPropertyChange &propChange) { + if (propChange.name() == QLatin1String("eyeball")) + it.key()->notifyPropertyChanges(*it.value()); + + it.key()->applyPropertyChanges(*it.value()); + return false; + }); + } + + // Now update the propeties from dynamic property values + for (auto it = dynamicPropertyChanges.cbegin(), ite = dynamicPropertyChanges.cend(); it != ite; ++it) + it.key()->applyPropertyChanges(it.value()); +} + void Q3DSSlidePlayer::onDurationChanged(float duration) { if (qFuzzyCompare(duration, m_data.duration)) |