1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Quick 3D.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qquick3dobject.h"
31 #include "qquick3dobject_p.h"
32 #include "qquick3dscenemanager_p.h"
33 #include "qquick3ditem2d_p.h"
34 
35 #include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
36 
37 #include <QtQml/private/qqmlglobal_p.h>
38 #include <QtQuick/private/qquickstategroup_p.h>
39 #include <QtQuick/private/qquickstate_p.h>
40 #include <QtQuick/private/qquickitem_p.h>
41 
42 #include <private/qv4object_p.h>
43 #include <private/qv4qobjectwrapper_p.h>
44 
45 QT_BEGIN_NAMESPACE
46 
47 
48 /*!
49     \qmltype Object3D
50     \inqmlmodule QtQuick3D
51     \brief Abstract base type of all 3D nodes and resources.
52 */
53 
54 /*!
55     \class QQuick3DObject
56     \inmodule QtQuick3D
57     \since 5.15
58     \brief Base class of all 3D nodes and resources.
59 */
60 
QQuick3DObject(QQuick3DObject * parent)61 QQuick3DObject::QQuick3DObject(QQuick3DObject *parent)
62     : QObject(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::Unknown)), parent)
63 {
64     Q_D(QQuick3DObject);
65     d->init(parent);
66 }
67 
~QQuick3DObject()68 QQuick3DObject::~QQuick3DObject()
69 {
70     Q_D(QQuick3DObject);
71     if (d->windowRefCount > 1)
72         d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow().
73     if (d->parentItem)
74         setParentItem(nullptr);
75     else if (d->sceneManager)
76         QQuick3DObjectPrivate::derefSceneManager(this);
77 
78     // XXX todo - optimize
79     while (!d->childItems.isEmpty())
80         d->childItems.constFirst()->setParentItem(nullptr);
81 
82     delete d->_stateGroup;
83     d->_stateGroup = nullptr;
84 }
85 
update()86 void QQuick3DObject::update()
87 {
88     Q_D(QQuick3DObject);
89     d->dirty(QQuick3DObjectPrivate::Content);
90 }
91 
setParentItem(QQuick3DObject * parentItem)92 void QQuick3DObject::setParentItem(QQuick3DObject *parentItem)
93 {
94     Q_D(QQuick3DObject);
95     if (parentItem == d->parentItem)
96         return;
97 
98     if (parentItem) {
99         QQuick3DObject *itemAncestor = parentItem;
100         while (itemAncestor != nullptr) {
101             if (Q_UNLIKELY(itemAncestor == this)) {
102                 qWarning() << "QSSGObject::setParentItem: Parent" << parentItem << "is already part of the subtree of" << this;
103                 return;
104             }
105             itemAncestor = itemAncestor->parentItem();
106         }
107     }
108 
109     d->removeFromDirtyList();
110 
111     QQuick3DObject *oldParentItem = d->parentItem;
112 
113     if (oldParentItem) {
114         QQuick3DObjectPrivate *op = QQuick3DObjectPrivate::get(oldParentItem);
115 
116         op->removeChild(this);
117     } else if (d->sceneManager) {
118         d->sceneManager->parentlessItems.remove(this);
119     }
120 
121     const auto parentSceneManager = parentItem ? QQuick3DObjectPrivate::get(parentItem)->sceneManager : nullptr;
122     if (d->sceneManager == parentSceneManager) {
123         // Avoid freeing and reallocating resources if the window stays the same.
124         d->parentItem = parentItem;
125     } else {
126         if (d->sceneManager)
127             QQuick3DObjectPrivate::derefSceneManager(this);
128         d->parentItem = parentItem;
129         if (parentSceneManager)
130             QQuick3DObjectPrivate::refSceneManager(this, parentSceneManager);
131     }
132 
133     d->dirty(QQuick3DObjectPrivate::ParentChanged);
134 
135     if (d->parentItem)
136         QQuick3DObjectPrivate::get(d->parentItem)->addChild(this);
137     else if (d->sceneManager)
138         d->sceneManager->parentlessItems.insert(this);
139 
140     d->itemChange(ItemParentHasChanged, d->parentItem);
141 
142     emit parentChanged();
143 }
144 
state() const145 QString QQuick3DObject::state() const
146 {
147     Q_D(const QQuick3DObject);
148     return d->state();
149 }
150 
setState(const QString & state)151 void QQuick3DObject::setState(const QString &state)
152 {
153     Q_D(QQuick3DObject);
154     d->setState(state);
155 }
156 
childItems() const157 QList<QQuick3DObject *> QQuick3DObject::childItems() const
158 {
159     Q_D(const QQuick3DObject);
160     return d->childItems;
161 }
162 
parentItem() const163 QQuick3DObject *QQuick3DObject::parentItem() const
164 {
165     Q_D(const QQuick3DObject);
166     return d->parentItem;
167 }
168 
markAllDirty()169 void QQuick3DObject::markAllDirty()
170 {
171 }
172 
itemChange(QQuick3DObject::ItemChange,const QQuick3DObject::ItemChangeData &)173 void QQuick3DObject::itemChange(QQuick3DObject::ItemChange, const QQuick3DObject::ItemChangeData &)
174 {
175 }
176 
QQuick3DObject(QQuick3DObjectPrivate & dd,QQuick3DObject * parent)177 QQuick3DObject::QQuick3DObject(QQuick3DObjectPrivate &dd, QQuick3DObject *parent)
178     : QObject(dd, parent)
179 {
180     Q_D(QQuick3DObject);
181     d->init(parent);
182 }
183 
classBegin()184 void QQuick3DObject::classBegin()
185 {
186     Q_D(QQuick3DObject);
187     d->componentComplete = false;
188     if (d->_stateGroup)
189         d->_stateGroup->classBegin();
190 }
191 
componentComplete()192 void QQuick3DObject::componentComplete()
193 {
194     Q_D(QQuick3DObject);
195     d->componentComplete = true;
196     if (d->_stateGroup)
197         d->_stateGroup->componentComplete();
198 
199     if (d->sceneManager && d->dirtyAttributes) {
200         d->addToDirtyList();
201         d->sceneManager->dirtyItem(this);
202     }
203 }
204 
isComponentComplete() const205 bool QQuick3DObject::isComponentComplete() const
206 {
207     Q_D(const QQuick3DObject);
208     return d->componentComplete;
209 }
210 
updatePropertyListener(QQuick3DObject * newO,QQuick3DObject * oldO,const QSharedPointer<QQuick3DSceneManager> & window,const QByteArray & propertyKey,QQuick3DObject::ConnectionMap & connections,const std::function<void (QQuick3DObject *)> & callFn)211 void QQuick3DObject::updatePropertyListener(QQuick3DObject *newO,
212                                             QQuick3DObject *oldO,
213                                             const QSharedPointer<QQuick3DSceneManager> &window,
214                                             const QByteArray &propertyKey,
215                                             QQuick3DObject::ConnectionMap &connections,
216                                             const std::function<void(QQuick3DObject *)> &callFn)
217 {
218     // disconnect previous destruction listern
219     if (oldO) {
220         if (window)
221             QQuick3DObjectPrivate::derefSceneManager(oldO);
222 
223         auto connection = connections.find(propertyKey);
224         if (connection != connections.end()) {
225             QObject::disconnect(connection.value());
226             connections.erase(connection);
227         }
228     }
229 
230     // listen for new map's destruction
231     if (newO) {
232         if (window)
233             QQuick3DObjectPrivate::refSceneManager(newO, window);
234         auto connection = QObject::connect(newO, &QObject::destroyed, [callFn](){
235             callFn(nullptr);
236         });
237         connections.insert(propertyKey, connection);
238     }
239 }
240 
QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type t)241 QQuick3DObjectPrivate::QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type t)
242     : _stateGroup(nullptr)
243     , dirtyAttributes(0)
244     , nextDirtyItem(nullptr)
245     , prevDirtyItem(nullptr)
246     , sceneManager(nullptr)
247     , windowRefCount(0)
248     , parentItem(nullptr)
249     , sortedChildItems(&childItems)
250     , subFocusItem(nullptr)
251     , type(t)
252 {
253 }
254 
~QQuick3DObjectPrivate()255 QQuick3DObjectPrivate::~QQuick3DObjectPrivate()
256 {
257     if (sortedChildItems != &childItems)
258         delete sortedChildItems;
259 }
260 
init(QQuick3DObject * parent)261 void QQuick3DObjectPrivate::init(QQuick3DObject *parent)
262 {
263     Q_Q(QQuick3DObject);
264 
265     if (parent)
266         q->setParentItem(parent);
267 }
268 
data()269 QQmlListProperty<QObject> QQuick3DObjectPrivate::data()
270 {
271     return QQmlListProperty<QObject>(q_func(),
272                                      nullptr,
273                                      QQuick3DObjectPrivate::data_append,
274                                      QQuick3DObjectPrivate::data_count,
275                                      QQuick3DObjectPrivate::data_at,
276                                      QQuick3DObjectPrivate::data_clear);
277 }
278 
resources()279 QQmlListProperty<QObject> QQuick3DObjectPrivate::resources()
280 {
281     return QQmlListProperty<QObject>(q_func(),
282                                      nullptr,
283                                      QQuick3DObjectPrivate::resources_append,
284                                      QQuick3DObjectPrivate::resources_count,
285                                      QQuick3DObjectPrivate::resources_at,
286                                      QQuick3DObjectPrivate::resources_clear);
287 }
288 
children()289 QQmlListProperty<QQuick3DObject> QQuick3DObjectPrivate::children()
290 {
291     return QQmlListProperty<QQuick3DObject>(q_func(),
292                                           nullptr,
293                                           QQuick3DObjectPrivate::children_append,
294                                           QQuick3DObjectPrivate::children_count,
295                                           QQuick3DObjectPrivate::children_at,
296                                           QQuick3DObjectPrivate::children_clear);
297 }
298 
states()299 QQmlListProperty<QQuickState> QQuick3DObjectPrivate::states()
300 {
301     return _states()->statesProperty();
302 }
303 
transitions()304 QQmlListProperty<QQuickTransition> QQuick3DObjectPrivate::transitions()
305 {
306     return _states()->transitionsProperty();
307 }
308 
state() const309 QString QQuick3DObjectPrivate::state() const
310 {
311     return _stateGroup ? _stateGroup->state() : QString();
312 }
313 
setState(const QString & state)314 void QQuick3DObjectPrivate::setState(const QString &state)
315 {
316     _states()->setState(state);
317 }
318 
data_append(QQmlListProperty<QObject> * prop,QObject * o)319 void QQuick3DObjectPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
320 {
321     if (!o)
322         return;
323 
324     QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
325 
326     if (QQuick3DObject *item = qmlobject_cast<QQuick3DObject *>(o)) {
327         item->setParentItem(that);
328 
329     } else {
330 //        QSSGSceneRenderer *thisSceneRenderer = qmlobject_cast<QSSGSceneRenderer *>(o);
331 //        item = that;
332 //        QSSGSceneRenderer *itemSceneRenderer = that->sceneRenderer();
333 //        while (!itemSceneRenderer && item && item->parentItem()) {
334 //            item = item->parentItem();
335 //            itemSceneRenderer = item->sceneRenderer();
336 //        }
337 
338 //        if (thisSceneRenderer) {
339 //            if (itemSceneRenderer) {
340 //                // qCDebug(lcTransient) << thisWindow << "is transient for" << itemWindow;
341 //                thisSceneRenderer->setTransientParent(itemSceneRenderer);
342 //            } else {
343 //                QObject::connect(item, SIGNAL(sceneRendererChanged(QSSGSceneRenderer *)), thisSceneRenderer, SLOT(setTransientParent_helper(QSSGSceneRenderer *)));
344 //            }
345 //        }
346 
347         QQuickItem *quickItem = qobject_cast<QQuickItem *>(o);
348         if (quickItem) {
349             QQuick3DItem2D *item2d = new QQuick3DItem2D(quickItem);
350             item2d->setParent(that);
351             item2d->setParentItem(that);
352         } else {
353             o->setParent(that);
354         }
355     }
356     resources_append(prop, o);
357 }
358 
data_count(QQmlListProperty<QObject> * property)359 int QQuick3DObjectPrivate::data_count(QQmlListProperty<QObject> *property)
360 {
361     QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
362     QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
363     QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
364     QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
365 
366     return resources_count(&resourcesProperty) + children_count(&childrenProperty);
367 }
368 
data_at(QQmlListProperty<QObject> * property,int i)369 QObject *QQuick3DObjectPrivate::data_at(QQmlListProperty<QObject> *property, int i)
370 {
371     QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
372     QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
373     QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
374     QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
375 
376     int resourcesCount = resources_count(&resourcesProperty);
377     if (i < resourcesCount)
378         return resources_at(&resourcesProperty, i);
379     const int j = i - resourcesCount;
380     if (j < children_count(&childrenProperty))
381         return children_at(&childrenProperty, j);
382     return nullptr;
383 }
384 
data_clear(QQmlListProperty<QObject> * property)385 void QQuick3DObjectPrivate::data_clear(QQmlListProperty<QObject> *property)
386 {
387     QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
388     QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
389     QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
390     QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
391 
392     resources_clear(&resourcesProperty);
393     children_clear(&childrenProperty);
394 }
395 
resources_at(QQmlListProperty<QObject> * prop,int index)396 QObject *QQuick3DObjectPrivate::resources_at(QQmlListProperty<QObject> *prop, int index)
397 {
398     QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
399     return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.value(index) : 0;
400 }
401 
resources_append(QQmlListProperty<QObject> * prop,QObject * object)402 void QQuick3DObjectPrivate::resources_append(QQmlListProperty<QObject> *prop, QObject *object)
403 {
404     QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
405     QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(quickItem);
406     if (!quickItemPrivate->extra.value().resourcesList.contains(object)) {
407         quickItemPrivate->extra.value().resourcesList.append(object);
408         // clang-format off
409         qmlobject_connect(object, QObject, SIGNAL(destroyed(QObject*)),
410                           quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
411         // clang-format on
412     }
413 }
414 
resources_count(QQmlListProperty<QObject> * prop)415 int QQuick3DObjectPrivate::resources_count(QQmlListProperty<QObject> *prop)
416 {
417     QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
418     return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.count() : 0;
419 }
420 
resources_clear(QQmlListProperty<QObject> * prop)421 void QQuick3DObjectPrivate::resources_clear(QQmlListProperty<QObject> *prop)
422 {
423     QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
424     QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(quickItem);
425     if (quickItemPrivate->extra.isAllocated()) { // If extra is not allocated resources is empty.
426         for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) {
427             // clang-format off
428             qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
429                                  quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
430             // clang-format on
431         }
432         quickItemPrivate->extra->resourcesList.clear();
433     }
434 }
435 
children_append(QQmlListProperty<QQuick3DObject> * prop,QQuick3DObject * o)436 void QQuick3DObjectPrivate::children_append(QQmlListProperty<QQuick3DObject> *prop, QQuick3DObject *o)
437 {
438     if (!o)
439         return;
440 
441     QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
442     if (o->parentItem() == that)
443         o->setParentItem(nullptr);
444 
445     o->setParentItem(that);
446 }
447 
children_count(QQmlListProperty<QQuick3DObject> * prop)448 int QQuick3DObjectPrivate::children_count(QQmlListProperty<QQuick3DObject> *prop)
449 {
450     QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
451     return p->childItems.count();
452 }
453 
children_at(QQmlListProperty<QQuick3DObject> * prop,int index)454 QQuick3DObject *QQuick3DObjectPrivate::children_at(QQmlListProperty<QQuick3DObject> *prop, int index)
455 {
456     QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
457     if (index >= p->childItems.count() || index < 0)
458         return nullptr;
459 
460     return p->childItems.at(index);
461 }
462 
children_clear(QQmlListProperty<QQuick3DObject> * prop)463 void QQuick3DObjectPrivate::children_clear(QQmlListProperty<QQuick3DObject> *prop)
464 {
465     QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
466     QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(that);
467     while (!p->childItems.isEmpty())
468         p->childItems.at(0)->setParentItem(nullptr);
469 }
470 
_q_resourceObjectDeleted(QObject * object)471 void QQuick3DObjectPrivate::_q_resourceObjectDeleted(QObject *object)
472 {
473     if (extra.isAllocated() && extra->resourcesList.contains(object))
474         extra->resourcesList.removeAll(object);
475 }
476 
addItemChangeListener(QQuick3DObjectChangeListener * listener,ChangeTypes types)477 void QQuick3DObjectPrivate::addItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
478 {
479     changeListeners.append(ChangeListener(listener, types));
480 }
481 
updateOrAddItemChangeListener(QQuick3DObjectChangeListener * listener,ChangeTypes types)482 void QQuick3DObjectPrivate::updateOrAddItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
483 {
484     const ChangeListener changeListener(listener, types);
485     const int index = changeListeners.indexOf(changeListener);
486     if (index > -1)
487         changeListeners[index].types = changeListener.types;
488     else
489         changeListeners.append(changeListener);
490 }
491 
removeItemChangeListener(QQuick3DObjectChangeListener * listener,ChangeTypes types)492 void QQuick3DObjectPrivate::removeItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
493 {
494     ChangeListener change(listener, types);
495     changeListeners.removeOne(change);
496 }
497 
_states()498 QQuickStateGroup *QQuick3DObjectPrivate::_states()
499 {
500     Q_Q(QQuick3DObject);
501     if (!_stateGroup) {
502         _stateGroup = new QQuickStateGroup;
503         if (!componentComplete)
504             _stateGroup->classBegin();
505         // clang-format off
506         qmlobject_connect(_stateGroup, QQuickStateGroup, SIGNAL(stateChanged(QString)),
507                           q, QQuick3DObject, SIGNAL(stateChanged()));
508         // clang-format on
509     }
510 
511     return _stateGroup;
512 }
513 
dirtyToString() const514 QString QQuick3DObjectPrivate::dirtyToString() const
515 {
516 #define DIRTY_TO_STRING(value)                                                                                         \
517     if (dirtyAttributes & value) {                                                                                     \
518         if (!rv.isEmpty())                                                                                             \
519             rv.append(QLatin1Char('|'));                                                                               \
520         rv.append(QLatin1String(#value));                                                                              \
521     }
522 
523     //    QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16);
524     QString rv;
525 
526     DIRTY_TO_STRING(TransformOrigin);
527     DIRTY_TO_STRING(Transform);
528     DIRTY_TO_STRING(BasicTransform);
529     DIRTY_TO_STRING(Position);
530     DIRTY_TO_STRING(Size);
531     DIRTY_TO_STRING(ZValue);
532     DIRTY_TO_STRING(Content);
533     DIRTY_TO_STRING(Smooth);
534     DIRTY_TO_STRING(OpacityValue);
535     DIRTY_TO_STRING(ChildrenChanged);
536     DIRTY_TO_STRING(ChildrenStackingChanged);
537     DIRTY_TO_STRING(ParentChanged);
538     DIRTY_TO_STRING(Clip);
539     DIRTY_TO_STRING(Window);
540     DIRTY_TO_STRING(EffectReference);
541     DIRTY_TO_STRING(Visible);
542     DIRTY_TO_STRING(HideReference);
543     DIRTY_TO_STRING(Antialiasing);
544 
545     return rv;
546 }
547 
dirty(QQuick3DObjectPrivate::DirtyType type)548 void QQuick3DObjectPrivate::dirty(QQuick3DObjectPrivate::DirtyType type)
549 {
550     Q_Q(QQuick3DObject);
551     if (!(dirtyAttributes & type) || (sceneManager && !prevDirtyItem)) {
552         dirtyAttributes |= type;
553         if (sceneManager && componentComplete) {
554             addToDirtyList();
555             sceneManager->dirtyItem(q);
556         }
557     }
558 }
559 
addToDirtyList()560 void QQuick3DObjectPrivate::addToDirtyList()
561 {
562     Q_Q(QQuick3DObject);
563 
564     Q_ASSERT(sceneManager);
565     if (!prevDirtyItem) {
566         Q_ASSERT(!nextDirtyItem);
567 
568         if (isResourceNode()) {
569             if (type == Type::Image) {
570                 // Will likely need to refactor this, but images need to come before other
571                 // resources
572                 nextDirtyItem = sceneManager->dirtyImageList;
573                 if (nextDirtyItem)
574                     QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
575                 prevDirtyItem = &sceneManager->dirtyImageList;
576                 sceneManager->dirtyImageList = q;
577             } else {
578                 nextDirtyItem = sceneManager->dirtyResourceList;
579                 if (nextDirtyItem)
580                     QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
581                 prevDirtyItem = &sceneManager->dirtyResourceList;
582                 sceneManager->dirtyResourceList = q;
583             }
584         } else {
585             if (type == Type::Light) {
586                 // needed for scoped lights second pass
587                sceneManager->dirtyLightList.append(q);
588             }
589 
590             nextDirtyItem = sceneManager->dirtySpatialNodeList;
591             if (nextDirtyItem)
592                 QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
593             prevDirtyItem = &sceneManager->dirtySpatialNodeList;
594             sceneManager->dirtySpatialNodeList = q;
595         }
596 
597         sceneManager->dirtyItem(q);
598     }
599     Q_ASSERT(prevDirtyItem);
600 }
601 
removeFromDirtyList()602 void QQuick3DObjectPrivate::removeFromDirtyList()
603 {
604     if (prevDirtyItem) {
605         if (nextDirtyItem)
606             QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem;
607         *prevDirtyItem = nextDirtyItem;
608         prevDirtyItem = nullptr;
609         nextDirtyItem = nullptr;
610     }
611     Q_ASSERT(!prevDirtyItem);
612     Q_ASSERT(!nextDirtyItem);
613 }
614 
isResourceNode() const615 bool QQuick3DObjectPrivate::isResourceNode() const
616 {
617     switch (type) {
618     case Type::Layer:
619     case Type::Node:
620     case Type::Light:
621     case Type::Camera:
622     case Type::Model:
623     case Type::Text:
624     case Type::Item2D:
625         return false;
626     case Type::SceneEnvironment:
627     case Type::DefaultMaterial:
628     case Type::PrincipledMaterial:
629     case Type::Image:
630     case Type::CustomMaterial:
631     case Type::Lightmaps:
632     case Type::Geometry:
633         return true;
634     default:
635         return false;
636     }
637 }
638 
isSpatialNode() const639 bool QQuick3DObjectPrivate::isSpatialNode() const
640 {
641     switch (type) {
642     case Type::Layer:
643     case Type::Node:
644     case Type::Light:
645     case Type::Camera:
646     case Type::Model:
647     case Type::Text:
648         return true;
649     case Type::SceneEnvironment:
650     case Type::DefaultMaterial:
651     case Type::PrincipledMaterial:
652     case Type::Image:
653     case Type::CustomMaterial:
654     case Type::Lightmaps:
655     case Type::Geometry:
656     default:
657         return false;
658     }
659 }
660 
setCulled(bool cull)661 void QQuick3DObjectPrivate::setCulled(bool cull)
662 {
663     if (cull == culled)
664         return;
665 
666     culled = cull;
667     if ((cull && ++extra.value().hideRefCount == 1) || (!cull && --extra.value().hideRefCount == 0))
668         dirty(HideReference);
669 }
670 
paintOrderChildItems() const671 QList<QQuick3DObject *> QQuick3DObjectPrivate::paintOrderChildItems() const
672 {
673     if (sortedChildItems)
674         return *sortedChildItems;
675 
676     sortedChildItems = const_cast<QList<QQuick3DObject *> *>(&childItems);
677 
678     return childItems;
679 }
680 
addChild(QQuick3DObject * child)681 void QQuick3DObjectPrivate::addChild(QQuick3DObject *child)
682 {
683     Q_Q(QQuick3DObject);
684 
685     Q_ASSERT(!childItems.contains(child));
686 
687     childItems.append(child);
688 
689     markSortedChildrenDirty(child);
690     dirty(QQuick3DObjectPrivate::ChildrenChanged);
691 
692     itemChange(QQuick3DObject::ItemChildAddedChange, child);
693 
694     emit q->childrenChanged();
695 }
696 
removeChild(QQuick3DObject * child)697 void QQuick3DObjectPrivate::removeChild(QQuick3DObject *child)
698 {
699     Q_Q(QQuick3DObject);
700 
701     Q_ASSERT(child);
702     Q_ASSERT(childItems.contains(child));
703     childItems.removeOne(child);
704     Q_ASSERT(!childItems.contains(child));
705 
706     markSortedChildrenDirty(child);
707     dirty(QQuick3DObjectPrivate::ChildrenChanged);
708 
709     itemChange(QQuick3DObject::ItemChildRemovedChange, child);
710 
711     emit q->childrenChanged();
712 }
713 
siblingOrderChanged()714 void QQuick3DObjectPrivate::siblingOrderChanged()
715 {
716     Q_Q(QQuick3DObject);
717     if (!changeListeners.isEmpty()) {
718         const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
719         for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
720             if (change.types & QQuick3DObjectPrivate::SiblingOrder) {
721                 change.listener->itemSiblingOrderChanged(q);
722             }
723         }
724     }
725 }
726 
markSortedChildrenDirty(QQuick3DObject * child)727 void QQuick3DObjectPrivate::markSortedChildrenDirty(QQuick3DObject *child)
728 {
729     Q_UNUSED(child);
730 }
731 
refSceneManager(const QSharedPointer<QQuick3DSceneManager> & c)732 void QQuick3DObjectPrivate::refSceneManager(const QSharedPointer<QQuick3DSceneManager> &c)
733 {
734     // An item needs a window if it is referenced by another item which has a window.
735     // Typically the item is referenced by a parent, but can also be referenced by a
736     // ShaderEffect or ShaderEffectSource. 'windowRefCount' counts how many items with
737     // a window is referencing this item. When the reference count goes from zero to one,
738     // or one to zero, the window of this item is updated and propagated to the children.
739     // As long as the reference count stays above zero, the window is unchanged.
740     // refWindow() increments the reference count.
741     // derefWindow() decrements the reference count.
742 
743     Q_Q(QQuick3DObject);
744     Q_ASSERT((sceneManager != nullptr) == (windowRefCount > 0));
745     Q_ASSERT(c);
746     if (++windowRefCount > 1) {
747         if (c != sceneManager)
748             qWarning("QSSGObject: Cannot use same item on different windows at the same time.");
749         return; // Window already set.
750     }
751 
752     Q_ASSERT(sceneManager == nullptr);
753     sceneManager = c;
754 
755     //    if (polishScheduled)
756     //        QSSGWindowPrivate::get(window)->itemsToPolish.append(q);
757 
758     if (!parentItem)
759         sceneManager->parentlessItems.insert(q);
760 
761     for (int ii = 0; ii < childItems.count(); ++ii) {
762         QQuick3DObject *child = childItems.at(ii);
763         QQuick3DObjectPrivate::refSceneManager(child, c);
764     }
765 
766     dirty(Window);
767 
768     itemChange(QQuick3DObject::ItemSceneChange, c);
769 }
770 
derefSceneManager()771 void QQuick3DObjectPrivate::derefSceneManager()
772 {
773     Q_Q(QQuick3DObject);
774 
775     if (!sceneManager)
776         return; // This can happen when destroying recursive shader effect sources.
777 
778     if (--windowRefCount > 0)
779         return; // There are still other references, so don't set window to null yet.
780 
781     removeFromDirtyList();
782     if (sceneManager) {
783         sceneManager->dirtyBoundingBoxList.removeAll(q);
784         sceneManager->dirtyLightList.removeAll(q);
785     }
786 
787     if (spatialNode)
788         sceneManager->cleanup(spatialNode);
789     if (!parentItem)
790         sceneManager->parentlessItems.remove(q);
791 
792     sceneManager.reset();
793 
794     spatialNode = nullptr;
795 
796     for (int ii = 0; ii < childItems.count(); ++ii) {
797         QQuick3DObject *child = childItems.at(ii);
798         QQuick3DObjectPrivate::derefSceneManager(child);
799     }
800 
801     dirty(Window);
802 
803     itemChange(QQuick3DObject::ItemSceneChange, sceneManager);
804 }
805 
updateSubFocusItem(QQuick3DObject * scope,bool focus)806 void QQuick3DObjectPrivate::updateSubFocusItem(QQuick3DObject *scope, bool focus)
807 {
808     Q_Q(QQuick3DObject);
809     Q_ASSERT(scope);
810 
811     QQuick3DObjectPrivate *scopePrivate = QQuick3DObjectPrivate::get(scope);
812 
813     QQuick3DObject *oldSubFocusItem = scopePrivate->subFocusItem;
814     // Correct focus chain in scope
815     if (oldSubFocusItem) {
816         QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
817         while (sfi && sfi != scope) {
818             QQuick3DObjectPrivate::get(sfi)->subFocusItem = nullptr;
819             sfi = sfi->parentItem();
820         }
821     }
822 
823     if (focus) {
824         scopePrivate->subFocusItem = q;
825         QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
826         while (sfi && sfi != scope) {
827             QQuick3DObjectPrivate::get(sfi)->subFocusItem = q;
828             sfi = sfi->parentItem();
829         }
830     } else {
831         scopePrivate->subFocusItem = nullptr;
832     }
833 }
834 
itemChange(QQuick3DObject::ItemChange change,const QQuick3DObject::ItemChangeData & data)835 void QQuick3DObjectPrivate::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &data)
836 {
837     Q_Q(QQuick3DObject);
838     switch (change) {
839     case QQuick3DObject::ItemRotationHasChanged:
840         // TODO:
841         qWarning("ItemRoationHasChange is unhandled!!!!");
842         break;
843     case QQuick3DObject::ItemChildAddedChange: {
844         q->itemChange(change, data);
845         if (!changeListeners.isEmpty()) {
846             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
847             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
848                 if (change.types & QQuick3DObjectPrivate::Children) {
849                     change.listener->itemChildAdded(q, data.item);
850                 }
851             }
852         }
853         break;
854     }
855     case QQuick3DObject::ItemChildRemovedChange: {
856         q->itemChange(change, data);
857         if (!changeListeners.isEmpty()) {
858             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
859             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
860                 if (change.types & QQuick3DObjectPrivate::Children) {
861                     change.listener->itemChildRemoved(q, data.item);
862                 }
863             }
864         }
865         break;
866     }
867     case QQuick3DObject::ItemSceneChange:
868         q->itemChange(change, data);
869         break;
870     case QQuick3DObject::ItemVisibleHasChanged: {
871         q->itemChange(change, data);
872         if (!changeListeners.isEmpty()) {
873             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
874             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
875                 if (change.types & QQuick3DObjectPrivate::Visibility) {
876                     change.listener->itemVisibilityChanged(q);
877                 }
878             }
879         }
880         break;
881     }
882     case QQuick3DObject::ItemEnabledHasChanged: {
883         q->itemChange(change, data);
884         if (!changeListeners.isEmpty()) {
885             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
886             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
887                 if (change.types & QQuick3DObjectPrivate::Enabled) {
888                     change.listener->itemEnabledChanged(q);
889                 }
890             }
891         }
892         break;
893     }
894     case QQuick3DObject::ItemParentHasChanged: {
895         q->itemChange(change, data);
896         if (!changeListeners.isEmpty()) {
897             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
898             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
899                 if (change.types & QQuick3DObjectPrivate::Parent) {
900                     change.listener->itemParentChanged(q, data.item);
901                 }
902             }
903         }
904         break;
905     }
906     case QQuick3DObject::ItemOpacityHasChanged: {
907         q->itemChange(change, data);
908         if (!changeListeners.isEmpty()) {
909             const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
910             for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
911                 if (change.types & QQuick3DObjectPrivate::Opacity) {
912                     change.listener->itemOpacityChanged(q);
913                 }
914             }
915         }
916         break;
917     }
918     case QQuick3DObject::ItemActiveFocusHasChanged:
919         q->itemChange(change, data);
920         break;
921     case QQuick3DObject::ItemAntialiasingHasChanged:
922         // fall through
923     case QQuick3DObject::ItemDevicePixelRatioHasChanged:
924         q->itemChange(change, data);
925         break;
926     }
927 }
928 
929 namespace QV4 {
930 namespace Heap {
931 struct QSSGItemWrapper : public QObjectWrapper
932 {
933     static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
934 };
935 }
936 }
937 
938 struct QSSGItemWrapper : public QV4::QObjectWrapper
939 {
940     V4_OBJECT2(QSSGItemWrapper, QV4::QObjectWrapper)
941 };
942 
943 DEFINE_OBJECT_VTABLE(QSSGItemWrapper);
944 
markObjects(QV4::Heap::Base * that,QV4::MarkStack * markStack)945 void QV4::Heap::QSSGItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
946 {
947     QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
948     if (QQuick3DObject *item = static_cast<QQuick3DObject *>(This->object())) {
949         for (QQuick3DObject *child : qAsConst(QQuick3DObjectPrivate::get(item)->childItems))
950             QV4::QObjectWrapper::markWrapper(child, markStack);
951     }
952     QObjectWrapper::markObjects(that, markStack);
953 }
954 
_q_createJSWrapper(QV4::ExecutionEngine * engine)955 quint64 QQuick3DObjectPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
956 {
957     return (engine->memoryManager->allocate<QSSGItemWrapper>(q_func()))->asReturnedValue();
958 }
959 
ExtraData()960 QQuick3DObjectPrivate::ExtraData::ExtraData() : hideRefCount(0) {}
961 
962 QT_END_NAMESPACE
963 
964 #include "moc_qquick3dobject.cpp"
965