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 "handler_p.h"
5#include <Qt3DAnimation/private/managers_p.h>
6#include <Qt3DAnimation/private/loadanimationclipjob_p.h>
7#include <Qt3DAnimation/private/findrunningclipanimatorsjob_p.h>
8#include <Qt3DAnimation/private/evaluateclipanimatorjob_p.h>
9#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
10#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
11#include <Qt3DAnimation/private/animationlogging_p.h>
12#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
13#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
14#include <Qt3DCore/private/qaspectjob_p.h>
15#include <Qt3DCore/private/vector_helper_p.h>
16
17QT_BEGIN_NAMESPACE
18
19namespace Qt3DAnimation {
20namespace Animation {
21
22Handler::Handler()
23 : m_animationClipLoaderManager(new AnimationClipLoaderManager)
24 , m_clockManager(new ClockManager)
25 , m_clipAnimatorManager(new ClipAnimatorManager)
26 , m_blendedClipAnimatorManager(new BlendedClipAnimatorManager)
27 , m_channelMappingManager(new ChannelMappingManager)
28 , m_channelMapperManager(new ChannelMapperManager)
29 , m_clipBlendNodeManager(new ClipBlendNodeManager)
30 , m_skeletonManager(new SkeletonManager)
31 , m_loadAnimationClipJob(new LoadAnimationClipJob)
32 , m_findRunningClipAnimatorsJob(new FindRunningClipAnimatorsJob)
33 , m_buildBlendTreesJob(new BuildBlendTreesJob)
34 , m_simulationTime(0)
35{
36 m_loadAnimationClipJob->setHandler(this);
37 m_findRunningClipAnimatorsJob->setHandler(this);
38 m_buildBlendTreesJob->setHandler(this);
39}
40
41Handler::~Handler()
42{
43}
44
45void Handler::setDirty(DirtyFlag flag, Qt3DCore::QNodeId nodeId)
46{
47 switch (flag) {
48 case AnimationClipDirty: {
49 QMutexLocker lock(&m_mutex);
50 const auto handle = m_animationClipLoaderManager->lookupHandle(id: nodeId);
51 if (!m_dirtyAnimationClips.contains(t: handle))
52 m_dirtyAnimationClips.push_back(t: handle);
53 break;
54 }
55
56 case ChannelMappingsDirty: {
57 break;
58 }
59
60 case ClipAnimatorDirty: {
61 QMutexLocker lock(&m_mutex);
62 const auto handle = m_clipAnimatorManager->lookupHandle(id: nodeId);
63 if (!m_dirtyClipAnimators.contains(t: handle))
64 m_dirtyClipAnimators.push_back(t: handle);
65 break;
66 }
67
68 case BlendedClipAnimatorDirty: {
69 QMutexLocker lock(&m_mutex);
70 const HBlendedClipAnimator handle = m_blendedClipAnimatorManager->lookupHandle(id: nodeId);
71 if (!m_dirtyBlendedAnimators.contains(t: handle))
72 m_dirtyBlendedAnimators.push_back(t: handle);
73 break;
74 }
75 }
76}
77
78void Handler::setClipAnimatorRunning(const HClipAnimator &handle, bool running)
79{
80 // Add clip to running set if not already present
81 if (running && !m_runningClipAnimators.contains(t: handle)) {
82 m_runningClipAnimators.push_back(t: handle);
83 ClipAnimator *clipAnimator = m_clipAnimatorManager->data(handle);
84 if (clipAnimator)
85 clipAnimator->setStartTime(m_simulationTime);
86 }
87
88 // If being marked as not running, remove from set of running clips
89 if (!running) {
90 m_runningClipAnimators.removeAll(t: handle);
91 }
92}
93
94void Handler::setBlendedClipAnimatorRunning(const HBlendedClipAnimator &handle, bool running)
95{
96 // Add clip to running set if not already present
97 if (running && !m_runningBlendedClipAnimators.contains(t: handle)) {
98 m_runningBlendedClipAnimators.push_back(t: handle);
99 BlendedClipAnimator *blendedClipAnimator = m_blendedClipAnimatorManager->data(handle);
100 if (blendedClipAnimator)
101 blendedClipAnimator->setStartTime(m_simulationTime);
102 }
103
104 // If being marked as not running, remove from set of running clips
105 if (!running) {
106 const auto it = std::find_if(first: m_runningBlendedClipAnimators.begin(),
107 last: m_runningBlendedClipAnimators.end(),
108 pred: [handle](const HBlendedClipAnimator &h) { return h == handle; });
109 if (it != m_runningBlendedClipAnimators.end())
110 m_runningBlendedClipAnimators.erase(pos: it);
111 }
112}
113
114// The vectors may get outdated when the application removes/deletes an
115// animator component in the meantime. Recognize this. This should be
116// relatively infrequent so in most cases the vectors will not change at all.
117void Handler::cleanupHandleList(QVector<HAnimationClip> *clips)
118{
119 for (auto it = clips->begin(); it != clips->end(); ) {
120 if (!m_animationClipLoaderManager->data(handle: *it))
121 it = clips->erase(pos: it);
122 else
123 ++it;
124 }
125}
126
127void Handler::cleanupHandleList(QVector<HClipAnimator> *animators)
128{
129 for (auto it = animators->begin(); it != animators->end(); ) {
130 if (!m_clipAnimatorManager->data(handle: *it))
131 it = animators->erase(pos: it);
132 else
133 ++it;
134 }
135}
136
137void Handler::cleanupHandleList(QVector<HBlendedClipAnimator> *animators)
138{
139 for (auto it = animators->begin(); it != animators->end(); ) {
140 if (!m_blendedClipAnimatorManager->data(handle: *it))
141 it = animators->erase(pos: it);
142 else
143 ++it;
144 }
145}
146
147std::vector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time)
148{
149 // Store the simulation time so we can mark the start time of
150 // animators which will allow us to calculate the local time of
151 // animation clips.
152 m_simulationTime = time;
153
154 std::vector<Qt3DCore::QAspectJobPtr> jobs;
155
156 QMutexLocker lock(&m_mutex);
157
158 // If there are any dirty animation clips that need loading,
159 // queue up a job for them
160 const bool hasLoadAnimationClipJob = !m_dirtyAnimationClips.isEmpty();
161 if (hasLoadAnimationClipJob) {
162 qCDebug(HandlerLogic) << "Added LoadAnimationClipJob";
163 cleanupHandleList(clips: &m_dirtyAnimationClips);
164 m_loadAnimationClipJob->addDirtyAnimationClips(animationClipHandles: m_dirtyAnimationClips);
165 jobs.push_back(x: m_loadAnimationClipJob);
166 m_dirtyAnimationClips.clear();
167 }
168
169 // If there are dirty clip animators, find the set that are able to
170 // run. I.e. are marked as running and have animation clips and
171 // channel mappings
172
173 const bool hasFindRunningClipAnimatorsJob = !m_dirtyClipAnimators.isEmpty();
174 if (hasFindRunningClipAnimatorsJob) {
175 qCDebug(HandlerLogic) << "Added FindRunningClipAnimatorsJob";
176 cleanupHandleList(animators: &m_dirtyClipAnimators);
177 m_findRunningClipAnimatorsJob->setDirtyClipAnimators(m_dirtyClipAnimators);
178 // Only set the dependency once
179 if (Q_UNLIKELY(m_findRunningClipAnimatorsJob->dependencies().empty()))
180 m_findRunningClipAnimatorsJob->addDependency(dependency: m_loadAnimationClipJob);
181 jobs.push_back(x: m_findRunningClipAnimatorsJob);
182 if (hasLoadAnimationClipJob)
183 m_dirtyClipAnimators.clear();
184 }
185
186 // Rebuild blending trees if a blend tree is dirty
187 const bool hasBuildBlendTreesJob = !m_dirtyBlendedAnimators.isEmpty();
188 if (hasBuildBlendTreesJob) {
189 const QVector<HBlendedClipAnimator> dirtyBlendedAnimators = Qt3DCore::moveAndClear(data&: m_dirtyBlendedAnimators);
190 m_buildBlendTreesJob->setBlendedClipAnimators(dirtyBlendedAnimators);
191 jobs.push_back(x: m_buildBlendTreesJob);
192 }
193
194 // TODO: Parallelise the animator evaluation and property building at a finer level
195
196 // If there are any running ClipAnimators, evaluate them for the current
197 // time and send property changes
198 cleanupHandleList(animators: &m_runningClipAnimators);
199 if (!m_runningClipAnimators.isEmpty()) {
200 qCDebug(HandlerLogic) << "Added EvaluateClipAnimatorJobs";
201
202 // Ensure we have a job per clip animator
203 const qsizetype oldSize = m_evaluateClipAnimatorJobs.size();
204 const qsizetype newSize = m_runningClipAnimators.size();
205 if (oldSize < newSize) {
206 m_evaluateClipAnimatorJobs.resize(size: newSize);
207 for (qsizetype i = oldSize; i < newSize; ++i) {
208 m_evaluateClipAnimatorJobs[i] = QSharedPointer<EvaluateClipAnimatorJob>::create();
209 m_evaluateClipAnimatorJobs[i]->setHandler(this);
210 }
211 }
212
213 // Set each job up with an animator to process and set dependencies
214 for (qsizetype i = 0; i < newSize; ++i) {
215 m_evaluateClipAnimatorJobs[i]->setClipAnimator(m_runningClipAnimators[i]);
216 Qt3DCore::QAspectJobPrivate::get(job: m_evaluateClipAnimatorJobs[i].data())->clearDependencies();
217 if (hasLoadAnimationClipJob)
218 m_evaluateClipAnimatorJobs[i]->addDependency(dependency: m_loadAnimationClipJob);
219 if (hasFindRunningClipAnimatorsJob)
220 m_evaluateClipAnimatorJobs[i]->addDependency(dependency: m_findRunningClipAnimatorsJob);
221 jobs.push_back(x: m_evaluateClipAnimatorJobs[i]);
222 }
223 }
224
225 // BlendClipAnimator execution
226 cleanupHandleList(animators: &m_runningBlendedClipAnimators);
227 if (!m_runningBlendedClipAnimators.isEmpty()) {
228 // Ensure we have a job per clip animator
229 const qsizetype oldSize = m_evaluateBlendClipAnimatorJobs.size();
230 const qsizetype newSize = m_runningBlendedClipAnimators.size();
231 if (oldSize < newSize) {
232 m_evaluateBlendClipAnimatorJobs.resize(size: newSize);
233 for (qsizetype i = oldSize; i < newSize; ++i) {
234 m_evaluateBlendClipAnimatorJobs[i] = QSharedPointer<EvaluateBlendClipAnimatorJob>::create();
235 m_evaluateBlendClipAnimatorJobs[i]->setHandler(this);
236 }
237 }
238
239 // Set each job up with an animator to process and set dependencies
240 for (qsizetype i = 0; i < newSize; ++i) {
241 m_evaluateBlendClipAnimatorJobs[i]->setBlendClipAnimator(m_runningBlendedClipAnimators[i]);
242 Qt3DCore::QAspectJobPrivate::get(job: m_evaluateBlendClipAnimatorJobs[i].data())->clearDependencies();
243 if (hasLoadAnimationClipJob)
244 m_evaluateBlendClipAnimatorJobs[i]->addDependency(dependency: m_loadAnimationClipJob);
245 if (hasBuildBlendTreesJob)
246 m_evaluateBlendClipAnimatorJobs[i]->addDependency(dependency: m_buildBlendTreesJob);
247 jobs.push_back(x: m_evaluateBlendClipAnimatorJobs[i]);
248 }
249 }
250
251 return jobs;
252}
253
254} // namespace Animation
255} // namespace Qt3DAnimation
256
257QT_END_NAMESPACE
258

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