1// Copyright (C) 2014 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 "qabstractaspect.h"
5#include "qabstractaspect_p.h"
6
7#include <QMetaObject>
8#include <QMetaProperty>
9
10#include <Qt3DCore/qcomponent.h>
11#include <Qt3DCore/qentity.h>
12
13#include <Qt3DCore/private/corelogging_p.h>
14#include <Qt3DCore/private/qaspectjobmanager_p.h>
15#include <Qt3DCore/private/qaspectmanager_p.h>
16#include <Qt3DCore/private/qnodevisitor_p.h>
17#include <Qt3DCore/private/qnode_p.h>
18#include <Qt3DCore/private/qscene_p.h>
19#include <Qt3DCore/private/qnode_p.h>
20#include <Qt3DCore/private/vector_helper_p.h>
21
22QT_BEGIN_NAMESPACE
23
24namespace Qt3DCore {
25
26QAbstractAspectPrivate::QAbstractAspectPrivate()
27 : QObjectPrivate()
28 , m_root(nullptr)
29 , m_rootId()
30 , m_aspectManager(nullptr)
31 , m_jobManager(nullptr)
32 , m_arbiter(nullptr)
33{
34}
35
36QAbstractAspectPrivate::~QAbstractAspectPrivate()
37{
38}
39
40QAbstractAspectPrivate *QAbstractAspectPrivate::get(QAbstractAspect *aspect)
41{
42 return aspect->d_func();
43}
44
45/*!
46 * \internal
47 * Called in the context of the main thread
48 */
49void QAbstractAspectPrivate::onEngineAboutToShutdown()
50{
51}
52
53/*! \internal */
54void QAbstractAspectPrivate::unregisterBackendType(const QMetaObject &mo)
55{
56 m_backendCreatorFunctors.remove(key: &mo);
57}
58
59/*!
60 * \class Qt3DCore::QAbstractAspect
61 * \inheaderfile Qt3DCore/QAbstractAspect
62 * \inherits QObject
63 * \inmodule Qt3DCore
64 * \brief QAbstractAspect is the base class for aspects that provide a vertical slice of behavior.
65 */
66
67/*!
68 * \fn void Qt3DCore::QAbstractAspect::registerBackendType(const Qt3DCore::QBackendNodeMapperPtr &functor)
69 * Registers backend with \a functor.
70 */
71
72/*!
73 * \macro QT3D_REGISTER_ASPECT(name, AspectType)
74 * \relates Qt3DCore::QAbstractAspect
75 *
76 * Convenience macro for registering \a AspectType for instantiation by the
77 * currently set Qt3DCore::QAspectFactory. This makes it possible to create an
78 * instance of \a AspectType in the aspect thread by later passing \a name to
79 * Qt3DCore::QAspectEngine::registerAspect(const QString &name).
80 *
81 * \note It is also possible to register a new aspect without using this macro
82 * by instead using Qt3DCore::QAspectEngine::registerAspect(QAbstractAspect *aspect)
83 * which will handle moving a previously created aspect instance to the aspect
84 * thread context.
85 *
86 * KDAB has published a few articles about writing custom Qt3D aspects
87 * \l {https://www.kdab.com/writing-custom-qt-3d-aspect/}{on their blog}. These
88 * provide an excellent starting point if you wish to learn more about it.
89 */
90
91
92/*!
93 * Constructs a new QAbstractAspect with \a parent
94 */
95QAbstractAspect::QAbstractAspect(QObject *parent)
96 : QAbstractAspect(*new QAbstractAspectPrivate, parent) {}
97
98/*!
99 * \typedef Qt3DCore::QAspectJobPtr
100 * \relates Qt3DCore::QAbstractAspect
101 *
102 * A shared pointer for QAspectJob.
103 */
104
105/*!
106 * \typedef Qt3DCore::QBackendNodeMapperPtr
107 * \relates Qt3DCore::QAbstractAspect
108 *
109 * A shared pointer for QBackendNodeMapper.
110 */
111
112/*!
113 * \internal
114 */
115QAbstractAspect::QAbstractAspect(QAbstractAspectPrivate &dd, QObject *parent)
116 : QObject(dd, parent)
117{
118}
119
120/*!
121 \internal
122*/
123QAbstractAspect::~QAbstractAspect()
124{
125}
126
127/*!
128 * \return root entity node id.
129 */
130QNodeId QAbstractAspect::rootEntityId() const noexcept
131{
132 Q_D(const QAbstractAspect);
133 return d->m_rootId;
134}
135
136/*!
137 * Registers backend with \a obj and \a functor.
138 */
139void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor)
140{
141 Q_D(QAbstractAspect);
142 d->m_backendCreatorFunctors.insert(key: &obj, value: functor);
143}
144
145void QAbstractAspect::unregisterBackendType(const QMetaObject &obj)
146{
147 Q_D(QAbstractAspect);
148 d->m_backendCreatorFunctors.remove(key: &obj);
149}
150
151QVariant QAbstractAspect::executeCommand(const QStringList &args)
152{
153 Q_UNUSED(args);
154 return QVariant();
155}
156
157std::vector<QAspectJobPtr> QAbstractAspect::jobsToExecute(qint64 time)
158{
159 Q_UNUSED(time);
160 return {};
161}
162
163QBackendNodeMapperPtr QAbstractAspectPrivate::mapperForNode(const QMetaObject *metaObj) const
164{
165 Q_ASSERT(metaObj);
166 QBackendNodeMapperPtr mapper;
167
168 while (metaObj != nullptr && mapper.isNull()) {
169 mapper = m_backendCreatorFunctors.value(key: metaObj);
170 metaObj = metaObj->superClass();
171 }
172 return mapper;
173}
174
175void QAbstractAspectPrivate::syncDirtyFrontEndNodes(const QList<QNode *> &nodes)
176{
177 for (auto node: std::as_const(t: nodes)) {
178 const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo;
179 const QBackendNodeMapperPtr backendNodeMapper = mapperForNode(metaObj);
180
181 if (!backendNodeMapper)
182 continue;
183
184 QBackendNode *backend = backendNodeMapper->get(id: node->id());
185 if (!backend)
186 continue;
187
188 syncDirtyFrontEndNode(node, backend, firstTime: false);
189 }
190}
191
192void QAbstractAspectPrivate::syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const
193{
194 backend->syncFromFrontEnd(frontEnd: node, firstTime);
195}
196
197void QAbstractAspectPrivate::syncDirtyEntityComponentNodes(const QList<ComponentRelationshipChange> &changes)
198{
199 auto getBackend = [this] (QNode *node) -> QBackendNode* {
200 const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo;
201 const QBackendNodeMapperPtr backendNodeMapper = mapperForNode(metaObj);
202
203 if (!backendNodeMapper)
204 return nullptr;
205
206 return backendNodeMapper->get(id: node->id());
207 };
208
209 for (const auto &change: std::as_const(t: changes)) {
210 auto entityBackend = getBackend(change.node);
211 if (!entityBackend)
212 continue;
213
214 auto componentBackend = getBackend(change.subNode);
215 if (!componentBackend)
216 continue;
217
218 switch (change.change) {
219 case ComponentRelationshipChange::Added:
220 QBackendNodePrivate::get(n: entityBackend)->componentAdded(frontend: change.subNode);
221 QBackendNodePrivate::get(n: componentBackend)->addedToEntity(frontend: change.node);
222 break;
223 case ComponentRelationshipChange::Removed:
224 QBackendNodePrivate::get(n: entityBackend)->componentRemoved(frontend: change.subNode);
225 QBackendNodePrivate::get(n: componentBackend)->removedFromEntity(frontend: change.node);
226 break;
227 }
228 }
229}
230
231QBackendNode *QAbstractAspectPrivate::createBackendNode(const NodeTreeChange &change) const
232{
233 const QMetaObject *metaObj = change.metaObj;
234 const QBackendNodeMapperPtr backendNodeMapper = mapperForNode(metaObj);
235
236 if (!backendNodeMapper)
237 return nullptr;
238
239 QBackendNode *backend = backendNodeMapper->get(id: change.id);
240 if (backend != nullptr)
241 return backend;
242
243 QNode *node = change.node;
244 QNodeId nodeId = qIdForNode(node);
245 backend = backendNodeMapper->create(id: nodeId);
246
247 if (!backend)
248 return nullptr;
249
250 // TODO: Find some place else to do all of this function from the arbiter
251 backend->setPeerId(nodeId);
252
253 // Backend could be null if the user decides that his functor should only
254 // perform some action when encountering a given type of item but doesn't need to
255 // return a QBackendNode pointer.
256
257 QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(n: backend);
258 backendPriv->setEnabled(node->isEnabled());
259
260 syncDirtyFrontEndNode(node, backend, firstTime: true);
261
262 return backend;
263}
264
265void QAbstractAspectPrivate::clearBackendNode(const NodeTreeChange &change) const
266{
267 const QMetaObject *metaObj = change.metaObj;
268 const QBackendNodeMapperPtr backendNodeMapper = mapperForNode(metaObj);
269
270 if (!backendNodeMapper)
271 return;
272
273 backendNodeMapper->destroy(id: change.id);
274}
275
276void QAbstractAspectPrivate::setRootAndCreateNodes(QEntity *rootObject, const QList<NodeTreeChange> &nodesChanges)
277{
278 qCDebug(Aspects) << Q_FUNC_INFO << "rootObject =" << rootObject;
279 if (rootObject == m_root)
280 return;
281
282 m_root = rootObject;
283 m_rootId = rootObject->id();
284
285 for (const NodeTreeChange &change : nodesChanges)
286 createBackendNode(change);
287}
288
289
290QServiceLocator *QAbstractAspectPrivate::services() const
291{
292 return m_aspectManager ? m_aspectManager->serviceLocator() : nullptr;
293}
294
295QAbstractAspectJobManager *QAbstractAspectPrivate::jobManager() const
296{
297 return m_jobManager;
298}
299
300std::vector<QAspectJobPtr> QAbstractAspectPrivate::jobsToExecute(qint64 time)
301{
302 Q_Q(QAbstractAspect);
303 auto res = q->jobsToExecute(time);
304
305 {
306 QMutexLocker lock(&m_singleShotMutex);
307 Qt3DCore::moveAtEnd(destination&: res, source: std::move(m_singleShotJobs));
308 m_singleShotJobs.clear();
309 }
310
311 return res;
312}
313
314void QAbstractAspectPrivate::jobsDone()
315{
316}
317
318void QAbstractAspectPrivate::frameDone()
319{
320}
321
322/*!
323 * Called in the context of the aspect thread once the aspect has been registered.
324 * This provides an opportunity for the aspect to do any initialization tasks that
325 * require to be in the aspect thread context such as creating QObject subclasses that
326 * must have affinity with this thread.
327 *
328 * \sa onUnregistered
329 */
330void QAbstractAspect::onRegistered()
331{
332}
333
334/*!
335 * Called in the context of the aspect thread during unregistration
336 * of the aspect. This gives the aspect a chance to do any final pieces of
337 * cleanup that it would not do when just changing to a new scene.
338 *
339 * \sa onRegistered
340 */
341void QAbstractAspect::onUnregistered()
342{
343}
344
345/*!
346 *
347 * Called in the QAspectThread context
348 */
349void QAbstractAspect::onEngineStartup()
350{
351}
352
353/*!
354 *
355 * Called in the QAspectThread context
356 */
357void QAbstractAspect::onEngineShutdown()
358{
359}
360
361/*!
362 * Called in the main thread once all the jobs have been executed.
363 *
364 * \note This is called after QAspectJob::postFrame is called on every jobs.
365 *
366 * \sa QAspectJob::postFrame
367 */
368void QAbstractAspect::jobsDone()
369{
370 Q_D(QAbstractAspect);
371 d->jobsDone();
372}
373
374/*!
375 * Called in the main thread when the frame processing is complete.
376 */
377void QAbstractAspect::frameDone()
378{
379 Q_D(QAbstractAspect);
380 d->frameDone();
381}
382
383void QAbstractAspect::scheduleSingleShotJob(const Qt3DCore::QAspectJobPtr &job)
384{
385 Q_D(QAbstractAspect);
386 QMutexLocker lock(&d->m_singleShotMutex);
387 d->m_singleShotJobs.push_back(x: job);
388}
389
390QStringList QAbstractAspect::dependencies() const
391{
392 return {};
393}
394
395namespace Debug {
396
397AsynchronousCommandReply::AsynchronousCommandReply(const QString &commandName, QObject *parent)
398 : QObject(parent)
399 , m_commandName(commandName)
400 , m_finished(false)
401{
402}
403
404void AsynchronousCommandReply::setFinished(bool replyFinished)
405{
406 m_finished = replyFinished;
407 if (m_finished)
408 emit finished(reply: this);
409}
410
411void AsynchronousCommandReply::setData(const QByteArray &data)
412{
413 m_data = data;
414}
415
416} // Debug
417
418
419} // of namespace Qt3DCore
420
421QT_END_NAMESPACE
422
423#include "moc_qabstractaspect_p.cpp"
424
425#include "moc_qabstractaspect.cpp"
426

source code of qt3d/src/core/aspects/qabstractaspect.cpp