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