| 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 | |