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