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 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | namespace Qt3DAnimation { |
20 | namespace Animation { |
21 | |
22 | Handler::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 | |
41 | Handler::~Handler() |
42 | { |
43 | } |
44 | |
45 | void 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 | |
78 | // Called by the Jobs |
79 | void Handler::setClipAnimatorRunning(const HClipAnimator &handle, bool running) |
80 | { |
81 | QMutexLocker lock(&m_mutex); |
82 | |
83 | // Add clip to running set if not already present |
84 | if (running && !m_runningClipAnimators.contains(t: handle)) { |
85 | m_runningClipAnimators.push_back(t: handle); |
86 | ClipAnimator *clipAnimator = m_clipAnimatorManager->data(handle); |
87 | if (clipAnimator) |
88 | clipAnimator->setStartTime(m_simulationTime); |
89 | } |
90 | |
91 | // If being marked as not running, remove from set of running clips |
92 | if (!running) { |
93 | m_runningClipAnimators.removeAll(t: handle); |
94 | } |
95 | } |
96 | |
97 | // Called by the Jobs |
98 | void Handler::setBlendedClipAnimatorRunning(const HBlendedClipAnimator &handle, bool running) |
99 | { |
100 | QMutexLocker lock(&m_mutex); |
101 | |
102 | // Add clip to running set if not already present |
103 | if (running && !m_runningBlendedClipAnimators.contains(t: handle)) { |
104 | m_runningBlendedClipAnimators.push_back(t: handle); |
105 | BlendedClipAnimator *blendedClipAnimator = m_blendedClipAnimatorManager->data(handle); |
106 | if (blendedClipAnimator) |
107 | blendedClipAnimator->setStartTime(m_simulationTime); |
108 | } |
109 | |
110 | // If being marked as not running, remove from set of running clips |
111 | if (!running) { |
112 | const auto it = std::find_if(first: m_runningBlendedClipAnimators.begin(), |
113 | last: m_runningBlendedClipAnimators.end(), |
114 | pred: [handle](const HBlendedClipAnimator &h) { return h == handle; }); |
115 | if (it != m_runningBlendedClipAnimators.end()) |
116 | m_runningBlendedClipAnimators.erase(pos: it); |
117 | } |
118 | } |
119 | |
120 | // The vectors may get outdated when the application removes/deletes an |
121 | // animator component in the meantime. Recognize this. This should be |
122 | // relatively infrequent so in most cases the vectors will not change at all. |
123 | void Handler::cleanupHandleList(QVector<HAnimationClip> *clips) |
124 | { |
125 | for (auto it = clips->begin(); it != clips->end(); ) { |
126 | if (!m_animationClipLoaderManager->data(handle: *it)) |
127 | it = clips->erase(pos: it); |
128 | else |
129 | ++it; |
130 | } |
131 | } |
132 | |
133 | void Handler::cleanupHandleList(QVector<HClipAnimator> *animators) |
134 | { |
135 | for (auto it = animators->begin(); it != animators->end(); ) { |
136 | if (!m_clipAnimatorManager->data(handle: *it)) |
137 | it = animators->erase(pos: it); |
138 | else |
139 | ++it; |
140 | } |
141 | } |
142 | |
143 | void Handler::cleanupHandleList(QVector<HBlendedClipAnimator> *animators) |
144 | { |
145 | for (auto it = animators->begin(); it != animators->end(); ) { |
146 | if (!m_blendedClipAnimatorManager->data(handle: *it)) |
147 | it = animators->erase(pos: it); |
148 | else |
149 | ++it; |
150 | } |
151 | } |
152 | |
153 | std::vector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time) |
154 | { |
155 | // Store the simulation time so we can mark the start time of |
156 | // animators which will allow us to calculate the local time of |
157 | // animation clips. |
158 | m_simulationTime = time; |
159 | |
160 | std::vector<Qt3DCore::QAspectJobPtr> jobs; |
161 | |
162 | QMutexLocker lock(&m_mutex); |
163 | |
164 | // If there are any dirty animation clips that need loading, |
165 | // queue up a job for them |
166 | const bool hasLoadAnimationClipJob = !m_dirtyAnimationClips.isEmpty(); |
167 | if (hasLoadAnimationClipJob) { |
168 | qCDebug(HandlerLogic) << "Added LoadAnimationClipJob" ; |
169 | cleanupHandleList(clips: &m_dirtyAnimationClips); |
170 | m_loadAnimationClipJob->addDirtyAnimationClips(animationClipHandles: m_dirtyAnimationClips); |
171 | jobs.push_back(x: m_loadAnimationClipJob); |
172 | m_dirtyAnimationClips.clear(); |
173 | } |
174 | |
175 | // If there are dirty clip animators, find the set that are able to |
176 | // run. I.e. are marked as running and have animation clips and |
177 | // channel mappings |
178 | |
179 | const bool hasFindRunningClipAnimatorsJob = !m_dirtyClipAnimators.isEmpty(); |
180 | if (hasFindRunningClipAnimatorsJob) { |
181 | qCDebug(HandlerLogic) << "Added FindRunningClipAnimatorsJob" ; |
182 | cleanupHandleList(animators: &m_dirtyClipAnimators); |
183 | m_findRunningClipAnimatorsJob->setDirtyClipAnimators(m_dirtyClipAnimators); |
184 | // Only set the dependency once |
185 | if (Q_UNLIKELY(m_findRunningClipAnimatorsJob->dependencies().empty())) |
186 | m_findRunningClipAnimatorsJob->addDependency(dependency: m_loadAnimationClipJob); |
187 | jobs.push_back(x: m_findRunningClipAnimatorsJob); |
188 | if (hasLoadAnimationClipJob) |
189 | m_dirtyClipAnimators.clear(); |
190 | } |
191 | |
192 | // Rebuild blending trees if a blend tree is dirty |
193 | const bool hasBuildBlendTreesJob = !m_dirtyBlendedAnimators.isEmpty(); |
194 | if (hasBuildBlendTreesJob) { |
195 | const QVector<HBlendedClipAnimator> dirtyBlendedAnimators = Qt3DCore::moveAndClear(data&: m_dirtyBlendedAnimators); |
196 | m_buildBlendTreesJob->setBlendedClipAnimators(dirtyBlendedAnimators); |
197 | jobs.push_back(x: m_buildBlendTreesJob); |
198 | } |
199 | |
200 | // TODO: Parallelise the animator evaluation and property building at a finer level |
201 | |
202 | // If there are any running ClipAnimators, evaluate them for the current |
203 | // time and send property changes |
204 | cleanupHandleList(animators: &m_runningClipAnimators); |
205 | if (!m_runningClipAnimators.isEmpty()) { |
206 | qCDebug(HandlerLogic) << "Added EvaluateClipAnimatorJobs" ; |
207 | |
208 | // Ensure we have a job per clip animator |
209 | const qsizetype oldSize = m_evaluateClipAnimatorJobs.size(); |
210 | const qsizetype newSize = m_runningClipAnimators.size(); |
211 | if (oldSize < newSize) { |
212 | m_evaluateClipAnimatorJobs.resize(size: newSize); |
213 | for (qsizetype i = oldSize; i < newSize; ++i) { |
214 | m_evaluateClipAnimatorJobs[i] = QSharedPointer<EvaluateClipAnimatorJob>::create(); |
215 | m_evaluateClipAnimatorJobs[i]->setHandler(this); |
216 | } |
217 | } |
218 | |
219 | // Set each job up with an animator to process and set dependencies |
220 | for (qsizetype i = 0; i < newSize; ++i) { |
221 | m_evaluateClipAnimatorJobs[i]->setClipAnimator(m_runningClipAnimators[i]); |
222 | Qt3DCore::QAspectJobPrivate::get(job: m_evaluateClipAnimatorJobs[i].data())->clearDependencies(); |
223 | if (hasLoadAnimationClipJob) |
224 | m_evaluateClipAnimatorJobs[i]->addDependency(dependency: m_loadAnimationClipJob); |
225 | if (hasFindRunningClipAnimatorsJob) |
226 | m_evaluateClipAnimatorJobs[i]->addDependency(dependency: m_findRunningClipAnimatorsJob); |
227 | jobs.push_back(x: m_evaluateClipAnimatorJobs[i]); |
228 | } |
229 | } |
230 | |
231 | // BlendClipAnimator execution |
232 | cleanupHandleList(animators: &m_runningBlendedClipAnimators); |
233 | if (!m_runningBlendedClipAnimators.isEmpty()) { |
234 | // Ensure we have a job per clip animator |
235 | const qsizetype oldSize = m_evaluateBlendClipAnimatorJobs.size(); |
236 | const qsizetype newSize = m_runningBlendedClipAnimators.size(); |
237 | if (oldSize < newSize) { |
238 | m_evaluateBlendClipAnimatorJobs.resize(size: newSize); |
239 | for (qsizetype i = oldSize; i < newSize; ++i) { |
240 | m_evaluateBlendClipAnimatorJobs[i] = QSharedPointer<EvaluateBlendClipAnimatorJob>::create(); |
241 | m_evaluateBlendClipAnimatorJobs[i]->setHandler(this); |
242 | } |
243 | } |
244 | |
245 | // Set each job up with an animator to process and set dependencies |
246 | for (qsizetype i = 0; i < newSize; ++i) { |
247 | m_evaluateBlendClipAnimatorJobs[i]->setBlendClipAnimator(m_runningBlendedClipAnimators[i]); |
248 | Qt3DCore::QAspectJobPrivate::get(job: m_evaluateBlendClipAnimatorJobs[i].data())->clearDependencies(); |
249 | if (hasLoadAnimationClipJob) |
250 | m_evaluateBlendClipAnimatorJobs[i]->addDependency(dependency: m_loadAnimationClipJob); |
251 | if (hasBuildBlendTreesJob) |
252 | m_evaluateBlendClipAnimatorJobs[i]->addDependency(dependency: m_buildBlendTreesJob); |
253 | jobs.push_back(x: m_evaluateBlendClipAnimatorJobs[i]); |
254 | } |
255 | } |
256 | |
257 | return jobs; |
258 | } |
259 | |
260 | } // namespace Animation |
261 | } // namespace Qt3DAnimation |
262 | |
263 | QT_END_NAMESPACE |
264 | |