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 | |
66 | QT_BEGIN_NAMESPACE |
67 | |
68 | namespace Qt3DCore { |
69 | |
70 | QNodePrivate::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 | |
86 | QNodePrivate::~QNodePrivate() |
87 | { |
88 | } |
89 | |
90 | void 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 | */ |
115 | void 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 | */ |
140 | void 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 | */ |
175 | void 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 | */ |
216 | void 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 | */ |
263 | void 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 | */ |
298 | void 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 | |
358 | void 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 | |
373 | void 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 | |
388 | void 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 | */ |
404 | void 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 | */ |
430 | void 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 | */ |
451 | void 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 |
466 | void 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 | */ |
487 | void 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 | */ |
540 | void 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 | */ |
555 | void 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 | */ |
580 | void 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 | */ |
594 | QScene *QNodePrivate::scene() const |
595 | { |
596 | return m_scene; |
597 | } |
598 | |
599 | /*! |
600 | \internal |
601 | */ |
602 | void 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 | |
614 | void 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 |
630 | void 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 | */ |
655 | void 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 | |
672 | void 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 | |
682 | void QNodePrivate::update() |
683 | { |
684 | if (m_changeArbiter) { |
685 | Q_Q(QNode); |
686 | m_changeArbiter->addDirtyFrontEndNode(node: q); |
687 | } |
688 | } |
689 | |
690 | void 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 | */ |
706 | QNodePrivate *QNodePrivate::get(QNode *q) |
707 | { |
708 | return q->d_func(); |
709 | } |
710 | |
711 | /*! |
712 | \internal |
713 | */ |
714 | const QNodePrivate *QNodePrivate::get(const QNode *q) |
715 | { |
716 | return q->d_func(); |
717 | } |
718 | |
719 | /*! |
720 | \internal |
721 | */ |
722 | void 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 | */ |
781 | QNode::QNode(QNode *parent) |
782 | : QNode(*new QNodePrivate, parent) {} |
783 | |
784 | /*! \internal */ |
785 | QNode::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 */ |
798 | QNode::~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 | */ |
815 | QNodeId 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 | */ |
832 | QNode *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 | */ |
843 | bool 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 | */ |
860 | bool 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 |
870 | void 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 | */ |
911 | QNodeVector 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 | } |
924 | void 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 | |
935 | void 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 | */ |
959 | bool 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 | */ |
974 | QNode::PropertyTrackingMode QNode::defaultPropertyTrackingMode() const |
975 | { |
976 | Q_D(const QNode); |
977 | return d->m_defaultPropertyTrackMode; |
978 | } |
979 | |
980 | void 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 | |
987 | QNode::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 | |
993 | void QNode::clearPropertyTracking(const QString &propertyName) |
994 | { |
995 | Q_D(QNode); |
996 | d->m_trackedPropertiesOverrides.remove(akey: propertyName); |
997 | d->updatePropertyTrackMode(); |
998 | } |
999 | |
1000 | void QNode::clearPropertyTrackings() |
1001 | { |
1002 | Q_D(QNode); |
1003 | d->m_trackedPropertiesOverrides.clear(); |
1004 | d->updatePropertyTrackMode(); |
1005 | } |
1006 | |
1007 | /*! |
1008 | * \obsolete |
1009 | */ |
1010 | QNodeCreatedChangeBasePtr 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 | */ |
1073 | QNodeCommand::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 | */ |
1099 | void QNode::sendReply(const QNodeCommandPtr &command) |
1100 | { |
1101 | Q_D(QNode); |
1102 | command->setDeliveryFlags(QSceneChange::BackendNodes); |
1103 | d->notifyObservers(change: command); |
1104 | } |
1105 | |
1106 | |
1107 | namespace { |
1108 | |
1109 | /*! \internal */ |
1110 | inline const QMetaObjectPrivate *priv(const uint* data) |
1111 | { |
1112 | return reinterpret_cast<const QMetaObjectPrivate*>(data); |
1113 | } |
1114 | |
1115 | /*! \internal */ |
1116 | inline 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 | */ |
1131 | const 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 | */ |
1157 | NodePostConstructorInit::NodePostConstructorInit(QObject *parent) |
1158 | : QObject(parent) |
1159 | , m_requestedProcessing(false) |
1160 | { |
1161 | } |
1162 | |
1163 | NodePostConstructorInit::~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 | */ |
1175 | void 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 | */ |
1197 | void 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 | */ |
1209 | void 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 | |
1220 | QT_END_NAMESPACE |
1221 | |
1222 | #include "moc_qnode.cpp" |
1223 | |