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 "qnode.h"
41#include "qnode_p.h"
42#include "qscene_p.h"
43
44#include <Qt3DCore/QComponent>
45#include <Qt3DCore/qaspectengine.h>
46#include <Qt3DCore/qdynamicpropertyupdatedchange.h>
47#include <Qt3DCore/qentity.h>
48#include <Qt3DCore/qnodedestroyedchange.h>
49#include <Qt3DCore/qpropertynodeaddedchange.h>
50#include <Qt3DCore/qpropertynoderemovedchange.h>
51#include <Qt3DCore/qpropertyupdatedchange.h>
52#include <QtCore/QChildEvent>
53#include <QtCore/QEvent>
54#include <QtCore/QMetaObject>
55#include <QtCore/QMetaProperty>
56
57#include <Qt3DCore/private/corelogging_p.h>
58#include <Qt3DCore/private/qdestructionidandtypecollector_p.h>
59#include <Qt3DCore/private/qnodevisitor_p.h>
60#include <Qt3DCore/private/qpostman_p.h>
61#include <Qt3DCore/private/qscene_p.h>
62#include <Qt3DCore/private/qaspectengine_p.h>
63#include <Qt3DCore/private/qaspectmanager_p.h>
64#include <QtCore/private/qmetaobject_p.h>
65
66QT_BEGIN_NAMESPACE
67
68namespace Qt3DCore {
69
70QNodePrivate::QNodePrivate()
71 : QObjectPrivate()
72 , m_changeArbiter(nullptr)
73 , m_typeInfo(nullptr)
74 , m_scene(nullptr)
75 , m_id(QNodeId::createId())
76 , m_blockNotifications(false)
77 , m_hasBackendNode(false)
78 , m_enabled(true)
79 , m_notifiedParent(false)
80 , m_defaultPropertyTrackMode(QNode::TrackFinalValues)
81 , m_propertyChangesSetup(false)
82 , m_signals(this)
83{
84}
85
86QNodePrivate::~QNodePrivate()
87{
88}
89
90void QNodePrivate::init(QNode *parent)
91{
92 if (!parent)
93 return;
94
95 // If we have a QNode parent that has a scene (and hence change arbiter),
96 // copy these to this QNode. If valid, then also notify the backend
97 // in a deferred way when the object is fully constructed. This is delayed
98 // until the object is fully constructed as it involves calling a virtual
99 // function of QNode.
100 m_parentId = parent->id();
101 const auto parentPrivate = get(q: parent);
102 m_scene = parentPrivate->m_scene;
103 Q_Q(QNode);
104 if (m_scene) {
105 // schedule the backend notification and scene registering -> set observers through scene
106 m_scene->postConstructorInit()->addNode(node: q);
107 }
108}
109
110/*!
111 * \internal
112 *
113 * Sends QNodeCreatedChange events to the aspects.
114 */
115void QNodePrivate::createBackendNode()
116{
117 // Do nothing if we already have already sent a node creation change
118 // and not a subsequent node destroyed change.
119 if (m_hasBackendNode || !m_scene || !m_scene->engine())
120 return;
121
122 Q_Q(QNode);
123 QAspectEnginePrivate::get(engine: m_scene->engine())->addNode(node: q);
124}
125
126/*!
127 * \internal
128 *
129 * Notify the backend that the parent lost this node as a child and
130 * that this node is being destroyed. We only send the node removed
131 * change for the parent's children property iff we have an id for
132 * a parent node. This is set/unset in the _q_addChild()/_q_removeChild()
133 * functions (and initialized in init() if there is a parent at
134 * construction time).
135 *
136 * Likewise, we only send the node destroyed change, iff we have
137 * previously sent a node created change. This is tracked via the
138 * m_hasBackendNode member.
139 */
140void QNodePrivate::notifyDestructionChangesAndRemoveFromScene()
141{
142 Q_Q(QNode);
143
144 // Ensure this node is not queued up for post-construction init
145 // to avoid crashing when the event loop spins.
146 if (m_scene && m_scene->postConstructorInit())
147 m_scene->postConstructorInit()->removeNode(node: q);
148
149 // We notify the backend that the parent lost us as a child
150 if (m_changeArbiter != nullptr && !m_parentId.isNull()) {
151 const auto change = QPropertyNodeRemovedChangePtr::create(arguments&: m_parentId, arguments: q);
152 change->setPropertyName("children");
153 notifyObservers(change);
154 }
155
156 // Tell the backend we are about to be destroyed
157 if (m_hasBackendNode && m_scene && m_scene->engine())
158 QAspectEnginePrivate::get(engine: m_scene->engine())->removeNode(node: q);
159
160 // We unset the scene from the node as its backend node was/is about to be destroyed
161 QNodeVisitor visitor;
162 visitor.traverse(rootNode_: q, instance: this, fN: &QNodePrivate::unsetSceneHelper);
163}
164
165/*!
166 * \internal
167 *
168 * Sends a QNodeCreatedChange event to the aspects and then also notifies the
169 * parent backend node of its new child. This is called in a deferred manner
170 * by NodePostConstructorInit::processNodes to notify the backend of newly created
171 * nodes with a parent that is already part of the scene.
172 *
173 * Also notify the scene of this node, so it may set it's change arbiter.
174 */
175void QNodePrivate::_q_postConstructorInit()
176{
177 Q_Q(QNode);
178
179 // If we've already done the work then bail out. This can happen if the
180 // user creates a QNode subclass with an explicit parent, then immediately
181 // sets the new QNode as a property on another node. In this case, the
182 // property setter will call this function directly, but as we can't
183 // un-schedule a deferred invocation, this function will be called again
184 // the next time the event loop spins. So, catch this case and abort.
185 if (m_hasBackendNode)
186 return;
187
188 // Check that the parent hasn't been unset since this call was enqueued
189 auto parentNode = q->parentNode();
190 if (!parentNode)
191 return;
192
193 // Set the scene on this node and all children it references so that all
194 // children have a scene set since createBackendNode will set
195 // m_hasBackendNode to true for all children, which would prevent them from
196 // ever having their scene set
197 if (m_scene) {
198 QNodeVisitor visitor;
199 visitor.traverse(rootNode_: q, instance: parentNode->d_func(), fN: &QNodePrivate::setSceneHelper);
200 }
201
202 // Let the backend know we have been added to the scene
203 createBackendNode();
204
205 // Let the backend parent know that they have a new child
206 Q_ASSERT(parentNode);
207 QNodePrivate::get(q: parentNode)->_q_addChild(childNode: q);
208}
209
210/*!
211 * \internal
212 *
213 * Called by _q_setParentHelper() or _q_postConstructorInit()
214 * on the main thread.
215 */
216void QNodePrivate::_q_addChild(QNode *childNode)
217{
218 Q_ASSERT(childNode);
219 Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node");
220
221 // Have we already notified the parent about its new child? If so, bail out
222 // early so that we do not send more than one new child event to the backend
223 QNodePrivate *childD = QNodePrivate::get(q: childNode);
224 if (childD->m_notifiedParent == true)
225 return;
226
227 // Store our id as the parentId in the child so that even if the child gets
228 // removed from the scene as part of the destruction of the parent, when the
229 // parent's children are deleted in the QObject dtor, we still have access to
230 // the parentId. If we didn't store this, we wouldn't have access at that time
231 // because the parent would then only be a QObject, the QNode part would have
232 // been destroyed already.
233 childD->m_parentId = m_id;
234
235 if (!m_scene)
236 return;
237
238 // We need to send a QPropertyNodeAddedChange to the backend
239 // to notify the backend that we have a new child
240 if (m_changeArbiter != nullptr) {
241 // Flag that we have notified the parent. We do this immediately before
242 // creating the change because that recurses back into this function and
243 // we need to catch that to avoid sending more than one new child event
244 // to the backend.
245 childD->m_notifiedParent = true;
246 const auto change = QPropertyNodeAddedChangePtr::create(arguments&: m_id, arguments&: childNode);
247 change->setPropertyName("children");
248 notifyObservers(change);
249 }
250
251 // Update the scene
252 // TODO: Fold this into the QAspectEnginePrivate::addNode so we don't have to
253 // traverse the sub tree three times!
254 QNodeVisitor visitor;
255 visitor.traverse(rootNode_: childNode, instance: this, fN: &QNodePrivate::addEntityComponentToScene);
256}
257
258/*!
259 * \internal
260 *
261 * Called by _q_setParentHelper on the main thread.
262 */
263void QNodePrivate::_q_removeChild(QNode *childNode)
264{
265 Q_ASSERT(childNode);
266 Q_ASSERT_X(childNode->parent() == q_func(), Q_FUNC_INFO, "not a child of this node");
267
268 QNodePrivate::get(q: childNode)->m_parentId = QNodeId();
269
270 // We notify the backend that we lost a child
271 if (m_changeArbiter != nullptr) {
272 const auto change = QPropertyNodeRemovedChangePtr::create(arguments&: m_id, arguments&: childNode);
273 change->setPropertyName("children");
274 notifyObservers(change);
275 }
276}
277
278/*!
279 * \internal
280 *
281 * Reparents the public QNode to \a parent. If the new parent is nullptr then this
282 * QNode is no longer part of the scene and so we notify the backend of its removal
283 * from its parent's list of children, and then send a QNodeDestroyedChange to the
284 * aspects so that the corresponding backend node is destroyed.
285 *
286 * If \a parent is not null, then we must tell its new parent about this QNode now
287 * being a child of it on the backend. If this QNode did not have a parent upon
288 * entry to this function, then we must first send a QNodeCreatedChange to the backend
289 * prior to sending the QPropertyNodeAddedChange to its parent.
290 *
291 * Note: This function should never be called from the ctor directly as the type may
292 * not be fully created yet and creating creation changes involves calling a virtual
293 * function on QNode. The function _q_notifyCreationAndChildChanges() is used
294 * for sending initial notification when a parent is passed to the QNode ctor.
295 * That function does a subset of this function with the assumption that the new object
296 * had no parent before (must be true as it is newly constructed).
297 */
298void QNodePrivate::_q_setParentHelper(QNode *parent)
299{
300 Q_Q(QNode);
301 QNode *oldParentNode = q->parentNode();
302
303 // If we had a parent, we let him know that we are about to change
304 // parent
305 if (oldParentNode && m_hasBackendNode) {
306 QNodePrivate::get(q: oldParentNode)->_q_removeChild(childNode: q);
307
308 // If we have an old parent but the new parent is null or if the new
309 // parent hasn't yet been added to the backend the backend node needs
310 // to be destroyed
311 // e.g:
312 // QEntity *child = new QEntity(some_parent);
313 // After some time, in a later event loop
314 // QEntity *newSubTreeRoot = new QEntity(someGlobalExisitingRoot)
315 // child->setParent(newSubTreeRoot)
316 if (!parent || !QNodePrivate::get(q: parent)->m_hasBackendNode)
317 notifyDestructionChangesAndRemoveFromScene();
318 }
319
320 // Flag that we need to notify any new parent
321 m_notifiedParent = false;
322
323 // Basically QObject::setParent but for QObjectPrivate
324 QObjectPrivate::setParent_helper(parent);
325
326 if (parent) {
327 // If we had no parent but are about to set one,
328 // we need to send a QNodeCreatedChange
329 QNodePrivate *newParentPrivate = QNodePrivate::get(q: parent);
330
331 // Set the scene helper / arbiter
332 if (newParentPrivate->m_scene) {
333 QNodeVisitor visitor;
334 visitor.traverse(rootNode_: q, instance: parent->d_func(), fN: &QNodePrivate::setSceneHelper);
335 }
336
337 // We want to make sure that subTreeRoot is always created before
338 // child.
339 // Given a case such as below
340 // QEntity *subTreeRoot = new QEntity(someGlobalExisitingRoot)
341 // QEntity *child = new QEntity();
342 // child->setParent(subTreeRoot)
343 // We need to take into account that subTreeRoot needs to be
344 // created in the backend before the child.
345 // Therefore we only call createBackendNode if the parent
346 // hasn't been created yet as we know that when the parent will be
347 // fully created, it will also send the changes for all of its
348 // children
349
350 if (newParentPrivate->m_hasBackendNode)
351 createBackendNode();
352
353 // If we have a valid new parent, we let him know that we are its child
354 QNodePrivate::get(q: parent)->_q_addChild(childNode: q);
355 }
356}
357
358void QNodePrivate::registerNotifiedProperties()
359{
360 Q_Q(QNode);
361 if (m_propertyChangesSetup)
362 return;
363
364 const int offset = QNode::staticMetaObject.propertyOffset();
365 const int count = q->metaObject()->propertyCount();
366
367 for (int index = offset; index < count; index++)
368 m_signals.connectToPropertyChange(object: q, propertyIndex: index);
369
370 m_propertyChangesSetup = true;
371}
372
373void QNodePrivate::unregisterNotifiedProperties()
374{
375 Q_Q(QNode);
376 if (!m_propertyChangesSetup)
377 return;
378
379 const int offset = QNode::staticMetaObject.propertyOffset();
380 const int count = q->metaObject()->propertyCount();
381
382 for (int index = offset; index < count; index++)
383 m_signals.disconnectFromPropertyChange(object: q, propertyIndex: index);
384
385 m_propertyChangesSetup = false;
386}
387
388void QNodePrivate::propertyChanged(int propertyIndex)
389{
390 Q_UNUSED(propertyIndex)
391
392 // Bail out early if we can to avoid the cost below
393 if (m_blockNotifications)
394 return;
395
396 update();
397}
398
399/*!
400 \internal
401 Recursively sets and adds the nodes in the subtree of base node \a root to the scene.
402 Also takes care of connecting Components and Entities together in the scene.
403 */
404void QNodePrivate::setSceneHelper(QNode *root)
405{
406 // Sets the scene
407 root->d_func()->setScene(m_scene);
408 // addObservable sets the QChangeArbiter
409 m_scene->addObservable(observable: root);
410
411 // We also need to handle QEntity <-> QComponent relationships
412 if (QComponent *c = qobject_cast<QComponent *>(object: root)) {
413 const QVector<QEntity *> entities = c->entities();
414 for (QEntity *entity : entities) {
415 if (!m_scene->hasEntityForComponent(componentUuid: c->id(), entityUuid: entity->id())) {
416 if (!c->isShareable() && !m_scene->entitiesForComponent(id: c->id()).isEmpty())
417 qWarning() << "Trying to assign a non shareable component to more than one Entity";
418 m_scene->addEntityForComponent(componentUuid: c->id(), entityUuid: entity->id());
419 }
420 }
421 }
422}
423
424/*!
425 \internal
426
427 Recursively unsets and remove nodes in the subtree of base node \a root from
428 the scene. Also takes care of removing Components and Entities connections.
429 */
430void QNodePrivate::unsetSceneHelper(QNode *node)
431{
432 QNodePrivate *nodePrivate = QNodePrivate::get(q: node);
433
434 // We also need to handle QEntity <-> QComponent relationships removal
435 if (QComponent *c = qobject_cast<QComponent *>(object: node)) {
436 const QVector<QEntity *> entities = c->entities();
437 for (QEntity *entity : entities) {
438 if (nodePrivate->m_scene)
439 nodePrivate->m_scene->removeEntityForComponent(componentUuid: c->id(), entityUuid: entity->id());
440 }
441 }
442
443 if (nodePrivate->m_scene != nullptr)
444 nodePrivate->m_scene->removeObservable(observable: node);
445 nodePrivate->setScene(nullptr);
446}
447
448/*!
449 \internal
450 */
451void QNodePrivate::addEntityComponentToScene(QNode *root)
452{
453 if (QEntity *e = qobject_cast<QEntity *>(object: root)) {
454 const auto components = e->components();
455 for (QComponent *c : components) {
456 if (!m_scene->hasEntityForComponent(componentUuid: c->id(), entityUuid: e->id()))
457 m_scene->addEntityForComponent(componentUuid: c->id(), entityUuid: e->id());
458 }
459 }
460}
461
462/*!
463 \internal
464 */
465// Called in the main thread by QScene -> following QEvent::childAdded / addChild
466void QNodePrivate::setArbiter(QLockableObserverInterface *arbiter)
467{
468 if (m_changeArbiter && m_changeArbiter != arbiter) {
469 unregisterNotifiedProperties();
470
471 // Remove node from dirtyFrontendNodeList on old arbiter
472 Q_Q(QNode);
473 m_changeArbiter->removeDirtyFrontEndNode(node: q);
474 }
475 m_changeArbiter = static_cast<QAbstractArbiter *>(arbiter);
476 if (m_changeArbiter)
477 registerNotifiedProperties();
478}
479
480/*!
481 * \internal
482 * Makes sure this node has a backend by traversing the tree up to the most distant ancestor
483 * without a backend node and initializing that node. This is done to make sure the parent nodes
484 * are always created before the child nodes, since child nodes reference parent nodes at creation
485 * time.
486 */
487void QNodePrivate::_q_ensureBackendNodeCreated()
488{
489 if (m_hasBackendNode)
490 return;
491
492 Q_Q(QNode);
493
494 QNode *nextNode = q;
495 QNode *topNodeWithoutBackend = nullptr;
496 while (nextNode != nullptr && !QNodePrivate::get(q: nextNode)->m_hasBackendNode) {
497 topNodeWithoutBackend = nextNode;
498 nextNode = nextNode->parentNode();
499 }
500 QNodePrivate::get(q: topNodeWithoutBackend)->_q_postConstructorInit();
501}
502
503/*!
504 \class Qt3DCore::QNode
505 \inherits QObject
506
507 \inmodule Qt3DCore
508 \since 5.5
509
510 \brief QNode is the base class of all Qt3D node classes used to build a
511 Qt3D scene.
512
513 The owernship of QNode is determined by the QObject parent/child
514 relationship between nodes. By itself, a QNode has no visual appearance
515 and no particular meaning, it is there as a way of building a node based tree
516 structure.
517
518 The parent of a QNode instance can only be another QNode instance.
519
520 Each QNode instance has a unique id that allows it to be recognizable
521 from other instances.
522
523 When properties are defined on a QNode subclass, their NOTIFY signal
524 will automatically generate notifications that the Qt3D backend aspects will
525 receive.
526
527 \sa QEntity, QComponent
528*/
529
530/*!
531 * Sends the \a change QSceneChangePtr to any QBackendNodes in the registered
532 * aspects that correspond to this QNode.
533 *
534 * You only need to call this function if you wish to send a specific type of
535 * change in place of the automatic handling.
536 *
537 * Note: as of Qt 5.14, change messages are deprecated and should not be used,
538 * in particular for properties.
539 */
540void QNode::notifyObservers(const QSceneChangePtr &change)
541{
542 Q_D(QNode);
543 d->notifyObservers(change);
544}
545
546/*!
547 \obsolete
548
549 Called when one or more backend aspects sends a notification \a change to the
550 current Qt3DCore::QNode instance.
551
552 \note This method should be reimplemented in your subclasses to properly
553 handle the \a change.
554*/
555void QNode::sceneChangeEvent(const QSceneChangePtr &change)
556{
557 Q_UNUSED(change)
558 if (change->type() == Qt3DCore::PropertyUpdated) {
559 // TODO: Do this more efficiently. We could pass the metaobject and property
560 // index to the animation aspect via the QChannelMapping. This would
561 // allow us to avoid the propertyIndex lookup here by sending them in
562 // a new subclass of QPropertyUpdateChange.
563 // Try to find property and call setter
564 auto e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(src: change);
565 const QMetaObject *mo = metaObject();
566 const int propertyIndex = mo->indexOfProperty(name: e->propertyName());
567 QMetaProperty mp = mo->property(index: propertyIndex);
568 bool wasBlocked = blockNotifications(block: true);
569 mp.write(obj: this, value: e->value());
570 blockNotifications(block: wasBlocked);
571 } else {
572 // Nothing is handling this change, warn the user.
573 qWarning() << Q_FUNC_INFO << "sceneChangeEvent should have been subclassed";
574 }
575}
576
577/*!
578 \internal
579 */
580void QNodePrivate::setScene(QScene *scene)
581{
582 if (m_scene != scene) {
583 if (m_scene != nullptr)
584 m_scene->removePropertyTrackDataForNode(id: m_id);
585 m_scene = scene;
586 // set PropertyTrackData in the scene
587 updatePropertyTrackMode();
588 }
589}
590
591/*!
592 \internal
593 */
594QScene *QNodePrivate::scene() const
595{
596 return m_scene;
597}
598
599/*!
600 \internal
601 */
602void QNodePrivate::notifyPropertyChange(const char *name, const QVariant &value)
603{
604 Q_UNUSED(name)
605 Q_UNUSED(value)
606
607 // Bail out early if we can to avoid operator new
608 if (m_blockNotifications)
609 return;
610
611 update();
612}
613
614void QNodePrivate::notifyDynamicPropertyChange(const QByteArray &name, const QVariant &value)
615{
616 Q_UNUSED(name)
617 Q_UNUSED(value)
618
619 // Bail out early if we can to avoid operator new
620 if (m_blockNotifications)
621 return;
622
623 update();
624}
625
626/*!
627 \internal
628 */
629// Called by the main thread
630void QNodePrivate::notifyObservers(const QSceneChangePtr &change)
631{
632 Q_ASSERT(change);
633
634 // Don't send notifications if we are blocking
635 if (m_blockNotifications && change->type() == PropertyUpdated)
636 return;
637
638 if (m_changeArbiter != nullptr) {
639 QAbstractPostman *postman = m_changeArbiter->postman();
640 if (postman != nullptr)
641 postman->notifyBackend(change);
642 }
643}
644
645// Inserts this tree into the main Scene tree.
646// Needed when SceneLoaders provide a cloned tree from the backend
647// and need to insert it in the main scene tree
648// QNode *root;
649// QNode *subtree;
650// QNodePrivate::get(root)->insertTree(subtree);
651
652/*!
653 \internal
654 */
655void QNodePrivate::insertTree(QNode *treeRoot, int depth)
656{
657 if (m_scene != nullptr) {
658 treeRoot->d_func()->setScene(m_scene);
659 m_scene->addObservable(observable: treeRoot);
660 }
661
662 for (QObject *c : treeRoot->children()) {
663 QNode *n = nullptr;
664 if ((n = qobject_cast<QNode *>(object: c)) != nullptr)
665 insertTree(treeRoot: n, depth: depth + 1);
666 }
667
668 if (depth == 0)
669 treeRoot->setParent(q_func());
670}
671
672void QNodePrivate::updatePropertyTrackMode()
673{
674 if (m_scene != nullptr) {
675 QScene::NodePropertyTrackData trackData;
676 trackData.defaultTrackMode = m_defaultPropertyTrackMode;
677 trackData.trackedPropertiesOverrides = m_trackedPropertiesOverrides;
678 m_scene->setPropertyTrackDataForNode(id: m_id, data: trackData);
679 }
680}
681
682void QNodePrivate::update()
683{
684 if (m_changeArbiter) {
685 Q_Q(QNode);
686 m_changeArbiter->addDirtyFrontEndNode(node: q);
687 }
688}
689
690void QNodePrivate::updateNode(QNode *node, const char *property, ChangeFlag change)
691{
692 if (m_changeArbiter) {
693 // Ensure node has its postConstructorInit called if we reach this
694 // point, we could otherwise endup referencing a node that has yet
695 // to be created in the backend
696 QNodePrivate::get(q: node)->_q_ensureBackendNodeCreated();
697
698 Q_Q(QNode);
699 m_changeArbiter->addDirtyFrontEndNode(node: q, subNode: node, property, change);
700 }
701}
702
703/*!
704 \internal
705 */
706QNodePrivate *QNodePrivate::get(QNode *q)
707{
708 return q->d_func();
709}
710
711/*!
712 \internal
713 */
714const QNodePrivate *QNodePrivate::get(const QNode *q)
715{
716 return q->d_func();
717}
718
719/*!
720 \internal
721 */
722void QNodePrivate::nodePtrDeleter(QNode *q)
723{
724 QObject *p = q->parent();
725 if (p == nullptr)
726 p = q;
727 p->deleteLater();
728}
729
730/*!
731 \fn Qt3DCore::QNodeId Qt3DCore::qIdForNode(Qt3DCore::QNode *node)
732 \relates Qt3DCore::QNode
733 \return node id for \a node.
734*/
735
736/*!
737 \fn template<typename T> Qt3DCore::QNodeIdVector Qt3DCore::qIdsForNodes(const T &nodes)
738 \relates Qt3DCore::QNode
739 \return vector of node ids for \a nodes.
740*/
741
742/*!
743 \fn void Qt3DCore::QNodeCommand::setReplyToCommandId(CommandId id)
744
745 Sets the command \a id to which the message is a reply.
746
747*/
748/*!
749 \fn Qt3DCore::QNode::PropertyTrackingMode Qt3DCore::QNode::defaultPropertyTrackingMode() const
750
751 Returns the default property tracking mode which determines whether a
752 QNode should be listening for property updates.
753
754*/
755/*!
756 \fn Qt3DCore::QNode::clearPropertyTracking(const QString &propertyName)
757
758 Clears the tracking property called \a propertyName.
759*/
760/*!
761 \fn Qt3DCore::QNode::PropertyTrackingMode Qt3DCore::QNode::propertyTracking(const QString &propertyName) const
762
763 Returns the tracking mode of \a propertyName.
764*/
765
766/*!
767 \fn Qt3DCore::QNode::setPropertyTracking(const QString &propertyName, Qt3DCore::QNode::PropertyTrackingMode trackMode)
768
769 Sets the property tracking for \a propertyName and \a trackMode.
770*/
771
772/*!
773 Creates a new QNode instance with parent \a parent.
774
775 \note The backend aspects will be notified that a QNode instance is
776 part of the scene only if it has a parent; unless this is the root node of
777 the Qt3D scene.
778
779 \sa setParent()
780*/
781QNode::QNode(QNode *parent)
782 : QNode(*new QNodePrivate, parent) {}
783
784/*! \internal */
785QNode::QNode(QNodePrivate &dd, QNode *parent)
786 : QObject(dd, parent)
787{
788 Q_D(QNode);
789 d->init(parent);
790}
791
792/*!
793 \fn Qt3DCore::QNode::nodeDestroyed()
794 Emitted when the node is destroyed.
795*/
796
797/*! \internal */
798QNode::~QNode()
799{
800 Q_D(QNode);
801 // Disconnect each connection that was stored
802 for (const auto &nodeConnectionPair : qAsConst(t&: d->m_destructionConnections))
803 QObject::disconnect(nodeConnectionPair.second);
804 d->m_destructionConnections.clear();
805 Q_EMIT nodeDestroyed();
806
807 // Notify the backend that the parent lost this node as a child and
808 // that this node is being destroyed.
809 d->notifyDestructionChangesAndRemoveFromScene();
810}
811
812/*!
813 Returns the id that uniquely identifies the QNode instance.
814*/
815QNodeId QNode::id() const
816{
817 Q_D(const QNode);
818 return d->m_id;
819}
820
821/*!
822 \property Qt3DCore::QNode::parent
823
824 Holds the immediate QNode parent, or null if the node has no parent.
825
826 Setting the parent will notify the backend aspects about current QNode
827 instance's parent change.
828
829 \note if \a parent happens to be null, this will actually notify that the
830 current QNode instance was removed from the scene.
831*/
832QNode *QNode::parentNode() const
833{
834 return qobject_cast<QNode*>(object: parent());
835}
836
837/*!
838 Returns \c true if aspect notifications are blocked; otherwise returns \c false.
839 By default, notifications are \e not blocked.
840
841 \sa blockNotifications()
842*/
843bool QNode::notificationsBlocked() const
844{
845 Q_D(const QNode);
846 return d->m_blockNotifications;
847}
848
849/*!
850 If \a block is \c true, property change notifications sent by this object
851 to aspects are blocked. If \a block is \c false, no such blocking will occur.
852
853 The return value is the previous value of notificationsBlocked().
854
855 Note that the other notification types will be sent even if the
856 notifications for this object have been blocked.
857
858 \sa notificationsBlocked()
859*/
860bool QNode::blockNotifications(bool block)
861{
862 Q_D(QNode);
863 bool previous = d->m_blockNotifications;
864 d->m_blockNotifications = block;
865 return previous;
866}
867
868// Note: should never be called from the ctor directly as the type may not be fully
869// created yet
870void QNode::setParent(QNode *parent)
871{
872 Q_D(QNode);
873
874 // If we already have a parent don't do anything. Be careful to ensure
875 // that QNode knows about the parent, not just QObject (by checking the ids)
876 if (parentNode() == parent &&
877 ((parent != nullptr && d->m_parentId == parentNode()->id()) || parent == nullptr))
878 return;
879
880 // remove ourself from postConstructorInit queue. The call to _q_setParentHelper
881 // will take care of creating the backend node if necessary depending on new parent.
882 if (d->m_scene)
883 d->m_scene->postConstructorInit()->removeNode(node: this);
884
885 d->_q_setParentHelper(parent);
886
887 // Block notifications as we want to let the _q_setParentHelper
888 // manually handle them
889 const bool blocked = blockNotifications(block: true);
890 emit parentChanged(parent);
891 blockNotifications(block: blocked);
892}
893
894/*!
895 \typedef Qt3DCore::QNodePtr
896 \relates Qt3DCore::QNode
897
898 A shared pointer for QNode.
899*/
900/*!
901 \typedef Qt3DCore::QNodeVector
902 \relates Qt3DCore::QNode
903
904 List of QNode pointers.
905*/
906
907/*!
908 * Returns a list filled with the QNode children of the current
909 * QNode instance.
910 */
911QNodeVector QNode::childNodes() const
912{
913 QNodeVector nodeChildrenList;
914 const QObjectList &objectChildrenList = QObject::children();
915 nodeChildrenList.reserve(asize: objectChildrenList.size());
916
917 for (QObject *c : objectChildrenList) {
918 if (QNode *n = qobject_cast<QNode *>(object: c))
919 nodeChildrenList.push_back(t: n);
920 }
921
922 return nodeChildrenList;
923}
924void QNode::setEnabled(bool isEnabled)
925{
926 Q_D(QNode);
927
928 if (d->m_enabled == isEnabled)
929 return;
930
931 d->m_enabled = isEnabled;
932 emit enabledChanged(enabled: isEnabled);
933}
934
935void QNode::setDefaultPropertyTrackingMode(QNode::PropertyTrackingMode mode)
936{
937 Q_D(QNode);
938 if (d->m_defaultPropertyTrackMode == mode)
939 return;
940
941 d->m_defaultPropertyTrackMode = mode;
942 // The backend doesn't care about such notification
943 const bool blocked = blockNotifications(block: true);
944 emit defaultPropertyTrackingModeChanged(mode);
945 blockNotifications(block: blocked);
946 d->updatePropertyTrackMode();
947}
948
949/*!
950 \property Qt3DCore::QNode::enabled
951
952 Holds the QNode enabled flag.
953 By default a QNode is always enabled.
954
955 \note the interpretation of what enabled means is aspect-dependent. Even if
956 enabled is set to \c false, some aspects may still consider the node in
957 some manner. This is documented on a class by class basis.
958*/
959bool QNode::isEnabled() const
960{
961 Q_D(const QNode);
962 return d->m_enabled;
963}
964
965/*!
966 \property Qt3DCore::QNode::defaultPropertyTrackingMode
967
968 Holds the default property tracking mode which determines whether a QNode should
969 be listening for property updates. This only applies to properties which
970 haven't been overridden by a call to setPropertyTracking.
971
972 By default it is set to QNode::TrackFinalValues
973*/
974QNode::PropertyTrackingMode QNode::defaultPropertyTrackingMode() const
975{
976 Q_D(const QNode);
977 return d->m_defaultPropertyTrackMode;
978}
979
980void QNode::setPropertyTracking(const QString &propertyName, QNode::PropertyTrackingMode trackMode)
981{
982 Q_D(QNode);
983 d->m_trackedPropertiesOverrides.insert(akey: propertyName, avalue: trackMode);
984 d->updatePropertyTrackMode();
985}
986
987QNode::PropertyTrackingMode QNode::propertyTracking(const QString &propertyName) const
988{
989 Q_D(const QNode);
990 return d->m_trackedPropertiesOverrides.value(akey: propertyName, adefaultValue: d->m_defaultPropertyTrackMode);
991}
992
993void QNode::clearPropertyTracking(const QString &propertyName)
994{
995 Q_D(QNode);
996 d->m_trackedPropertiesOverrides.remove(akey: propertyName);
997 d->updatePropertyTrackMode();
998}
999
1000void QNode::clearPropertyTrackings()
1001{
1002 Q_D(QNode);
1003 d->m_trackedPropertiesOverrides.clear();
1004 d->updatePropertyTrackMode();
1005}
1006
1007/*!
1008 * \obsolete
1009 */
1010QNodeCreatedChangeBasePtr QNode::createNodeCreationChange() const
1011{
1012 // Uncomment this when implementing new frontend and backend types.
1013 // Any classes that don't override this function will be noticeable here.
1014 // Note that some classes actually don't need to override as they have
1015 // no additional data to send. In those cases this default implementation
1016 // is perfectly fine.
1017 // const QMetaObject *mo = metaObject();
1018 // qDebug() << Q_FUNC_INFO << mo->className();
1019 return QNodeCreatedChangeBasePtr::create(arguments: this);
1020}
1021
1022/*!
1023 \fn Qt3DCore::QNodeCommand::CommandId Qt3DCore::QNodeCommand::inReplyTo() const
1024
1025 Returns the id of the original QNodeCommand message that
1026 was sent to the backend.
1027
1028*/
1029/*!
1030 \fn void Qt3DCore::QNodeCommand::setData(const QVariant &data)
1031
1032 Sets the data (\a data) in the backend node to perform
1033 the operations requested.
1034*/
1035/*!
1036 \fn void Qt3DCore::QNodeCommand::setName(const QString &name)
1037
1038
1039 Sets the data (\a name) in the backend node to perform
1040 the operations requested.
1041*/
1042
1043/*!
1044 \enum Qt3DCore::QNode::PropertyTrackingMode
1045
1046 Indicates how a QNode listens for property updates.
1047
1048 \value TrackFinalValues
1049 Tracks final values
1050 \value DontTrackValues
1051 Does not track values
1052 \value TrackAllValues
1053 Tracks all values
1054*/
1055/*!
1056 \fn Qt3DCore::QNode::clearPropertyTrackings()
1057
1058 Erases all values that have been saved by the property tracking.
1059*/
1060/*!
1061 * \brief Sends a command message to the backend node
1062 * \obsolete
1063 *
1064 * Creates a QNodeCommand message and dispatches it to the backend node. The
1065 * command is given and a \a name and some \a data which can be used in the
1066 * backend node to perform various operations.
1067 * This returns a CommandId which can be used to identify the initial command
1068 * when receiving a message in reply. If the command message is to be sent in
1069 * reply to another command, \a replyTo contains the id of that command.
1070 *
1071 * \sa QNodeCommand, QNode::sendReply
1072 */
1073QNodeCommand::CommandId QNode::sendCommand(const QString &name,
1074 const QVariant &data,
1075 QNodeCommand::CommandId replyTo)
1076{
1077 Q_D(QNode);
1078
1079 // Bail out early, if we can, to avoid operator new
1080 if (d->m_blockNotifications)
1081 return QNodeCommand::CommandId(0);
1082
1083 auto e = QNodeCommandPtr::create(arguments&: d->m_id);
1084 e->setName(name);
1085 e->setData(data);
1086 e->setReplyToCommandId(replyTo);
1087 d->notifyObservers(change: e);
1088 return e->commandId();
1089}
1090
1091/*!
1092 * \brief Send a \a command back to the backend node.
1093 * \obsolete
1094 *
1095 * Assumes the command is to be to sent back in reply to itself to the backend node.
1096 *
1097 * \sa QNodeCommand, QNode::sendCommand
1098 */
1099void QNode::sendReply(const QNodeCommandPtr &command)
1100{
1101 Q_D(QNode);
1102 command->setDeliveryFlags(QSceneChange::BackendNodes);
1103 d->notifyObservers(change: command);
1104}
1105
1106
1107namespace {
1108
1109/*! \internal */
1110inline const QMetaObjectPrivate *priv(const uint* data)
1111{
1112 return reinterpret_cast<const QMetaObjectPrivate*>(data);
1113}
1114
1115/*! \internal */
1116inline bool isDynamicMetaObject(const QMetaObject *mo)
1117{
1118 return (priv(data: mo->d.data)->flags & DynamicMetaObject);
1119}
1120
1121} // anonymous
1122
1123/*!
1124 * \internal
1125 *
1126 * Find the most derived metaobject that doesn't have a dynamic
1127 * metaobject farther up the chain.
1128 * TODO: Add support to QMetaObject to explicitly say if it's a dynamic
1129 * or static metaobject so we don't need this logic
1130 */
1131const QMetaObject *QNodePrivate::findStaticMetaObject(const QMetaObject *metaObject)
1132{
1133 const QMetaObject *lastStaticMetaobject = nullptr;
1134 auto mo = metaObject;
1135 while (mo) {
1136 const bool dynamicMetaObject = isDynamicMetaObject(mo);
1137 if (dynamicMetaObject)
1138 lastStaticMetaobject = nullptr;
1139
1140 if (!dynamicMetaObject && !lastStaticMetaobject)
1141 lastStaticMetaobject = mo;
1142
1143 mo = mo->superClass();
1144 }
1145 Q_ASSERT(lastStaticMetaobject);
1146 return lastStaticMetaobject;
1147}
1148
1149/*!
1150 * \internal
1151 *
1152 * NodePostConstructorInit handles calling QNode::_q_postConstructorInit for
1153 * all nodes. By keeping track of nodes that need initialization we can
1154 * create them all together ensuring they get sent to the backend in a single
1155 * batch.
1156 */
1157NodePostConstructorInit::NodePostConstructorInit(QObject *parent)
1158 : QObject(parent)
1159 , m_requestedProcessing(false)
1160{
1161}
1162
1163NodePostConstructorInit::~NodePostConstructorInit() {}
1164
1165/*!
1166 * \internal
1167 *
1168 * Add a node to the list of nodes needing a call to _q_postConstructorInit
1169 * We only add the node if it does not have an ancestor already in the queue
1170 * because initializing the ancestor will initialize all it's children.
1171 * This ensures that all backend nodes are created from the top-down, with
1172 * all parents created before their children
1173 *
1174 */
1175void NodePostConstructorInit::addNode(QNode *node)
1176{
1177 Q_ASSERT(node);
1178 QNode *nextNode = node;
1179 while (nextNode != nullptr && !m_nodesToConstruct.contains(t: QNodePrivate::get(q: nextNode)))
1180 nextNode = nextNode->parentNode();
1181
1182 if (!nextNode) {
1183 m_nodesToConstruct.append(t: QNodePrivate::get(q: node));
1184 if (!m_requestedProcessing){
1185 QMetaObject::invokeMethod(obj: this, member: "processNodes", type: Qt::QueuedConnection);
1186 m_requestedProcessing = true;
1187 }
1188 }
1189}
1190
1191/*!
1192 * \internal
1193 *
1194 * Remove a node from the queue. This will ensure none of its
1195 * children get initialized
1196 */
1197void NodePostConstructorInit::removeNode(QNode *node)
1198{
1199 Q_ASSERT(node);
1200 m_nodesToConstruct.removeAll(t: QNodePrivate::get(q: node));
1201}
1202
1203/*!
1204 * \internal
1205 *
1206 * call _q_postConstructorInit for all nodes in the queue
1207 * and clear the queue
1208 */
1209void NodePostConstructorInit::processNodes()
1210{
1211 m_requestedProcessing = false;
1212 while (!m_nodesToConstruct.empty()) {
1213 auto node = m_nodesToConstruct.takeFirst();
1214 node->_q_postConstructorInit();
1215 }
1216}
1217
1218} // namespace Qt3DCore
1219
1220QT_END_NAMESPACE
1221
1222#include "moc_qnode.cpp"
1223

source code of qt3d/src/core/nodes/qnode.cpp