summaryrefslogtreecommitdiffstats
path: root/src/quick3d/imports/scene3d/scene3dview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick3d/imports/scene3d/scene3dview.cpp')
-rw-r--r--src/quick3d/imports/scene3d/scene3dview.cpp318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/quick3d/imports/scene3d/scene3dview.cpp b/src/quick3d/imports/scene3d/scene3dview.cpp
new file mode 100644
index 000000000..9d298b435
--- /dev/null
+++ b/src/quick3d/imports/scene3d/scene3dview.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or 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.GPL2 and 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "scene3dview_p.h"
+#include <Qt3DCore/QEntity>
+#include <Qt3DRender/QRenderSettings>
+#include <Qt3DRender/QFrameGraphNode>
+#include <Qt3DRender/QLayer>
+#include <Qt3DRender/QLayerFilter>
+#include <Qt3DRender/QViewport>
+#include <scene3dsgnode_p.h>
+#include <scene3ditem_p.h>
+#include <QQuickWindow>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+/*!
+ \qmltype Scene3DView
+ \inherits Item
+ \inqmlmodule QtQuick.Scene3D
+ \since 5.14
+
+ \preliminary
+
+ \brief The Scene3DView type is used to integrate a Qt 3D sub scene into a
+ QtQuick 2 scene using Scene3D. Whereas you should only use a single Scene3D
+ instance per application, you can have multiple Scene3DView instances.
+
+ Essentially, if you need to render multiple scenes each in a separate view,
+ you should use a single Scene3D instance and as many Scene3DView items as
+ you have scenes to render.
+
+ Typical usage looks like:
+ \qml
+ Scene3D {
+ id: mainScene3D
+ anchors.fill: parent
+ }
+
+ Scene3DView {
+ id: view1
+ scene3D: mainScene3D
+ width: 200
+ height: 200
+ Entity {
+ ...
+ }
+ }
+
+ Scene3DView {
+ id: view2
+ scene3D: mainScene3D
+ width: 200
+ height: 200
+ x: 200
+ Entity {
+ ...
+ }
+ }
+ \endqml
+
+ There are a few limitations when using Scene3DView:
+ \list
+ \li The Scene3D compositingMode has to be set to FBO
+ \li The Scene3D is sized to occupy the full window size (at the very least
+ it must be sized as wide as the area occupied by all Scene3DViews)
+ \li The Scene3D instance is instantiated prior to any Scene3DView
+ \li The Scene3D entity property is left unset
+ \endlist
+
+ Scene3D behaves likes a texture atlas from which all Scene3DView instances.
+ For this reason, care should be taken that only the first Scene3DView
+ declared in the scene clears the color/depth. Additionally overlapping
+ Scene3DView instances is discouraged as this might not produce the expected
+ output.
+
+ It is expected that a Scene3DView's Entity provide a RenderSettings with a
+ valid SceneGraph. Please note that only the RenderSettings of the first
+ Scene3DView instantiated will be taken into account.
+
+ There are no restriction on the sharing of elements between different scenes
+ in different Scene3DView instances.
+ */
+
+namespace {
+
+Qt3DRender::QFrameGraphNode *frameGraphFromEntity(Qt3DCore::QEntity *entity)
+{
+ const auto renderSettingsComponents = entity->componentsOfType<Qt3DRender::QRenderSettings>();
+
+ if (renderSettingsComponents.size() > 0) {
+ Qt3DRender::QRenderSettings *renderSettings = renderSettingsComponents.first();
+ return renderSettings->activeFrameGraph();
+ }
+ return nullptr;
+}
+
+}
+
+Scene3DView::Scene3DView(QQuickItem *parent)
+ : QQuickItem(parent)
+ , m_scene3D(nullptr)
+ , m_entity(nullptr)
+ , m_previousFGParent(nullptr)
+ , m_holderEntity(new Qt3DCore::QEntity())
+ , m_holderLayer(new Qt3DRender::QLayer())
+ , m_holderLayerFilter(new Qt3DRender::QLayerFilter())
+ , m_holderViewport(new Qt3DRender::QViewport())
+ , m_dirtyFlags(DirtyNode|DirtyTexture)
+ , m_texture(nullptr)
+{
+ setFlag(QQuickItem::ItemHasContents, true);\
+
+ m_holderLayer->setRecursive(true);
+ m_holderEntity->addComponent(m_holderLayer);
+ m_holderLayerFilter->setParent(m_holderViewport);
+ m_holderLayerFilter->addLayer(m_holderLayer);
+}
+
+Scene3DView::~Scene3DView()
+{
+ if (m_entity)
+ abandonSubtree(m_entity);
+
+ if (m_scene3D)
+ m_scene3D->removeView(this);
+}
+
+Qt3DCore::QEntity *Scene3DView::entity() const
+{
+ return m_entity;
+}
+
+Scene3DItem *Scene3DView::scene3D() const
+{
+ return m_scene3D;
+}
+
+Qt3DCore::QEntity *Scene3DView::viewSubtree() const
+{
+ return m_holderEntity;
+}
+
+QFrameGraphNode *Scene3DView::viewFrameGraph() const
+{
+ return m_holderViewport;
+}
+
+// Called by Scene3DRender::beforeSynchronizing in RenderThread
+void Scene3DView::setTexture(QSGTexture *texture)
+{
+ m_dirtyFlags |= DirtyTexture;
+ m_texture = texture;
+ QQuickItem::update();
+}
+
+QSGTexture *Scene3DView::texture() const
+{
+ return m_texture;
+}
+
+// Called by Scene3DRender::beforeSynchronizing in RenderThread
+void Scene3DView::markSGNodeDirty()
+{
+ m_dirtyFlags |= DirtyNode;
+ QQuickItem::update();
+}
+
+// Main Thread
+void Scene3DView::setEntity(Qt3DCore::QEntity *entity)
+{
+ if (m_entity == entity)
+ return;
+
+ if (m_entity)
+ abandonSubtree(m_entity);
+
+ m_entity = entity;
+ emit entityChanged();
+
+ if (m_entity)
+ adoptSubtree(m_entity);
+}
+
+// Main Thread
+void Scene3DView::setScene3D(Scene3DItem *scene3D)
+{
+ if (m_scene3D == scene3D)
+ return;
+
+ if (m_scene3D) {
+ m_scene3D->removeView(this);
+ QObject::disconnect(m_scene3DDestroyedConnection);
+ }
+
+ setTexture(nullptr);
+ m_scene3D = scene3D;
+ emit scene3DChanged();
+
+
+ if (m_scene3D) {
+ m_scene3DDestroyedConnection = QObject::connect(m_scene3D,
+ &Scene3DItem::destroyed,
+ this,
+ [this] {
+ m_scene3D = nullptr;
+ });
+ m_scene3D->addView(this);
+ }
+}
+
+// Render Thread
+QSGNode *Scene3DView::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
+{
+ Scene3DSGNode *fboNode = static_cast<Scene3DSGNode *>(node);
+ if (fboNode == nullptr)
+ fboNode = new Scene3DSGNode();
+
+ // We only need to draw a sub part of the texture based
+ // on our size, Scene3D essentially acts as a TextureAtlas
+ const QRectF itemRect(mapRectToScene(boundingRect()));
+ const QSize winSize = window() ? window()->size() : QSize();
+ const QRectF normalizedViewportRect(itemRect.x() / winSize.width(),
+ itemRect.y() / winSize.height(),
+ itemRect.width() / winSize.width(),
+ itemRect.height() / winSize.height());
+ // Swap Y axis to match GL coordinates
+ const QRectF textureRect(itemRect.x() / winSize.width(),
+ 1.0f - (itemRect.y() / winSize.height()),
+ itemRect.width() / winSize.width(),
+ -(itemRect.height() / winSize.height()));
+
+ // TO DO: Should be done from main thread
+ // updateViewport
+ m_holderViewport->setNormalizedRect(normalizedViewportRect);
+
+ // update node rect and texture coordinates
+ fboNode->setRect(boundingRect(), textureRect);
+
+ if (m_dirtyFlags & DirtyTexture) {
+ fboNode->setTexture(m_texture);
+ m_dirtyFlags.setFlag(DirtyTexture, false);
+ }
+ if (m_dirtyFlags & DirtyNode) {
+ fboNode->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyFlags.setFlag(DirtyNode, false);
+ }
+
+ return fboNode;
+}
+
+// Main Thread
+void Scene3DView::adoptSubtree(Qt3DCore::QEntity *subtree)
+{
+ // Reparent FrameGraph
+ Qt3DRender::QFrameGraphNode *fgNode = frameGraphFromEntity(subtree);
+ if (fgNode) {
+ m_previousFGParent = fgNode->parentNode();
+ fgNode->setParent(m_holderLayerFilter);
+ }
+
+ // Insert Entity Subtree
+ subtree->setParent(m_holderEntity);
+}
+
+// Main Thread
+void Scene3DView::abandonSubtree(Qt3DCore::QEntity *subtree)
+{
+ // Remove FrameGraph part
+ Qt3DRender::QFrameGraphNode *fgNode = frameGraphFromEntity(subtree);
+ if (fgNode)
+ fgNode->setParent(m_previousFGParent);
+
+ // Remove Entity Subtree
+ subtree->setParent(Q_NODE_NULLPTR);
+}
+
+} // Qt3DRender
+
+QT_END_NAMESPACE