diff options
Diffstat (limited to 'src/quick3d/imports/scene3d/scene3dview.cpp')
-rw-r--r-- | src/quick3d/imports/scene3d/scene3dview.cpp | 318 |
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 |