1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dobject.h"
5#include "qquick3dobject_p.h"
6#include "qquick3dscenemanager_p.h"
7#include "qquick3ditem2d_p.h"
8#include "qquick3dmodel_p.h"
9
10#include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
11
12#include <QtQml/private/qqmlglobal_p.h>
13#include <QtQuick/private/qquickstategroup_p.h>
14#include <QtQuick/private/qquickstate_p.h>
15#include <QtQuick/private/qquickitem_p.h>
16
17#include <private/qv4object_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19
20QT_BEGIN_NAMESPACE
21
22
23/*!
24 \qmltype Object3D
25 \inqmlmodule QtQuick3D
26 \instantiates QQuick3DObject
27 \inherits QtObject
28 \brief Abstract base type of all 3D nodes and resources.
29
30 Object3D is the base class for all Qt Quick 3D types. This includes:
31
32 \list
33
34 \li Spatial types that represent objects in the 3D scene, these will normally have a position and/or a direction.
35 For example, \l Model, \l Camera, or \l Light. Such types inherit from \l Node, which in turn inherits from Object3D.
36
37 \li Resource types that do not themselves represent an object in the 3D world, but rather serve
38 as components to \l Node subclasses, providing data of some kind. This includes, among
39 others, \l Material, \l Geometry, and \l Texture.
40
41 \endlist
42
43 In addition to the above types, Object3D can also serve as the parent for \l{Item}{Qt
44 Quick items}, as well as arbitrary QObject instances. For more information on adding
45 2D items to the 3D scene, refer to \l{Qt Quick 3D Scenes with 2D Content}.
46
47 \sa Node
48*/
49
50/*!
51 \class QQuick3DObject
52 \inmodule QtQuick3D
53 \since 5.15
54 \brief Base class of all 3D nodes and resources.
55
56 Object3D is the base class for all Qt Quick 3D scene objects. Currently the
57 types available in C++ are:
58
59 \list
60 \li QQuick3DGeometry
61 \li QQuick3DTextureData
62 \endlist
63
64 Both of these types are resource objects which directly inherit QQuick3DObject.
65
66 It should not be necessary to use QQuick3DObject directly anywhere currently
67 because it is just an interface for supporting spatial items and resources in
68 a 3D scene, as well as exposing similar functionality as QQuickItem for 3D
69 scene content.
70*/
71
72QQuick3DObject::QQuick3DObject(QQuick3DObject *parent)
73 : QObject(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::Unknown)), parent)
74{
75 Q_D(QQuick3DObject);
76 d->init(parent);
77}
78
79QQuick3DObject::~QQuick3DObject()
80{
81 Q_D(QQuick3DObject);
82 // XXX todo - optimize
83 while (!d->childItems.isEmpty())
84 d->childItems.constFirst()->setParentItem(nullptr);
85
86 delete d->_stateGroup;
87 d->_stateGroup = nullptr;
88 delete d->contentItem2d;
89 d->contentItem2d = nullptr;
90
91 if (d->parentItem)
92 setParentItem(nullptr);
93
94 if (d->sceneRefCount > 1)
95 d->sceneRefCount = 1; // Make sure the scene is set to null in next call to derefSceneManager().
96
97 if (!d->parentItem && d->sceneManager)
98 QQuick3DObjectPrivate::derefSceneManager(obj: this);
99}
100
101void QQuick3DObject::update()
102{
103 Q_D(QQuick3DObject);
104 d->dirty(QQuick3DObjectPrivate::Content);
105}
106
107/*!
108 \qmlproperty Object3D QtQuick::Object3D::parent
109 This property holds the parent of the Object3D in a 3D scene.
110
111 \note An Object3D's parent may not necessarily be the same as its object
112 parent. This is necessary because the object parent may be an item that is
113 not of type Object3D, for example the root object in a scene.
114*/
115/*!
116 \property QQuick3DObject::parent
117 This property holds the parent of the Object3D in a 3D scene.
118
119 \note An Object3D's parent may not necessarily be the same as its object
120 parent. This is necessary because the object parent may be an item that is
121 not of type Object3D, for example the root object in a scene.
122
123 \note Currently for 3D items to be correctly handled by the scene manager
124 when parenting 3D objects from C++ it is necessary to call
125 QQuick3DObject::setParentItem before the QObject::setParent. This requirement is
126 likely to change in a future release though.
127 \code
128 QQuick3DObject *newItem = new QQuick3DObject();
129 newItem->setParentItem(parentItem);
130 newItem->setParent(parentItem);
131 \endcode
132*/
133
134void QQuick3DObject::setParentItem(QQuick3DObject *parentItem)
135{
136 Q_D(QQuick3DObject);
137 if (parentItem == d->parentItem)
138 return;
139
140 if (parentItem) {
141 QQuick3DObject *itemAncestor = parentItem;
142 while (itemAncestor != nullptr) {
143 if (Q_UNLIKELY(itemAncestor == this)) {
144 qWarning() << "QSSGObject::setParentItem: Parent" << parentItem << "is already part of the subtree of" << this;
145 return;
146 }
147 itemAncestor = itemAncestor->parentItem();
148 }
149 }
150
151 d->removeFromDirtyList();
152
153 QQuick3DObject *oldParentItem = d->parentItem;
154
155 if (oldParentItem) {
156 QQuick3DObjectPrivate *op = QQuick3DObjectPrivate::get(item: oldParentItem);
157
158 op->removeChild(this);
159 } else if (d->sceneManager) {
160 d->sceneManager->parentlessItems.remove(value: this);
161 }
162
163 const auto parentSceneManager = parentItem ? QQuick3DObjectPrivate::get(item: parentItem)->sceneManager : nullptr;
164 if (d->sceneManager == parentSceneManager) {
165 // Avoid freeing and reallocating resources if the window stays the same.
166 d->parentItem = parentItem;
167 } else {
168 if (d->sceneManager)
169 QQuick3DObjectPrivate::derefSceneManager(obj: this);
170 d->parentItem = parentItem;
171 if (parentSceneManager)
172 QQuick3DObjectPrivate::refSceneManager(obj: this, mgr&: *parentSceneManager);
173 }
174
175 d->dirty(QQuick3DObjectPrivate::ParentChanged);
176
177 if (d->parentItem)
178 QQuick3DObjectPrivate::get(item: d->parentItem)->addChild(this);
179 else if (d->sceneManager)
180 d->sceneManager->parentlessItems.insert(value: this);
181
182 d->itemChange(ItemParentHasChanged, d->parentItem);
183
184 emit parentChanged();
185}
186
187QString QQuick3DObject::state() const
188{
189 Q_D(const QQuick3DObject);
190 return d->state();
191}
192
193void QQuick3DObject::setState(const QString &state)
194{
195 Q_D(QQuick3DObject);
196 d->setState(state);
197}
198
199QList<QQuick3DObject *> QQuick3DObject::childItems() const
200{
201 Q_D(const QQuick3DObject);
202 return d->childItems;
203}
204
205QQuick3DObject *QQuick3DObject::parentItem() const
206{
207 Q_D(const QQuick3DObject);
208 return d->parentItem;
209}
210
211QSSGRenderGraphObject *QQuick3DObject::updateSpatialNode(QSSGRenderGraphObject *node)
212{
213 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(this, node);
214 return node;
215}
216
217void QQuick3DObject::markAllDirty()
218{
219}
220
221void QQuick3DObject::itemChange(QQuick3DObject::ItemChange, const QQuick3DObject::ItemChangeData &)
222{
223}
224
225QQuick3DObject::QQuick3DObject(QQuick3DObjectPrivate &dd, QQuick3DObject *parent)
226 : QObject(dd, parent)
227{
228 Q_D(QQuick3DObject);
229 d->init(parent);
230}
231
232void QQuick3DObject::classBegin()
233{
234 Q_D(QQuick3DObject);
235 d->componentComplete = false;
236 if (d->_stateGroup)
237 d->_stateGroup->classBegin();
238}
239
240void QQuick3DObject::componentComplete()
241{
242 Q_D(QQuick3DObject);
243 d->componentComplete = true;
244 if (d->_stateGroup)
245 d->_stateGroup->componentComplete();
246
247 if (d->sceneManager && d->dirtyAttributes) {
248 d->addToDirtyList();
249 }
250 Q_QUICK3D_PROFILE_REGISTER_D(this);
251}
252
253bool QQuick3DObject::isComponentComplete() const
254{
255 Q_D(const QQuick3DObject);
256 return d->componentComplete;
257}
258
259void QQuick3DObject::preSync()
260{
261
262}
263
264QQuick3DObjectPrivate::QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type t)
265 : _stateGroup(nullptr)
266 , dirtyAttributes(0)
267 , nextDirtyItem(nullptr)
268 , prevDirtyItem(nullptr)
269 , sceneManager(nullptr)
270 , sceneRefCount(0)
271 , parentItem(nullptr)
272 , subFocusItem(nullptr)
273 , type(t)
274{
275}
276
277QQuick3DObjectPrivate::~QQuick3DObjectPrivate()
278{
279}
280
281void QQuick3DObjectPrivate::init(QQuick3DObject *parent)
282{
283 Q_Q(QQuick3DObject);
284
285 if (parent)
286 q->setParentItem(parent);
287}
288
289/*!
290 \qmlproperty list<Object> QtQuick3D::Object3D::data
291 \qmldefault
292
293 The data property allows you to freely mix Object3D children and resources
294 in an object. If you assign a Object3D to the data list it becomes a child
295 and if you assign any other object type, it is added as a resource.
296
297 So you can write:
298 \qml
299 Object3D {
300 Node {}
301 DirectionalLight {}
302 Timer {}
303 }
304 \endqml
305
306 instead of:
307 \qml
308 Item {
309 children: [
310 Node {},
311 DirectionalLight {}
312 ]
313 resources: [
314 Timer {}
315 ]
316 }
317 \endqml
318
319 It should not generally be necessary to refer to the \c data property,
320 as it is the default property for Object3D and thus all child objects are
321 automatically assigned to this property.
322 */
323
324/*!
325 \property QQuick3DObject::data
326 \internal
327*/
328QQmlListProperty<QObject> QQuick3DObjectPrivate::data()
329{
330 return QQmlListProperty<QObject>(q_func(),
331 nullptr,
332 QQuick3DObjectPrivate::data_append,
333 QQuick3DObjectPrivate::data_count,
334 QQuick3DObjectPrivate::data_at,
335 QQuick3DObjectPrivate::data_clear);
336}
337
338/*!
339 \property QQuick3DObject::resources
340 \internal
341*/
342QQmlListProperty<QObject> QQuick3DObjectPrivate::resources()
343{
344 return QQmlListProperty<QObject>(q_func(),
345 nullptr,
346 QQuick3DObjectPrivate::resources_append,
347 QQuick3DObjectPrivate::resources_count,
348 QQuick3DObjectPrivate::resources_at,
349 QQuick3DObjectPrivate::resources_clear);
350}
351
352/*!
353 \qmlproperty list<Object3D> QtQuick3D::Object3D::children
354 \qmlproperty list<Object> QtQuick3D::Object3D::resources
355
356 The children property contains the list of visual children of this object.
357 The resources property contains non-visual resources that you want to
358 reference by name.
359
360 It is not generally necessary to refer to these properties when adding
361 child objects or resources, as the default \l data property will
362 automatically assign child objects to the \c children and \c resources
363 properties as appropriate. See the \l QtQuick3D::Object3D::data
364 documentation for details.
365
366 \note QtQuick3D::Object3D::resources does not return a list of 3D resources
367 despite the name. The name comes from the semantics of QQuickItem. 3D
368 resources are subclasses of QQuickObjec3D and thus will be returned in the
369 list of QtQuick3D::Objec3D::children.
370*/
371/*!
372 \property QQuick3DObject::children
373 \internal
374*/
375QQmlListProperty<QQuick3DObject> QQuick3DObjectPrivate::children()
376{
377 return QQmlListProperty<QQuick3DObject>(q_func(),
378 nullptr,
379 QQuick3DObjectPrivate::children_append,
380 QQuick3DObjectPrivate::children_count,
381 QQuick3DObjectPrivate::children_at,
382 QQuick3DObjectPrivate::children_clear);
383}
384
385/*!
386 \qmlproperty list<State> QtQuick3D::Object3D::states
387
388 This property holds the list of possible states for this object. To change
389 the state of this object, set the \l state property to one of these states,
390 or set the \l state property to an empty string to revert the object to its
391 default state.
392
393 This property is specified as a list of \l State objects. For example,
394 below is an QtQuick3D::Node with "above_state" and "below_state" states:
395
396 \qml
397 import QtQuick
398 import QtQuick3D
399
400 Node {
401 id: root
402 y: 0
403
404 states: [
405 State {
406 name: "above_state"
407 PropertyChanges { target: root; y: 100 }
408 },
409 State {
410 name: "below_state"
411 PropertyChanges { target: root; y: -100 }
412 }
413 ]
414 }
415 \endqml
416
417 See \l{Qt Quick States} and \l{Animation and Transitions in Qt Quick} for
418 more details on using states and transitions.
419
420 \note This property works the same as QtQuick::Item::states but is
421 necessary because QtQuick3D::Object3D is not a QtQuick::Item subclass.
422
423 \sa QtQuick3D::Object3D::transitions
424*/
425
426/*!
427 \property QQuick3DObject::states
428 \internal
429*/
430QQmlListProperty<QQuickState> QQuick3DObjectPrivate::states()
431{
432 return _states()->statesProperty();
433}
434
435/*!
436 \qmlproperty list<Transition> QtQuick3D::Object3D::transitions
437
438 This property holds the list of transitions for this object. These define the
439 transitions to be applied to the object whenever it changes its \l state.
440
441 This property is specified as a list of \l Transition objects. For example:
442
443 \qml
444 import QtQuick
445 import QtQuick3D
446
447 Node {
448 transitions: [
449 Transition {
450 //...
451 },
452 Transition {
453 //...
454 }
455 ]
456 }
457 \endqml
458
459 See \l{Qt Quick States} and \l{Animation and Transitions in Qt Quick} for
460 more details on using states and transitions.
461
462 \note This property works the same as QtQuick::Item::transitions but is
463 necessary because QtQuick3D::Object3D is not a QtQuick::Item subclass.
464
465 \sa QtQuick3D::Object3D::states
466*/
467/*!
468 \property QQuick3DObject::transitions
469 \internal
470 */
471
472QQmlListProperty<QQuickTransition> QQuick3DObjectPrivate::transitions()
473{
474 return _states()->transitionsProperty();
475}
476
477/*!
478 \qmlproperty string QtQuick3D::Object3D::state
479
480 This property holds the name of the current state of the object.
481
482 If the item is in its default state, that is, no explicit state has been
483 set, then this property holds an empty string. Likewise, you can return
484 an item to its default state by setting this property to an empty string.
485
486 \sa {Qt Quick States}
487*/
488/*!
489 \property QQuick3DObject::state
490
491 This property holds the name of the current state of the object.
492
493 If the item is in its default state, that is, no explicit state has been
494 set, then this property holds an empty string. Likewise, you can return
495 an item to its default state by setting this property to an empty string.
496
497 \sa {Qt Quick States}
498*/
499
500QString QQuick3DObjectPrivate::state() const
501{
502 return _stateGroup ? _stateGroup->state() : QString();
503}
504
505void QQuick3DObjectPrivate::setState(const QString &state)
506{
507 _states()->setState(state);
508}
509
510void QQuick3DObjectPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
511{
512 if (!o)
513 return;
514
515 QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
516 QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item: that);
517
518 if (QQuick3DObject *item = qmlobject_cast<QQuick3DObject *>(object: o)) {
519 item->setParentItem(that);
520
521 } else {
522 QQuickItem *quickItem = qobject_cast<QQuickItem *>(o);
523 if (quickItem) {
524 if (!privateItem->contentItem2d) {
525 privateItem->contentItem2d = new QQuick3DItem2D(quickItem);
526 privateItem->contentItem2d->setParent(that);
527 privateItem->contentItem2d->setParentItem(that);
528 } else {
529 privateItem->contentItem2d->addChildItem(item: quickItem);
530 }
531 qmlobject_connect(privateItem->contentItem2d, QQuick3DItem2D, SIGNAL(allChildrenRemoved()),
532 that, QQuick3DObject, SLOT(_q_cleanupContentItem2D()));
533 } else {
534 o->setParent(that);
535 }
536 }
537 resources_append(prop, o);
538}
539
540qsizetype QQuick3DObjectPrivate::data_count(QQmlListProperty<QObject> *property)
541{
542 QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
543 QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
544 QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
545 QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
546
547 return resources_count(&resourcesProperty) + children_count(&childrenProperty);
548}
549
550QObject *QQuick3DObjectPrivate::data_at(QQmlListProperty<QObject> *property, qsizetype i)
551{
552 QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
553 QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
554 QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
555 QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
556
557 int resourcesCount = resources_count(&resourcesProperty);
558 if (i < resourcesCount)
559 return resources_at(&resourcesProperty, i);
560 const int j = i - resourcesCount;
561 if (j < children_count(&childrenProperty))
562 return children_at(&childrenProperty, j);
563 return nullptr;
564}
565
566void QQuick3DObjectPrivate::data_clear(QQmlListProperty<QObject> *property)
567{
568 QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
569 QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
570 QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
571 QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
572
573 resources_clear(&resourcesProperty);
574 children_clear(&childrenProperty);
575}
576
577QObject *QQuick3DObjectPrivate::resources_at(QQmlListProperty<QObject> *prop, qsizetype index)
578{
579 QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(item: static_cast<QQuick3DObject *>(prop->object));
580 return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.value(i: index) : 0;
581}
582
583void QQuick3DObjectPrivate::resources_append(QQmlListProperty<QObject> *prop, QObject *object)
584{
585 QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
586 QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(item: quickItem);
587 if (!quickItemPrivate->extra.value().resourcesList.contains(t: object)) {
588 quickItemPrivate->extra.value().resourcesList.append(t: object);
589 // clang-format off
590 qmlobject_connect(object, QObject, SIGNAL(destroyed(QObject*)),
591 quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
592 // clang-format on
593 }
594}
595
596qsizetype QQuick3DObjectPrivate::resources_count(QQmlListProperty<QObject> *prop)
597{
598 QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(item: static_cast<QQuick3DObject *>(prop->object));
599 return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.size() : 0;
600}
601
602void QQuick3DObjectPrivate::resources_clear(QQmlListProperty<QObject> *prop)
603{
604 QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
605 QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(item: quickItem);
606 if (quickItemPrivate->extra.isAllocated()) { // If extra is not allocated resources is empty.
607 for (QObject *object : std::as_const(t&: quickItemPrivate->extra->resourcesList)) {
608 // clang-format off
609 qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
610 quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
611 // clang-format on
612 }
613 quickItemPrivate->extra->resourcesList.clear();
614 }
615}
616
617void QQuick3DObjectPrivate::children_append(QQmlListProperty<QQuick3DObject> *prop, QQuick3DObject *o)
618{
619 if (!o)
620 return;
621
622 QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
623 if (o->parentItem() == that)
624 o->setParentItem(nullptr);
625
626 o->setParentItem(that);
627}
628
629qsizetype QQuick3DObjectPrivate::children_count(QQmlListProperty<QQuick3DObject> *prop)
630{
631 QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(item: static_cast<QQuick3DObject *>(prop->object));
632 return p->childItems.size();
633}
634
635QQuick3DObject *QQuick3DObjectPrivate::children_at(QQmlListProperty<QQuick3DObject> *prop, qsizetype index)
636{
637 QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(item: static_cast<QQuick3DObject *>(prop->object));
638 if (index >= p->childItems.size() || index < 0)
639 return nullptr;
640
641 return p->childItems.at(i: index);
642}
643
644void QQuick3DObjectPrivate::children_clear(QQmlListProperty<QQuick3DObject> *prop)
645{
646 QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
647 QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(item: that);
648 while (!p->childItems.isEmpty())
649 p->childItems.at(i: 0)->setParentItem(nullptr);
650}
651
652void QQuick3DObjectPrivate::_q_resourceObjectDeleted(QObject *object)
653{
654 if (extra.isAllocated() && extra->resourcesList.contains(t: object))
655 extra->resourcesList.removeAll(t: object);
656}
657
658void QQuick3DObjectPrivate::_q_cleanupContentItem2D()
659{
660 // Only Schedule the item for deletion, as this may get called
661 // as a result of teardown as well leading to a double delete
662 if (contentItem2d)
663 contentItem2d->deleteLater();
664 contentItem2d = nullptr;
665}
666
667void QQuick3DObjectPrivate::addItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
668{
669 changeListeners.append(t: ChangeListener(listener, types));
670}
671
672void QQuick3DObjectPrivate::updateOrAddItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
673{
674 const ChangeListener changeListener(listener, types);
675 const int index = changeListeners.indexOf(t: changeListener);
676 if (index > -1)
677 changeListeners[index].types = changeListener.types;
678 else
679 changeListeners.append(t: changeListener);
680}
681
682void QQuick3DObjectPrivate::removeItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
683{
684 ChangeListener change(listener, types);
685 changeListeners.removeOne(t: change);
686}
687
688QQuickStateGroup *QQuick3DObjectPrivate::_states()
689{
690 Q_Q(QQuick3DObject);
691 if (!_stateGroup) {
692 _stateGroup = new QQuickStateGroup;
693 if (!componentComplete)
694 _stateGroup->classBegin();
695 // clang-format off
696 qmlobject_connect(_stateGroup, QQuickStateGroup, SIGNAL(stateChanged(QString)),
697 q, QQuick3DObject, SIGNAL(stateChanged()));
698 // clang-format on
699 }
700
701 return _stateGroup;
702}
703
704QString QQuick3DObjectPrivate::dirtyToString() const
705{
706#define DIRTY_TO_STRING(value) \
707 if (dirtyAttributes & value) { \
708 if (!rv.isEmpty()) \
709 rv.append(QLatin1Char('|')); \
710 rv.append(QLatin1String(#value)); \
711 }
712
713 // QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16);
714 QString rv;
715
716 DIRTY_TO_STRING(TransformOrigin);
717 DIRTY_TO_STRING(Transform);
718 DIRTY_TO_STRING(BasicTransform);
719 DIRTY_TO_STRING(Position);
720 DIRTY_TO_STRING(Size);
721 DIRTY_TO_STRING(ZValue);
722 DIRTY_TO_STRING(Content);
723 DIRTY_TO_STRING(Smooth);
724 DIRTY_TO_STRING(OpacityValue);
725 DIRTY_TO_STRING(ChildrenChanged);
726 DIRTY_TO_STRING(ChildrenStackingChanged);
727 DIRTY_TO_STRING(ParentChanged);
728 DIRTY_TO_STRING(Clip);
729 DIRTY_TO_STRING(Window);
730 DIRTY_TO_STRING(EffectReference);
731 DIRTY_TO_STRING(Visible);
732 DIRTY_TO_STRING(HideReference);
733 DIRTY_TO_STRING(Antialiasing);
734 DIRTY_TO_STRING(InstanceRootChanged);
735
736 return rv;
737}
738
739void QQuick3DObjectPrivate::dirty(QQuick3DObjectPrivate::DirtyType type)
740{
741 // NOTE: Models that get an instance root has an "external" node that affects its transform
742 // we therefore give models with an instance root a lower priority in the update list (See: addToDirtyList()).
743 // For this to work we need to re-evaluate models priority when the instance root changes.
744 if ((type & InstanceRootChanged) != 0)
745 removeFromDirtyList();
746
747 if (!(dirtyAttributes & type) || (sceneManager && !prevDirtyItem)) {
748 dirtyAttributes |= type;
749 if (sceneManager && componentComplete) {
750 addToDirtyList();
751 }
752 }
753}
754
755void QQuick3DObjectPrivate::addToDirtyList()
756{
757 Q_Q(QQuick3DObject);
758
759 Q_ASSERT(sceneManager);
760 if (!prevDirtyItem) {
761 Q_ASSERT(!nextDirtyItem);
762
763 if (QSSGRenderGraphObject::isNodeType(type)) {
764 // NOTE: We do special handling of models with an instance root (that is not itself...)
765 // to ensure those models are processed after instance root nodes.
766 const bool hasInstanceRoot = (type == Type::Model && static_cast<QQuick3DModel *>(q)->instanceRoot() && static_cast<QQuick3DModel *>(q)->instanceRoot() != q);
767 const auto dirtyListIdx = !hasInstanceRoot ? QQuick3DSceneManager::nodeListIndex(type)
768 : size_t(QQuick3DSceneManager::NodePriority::ModelWithInstanceRoot);
769 nextDirtyItem = sceneManager->dirtyNodes[dirtyListIdx];
770 if (nextDirtyItem)
771 QQuick3DObjectPrivate::get(item: nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
772 prevDirtyItem = &sceneManager->dirtyNodes[dirtyListIdx];
773 sceneManager->dirtyNodes[dirtyListIdx] = q;
774 } else if (QSSGRenderGraphObject::isExtension(type)) {
775 const auto dirtyListIdx = QQuick3DSceneManager::extensionListIndex(type);
776 nextDirtyItem = sceneManager->dirtyExtensions[dirtyListIdx];
777 if (nextDirtyItem)
778 QQuick3DObjectPrivate::get(item: nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
779 prevDirtyItem = &sceneManager->dirtyExtensions[dirtyListIdx];
780 sceneManager->dirtyExtensions[dirtyListIdx] = q;
781 } else {
782 const auto dirtyListIdx = QQuick3DSceneManager::resourceListIndex(type);
783 nextDirtyItem = sceneManager->dirtyResources[dirtyListIdx];
784 if (nextDirtyItem)
785 QQuick3DObjectPrivate::get(item: nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
786 prevDirtyItem = &sceneManager->dirtyResources[dirtyListIdx];
787 sceneManager->dirtyResources[dirtyListIdx] = q;
788 }
789 }
790 sceneManager->dirtyItem(item: q);
791 Q_ASSERT(prevDirtyItem);
792}
793
794void QQuick3DObjectPrivate::removeFromDirtyList()
795{
796 if (prevDirtyItem) {
797 if (nextDirtyItem)
798 QQuick3DObjectPrivate::get(item: nextDirtyItem)->prevDirtyItem = prevDirtyItem;
799 *prevDirtyItem = nextDirtyItem;
800 prevDirtyItem = nullptr;
801 nextDirtyItem = nullptr;
802 }
803 Q_ASSERT(!prevDirtyItem);
804 Q_ASSERT(!nextDirtyItem);
805}
806
807void QQuick3DObjectPrivate::setCulled(bool cull)
808{
809 if (cull == culled)
810 return;
811
812 culled = cull;
813 if ((cull && ++extra.value().hideRefCount == 1) || (!cull && --extra.value().hideRefCount == 0))
814 dirty(type: HideReference);
815}
816
817void QQuick3DObjectPrivate::addChild(QQuick3DObject *child)
818{
819 Q_Q(QQuick3DObject);
820
821 Q_ASSERT(!childItems.contains(child));
822
823 childItems.append(t: child);
824
825 dirty(type: QQuick3DObjectPrivate::ChildrenChanged);
826
827 itemChange(QQuick3DObject::ItemChildAddedChange, child);
828
829 emit q->childrenChanged();
830}
831
832void QQuick3DObjectPrivate::removeChild(QQuick3DObject *child)
833{
834 Q_Q(QQuick3DObject);
835
836 Q_ASSERT(child);
837 Q_ASSERT(childItems.contains(child));
838 childItems.removeOne(t: child);
839 Q_ASSERT(!childItems.contains(child));
840
841 dirty(type: QQuick3DObjectPrivate::ChildrenChanged);
842
843 itemChange(QQuick3DObject::ItemChildRemovedChange, child);
844
845 emit q->childrenChanged();
846}
847
848void QQuick3DObjectPrivate::siblingOrderChanged()
849{
850 Q_Q(QQuick3DObject);
851 if (!changeListeners.isEmpty()) {
852 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
853 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
854 if (change.types & QQuick3DObjectPrivate::SiblingOrder) {
855 change.listener->itemSiblingOrderChanged(q);
856 }
857 }
858 }
859}
860
861void QQuick3DObjectPrivate::refSceneManager(QQuick3DSceneManager &c)
862{
863 // An item needs a scene manager if it is referenced by another item which has a scene manager.
864 // Typically the item is referenced by a parent, but can also be referenced by a
865 // ShaderEffect or ShaderEffectSource. 'sceneRefCount' counts how many items with
866 // a scene manager is referencing this item. When the reference count goes from zero to one,
867 // or one to zero, the scene manager of this item is updated and propagated to the children.
868 // As long as the reference count stays above zero, the scene manager is unchanged.
869 // refSceneManager() increments the reference count.
870 // derefSceneManager() decrements the reference count.
871
872 Q_Q(QQuick3DObject);
873
874 // Handle the case where the view has been deleted while the object lives on.
875 if (sceneManager.isNull() && sceneRefCount == 1)
876 sceneRefCount = 0;
877
878 Q_ASSERT((sceneManager != nullptr) == (sceneRefCount > 0));
879 if (++sceneRefCount > 1) {
880 // Sanity check. Even if there's a different scene manager the window should be the same.
881 if (c.window() != sceneManager->window()) {
882 qWarning(msg: "QSSGObject: Cannot use same item on different windows at the same time.");
883 return;
884 }
885
886 // NOTE: Simple tracking for resources that are shared between scenes.
887 if (&c != sceneManager) {
888 sharedResource = true;
889 for (int ii = 0; ii < childItems.size(); ++ii) {
890 QQuick3DObject *child = childItems.at(i: ii);
891 QQuick3DObjectPrivate::get(item: child)->sharedResource = sharedResource;
892 QQuick3DObjectPrivate::refSceneManager(obj: child, mgr&: c);
893 }
894 }
895
896 return; // Scene manager already set.
897 }
898
899 Q_ASSERT(sceneManager == nullptr);
900 sceneManager = &c;
901
902 // if (polishScheduled)
903 // QSSGWindowPrivate::get(window)->itemsToPolish.append(q);
904
905 if (!parentItem)
906 sceneManager->parentlessItems.insert(value: q);
907 else
908 sharedResource = QQuick3DObjectPrivate::get(item: parentItem)->sharedResource;
909
910 for (int ii = 0; ii < childItems.size(); ++ii) {
911 QQuick3DObject *child = childItems.at(i: ii);
912 QQuick3DObjectPrivate::get(item: child)->sharedResource = sharedResource;
913 QQuick3DObjectPrivate::refSceneManager(obj: child, mgr&: c);
914 }
915
916 dirty(type: Window);
917
918 itemChange(QQuick3DObject::ItemSceneChange, &c);
919}
920
921void QQuick3DObjectPrivate::derefSceneManager()
922{
923 Q_Q(QQuick3DObject);
924
925 if (!sceneManager)
926 return; // This can happen when destroying recursive shader effect sources.
927
928 if (--sceneRefCount > 0)
929 return; // There are still other references, so don't set the scene manager to null yet.
930
931 removeFromDirtyList();
932 if (sceneManager)
933 sceneManager->dirtyBoundingBoxList.removeAll(t: q);
934
935 if (spatialNode)
936 sceneManager->cleanup(item: spatialNode);
937 if (!parentItem)
938 sceneManager->parentlessItems.remove(value: q);
939
940 spatialNode = nullptr;
941
942 for (int ii = 0; ii < childItems.size(); ++ii) {
943 QQuick3DObject *child = childItems.at(i: ii);
944 QQuick3DObjectPrivate::derefSceneManager(obj: child);
945 }
946
947 sceneManager = nullptr;
948
949 dirty(type: Window);
950
951 itemChange(QQuick3DObject::ItemSceneChange, sceneManager.data());
952}
953
954void QQuick3DObjectPrivate::updateSubFocusItem(QQuick3DObject *scope, bool focus)
955{
956 Q_Q(QQuick3DObject);
957 Q_ASSERT(scope);
958
959 QQuick3DObjectPrivate *scopePrivate = QQuick3DObjectPrivate::get(item: scope);
960
961 QQuick3DObject *oldSubFocusItem = scopePrivate->subFocusItem;
962 // Correct focus chain in scope
963 if (oldSubFocusItem) {
964 QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
965 while (sfi && sfi != scope) {
966 QQuick3DObjectPrivate::get(item: sfi)->subFocusItem = nullptr;
967 sfi = sfi->parentItem();
968 }
969 }
970
971 if (focus) {
972 scopePrivate->subFocusItem = q;
973 QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
974 while (sfi && sfi != scope) {
975 QQuick3DObjectPrivate::get(item: sfi)->subFocusItem = q;
976 sfi = sfi->parentItem();
977 }
978 } else {
979 scopePrivate->subFocusItem = nullptr;
980 }
981}
982
983void QQuick3DObjectPrivate::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &data)
984{
985 Q_Q(QQuick3DObject);
986 switch (change) {
987 case QQuick3DObject::ItemRotationHasChanged:
988 // TODO:
989 qWarning(msg: "ItemRoationHasChange is unhandled!!!!");
990 break;
991 case QQuick3DObject::ItemChildAddedChange: {
992 q->itemChange(change, data);
993 if (!changeListeners.isEmpty()) {
994 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
995 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
996 if (change.types & QQuick3DObjectPrivate::Children) {
997 change.listener->itemChildAdded(q, data.item);
998 }
999 }
1000 }
1001 break;
1002 }
1003 case QQuick3DObject::ItemChildRemovedChange: {
1004 q->itemChange(change, data);
1005 if (!changeListeners.isEmpty()) {
1006 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
1007 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
1008 if (change.types & QQuick3DObjectPrivate::Children) {
1009 change.listener->itemChildRemoved(q, data.item);
1010 }
1011 }
1012 }
1013 break;
1014 }
1015 case QQuick3DObject::ItemSceneChange:
1016 q->itemChange(change, data);
1017 break;
1018 case QQuick3DObject::ItemVisibleHasChanged: {
1019 q->itemChange(change, data);
1020 if (!changeListeners.isEmpty()) {
1021 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
1022 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
1023 if (change.types & QQuick3DObjectPrivate::Visibility) {
1024 change.listener->itemVisibilityChanged(q);
1025 }
1026 }
1027 }
1028 break;
1029 }
1030 case QQuick3DObject::ItemEnabledHasChanged: {
1031 q->itemChange(change, data);
1032 if (!changeListeners.isEmpty()) {
1033 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
1034 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
1035 if (change.types & QQuick3DObjectPrivate::Enabled) {
1036 change.listener->itemEnabledChanged(q);
1037 }
1038 }
1039 }
1040 break;
1041 }
1042 case QQuick3DObject::ItemParentHasChanged: {
1043 q->itemChange(change, data);
1044 if (!changeListeners.isEmpty()) {
1045 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
1046 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
1047 if (change.types & QQuick3DObjectPrivate::Parent) {
1048 change.listener->itemParentChanged(q, data.item);
1049 }
1050 }
1051 }
1052 break;
1053 }
1054 case QQuick3DObject::ItemOpacityHasChanged: {
1055 q->itemChange(change, data);
1056 if (!changeListeners.isEmpty()) {
1057 const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
1058 for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
1059 if (change.types & QQuick3DObjectPrivate::Opacity) {
1060 change.listener->itemOpacityChanged(q);
1061 }
1062 }
1063 }
1064 break;
1065 }
1066 case QQuick3DObject::ItemActiveFocusHasChanged:
1067 q->itemChange(change, data);
1068 break;
1069 case QQuick3DObject::ItemAntialiasingHasChanged:
1070 // fall through
1071 case QQuick3DObject::ItemDevicePixelRatioHasChanged:
1072 q->itemChange(change, data);
1073 break;
1074 }
1075}
1076
1077namespace QV4 {
1078namespace Heap {
1079struct QSSGItemWrapper : public QObjectWrapper
1080{
1081 static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
1082};
1083}
1084}
1085
1086struct QSSGItemWrapper : public QV4::QObjectWrapper
1087{
1088 V4_OBJECT2(QSSGItemWrapper, QV4::QObjectWrapper)
1089};
1090
1091DEFINE_OBJECT_VTABLE(QSSGItemWrapper);
1092
1093void QV4::Heap::QSSGItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
1094{
1095 QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
1096 if (QQuick3DObject *item = static_cast<QQuick3DObject *>(This->object())) {
1097 for (QQuick3DObject *child : std::as_const(t&: QQuick3DObjectPrivate::get(item)->childItems))
1098 QV4::QObjectWrapper::markWrapper(object: child, markStack);
1099 }
1100 QObjectWrapper::markObjects(that, markStack);
1101}
1102
1103quint64 QQuick3DObjectPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
1104{
1105 return (engine->memoryManager->allocate<QSSGItemWrapper>(args: q_func()))->asReturnedValue();
1106}
1107
1108QQuick3DObjectPrivate::ExtraData::ExtraData() : hideRefCount(0) {}
1109
1110QT_END_NAMESPACE
1111
1112#include "moc_qquick3dobject.cpp"
1113

source code of qtquick3d/src/quick3d/qquick3dobject.cpp