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