1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "evaluateblendclipanimatorjob_p.h"
5#include <Qt3DCore/private/qaspectmanager_p.h>
6#include <Qt3DCore/private/qskeleton_p.h>
7#include <Qt3DAnimation/qblendedclipanimator.h>
8#include <Qt3DAnimation/private/handler_p.h>
9#include <Qt3DAnimation/private/managers_p.h>
10#include <Qt3DAnimation/private/animationlogging_p.h>
11#include <Qt3DAnimation/private/animationutils_p.h>
12#include <Qt3DAnimation/private/clipblendvalue_p.h>
13#include <Qt3DAnimation/private/lerpclipblend_p.h>
14#include <Qt3DAnimation/private/clipblendnodevisitor_p.h>
15#include <Qt3DAnimation/private/job_common_p.h>
16
17QT_BEGIN_NAMESPACE
18
19namespace Qt3DAnimation {
20namespace Animation {
21
22EvaluateBlendClipAnimatorJob::EvaluateBlendClipAnimatorJob()
23 : AbstractEvaluateClipAnimatorJob()
24 , m_handler(nullptr)
25{
26 SET_JOB_RUN_STAT_TYPE(this, JobTypes::EvaluateBlendClipAnimator, 0)
27}
28
29void EvaluateBlendClipAnimatorJob::run()
30{
31 // Find the set of clips that need to be evaluated by querying each node
32 // in the blend tree.
33 // TODO: We should be able to cache this for each blend animator and only
34 // update when a node indicates its dependencies have changed as a result
35 // of blend factors changing
36
37 BlendedClipAnimator *blendedClipAnimator = m_handler->blendedClipAnimatorManager()->data(handle: m_blendClipAnimatorHandle);
38 Q_ASSERT(blendedClipAnimator);
39 const bool running = blendedClipAnimator->isRunning();
40 const bool seeking = blendedClipAnimator->isSeeking();
41 if (!running && !seeking) {
42 m_handler->setBlendedClipAnimatorRunning(handle: m_blendClipAnimatorHandle, running: false);
43 return;
44 }
45
46 Qt3DCore::QNodeId blendTreeRootId = blendedClipAnimator->blendTreeRootId();
47 const QVector<Qt3DCore::QNodeId> valueNodeIdsToEvaluate = gatherValueNodesToEvaluate(handler: m_handler, blendTreeRootId);
48
49 // Calculate the resulting duration of the blend tree based upon its current state
50 ClipBlendNodeManager *blendNodeManager = m_handler->clipBlendNodeManager();
51 ClipBlendNode *blendTreeRootNode = blendNodeManager->lookupNode(id: blendTreeRootId);
52 Q_ASSERT(blendTreeRootNode);
53 const double duration = blendTreeRootNode->duration();
54
55 Clock *clock = m_handler->clockManager()->lookupResource(id: blendedClipAnimator->clockId());
56
57 qint64 globalTimeNS = m_handler->simulationTime();
58 qint64 nsSincePreviousFrame = seeking ? toNsecs(seconds: duration * blendedClipAnimator->normalizedLocalTime())
59 : blendedClipAnimator->nsSincePreviousFrame(currentGlobalTimeNS: globalTimeNS);
60
61 // Calculate the phase given the blend tree duration and global time
62 AnimatorEvaluationData animatorData = evaluationDataForAnimator(animator: blendedClipAnimator, clock, nsSincePreviousFrame);
63 const double phase = phaseFromElapsedTime(t_current_local: animatorData.currentTime, t_elapsed_global: animatorData.elapsedTime,
64 playbackRate: animatorData.playbackRate,
65 duration,
66 loopCount: animatorData.loopCount,
67 currentLoop&: animatorData.currentLoop);
68
69 // Iterate over the value nodes of the blend tree, evaluate the
70 // contained animation clips at the current phase and store the results
71 // in the animator indexed by node.
72 AnimationClipLoaderManager *clipLoaderManager = m_handler->animationClipLoaderManager();
73 for (const auto &valueNodeId : valueNodeIdsToEvaluate) {
74 ClipBlendValue *valueNode = static_cast<ClipBlendValue *>(blendNodeManager->lookupNode(id: valueNodeId));
75 Q_ASSERT(valueNode);
76 AnimationClip *clip = clipLoaderManager->lookupResource(id: valueNode->clipId());
77 Q_ASSERT(clip);
78
79 ClipResults rawClipResults = evaluateClipAtPhase(clip, phase: float(phase));
80
81 // Reformat the clip results into the layout used by this animator/blend tree
82 const ClipFormat format = valueNode->clipFormat(animatorId: blendedClipAnimator->peerId());
83 ClipResults formattedClipResults = formatClipResults(rawClipResults, format: format.sourceClipIndices);
84 applyComponentDefaultValues(componentDefaults: format.defaultComponentValues, formattedClipResults);
85 valueNode->setClipResults(animatorId: blendedClipAnimator->peerId(), clipResults: formattedClipResults);
86 }
87
88 // Evaluate the blend tree
89 ClipResults blendedResults = evaluateBlendTree(handler: m_handler, animator: blendedClipAnimator, blendNodeId: blendTreeRootId);
90
91 const double localTime = phase * duration;
92 blendedClipAnimator->setLastGlobalTimeNS(globalTimeNS);
93 blendedClipAnimator->setLastLocalTime(localTime);
94 blendedClipAnimator->setLastNormalizedLocalTime(float(phase));
95 blendedClipAnimator->setCurrentLoop(animatorData.currentLoop);
96
97 // Prepare the change record
98 const bool finalFrame = isFinalFrame(localTime, duration, currentLoop: animatorData.currentLoop, loopCount: animatorData.loopCount, playbackRate: animatorData.playbackRate);
99 const QVector<MappingData> mappingData = blendedClipAnimator->mappingData();
100 auto record = prepareAnimationRecord(animatorId: blendedClipAnimator->peerId(),
101 mappingDataVec: mappingData,
102 channelResults: blendedResults,
103 finalFrame,
104 normalizedLocalTime: float(phase));
105
106 // Trigger callbacks either on this thread or by notifying the gui thread.
107 auto callbacks = prepareCallbacks(mappingDataVec: mappingData, channelResults: blendedResults);
108
109 // Update the normalized time on the backend node so that
110 // frontend <-> backend sync will not mark things dirty
111 // unless the frontend normalized time really is different
112 blendedClipAnimator->setNormalizedLocalTime(normalizedTime: record.normalizedTime, allowMarkDirty: false);
113
114 setPostFrameData(record, callbacks);
115}
116
117} // Animation
118} // Qt3DAnimation
119
120QT_END_NAMESPACE
121

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