summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/3dstudioruntime2/3dstudioruntime2.pro3
-rw-r--r--src/imports/studio3d/plugin.cpp4
-rw-r--r--src/runtime/aping/aping.pri10
-rw-r--r--src/runtime/aping/q3dsgroup3d.cpp73
-rw-r--r--src/runtime/aping/q3dsgroup3d_p.h64
-rw-r--r--src/runtime/aping/q3dslayer3d.cpp134
-rw-r--r--src/runtime/aping/q3dslayer3d_p.h15
-rw-r--r--src/runtime/aping/q3dsnode3d.cpp60
-rw-r--r--src/runtime/aping/q3dsnode3d_p.h63
-rw-r--r--src/runtime/aping/q3dsobject3d.cpp222
-rw-r--r--src/runtime/aping/q3dsobject3d_p.h96
-rw-r--r--src/runtime/aping/q3dsstudio3dengine.cpp17
-rw-r--r--src/runtime/aping/q3dsstudio3dengine_p.h4
-rw-r--r--src/runtime/profileui/q3dsprofileui.cpp5
-rw-r--r--src/runtime/profileui/q3dsprofileui_p.h1
-rw-r--r--src/runtime/q3dsinputmanager_p.h2
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/studio3dengine/data/simple.qml22
-rw-r--r--tests/auto/studio3dengine/studio3dengine.pro8
-rw-r--r--tests/auto/studio3dengine/studio3dengine.qrc5
-rw-r--r--tests/auto/studio3dengine/tst_studio3dengine.cpp334
-rw-r--r--tests/manual/manual.pro4
-rw-r--r--tests/manual/pureqml3d/main.cpp (renamed from examples/3dstudioruntime2/pureqml3d/main.cpp)3
-rw-r--r--tests/manual/pureqml3d/main.qml (renamed from examples/3dstudioruntime2/pureqml3d/main.qml)23
-rw-r--r--tests/manual/pureqml3d/pureqml3d.pro (renamed from examples/3dstudioruntime2/pureqml3d/pureqml3d.pro)5
-rw-r--r--tests/manual/pureqml3d/pureqml3d.qrc (renamed from examples/3dstudioruntime2/pureqml3d/pureqml3d.qrc)0
26 files changed, 1090 insertions, 93 deletions
diff --git a/examples/3dstudioruntime2/3dstudioruntime2.pro b/examples/3dstudioruntime2/3dstudioruntime2.pro
index 71cf65c..37e384d 100644
--- a/examples/3dstudioruntime2/3dstudioruntime2.pro
+++ b/examples/3dstudioruntime2/3dstudioruntime2.pro
@@ -7,8 +7,7 @@ SUBDIRS += \
qtHaveModule(quick) {
SUBDIRS += simpleqml \
qmldatainput \
- layersinquick \
- pureqml3d
+ layersinquick
}
qtHaveModule(widgets) {
diff --git a/src/imports/studio3d/plugin.cpp b/src/imports/studio3d/plugin.cpp
index 7925f40..479af16 100644
--- a/src/imports/studio3d/plugin.cpp
+++ b/src/imports/studio3d/plugin.cpp
@@ -46,6 +46,7 @@
// API NG
#include <private/q3dsstudio3dengine_p.h>
#include <private/q3dslayer3d_p.h>
+#include <private/q3dsgroup3d_p.h>
static void initResources()
{
@@ -82,6 +83,9 @@ public:
// API NG
qmlRegisterType<Q3DSStudio3DEngine>(uri, 2, 1, "Studio3DEngine");
qmlRegisterType<Q3DSLayer3D>(uri, 2, 1, "Layer3D");
+ qmlRegisterUncreatableType<Q3DSObject3D>(uri, 2, 1, "Object3D", QLatin1String("Object3D is a base class"));
+ qmlRegisterUncreatableType<Q3DSNode3D>(uri, 2, 1, "Node3D", QLatin1String("Node3D is a base class"));
+ qmlRegisterType<Q3DSGroup3D>(uri, 2, 1, "Group3D");
}
};
diff --git a/src/runtime/aping/aping.pri b/src/runtime/aping/aping.pri
index 32e64ef..63ca117 100644
--- a/src/runtime/aping/aping.pri
+++ b/src/runtime/aping/aping.pri
@@ -1,7 +1,13 @@
SOURCES += \
$$PWD/q3dsstudio3dengine.cpp \
- $$PWD/q3dslayer3d.cpp
+ $$PWD/q3dslayer3d.cpp \
+ $$PWD/q3dsobject3d.cpp \
+ $$PWD/q3dsnode3d.cpp \
+ $$PWD/q3dsgroup3d.cpp
HEADERS += \
$$PWD/q3dsstudio3dengine_p.h \
- $$PWD/q3dslayer3d_p.h
+ $$PWD/q3dslayer3d_p.h \
+ $$PWD/q3dsobject3d_p.h \
+ $$PWD/q3dsnode3d_p.h \
+ $$PWD/q3dsgroup3d_p.h
diff --git a/src/runtime/aping/q3dsgroup3d.cpp b/src/runtime/aping/q3dsgroup3d.cpp
new file mode 100644
index 0000000..98f83fe
--- /dev/null
+++ b/src/runtime/aping/q3dsgroup3d.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dsgroup3d_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype Group3D
+ \instantiates Q3DSGroup3D
+ \inqmlmodule QtStudio3D
+ \ingroup 3dstudioruntime2
+ \inherits Node3D
+ \since Qt 3D Studio 2.1
+
+ \internal
+ */
+
+Q3DSGroup3D::Q3DSGroup3D()
+{
+}
+
+Q3DSGroup3D::~Q3DSGroup3D()
+{
+}
+
+Q3DSGraphObject *Q3DSGroup3D::createObject(Q3DSUipPresentation *presentation)
+{
+ // id is always unique, name does not have to be. We will use the same unique string for both.
+ const QByteArray id = makeIdAndName();
+
+ Q3DSGraphObject *obj = presentation->newObject<Q3DSGroupNode>(id);
+ obj->setName(QString::fromLatin1(id));
+
+ syncProperties();
+
+ return obj;
+}
+
+void Q3DSGroup3D::syncProperties()
+{
+ Q3DSNode3D::syncProperties();
+
+ // ###
+}
+
+QT_END_NAMESPACE
diff --git a/src/runtime/aping/q3dsgroup3d_p.h b/src/runtime/aping/q3dsgroup3d_p.h
new file mode 100644
index 0000000..506b08d
--- /dev/null
+++ b/src/runtime/aping/q3dsgroup3d_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSGROUP3D_P_H
+#define Q3DSGROUP3D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "q3dsnode3d_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSV_PRIVATE_EXPORT Q3DSGroup3D : public Q3DSNode3D
+{
+ Q_OBJECT
+
+public:
+ Q3DSGroup3D();
+ ~Q3DSGroup3D();
+
+ Q3DSGroupNode *groupNode() const { return static_cast<Q3DSGroupNode *>(object()); }
+
+ Q3DSGraphObject *createObject(Q3DSUipPresentation *presentation) override;
+ void syncProperties() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSGROUP3D_P_H
diff --git a/src/runtime/aping/q3dslayer3d.cpp b/src/runtime/aping/q3dslayer3d.cpp
index 0e9b285..6abcb20 100644
--- a/src/runtime/aping/q3dslayer3d.cpp
+++ b/src/runtime/aping/q3dslayer3d.cpp
@@ -28,6 +28,7 @@
****************************************************************************/
#include "q3dslayer3d_p.h"
+#include "q3dsobject3d_p.h"
#include "q3dsstudio3dengine_p.h"
#include "q3dsuippresentation_p.h"
#include <QImage>
@@ -71,23 +72,38 @@ Q3DSLayer3D::Q3DSLayer3D(QQuickItem *parent)
setFlag(QQuickItem::ItemHasContents, true);
}
+// The concept of slides is not exposed in the API. All objects are
+// automatically added to an implicit slide.
+Q3DSSlide *Q3DSLayer3D::slide() const
+{
+ return static_cast<Q3DSSlide *>(m_presentation->masterSlide()->firstChild());
+}
+
Q3DSLayer3D::~Q3DSLayer3D()
{
if (m_engine)
m_engine->unregisterView(this);
+ // Null out m_layer to prevent children from choking when ~QQuickItem
+ // generates a parent-is-null change after we return from here. In
+ // addition, null out m_object as well recursively since the whole
+ // Q3DSGraphObject tree under m_layer3DS is going to be destroyed.
+ for (QQuickItem *item : childItems()) {
+ Q3DSObject3D *child = qobject_cast<Q3DSObject3D *>(item);
+ if (child)
+ child->invalidate(Q3DSObject3D::InvalidateLayer | Q3DSObject3D::InvalidateObject);
+ }
+
if (m_presentation) {
- Q3DSSlide *slide = static_cast<Q3DSSlide *>(m_presentation->masterSlide()->firstChild());
- Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [slide](Q3DSGraphObject *obj) {
- slide->removeObject(obj);
+ Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [this](Q3DSGraphObject *obj) {
+ slide()->removeObject(obj);
});
// going away for good, so removeChildNode + unregister id
m_presentation->unlinkObject(m_layer3DS);
- delete m_layer3DS;
+ delete m_layer3DS; // also destroys all its children
}
delete m_textureProvider;
- delete m_dummyTexture;
}
// Relying on updatePaintNode to keep the SG node up-to-date is not feasable
@@ -118,6 +134,7 @@ QSGNode *Q3DSLayer3D::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
n->setRect(0, 0, dummySize.width(), dummySize.height());
n->setSourceRect(0, 0, dummySize.width(), dummySize.height());
n->setTexture(m_dummyTexture);
+ n->setOwnsTexture(true);
}
m_node = n;
@@ -161,7 +178,7 @@ QObject *Q3DSLayer3D::engine() const
void Q3DSLayer3D::setEngine(QObject *e)
{
Q3DSStudio3DEngine *item = qobject_cast<Q3DSStudio3DEngine *>(e);
- if (!item) {
+ if (e && !item) {
qWarning() << e << "is not a Studio3DEngine";
return;
}
@@ -177,13 +194,16 @@ void Q3DSLayer3D::setEngine(QObject *e)
if (m_engine) {
m_engine->registerView(this);
- sync();
+ connect(m_engine, &QObject::destroyed, this, [this](QObject *obj) {
+ if (obj == m_engine) {
+ m_engine = nullptr;
+ m_presentation = nullptr;
+ m_layer3DS = nullptr;
+ }
+ });
}
- connect(m_engine, &QObject::destroyed, this, [this](QObject *obj) {
- if (obj == m_engine)
- m_engine = nullptr;
- });
+ sync();
emit engineChanged();
if (window())
@@ -198,27 +218,11 @@ static QByteArray makeIdAndName(Q3DSLayer3D *obj)
return id;
}
-static void addTestContent(Q3DSUipPresentation *pres, Q3DSGraphObject *parent)
-{
- Q3DSCameraNode *camera1 = pres->newObject<Q3DSCameraNode>(parent->id() + QByteArrayLiteral("_cam"));
- parent->appendChildNode(camera1);
-
- Q3DSLightNode *light1 = pres->newObject<Q3DSLightNode>(parent->id() + QByteArrayLiteral("_light"));
- parent->appendChildNode(light1);
-
- Q3DSModelNode *model1 = pres->newObject<Q3DSModelNode>(parent->id() + QByteArrayLiteral("_model"));
- model1->setMesh(QLatin1String("#Sphere"));
- parent->appendChildNode(model1);
-
- Q3DSDefaultMaterial *mat1 = pres->newObject<Q3DSDefaultMaterial>(parent->id() + QByteArrayLiteral("_mat"));
- const QColor c = QColor::fromRgbF((qrand() % 256) / 255.0f, (qrand() % 256) / 255.0f, (qrand() % 256) / 255.0f);
- mat1->setDiffuse(c);
- model1->appendChildNode(mat1);
-}
-
void Q3DSLayer3D::sync()
{
if (!m_layer3DS) {
+ m_engineChanged = false;
+
if (!m_engine)
return;
@@ -231,54 +235,52 @@ void Q3DSLayer3D::sync()
m_layer3DS = m_presentation->newObject<Q3DSLayerNode>(id);
m_layer3DS->setName(QString::fromLatin1(id));
- // communicate the explicit size of the layer as early as possible in
- // order to avoid creating textures with a dummy initial size
+ // Communicate the explicit size of the layer as early as possible in
+ // order to avoid creating textures with a dummy initial size.
if (window()) {
const QSize sz = size().toSize() * window()->effectiveDevicePixelRatio();
m_engine->handleViewGeometryChange(this, sz);
}
- Q3DSSlide *slide = static_cast<Q3DSSlide *>(m_presentation->masterSlide()->firstChild());
+ syncProperties();
- // ### to be removed
- addTestContent(m_presentation, m_layer3DS);
+ // Notify the waiting Object3D, if there is one, to create its subtree
+ // and parent it to our m_layer3DS.
+ emit layerNodeCreated();
- Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [slide](Q3DSGraphObject *obj) {
- slide->addObject(obj);
- });
+ slide()->addObject(m_layer3DS);
+ // The children of m_layer3DS are assumed to be already added to the
+ // slide, that is the responsibility of the layerNodeCreated handler in
+ // Q3DSObject3D on this path.
- // the order of Layer nodes in the Q3DSGraphObject tree does not matter,
- // it does not have to match the Layer3D order in the QQuickItem tree
+ // The order of Layer nodes in the Q3DSGraphObject tree does not matter,
+ // it does not have to match the Layer3D order in the QQuickItem tree.
+ // The moment of truth. This is when things become alive.
m_presentation->scene()->appendChildNode(m_layer3DS);
+ } else if (m_engineChanged) {
m_engineChanged = false;
- } else {
- if (m_engineChanged) {
- if (m_presentation) {
- Q3DSSlide *slide = static_cast<Q3DSSlide *>(m_presentation->masterSlide()->firstChild());
- Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [slide](Q3DSGraphObject *obj) {
- slide->removeObject(obj);
- });
- m_presentation->scene()->removeChildNode(m_layer3DS);
- }
- if (!m_engine) {
- m_presentation = nullptr;
- return;
- }
+ if (m_presentation) {
+ Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [this](Q3DSGraphObject *obj) {
+ slide()->removeObject(obj);
+ });
+ m_presentation->scene()->removeChildNode(m_layer3DS);
+ }
- m_presentation = m_engine->presentation();
- if (!m_presentation)
- return;
+ if (!m_engine) {
+ m_presentation = nullptr;
+ // engine was set to null, keep the layer object around since
+ // we may get associated with the same or another engine later on
+ return;
+ }
- Q3DSSlide *slide = static_cast<Q3DSSlide *>(m_presentation->masterSlide()->firstChild());
- Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [slide](Q3DSGraphObject *obj) {
- slide->addObject(obj);
+ m_presentation = m_engine->presentation();
+ if (m_presentation) {
+ Q3DSUipPresentation::forAllObjectsInSubTree(m_layer3DS, [this](Q3DSGraphObject *obj) {
+ slide()->addObject(obj);
});
-
m_presentation->scene()->appendChildNode(m_layer3DS);
-
- m_engineChanged = false;
}
}
@@ -308,11 +310,6 @@ QSGTextureProvider *Q3DSLayer3D::textureProvider() const
return m_textureProvider;
}
-bool Q3DSLayer3D::nodeHasDummyTexture() const
-{
- return m_node && m_node->texture() == m_dummyTexture;
-}
-
void Q3DSLayer3D::notifyTextureChange(QSGTexture *t)
{
// render thread
@@ -323,4 +320,9 @@ void Q3DSLayer3D::notifyTextureChange(QSGTexture *t)
m_lastTexture = t;
}
+void Q3DSLayer3D::syncProperties()
+{
+ // ###
+}
+
QT_END_NAMESPACE
diff --git a/src/runtime/aping/q3dslayer3d_p.h b/src/runtime/aping/q3dslayer3d_p.h
index 0e11f86..64ac69c 100644
--- a/src/runtime/aping/q3dslayer3d_p.h
+++ b/src/runtime/aping/q3dslayer3d_p.h
@@ -41,7 +41,7 @@
// We mean it.
//
-#include <Qt3DStudioRuntime2/q3dsruntimeglobal.h>
+#include <Qt3DStudioRuntime2/private/q3dsruntimeglobal_p.h>
#include <QQuickItem>
QT_BEGIN_NAMESPACE
@@ -50,10 +50,11 @@ class Q3DSStudio3DEngine;
class Q3DSLayerTextureProvider;
class Q3DSLayerNode;
class Q3DSUipPresentation;
+class Q3DSSlide;
class QSGTexture;
class QSGImageNode;
-class Q3DSV_EXPORT Q3DSLayer3D : public QQuickItem
+class Q3DSV_PRIVATE_EXPORT Q3DSLayer3D : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QObject *engine READ engine WRITE setEngine NOTIFY engineChanged)
@@ -69,13 +70,19 @@ public:
QSGTextureProvider *textureProvider() const override;
QSGImageNode *node() const { return m_node; }
- bool nodeHasDummyTexture() const;
void notifyTextureChange(QSGTexture *t);
- Q3DSLayerNode *layer() const { return m_layer3DS; }
+ Q3DSUipPresentation *presentation() const { return m_presentation; }
+ Q3DSSlide *slide() const;
+ Q3DSLayerNode *layerNode() const { return m_layer3DS; }
+
+ bool isLive() const { return layerNode() && presentation(); }
+
+ void syncProperties();
signals:
void engineChanged();
+ void layerNodeCreated();
private:
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *nodeData) override;
diff --git a/src/runtime/aping/q3dsnode3d.cpp b/src/runtime/aping/q3dsnode3d.cpp
new file mode 100644
index 0000000..58873c3
--- /dev/null
+++ b/src/runtime/aping/q3dsnode3d.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dsnode3d_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype Node3D
+ \instantiates Q3DSNode3D
+ \inqmlmodule QtStudio3D
+ \ingroup 3dstudioruntime2
+ \inherits Object3D
+ \since Qt 3D Studio 2.1
+
+ \internal
+ */
+
+Q3DSNode3D::Q3DSNode3D()
+{
+}
+
+Q3DSNode3D::~Q3DSNode3D()
+{
+}
+
+void Q3DSNode3D::syncProperties()
+{
+ Q3DSObject3D::syncProperties();
+
+ // ###
+}
+
+QT_END_NAMESPACE
diff --git a/src/runtime/aping/q3dsnode3d_p.h b/src/runtime/aping/q3dsnode3d_p.h
new file mode 100644
index 0000000..9ec5d77
--- /dev/null
+++ b/src/runtime/aping/q3dsnode3d_p.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSNODE3D_P_H
+#define Q3DSNODE3D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "q3dsobject3d_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSV_PRIVATE_EXPORT Q3DSNode3D : public Q3DSObject3D
+{
+ Q_OBJECT
+
+public:
+ Q3DSNode3D();
+ ~Q3DSNode3D();
+
+ Q3DSNode *node() const { return static_cast<Q3DSNode *>(object()); }
+
+ void syncProperties() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSNODE3D_P_H
diff --git a/src/runtime/aping/q3dsobject3d.cpp b/src/runtime/aping/q3dsobject3d.cpp
new file mode 100644
index 0000000..28fe872
--- /dev/null
+++ b/src/runtime/aping/q3dsobject3d.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dsobject3d_p.h"
+#include "q3dslayer3d_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype Object3D
+ \instantiates Q3DSObject3D
+ \inqmlmodule QtStudio3D
+ \ingroup 3dstudioruntime2
+ \inherits Object
+ \since Qt 3D Studio 2.1
+
+ \internal
+ */
+
+// No parent parameter here. QML does not need it and not offering it helps
+// autotests avoiding the trap of overridden-virtuals-not-called-from-ctor
+// problems (like that we lose the ItemParentHasChanged because our
+// itemChange() is not getting called)
+Q3DSObject3D::Q3DSObject3D()
+{
+ setFlag(ItemHasContents, false);
+}
+
+Q3DSObject3D::~Q3DSObject3D()
+{
+ // When the object is not attached to a parent, we own it,
+ // otherwise the parent takes care of destroying it.
+ if (m_object && !m_object->parent())
+ delete m_object;
+}
+
+// Deferred fun: Creating the underlying Q3DSGraphObject is not posssible
+// without the presentation. The presentation comes from the engine via a
+// Layer3D which we do not know until we or an ancestor gets parented to one.
+
+void Q3DSObject3D::itemChange(ItemChange change, const ItemChangeData &changeData)
+{
+ switch (change) {
+ case ItemParentHasChanged:
+ // m_layer == null && newParent == null means we are in the Layer3D's
+ // ~QQuickItem and there is nothing to do
+ if (m_object && m_object->parent() && m_layer) {
+ Q3DSUipPresentation::forAllObjectsInSubTree(m_object, [this](Q3DSGraphObject *obj) {
+ m_layer->slide()->removeObject(obj);
+ });
+ m_object->parent()->removeChildNode(m_object);
+ invalidate(InvalidateLayer);
+ }
+ if (changeData.item) {
+ // cancel outstanding operations
+ m_pendingObjectParent = nullptr;
+ if (m_hasPendingActivate) {
+ // in case our parent changes twice before the Layer3D gets added to a window
+ m_hasPendingActivate = false;
+ disconnect(m_layerConn);
+ }
+ // see if we are parented to a Layer3D or an Object3D
+ Q3DSLayer3D *layer3D = qobject_cast<Q3DSLayer3D *>(changeData.item);
+ if (layer3D) {
+ // if the layer has a backing Q3DSLayerNode and is attached to
+ // a Studio3DEngine then we can proceed right away
+ if (layer3D->isLive()) {
+ m_layer = layer3D;
+ activate();
+ } else {
+ // defer otherwise
+ m_hasPendingActivate = true;
+ m_layerConn = connect(layer3D, &Q3DSLayer3D::layerNodeCreated, this, [this, layer3D] {
+ m_layer = layer3D;
+ activate();
+ });
+ }
+ } else {
+ Q3DSObject3D *obj3D = qobject_cast<Q3DSObject3D *>(changeData.item);
+ if (obj3D) {
+ // if the backing Q3DSGraphObject is present, check if the
+ // same is true for the new parent and if it is associated
+ // with a Layer3D that is attached to a Studio3DEngine
+ // since things are simple then
+ if (m_object && obj3D->object() && obj3D->layer() && obj3D->layer()->isLive()) {
+ // there will not be an activate() so do everything needed right here
+ setLayer_recursive(obj3D->layer());
+ Q3DSUipPresentation::forAllObjectsInSubTree(m_object, [this](Q3DSGraphObject *obj) {
+ m_layer->slide()->addObject(obj);
+ });
+ obj3D->object()->appendChildNode(m_object);
+ m_pendingObjectParent = nullptr;
+ } else {
+ // defer otherwise, there will be an activate() eventually (or not but that's fine too)
+ m_pendingObjectParent = obj3D;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Q3DSObject3D::activate_helper(Q3DSLayer3D *layer)
+{
+ m_layer = layer;
+
+ // m_object may or may not be null.
+ //
+ // When this Object3D has never been associated with a subtree belonging to
+ // a Layer3D attached to a window, then m_object is still null.
+ //
+ // Otherwise m_object is valid, that is the case where a fully initialized
+ // Object3D with an existing underlying Q3DSGraphObject gets moved out from
+ // under a Layer3D, and then later gets added to another one.
+
+ if (!m_object) {
+ m_object = createObject(m_layer->presentation());
+ if (!m_object)
+ return;
+ }
+
+ m_layer->slide()->addObject(m_object);
+
+ if (m_pendingObjectParent) {
+ Q_ASSERT(m_pendingObjectParent->object());
+ m_pendingObjectParent->object()->appendChildNode(m_object);
+ m_pendingObjectParent = nullptr;
+ }
+
+ for (QQuickItem *item : childItems()) {
+ Q3DSObject3D *child = qobject_cast<Q3DSObject3D *>(item);
+ if (child)
+ child->activate_helper(m_layer);
+ }
+}
+
+void Q3DSObject3D::activate()
+{
+ Q_ASSERT(!m_pendingObjectParent); // this must be an Object3D parented to a Layer3D
+ Q_ASSERT(m_layer && m_layer->isLive());
+
+ m_hasPendingActivate = false;
+
+ activate_helper(m_layer);
+ if (!m_object) // can happen only if the subclass tries a non-unique id, handle it gracefully nonetheless
+ return;
+
+ // Connect the object and its children to the Q3DSLayerNode. If that is
+ // parented to a scene then the subtree becomes alive now.
+ m_layer->layerNode()->appendChildNode(m_object);
+}
+
+void Q3DSObject3D::setLayer_recursive(Q3DSLayer3D *layer)
+{
+ m_layer = layer;
+
+ for (QQuickItem *item : childItems()) {
+ Q3DSObject3D *child = qobject_cast<Q3DSObject3D *>(item);
+ if (child)
+ child->setLayer_recursive(layer);
+ }
+}
+
+void Q3DSObject3D::invalidate(InvalidateFlags flags)
+{
+ if (flags.testFlag(InvalidateLayer))
+ m_layer = nullptr;
+
+ if (flags.testFlag(InvalidateObject))
+ m_object = nullptr;
+
+ for (QQuickItem *item : childItems()) {
+ Q3DSObject3D *child = qobject_cast<Q3DSObject3D *>(item);
+ if (child)
+ child->invalidate(flags);
+ }
+}
+
+QByteArray Q3DSObject3D::makeIdAndName() const
+{
+ QByteArray id = metaObject()->className();
+ id += QByteArrayLiteral("_0x");
+ id += QByteArray::number((quintptr) this, 16);
+ return id;
+}
+
+void Q3DSObject3D::syncProperties()
+{
+ // ###
+}
+
+QT_END_NAMESPACE
diff --git a/src/runtime/aping/q3dsobject3d_p.h b/src/runtime/aping/q3dsobject3d_p.h
new file mode 100644
index 0000000..e588fdc
--- /dev/null
+++ b/src/runtime/aping/q3dsobject3d_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSOBJECT3D_P_H
+#define Q3DSOBJECT3D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DStudioRuntime2/private/q3dsruntimeglobal_p.h>
+#include <QQuickItem>
+#include <private/q3dsuippresentation_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSLayer3D;
+
+class Q3DSV_PRIVATE_EXPORT Q3DSObject3D : public QQuickItem
+{
+ Q_OBJECT
+
+public:
+ Q3DSObject3D();
+ ~Q3DSObject3D();
+
+ virtual Q3DSGraphObject *createObject(Q3DSUipPresentation *presentation) = 0;
+ virtual void syncProperties();
+
+ Q3DSGraphObject *object() const { return m_object; }
+ Q3DSLayer3D *layer() const { return m_layer; }
+
+ enum InvalidateFlag {
+ InvalidateObject = 0x01,
+ InvalidateLayer = 0x02
+ };
+ Q_DECLARE_FLAGS(InvalidateFlags, InvalidateFlag)
+
+ void invalidate(InvalidateFlags flags);
+
+protected:
+ void itemChange(ItemChange, const ItemChangeData &) override;
+ QByteArray makeIdAndName() const;
+
+private:
+ void activate();
+ void activate_helper(Q3DSLayer3D *layer);
+ void setLayer_recursive(Q3DSLayer3D *layer);
+
+ Q3DSGraphObject *m_object = nullptr;
+ Q3DSLayer3D *m_layer = nullptr;
+ Q3DSObject3D *m_pendingObjectParent = nullptr;
+ bool m_hasPendingActivate = false;
+ QMetaObject::Connection m_layerConn;
+
+ friend class Q3DSLayer3D;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSObject3D::InvalidateFlags)
+
+QT_END_NAMESPACE
+
+#endif // Q3DSOBJECT3D_P_H
diff --git a/src/runtime/aping/q3dsstudio3dengine.cpp b/src/runtime/aping/q3dsstudio3dengine.cpp
index 3635c5c..8b24add 100644
--- a/src/runtime/aping/q3dsstudio3dengine.cpp
+++ b/src/runtime/aping/q3dsstudio3dengine.cpp
@@ -342,7 +342,7 @@ void Q3DSStudio3DEngine::updateViews()
QVector<Renderer::Layer> layers;
// Gather the layer names (or ids) that need to be exposed.
for (Q3DSLayer3D *layerItem : m_views) {
- Q3DSLayerNode *layer3DS = layerItem->layer();
+ Q3DSLayerNode *layer3DS = layerItem->layerNode();
if (!layer3DS)
continue;
Qt3DCore::QNodeId nodeId = m_engine->layerTextureNodeId(layer3DS);
@@ -413,7 +413,9 @@ Q3DSStudio3DEngine::Renderer::~Renderer()
void Q3DSStudio3DEngine::Renderer::invalidateEngine()
{
- m_engineInvalid.store(1);
+ Q_ASSERT(thread() != m_engine->thread() && thread() != QThread::currentThread());
+ QMutexLocker lock(&m_engineInvalidLock);
+ m_engineInvalid = true;
}
void Q3DSStudio3DEngine::Renderer::notifyEngineStart()
@@ -424,7 +426,8 @@ void Q3DSStudio3DEngine::Renderer::notifyEngineStart()
void Q3DSStudio3DEngine::Renderer::render()
{
// m_engine may be destroyed already
- if (m_engineInvalid.load())
+ QMutexLocker lock(&m_engineInvalidLock);
+ if (m_engineInvalid)
return; // the end is nigh - sit back and relax til the RendererReleaser comes for us
QQuickWindow *w = m_engine->window();
@@ -480,12 +483,10 @@ void Q3DSStudio3DEngine::Renderer::render()
if (viewNode) {
if (uint(viewNode->texture()->textureId()) != id || viewNode->rect().size() != layer.itemSizeWithoutDpr) {
QSGTexture *t = w->createTextureFromId(id, layer.pixelSize, QQuickWindow::TextureHasAlphaChannel);
+ Q_ASSERT(t);
t->setFiltering(QSGTexture::Linear);
- if (!layer.item->nodeHasDummyTexture())
- delete viewNode->texture();
-
- viewNode->setTexture(t);
+ viewNode->setTexture(t); // owns, so previous texture is destroyed
layer.item->notifyTextureChange(t);
viewNode->setRect(QRectF(QPointF(0, 0), layer.itemSizeWithoutDpr));
@@ -523,7 +524,7 @@ void Q3DSStudio3DEngine::handleViewGeometryChange(Q3DSLayer3D *view, const QSize
if (!m_engine)
return;
- Q3DSLayerNode *layer3DS = view->layer();
+ Q3DSLayerNode *layer3DS = view->layerNode();
if (!layer3DS)
return;
diff --git a/src/runtime/aping/q3dsstudio3dengine_p.h b/src/runtime/aping/q3dsstudio3dengine_p.h
index 66ff157..70c7a7a 100644
--- a/src/runtime/aping/q3dsstudio3dengine_p.h
+++ b/src/runtime/aping/q3dsstudio3dengine_p.h
@@ -42,6 +42,7 @@
//
#include <Qt3DStudioRuntime2/private/q3dsruntimeglobal_p.h>
+#include <QMutex>
#include <QQuickItem>
#include <Qt3DCore/QNodeId>
@@ -115,7 +116,8 @@ private:
void render();
Q3DSStudio3DEngine *m_engine;
- QAtomicInt m_engineInvalid;
+ bool m_engineInvalid = false;
+ QMutex m_engineInvalidLock;
QAtomicInt m_engineStarted;
Qt3DCore::QAspectEngine *m_aspectEngine;
Qt3DRender::QRenderAspect *m_renderAspect = nullptr;
diff --git a/src/runtime/profileui/q3dsprofileui.cpp b/src/runtime/profileui/q3dsprofileui.cpp
index 331966b..8d347b1 100644
--- a/src/runtime/profileui/q3dsprofileui.cpp
+++ b/src/runtime/profileui/q3dsprofileui.cpp
@@ -1108,7 +1108,7 @@ Q3DSProfileUi::Q3DSProfileUi(Q3DSGuiData *guiData, Q3DSProfiler *profiler, Conso
m_guiItem = static_cast<Q3DSImGuiItem *>(m_data->imguiQuickItem);
if (m_guiItem->isVisible())
setVisible(true);
- QObject::connect(m_guiItem, &QQuickItem::visibleChanged, [this] {
+ m_itemVisibleConn = QObject::connect(m_guiItem, &QQuickItem::visibleChanged, [this] {
setVisible(m_guiItem->isVisible());
});
return;
@@ -1129,6 +1129,9 @@ Q3DSProfileUi::Q3DSProfileUi(Q3DSGuiData *guiData, Q3DSProfiler *profiler, Conso
Q3DSProfileUi::~Q3DSProfileUi()
{
+ if (m_itemVisibleConn)
+ QObject::disconnect(m_itemVisibleConn);
+
delete m_guiMgr;
delete m_view;
}
diff --git a/src/runtime/profileui/q3dsprofileui_p.h b/src/runtime/profileui/q3dsprofileui_p.h
index 4b1da8f..6e9abf2 100644
--- a/src/runtime/profileui/q3dsprofileui_p.h
+++ b/src/runtime/profileui/q3dsprofileui_p.h
@@ -77,6 +77,7 @@ private:
bool m_visible = false;
bool m_hadDialogsEnabled = false;
QMetaObject::Connection m_itemFrameConn;
+ QMetaObject::Connection m_itemVisibleConn;
};
QT_END_NAMESPACE
diff --git a/src/runtime/q3dsinputmanager_p.h b/src/runtime/q3dsinputmanager_p.h
index 08a0e31..e7c8fee 100644
--- a/src/runtime/q3dsinputmanager_p.h
+++ b/src/runtime/q3dsinputmanager_p.h
@@ -102,9 +102,9 @@ private:
PickRequest(Q3DSLayerNode *layer3DS_, const QPoint& pos_, const InputState &inputState_)
: layer3DS(layer3DS_), pos(pos_), inputState(inputState_)
{ }
+ Q3DSLayerNode *layer3DS = nullptr; // set only when the layer to pick from is known in advance
QPoint pos;
InputState inputState;
- Q3DSLayerNode *layer3DS = nullptr; // set only when the layer to pick from is known in advance
};
QQueue<PickRequest> m_pickRequests;
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 0d36323..3a06f0e 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -15,6 +15,10 @@ SUBDIRS += \
surfaceviewer \
q3dslancelot
-qtHaveModule(quick): SUBDIRS += studio3d
+qtHaveModule(quick) {
+ SUBDIRS += \
+ studio3d \
+ studio3dengine
+}
qtHaveModule(widgets): SUBDIRS += widget
diff --git a/tests/auto/studio3dengine/data/simple.qml b/tests/auto/studio3dengine/data/simple.qml
new file mode 100644
index 0000000..20a6c67
--- /dev/null
+++ b/tests/auto/studio3dengine/data/simple.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import QtStudio3D 2.1
+
+Rectangle {
+ id: root
+ color: "lightGray"
+
+ Studio3DEngine {
+ id: s3d
+ }
+
+ Layer3D {
+ engine: s3d
+ width: 200
+ height: 200
+
+ Group3D {
+ Group3D {
+ }
+ }
+ }
+}
diff --git a/tests/auto/studio3dengine/studio3dengine.pro b/tests/auto/studio3dengine/studio3dengine.pro
new file mode 100644
index 0000000..5f06355
--- /dev/null
+++ b/tests/auto/studio3dengine/studio3dengine.pro
@@ -0,0 +1,8 @@
+TARGET = tst_q3dsstudio3dengine
+CONFIG += testcase
+
+QT += qml quick testlib 3dstudioruntime2-private
+
+SOURCES += tst_studio3dengine.cpp
+
+RESOURCES += studio3dengine.qrc
diff --git a/tests/auto/studio3dengine/studio3dengine.qrc b/tests/auto/studio3dengine/studio3dengine.qrc
new file mode 100644
index 0000000..c371efb
--- /dev/null
+++ b/tests/auto/studio3dengine/studio3dengine.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data/simple.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/studio3dengine/tst_studio3dengine.cpp b/tests/auto/studio3dengine/tst_studio3dengine.cpp
new file mode 100644
index 0000000..4694e81
--- /dev/null
+++ b/tests/auto/studio3dengine/tst_studio3dengine.cpp
@@ -0,0 +1,334 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtCore/qurl.h>
+
+#include <private/q3dsuippresentation_p.h>
+#include <private/q3dslayer3d_p.h>
+#include <private/q3dsgroup3d_p.h>
+
+#include "../shared/shared.h"
+
+class tst_Studio3DEngine : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_Studio3DEngine();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void testSimple();
+ void testReparent();
+ void testReparentSubTree();
+ void testEngineChangeToNullAndBack();
+ void testLayerParentNullAndBack();
+};
+
+tst_Studio3DEngine::tst_Studio3DEngine()
+{
+}
+
+void tst_Studio3DEngine::initTestCase()
+{
+ if (!isOpenGLGoodEnough())
+ QSKIP("This platform does not support OpenGL proper");
+
+ QSurfaceFormat::setDefaultFormat(Q3DS::surfaceFormat());
+
+ // do not bother disabling dialogs, Studio3DEngine is expected to avoid those implicitly
+}
+
+void tst_Studio3DEngine::cleanupTestCase()
+{
+}
+
+void tst_Studio3DEngine::testSimple()
+{
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl(QLatin1String("qrc:/data/simple.qml")));
+
+ QVERIFY(view.rootObject());
+ QVERIFY(view.rootObject()->childItems().count() > 0);
+
+ QQuickItem *s3d = view.rootObject()->childItems()[0];
+ QVERIFY(s3d);
+ const QMetaObject *s3dMeta = s3d->metaObject();
+ QVERIFY(!strcmp(s3dMeta->className(), "Q3DSStudio3DEngine"));
+
+ view.resize(600, 500);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("This test needs Qt Quick on OpenGL");
+
+ Q3DSLayer3D *layer3d = qobject_cast<Q3DSLayer3D *>(view.rootObject()->childItems()[1]);
+ QVERIFY(layer3d);
+ QCOMPARE(layer3d->engine(), s3d);
+
+ Q3DSLayerNode *layer3DS = layer3d->layerNode();
+ QVERIFY(layer3DS);
+ QCOMPARE(layer3DS->explicitSize(), layer3d->size() * view.effectiveDevicePixelRatio());
+
+ QCOMPARE(layer3d->childItems().count(), 1);
+ Q3DSGroup3D *group3d_1 = qobject_cast<Q3DSGroup3D *>(layer3d->childItems()[0]);
+ QVERIFY(group3d_1);
+ QCOMPARE(group3d_1->layer(), layer3d);
+
+ QCOMPARE(group3d_1->object()->type(), Q3DSGraphObject::Group);
+ Q3DSGroupNode *group3DS_1 = static_cast<Q3DSGroupNode *>(group3d_1->object());
+ QVERIFY(group3DS_1);
+
+ QCOMPARE(group3d_1->childItems().count(), 1);
+ Q3DSGroup3D *group3d_2 = qobject_cast<Q3DSGroup3D *>(group3d_1->childItems()[0]);
+ QVERIFY(group3d_2);
+ QCOMPARE(group3d_2->layer(), layer3d);
+
+ QCOMPARE(group3d_2->object()->type(), Q3DSGraphObject::Group);
+ Q3DSGroupNode *group3DS_2 = static_cast<Q3DSGroupNode *>(group3d_2->object());
+ QVERIFY(group3DS_2);
+
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(layer3DS->parent(), layer3d->presentation()->scene());
+
+ QVERIFY(layer3d->slide()->parent() == layer3d->presentation()->masterSlide());
+ QVERIFY(!layer3d->slide()->nextSibling());
+ const QSet<Q3DSGraphObject *> slideObjects = layer3d->slide()->objects();
+ QCOMPARE(slideObjects.count(), 3);
+}
+
+void tst_Studio3DEngine::testReparent()
+{
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl(QLatin1String("qrc:/data/simple.qml")));
+
+ view.resize(600, 500);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("This test needs Qt Quick on OpenGL");
+
+ Q3DSLayer3D *layer3d = qobject_cast<Q3DSLayer3D *>(view.rootObject()->childItems()[1]);
+ Q3DSLayerNode *layer3DS = layer3d->layerNode();
+ Q3DSGroup3D *group3d_1 = qobject_cast<Q3DSGroup3D *>(layer3d->childItems()[0]);
+ Q3DSGroupNode *group3DS_1 = static_cast<Q3DSGroupNode *>(group3d_1->object());
+ Q3DSGroup3D *group3d_2 = qobject_cast<Q3DSGroup3D *>(group3d_1->childItems()[0]);
+ Q3DSGroupNode *group3DS_2 = static_cast<Q3DSGroupNode *>(group3d_2->object());
+
+ // Layer(Group1(Group2))
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(layer3d->slide()->objects().count(), 3);
+
+ // Layer(Group1 Group2)
+ group3d_2->setParentItem(layer3d);
+ QCOMPARE(group3DS_2->parent(), layer3DS);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(group3DS_1->nextSibling(), group3DS_2);
+ QCOMPARE(layer3d->slide()->objects().count(), 3);
+
+ // Layer(Group2 Group1)
+ group3d_2->setParentItem(nullptr);
+ QCOMPARE(group3DS_2->parent(), nullptr);
+ QCOMPARE(group3d_2->layer(), nullptr);
+ group3d_1->setParentItem(nullptr);
+ QCOMPARE(group3DS_1->parent(), nullptr);
+ QCOMPARE(group3d_1->layer(), nullptr);
+ group3d_2->setParentItem(layer3d);
+ QCOMPARE(group3DS_2->parent(), layer3DS);
+ QCOMPARE(group3DS_2->nextSibling(), nullptr);
+ group3d_1->setParentItem(layer3d);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(group3d_1->layer(), layer3d);
+ QCOMPARE(group3DS_2->nextSibling(), group3DS_1);
+ QCOMPARE(group3d_2->layer(), layer3d);
+ QCOMPARE(layer3d->slide()->objects().count(), 3);
+
+ // Layer(Group1) Group2
+ group3d_2->setParentItem(nullptr);
+ QCOMPARE(group3d_2->object(), group3DS_2);
+ QCOMPARE(group3DS_2->parent(), nullptr);
+ QCOMPARE(group3DS_2->nextSibling(), nullptr);
+ QCOMPARE(group3d_2->layer(), nullptr);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(group3d_1->layer(), layer3d);
+ QCOMPARE(layer3d->slide()->objects().count(), 2);
+
+ // Layer(Group1(Group2))
+ group3d_2->setParentItem(group3d_1);
+ QCOMPARE(group3d_2->object(), group3DS_2);
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3d_2->layer(), layer3d);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(group3d_1->layer(), layer3d);
+ QCOMPARE(layer3d->slide()->objects().count(), 3);
+}
+
+void tst_Studio3DEngine::testReparentSubTree()
+{
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl(QLatin1String("qrc:/data/simple.qml")));
+
+ view.resize(600, 500);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("This test needs Qt Quick on OpenGL");
+
+ Q3DSLayer3D *layer3d = qobject_cast<Q3DSLayer3D *>(view.rootObject()->childItems()[1]);
+ Q3DSLayerNode *layer3DS = layer3d->layerNode();
+ Q3DSGroup3D *group3d_1 = qobject_cast<Q3DSGroup3D *>(layer3d->childItems()[0]);
+ Q3DSGroupNode *group3DS_1 = static_cast<Q3DSGroupNode *>(group3d_1->object());
+ Q3DSGroup3D *group3d_2 = qobject_cast<Q3DSGroup3D *>(group3d_1->childItems()[0]);
+ Q3DSGroupNode *group3DS_2 = static_cast<Q3DSGroupNode *>(group3d_2->object());
+
+ // Layer(Group1(Group2))
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(layer3d->slide()->objects().count(), 3);
+
+ // Layer Group1(Group2)
+ group3d_1->setParentItem(nullptr);
+ QCOMPARE(group3DS_1->parent(), nullptr);
+ QCOMPARE(group3d_1->layer(), nullptr);
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3d_2->layer(), nullptr);
+ QCOMPARE(layer3d->slide()->objects().count(), 1);
+
+ // Layer Group1(Group2(Group3))
+ // Group3 not activated at this point since the tree it gets added to is not live
+ Q3DSGroup3D *group3d_3 = new Q3DSGroup3D;
+ group3d_3->setParentItem(group3d_2);
+ QCOMPARE(group3d_3->layer(), nullptr);
+ QCOMPARE(group3d_3->object(), nullptr);
+ QCOMPARE(layer3d->slide()->objects().count(), 1);
+
+ // Layer(Group1(Group2(Group3)))
+ group3d_1->setParentItem(layer3d);
+ QCOMPARE(group3DS_1->parent(), layer3DS);
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3d_3->object()->parent(), group3DS_2);
+ QCOMPARE(group3d_3->layer(), layer3d);
+
+ Q3DSGroupNode *group3DS_3 = group3d_3->groupNode();
+ QVERIFY(layer3d->slide()->objects().contains(group3DS_3));
+ QCOMPARE(layer3d->slide()->objects().count(), 4);
+
+ // Layer(Group1 Group2(Group3))
+ group3d_2->setParentItem(nullptr);
+ QCOMPARE(group3d_2->object(), group3DS_2);
+ QCOMPARE(group3DS_2->parent(), nullptr);
+ QCOMPARE(group3d_2->layer(), nullptr);
+ QCOMPARE(group3d_3->object(), group3DS_3);
+ QCOMPARE(group3DS_3->parent(), group3DS_2);
+ QCOMPARE(group3d_3->layer(), nullptr);
+
+ // Layer(Group1(Group2(Group3))
+ group3d_2->setParentItem(group3d_1);
+ QCOMPARE(group3d_2->object(), group3DS_2);
+ QCOMPARE(group3DS_2->parent(), group3DS_1);
+ QCOMPARE(group3d_2->layer(), layer3d);
+ QCOMPARE(group3d_3->object(), group3DS_3);
+ QCOMPARE(group3DS_3->parent(), group3DS_2);
+ QCOMPARE(group3d_3->layer(), layer3d);
+}
+
+void tst_Studio3DEngine::testEngineChangeToNullAndBack()
+{
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl(QLatin1String("qrc:/data/simple.qml")));
+
+ view.resize(600, 500);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("This test needs Qt Quick on OpenGL");
+
+ QQuickItem *s3d = view.rootObject()->childItems()[0];
+ Q3DSLayer3D *layer3d = qobject_cast<Q3DSLayer3D *>(view.rootObject()->childItems()[1]);
+ Q3DSLayerNode *layer3DS = layer3d->layerNode();
+
+ // nulling out the engine is valid, it leads to nulling the presentation
+ // but keeping all underlying objects alive [NB the engine cannot actually
+ // change to another Studio3DEngine since object IDs are not transferred
+ // between presentations]
+
+ QCOMPARE(layer3DS->parent(), layer3d->presentation()->scene());
+ layer3d->setEngine(nullptr);
+ QCOMPARE(layer3d->layerNode(), layer3DS);
+ QCOMPARE(layer3DS->parent(), nullptr);
+ QCOMPARE(layer3d->presentation(), nullptr);
+
+ layer3d->setEngine(s3d);
+ QCOMPARE(layer3d->layerNode(), layer3DS);
+ QCOMPARE(layer3DS->parent(), layer3d->presentation()->scene());
+}
+
+void tst_Studio3DEngine::testLayerParentNullAndBack()
+{
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl(QLatin1String("qrc:/data/simple.qml")));
+
+ view.resize(600, 500);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("This test needs Qt Quick on OpenGL");
+
+ Q3DSLayer3D *layer3d = qobject_cast<Q3DSLayer3D *>(view.rootObject()->childItems()[1]);
+ Q3DSLayerNode *layer3DS = layer3d->layerNode();
+
+ QQuickItem *layerParent = layer3d->parentItem();
+ layer3d->setParentItem(nullptr);
+ QCOMPARE(layer3d->layerNode(), layer3DS);
+ QVERIFY(layer3d->presentation());
+
+ layer3d->setParentItem(layerParent);
+ QCOMPARE(layer3d->layerNode(), layer3DS);
+}
+
+QTEST_MAIN(tst_Studio3DEngine)
+
+#include "tst_studio3dengine.moc"
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 5fe1eb4..6610fe6 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -5,3 +5,7 @@ qtHaveModule(widgets) {
qt3dsexplorer \
datamodelgen
}
+qtHaveModule(quick) {
+ SUBDIRS += \
+ pureqml3d
+}
diff --git a/examples/3dstudioruntime2/pureqml3d/main.cpp b/tests/manual/pureqml3d/main.cpp
index 38f98e0..814a6a0 100644
--- a/examples/3dstudioruntime2/pureqml3d/main.cpp
+++ b/tests/manual/pureqml3d/main.cpp
@@ -54,6 +54,7 @@
int main(int argc, char *argv[])
{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qputenv("QSG_INFO", "1");
QGuiApplication app(argc, argv);
@@ -67,7 +68,7 @@ int main(int argc, char *argv[])
viewer.setTitle(QStringLiteral("Qt 3D Studio Pure QML Example"));
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
- viewer.resize(1280, 720);
+ viewer.resize(1280, 600);
viewer.show();
return app.exec();
diff --git a/examples/3dstudioruntime2/pureqml3d/main.qml b/tests/manual/pureqml3d/main.qml
index c747d9f..d858416 100644
--- a/examples/3dstudioruntime2/pureqml3d/main.qml
+++ b/tests/manual/pureqml3d/main.qml
@@ -75,14 +75,33 @@ Rectangle {
Layer3D {
engine: s3d
anchors.fill: parent
+
+ Group3D {
+ }
}
}
}
}
+ Button {
+ anchors.bottom: parent.bottom
+ text: "Profile UI"
+ onClicked: prof.visible = !prof.visible
+ focusPolicy: Qt.NoFocus
+ }
- Studio3DProfiler {
+ Rectangle {
+ id: prof
+ visible: false
anchors.fill: parent
- focus: true
+ anchors.margins: 40
+ border.color: "green"
+ border.width: 4
+ color: "transparent"
+
+ Studio3DProfiler {
+ anchors.fill: parent
+ focus: true
+ }
}
}
diff --git a/examples/3dstudioruntime2/pureqml3d/pureqml3d.pro b/tests/manual/pureqml3d/pureqml3d.pro
index fcc798b..b65b8e1 100644
--- a/examples/3dstudioruntime2/pureqml3d/pureqml3d.pro
+++ b/tests/manual/pureqml3d/pureqml3d.pro
@@ -1,4 +1,4 @@
-TEMPLATE = app
+TARGET = pureqml3d
QT += quick 3dstudioruntime2
@@ -9,6 +9,3 @@ RESOURCES += pureqml3d.qrc
OTHER_FILES += \
main.qml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/3dstudioruntime2/$$TARGET
-INSTALLS += target
diff --git a/examples/3dstudioruntime2/pureqml3d/pureqml3d.qrc b/tests/manual/pureqml3d/pureqml3d.qrc
index 5f6483a..5f6483a 100644
--- a/examples/3dstudioruntime2/pureqml3d/pureqml3d.qrc
+++ b/tests/manual/pureqml3d/pureqml3d.qrc