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 | void 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 | |
94 | void 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. |
117 | void 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 | |
127 | void 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 | |
137 | void 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 | |
147 | std::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 | |
257 | QT_END_NAMESPACE |
258 | |