1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qabstractaspect.h"
41#include "qabstractaspect_p.h"
42
43#include <QMetaObject>
44#include <QMetaProperty>
45
46#include <Qt3DCore/qcomponent.h>
47#include <Qt3DCore/qentity.h>
48#include <Qt3DCore/qpropertyupdatedchange.h>
49#include <Qt3DCore/qpropertyvalueaddedchange.h>
50#include <Qt3DCore/qpropertyvalueremovedchange.h>
51#include <Qt3DCore/qcomponentaddedchange.h>
52#include <Qt3DCore/qcomponentremovedchange.h>
53
54#include <Qt3DCore/private/corelogging_p.h>
55#include <Qt3DCore/private/qaspectjobmanager_p.h>
56#include <Qt3DCore/private/qaspectmanager_p.h>
57#include <Qt3DCore/private/qchangearbiter_p.h>
58#include <Qt3DCore/private/qnodevisitor_p.h>
59#include <Qt3DCore/private/qnode_p.h>
60#include <Qt3DCore/private/qscene_p.h>
61#include <Qt3DCore/private/qnode_p.h>
62
63QT_BEGIN_NAMESPACE
64
65namespace Qt3DCore {
66
67QAbstractAspectPrivate::QAbstractAspectPrivate()
68 : QObjectPrivate()
69 , m_root(nullptr)
70 , m_rootId()
71 , m_aspectManager(nullptr)
72 , m_jobManager(nullptr)
73 , m_arbiter(nullptr)
74{
75}
76
77QAbstractAspectPrivate::~QAbstractAspectPrivate()
78{
79}
80
81QAbstractAspectPrivate *QAbstractAspectPrivate::get(QAbstractAspect *aspect)
82{
83 return aspect->d_func();
84}
85
86/*!
87 * \internal
88 * Called in the context of the main thread
89 */
90void QAbstractAspectPrivate::onEngineAboutToShutdown()
91{
92}
93
94/*! \internal */
95void QAbstractAspectPrivate::unregisterBackendType(const QMetaObject &mo)
96{
97 m_backendCreatorFunctors.remove(akey: &mo);
98}
99
100/*!
101 * \class Qt3DCore::QAbstractAspect
102 * \inheaderfile Qt3DCore/QAbstractAspect
103 * \inherits QObject
104 * \inmodule Qt3DCore
105 * \brief QAbstractAspect is the base class for aspects that provide a vertical slice of behavior.
106 */
107
108/*!
109 * \fn void Qt3DCore::QAbstractAspect::registerBackendType(const Qt3DCore::QBackendNodeMapperPtr &functor)
110 * Registers backend with \a functor.
111 */
112
113/*!
114 * \macro QT3D_REGISTER_ASPECT(name, AspectType)
115 * \relates Qt3DCore::QAbstractAspect
116 *
117 * Convenience macro for registering \a AspectType for instantiation by the
118 * currently set Qt3DCore::QAspectFactory. This makes it possible to create an
119 * instance of \a AspectType in the aspect thread by later passing \a name to
120 * Qt3DCore::QAspectEngine::registerAspect(const QString &name).
121 *
122 * \note It is also possible to register a new aspect without using this macro
123 * by instead using Qt3DCore::QAspectEngine::registerAspect(QAbstractAspect *aspect)
124 * which will handle moving a previously created aspect instance to the aspect
125 * thread context.
126 *
127 * KDAB has published a few articles about writing custom Qt3D aspects
128 * \l {https://www.kdab.com/writing-custom-qt-3d-aspect/}{on their blog}. These
129 * provide an excellent starting point if you wish to learn more about it.
130 */
131
132
133/*!
134 * Constructs a new QAbstractAspect with \a parent
135 */
136QAbstractAspect::QAbstractAspect(QObject *parent)
137 : QAbstractAspect(*new QAbstractAspectPrivate, parent) {}
138
139/*!
140 * \typedef Qt3DCore::QAspectJobPtr
141 * \relates Qt3DCore::QAbstractAspect
142 *
143 * A shared pointer for QAspectJob.
144 */
145
146/*!
147 * \typedef Qt3DCore::QBackendNodeMapperPtr
148 * \relates Qt3DCore::QAbstractAspect
149 *
150 * A shared pointer for QBackendNodeMapper.
151 */
152
153/*!
154 * \internal
155 */
156QAbstractAspect::QAbstractAspect(QAbstractAspectPrivate &dd, QObject *parent)
157 : QObject(dd, parent)
158{
159}
160
161/*!
162 \internal
163*/
164QAbstractAspect::~QAbstractAspect()
165{
166}
167
168/*!
169 * \return root entity node id.
170 */
171QNodeId QAbstractAspect::rootEntityId() const Q_DECL_NOEXCEPT
172{
173 Q_D(const QAbstractAspect);
174 return d->m_rootId;
175}
176
177/*!
178 * Registers backend with \a obj and \a functor.
179 */
180void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor)
181{
182 Q_D(QAbstractAspect);
183 d->m_backendCreatorFunctors.insert(akey: &obj, avalue: {functor, QAbstractAspectPrivate::DefaultMapper});
184}
185
186void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor, bool supportsSyncing)
187{
188 Q_D(QAbstractAspect);
189 const auto f = supportsSyncing ? QAbstractAspectPrivate::SupportsSyncing : QAbstractAspectPrivate::DefaultMapper;
190 d->m_backendCreatorFunctors.insert(akey: &obj, avalue: {functor, f});
191}
192
193void QAbstractAspect::unregisterBackendType(const QMetaObject &obj)
194{
195 Q_D(QAbstractAspect);
196 d->m_backendCreatorFunctors.remove(akey: &obj);
197}
198
199QVariant QAbstractAspect::executeCommand(const QStringList &args)
200{
201 Q_UNUSED(args)
202 return QVariant();
203}
204
205QVector<QAspectJobPtr> QAbstractAspect::jobsToExecute(qint64 time)
206{
207 Q_UNUSED(time)
208 return QVector<QAspectJobPtr>();
209}
210
211QAbstractAspectPrivate::BackendNodeMapperAndInfo QAbstractAspectPrivate::mapperForNode(const QMetaObject *metaObj) const
212{
213 Q_ASSERT(metaObj);
214 BackendNodeMapperAndInfo info;
215
216 while (metaObj != nullptr && info.first.isNull()) {
217 info = m_backendCreatorFunctors.value(akey: metaObj);
218 metaObj = metaObj->superClass();
219 }
220 return info;
221}
222
223void QAbstractAspectPrivate::syncDirtyFrontEndNodes(const QVector<QNode *> &nodes)
224{
225 for (auto node: qAsConst(t: nodes)) {
226 const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo;
227 const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj);
228 const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first;
229
230 if (!backendNodeMapper)
231 continue;
232
233 QBackendNode *backend = backendNodeMapper->get(id: node->id());
234 if (!backend)
235 continue;
236
237 const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing;
238 if (supportsSyncing)
239 syncDirtyFrontEndNode(node, backend, firstTime: false);
240 else
241 sendPropertyMessages(node, backend);
242 }
243}
244
245void QAbstractAspectPrivate::syncDirtyFrontEndSubNodes(const QVector<NodeRelationshipChange> &nodes)
246{
247 for (const auto &nodeChange: qAsConst(t: nodes)) {
248 auto getBackend = [this](QNode *node) -> std::tuple<QBackendNode *, bool> {
249 const QMetaObject *metaObj = QNodePrivate::get(q: node)->m_typeInfo;
250 if (!metaObj)
251 return {};
252 const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj);
253 const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first;
254
255 if (!backendNodeMapper)
256 return {};
257
258 QBackendNode *backend = backendNodeMapper->get(id: node->id());
259 if (!backend)
260 return {};
261
262 const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing;
263
264 return std::tuple<QBackendNode *, bool>(backend, supportsSyncing);
265 };
266
267 auto nodeInfo = getBackend(nodeChange.node);
268 if (!std::get<0>(t&: nodeInfo))
269 continue;
270
271 auto subNodeInfo = getBackend(nodeChange.subNode);
272 if (!std::get<0>(t&: subNodeInfo))
273 continue;
274
275 switch (nodeChange.change) {
276 case PropertyValueAdded: {
277 if (std::get<1>(t&: nodeInfo))
278 break; // do nothing as the node will be dirty anyway
279
280 QPropertyValueAddedChange change(nodeChange.node->id());
281 change.setPropertyName(nodeChange.property);
282 change.setAddedValue(QVariant::fromValue(value: nodeChange.subNode->id()));
283 QPropertyValueAddedChangePtr pChange(&change, [](QPropertyValueAddedChange *) { });
284 std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange);
285 }
286 break;
287 case PropertyValueRemoved: {
288 if (std::get<1>(t&: nodeInfo))
289 break; // do nothing as the node will be dirty anyway
290
291 QPropertyValueRemovedChange change(nodeChange.node->id());
292 change.setPropertyName(nodeChange.property);
293 change.setRemovedValue(QVariant::fromValue(value: nodeChange.subNode->id()));
294 QPropertyValueRemovedChangePtr pChange(&change, [](QPropertyValueRemovedChange *) { });
295 std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange);
296 }
297 break;
298 case ComponentAdded: {
299 // let the entity know it has a new component
300 if (std::get<1>(t&: nodeInfo)) {
301 QBackendNodePrivate::get(n: std::get<0>(t&: nodeInfo))->componentAdded(frontend: nodeChange.subNode);
302 } else {
303 QComponentAddedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node));
304 QComponentAddedChangePtr pChange(&change, [](QComponentAddedChange *) { });
305 std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange);
306 }
307
308 // let the component know it was added to an entity
309 if (std::get<1>(t&: subNodeInfo)) {
310 QBackendNodePrivate::get(n: std::get<0>(t&: subNodeInfo))->addedToEntity(frontend: nodeChange.node);
311 } else {
312 QComponentAddedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node));
313 QComponentAddedChangePtr pChange(&change, [](QComponentAddedChange *) { });
314 std::get<0>(t&: subNodeInfo)->sceneChangeEvent(e: pChange);
315 }
316 }
317 break;
318 case ComponentRemoved: {
319 // let the entity know a component was removed
320 if (std::get<1>(t&: nodeInfo)) {
321 QBackendNodePrivate::get(n: std::get<0>(t&: nodeInfo))->componentRemoved(frontend: nodeChange.subNode);
322 } else {
323 QComponentRemovedChange change(qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode), qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node));
324 QComponentRemovedChangePtr pChange(&change, [](QComponentRemovedChange *) { });
325 std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange);
326 }
327
328 // let the component know it was removed from an entity
329 if (std::get<1>(t&: subNodeInfo)) {
330 QBackendNodePrivate::get(n: std::get<0>(t&: subNodeInfo))->removedFromEntity(frontend: nodeChange.node);
331 } else {
332 QComponentRemovedChange change(qobject_cast<Qt3DCore::QEntity *>(object: nodeChange.node), qobject_cast<Qt3DCore::QComponent *>(object: nodeChange.subNode));
333 QComponentRemovedChangePtr pChange(&change, [](QComponentRemovedChange *) { });
334 std::get<0>(t&: nodeInfo)->sceneChangeEvent(e: pChange);
335 }
336 }
337 break;
338 default:
339 break;
340 }
341 }
342}
343
344void QAbstractAspectPrivate::syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const
345{
346 Q_ASSERT(false); // overload in derived class
347 if (!firstTime)
348 sendPropertyMessages(node, backend);
349}
350
351void QAbstractAspectPrivate::sendPropertyMessages(QNode *node, QBackendNode *backend) const
352{
353 const int offset = QNode::staticMetaObject.propertyOffset();
354 const auto metaObj = node->metaObject();
355 const int count = metaObj->propertyCount();
356
357 const auto toBackendValue = [](const QVariant &data) -> QVariant
358 {
359 if (data.canConvert<QNode*>()) {
360 QNode *node = data.value<QNode*>();
361
362 // Ensure the node and all ancestors have issued their node creation changes.
363 // We can end up here if a newly created node with a parent is immediately set
364 // as a property on another node. In this case the deferred call to
365 // _q_postConstructorInit() will not have happened yet as the event
366 // loop will still be blocked. We need to do this for all ancestors,
367 // since the subtree of this node otherwise can end up on the backend
368 // with a reference to a non-existent parent.
369 if (node)
370 QNodePrivate::get(q: node)->_q_ensureBackendNodeCreated();
371
372 const QNodeId id = node ? node->id() : QNodeId();
373 return QVariant::fromValue(value: id);
374 }
375
376 return data;
377 };
378
379 QPropertyUpdatedChange change(node->id());
380 QPropertyUpdatedChangePtr pchange(&change, [](QPropertyUpdatedChange *) { });
381 for (int index = offset; index < count; index++) {
382 const QMetaProperty pro = metaObj->property(index);
383 change.setPropertyName(pro.name());
384 change.setValue(toBackendValue(pro.read(obj: node)));
385 backend->sceneChangeEvent(e: pchange);
386 }
387
388 auto const dynamicProperties = node->dynamicPropertyNames();
389 for (const QByteArray &name: dynamicProperties) {
390 change.setPropertyName(name.data());
391 change.setValue(toBackendValue(node->property(name: name.data())));
392 backend->sceneChangeEvent(e: pchange);
393 }
394}
395
396QBackendNode *QAbstractAspectPrivate::createBackendNode(const NodeTreeChange &change) const
397{
398 const QMetaObject *metaObj = change.metaObj;
399 const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj);
400 const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first;
401
402 if (!backendNodeMapper)
403 return nullptr;
404
405 QBackendNode *backend = backendNodeMapper->get(id: change.id);
406 if (backend != nullptr)
407 return backend;
408
409 QNode *node = change.node;
410 QNodeCreatedChangeBasePtr creationChange;
411 const bool supportsSyncing = backendNodeMapperInfo.second & SupportsSyncing;
412 if (supportsSyncing) {
413 // All objects modified to use syncing should only use the id in the creation functor
414 QNodeCreatedChangeBase changeObj(node);
415 creationChange = QNodeCreatedChangeBasePtr(&changeObj, [](QNodeCreatedChangeBase *) {});
416 backend = backendNodeMapper->create(change: creationChange);
417 } else {
418 creationChange = node->createNodeCreationChange();
419 backend = backendNodeMapper->create(change: creationChange);
420 }
421
422 if (!backend)
423 return nullptr;
424
425 // TODO: Find some place else to do all of this function from the arbiter
426 backend->setPeerId(change.id);
427
428 // Backend could be null if the user decides that his functor should only
429 // perform some action when encountering a given type of item but doesn't need to
430 // return a QBackendNode pointer.
431
432 QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(n: backend);
433 backendPriv->setEnabled(node->isEnabled());
434
435 // TO DO: Find a way to specify the changes to observe
436 // Register backendNode with QChangeArbiter
437 if (m_arbiter != nullptr) { // Unit tests may not have the arbiter registered
438 qCDebug(Nodes) << q_func()->objectName() << "Creating backend node for node id"
439 << node->id() << "of type" << QNodePrivate::get(q: node)->m_typeInfo->className();
440 m_arbiter->registerObserver(observer: backendPriv, nodeId: backend->peerId(), changeFlags: AllChanges);
441 if (backend->mode() == QBackendNode::ReadWrite)
442 m_arbiter->scene()->addObservable(observable: backendPriv, id: backend->peerId());
443 }
444
445 if (supportsSyncing)
446 syncDirtyFrontEndNode(node, backend, firstTime: true);
447 else
448 backend->initializeFromPeer(change: creationChange);
449
450 return backend;
451}
452
453void QAbstractAspectPrivate::clearBackendNode(const NodeTreeChange &change) const
454{
455 const QMetaObject *metaObj = change.metaObj;
456 const BackendNodeMapperAndInfo backendNodeMapperInfo = mapperForNode(metaObj);
457 const QBackendNodeMapperPtr backendNodeMapper = backendNodeMapperInfo.first;
458
459 if (!backendNodeMapper)
460 return;
461
462 // Request the mapper to destroy the corresponding backend node
463 QBackendNode *backend = backendNodeMapper->get(id: change.id);
464 if (backend) {
465 qCDebug(Nodes) << "Deleting backend node for node id"
466 << change.id << "of type" << metaObj->className();
467 QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(n: backend);
468 m_arbiter->unregisterObserver(observer: backendPriv, nodeId: backend->peerId());
469 if (backend->mode() == QBackendNode::ReadWrite)
470 m_arbiter->scene()->removeObservable(observable: backendPriv, id: backend->peerId());
471 backendNodeMapper->destroy(id: change.id);
472 }
473}
474
475void QAbstractAspectPrivate::setRootAndCreateNodes(QEntity *rootObject, const QVector<NodeTreeChange> &nodesChanges)
476{
477 qCDebug(Aspects) << Q_FUNC_INFO << "rootObject =" << rootObject;
478 if (rootObject == m_root)
479 return;
480
481 m_root = rootObject;
482 m_rootId = rootObject->id();
483
484 for (const NodeTreeChange &change : nodesChanges)
485 createBackendNode(change);
486}
487
488
489QServiceLocator *QAbstractAspectPrivate::services() const
490{
491 return m_aspectManager ? m_aspectManager->serviceLocator() : nullptr;
492}
493
494QAbstractAspectJobManager *QAbstractAspectPrivate::jobManager() const
495{
496 return m_jobManager;
497}
498
499QVector<QAspectJobPtr> QAbstractAspectPrivate::jobsToExecute(qint64 time)
500{
501 Q_Q(QAbstractAspect);
502 auto res = q->jobsToExecute(time);
503
504 {
505 QMutexLocker lock(&m_singleShotMutex);
506 res << m_singleShotJobs;
507 m_singleShotJobs.clear();
508 }
509
510 return res;
511}
512
513void QAbstractAspectPrivate::jobsDone()
514{
515}
516
517void QAbstractAspectPrivate::frameDone()
518{
519}
520
521/*!
522 * Called in the context of the aspect thread once the aspect has been registered.
523 * This provides an opportunity for the aspect to do any initialization tasks that
524 * require to be in the aspect thread context such as creating QObject subclasses that
525 * must have affinity with this thread.
526 *
527 * \sa onUnregistered
528 */
529void QAbstractAspect::onRegistered()
530{
531}
532
533/*!
534 * Called in the context of the aspect thread during unregistration
535 * of the aspect. This gives the aspect a chance to do any final pieces of
536 * cleanup that it would not do when just changing to a new scene.
537 *
538 * \sa onRegistered
539 */
540void QAbstractAspect::onUnregistered()
541{
542}
543
544/*!
545 *
546 * Called in the QAspectThread context
547 */
548void QAbstractAspect::onEngineStartup()
549{
550}
551
552/*!
553 *
554 * Called in the QAspectThread context
555 */
556void QAbstractAspect::onEngineShutdown()
557{
558}
559
560void QAbstractAspect::scheduleSingleShotJob(const Qt3DCore::QAspectJobPtr &job)
561{
562 Q_D(QAbstractAspect);
563 QMutexLocker lock(&d->m_singleShotMutex);
564 d->m_singleShotJobs.push_back(t: job);
565}
566
567namespace Debug {
568
569AsynchronousCommandReply::AsynchronousCommandReply(const QString &commandName, QObject *parent)
570 : QObject(parent)
571 , m_commandName(commandName)
572 , m_finished(false)
573{
574}
575
576void AsynchronousCommandReply::setFinished(bool replyFinished)
577{
578 m_finished = replyFinished;
579 if (m_finished)
580 emit finished(reply: this);
581}
582
583void AsynchronousCommandReply::setData(const QByteArray &data)
584{
585 m_data = data;
586}
587
588} // Debug
589
590
591} // of namespace Qt3DCore
592
593QT_END_NAMESPACE
594

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