diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2019-08-08 14:45:29 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2019-08-27 17:08:23 +0200 |
commit | 91dc1e1a61651a82a0ee5ce6ad3b24e82f526be6 (patch) | |
tree | f54c30c337a9bae1e0b5d9c8622dd599344f46bf /src/quick3d | |
parent | f9306d34bde648975b2a3e24eea01aaa4b6bd679 (diff) |
Introduce Scene3DView
Allows to render several 3D scenes using a single Scene3D. To be used when
you need multiple Scene3D instances.
[ChangeLog] Introduce Scene3DView to render multiple distinct 3D scenes
Change-Id: I5d51c5935218cc84c15d57def3703aa0d92040ba
Reviewed-by: Mike Krus <mike.krus@kdab.com>
Diffstat (limited to 'src/quick3d')
-rw-r--r-- | src/quick3d/imports/scene3d/importsscene3d.pro | 6 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/qtquickscene3dplugin.cpp | 2 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3ditem.cpp | 117 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3ditem_p.h | 9 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3drenderer.cpp | 26 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3drenderer_p.h | 5 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3dsgnode.cpp | 7 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3dsgnode_p.h | 2 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3dview.cpp | 318 | ||||
-rw-r--r-- | src/quick3d/imports/scene3d/scene3dview_p.h | 137 |
10 files changed, 613 insertions, 16 deletions
diff --git a/src/quick3d/imports/scene3d/importsscene3d.pro b/src/quick3d/imports/scene3d/importsscene3d.pro index a97dac09a..e29f7b831 100644 --- a/src/quick3d/imports/scene3d/importsscene3d.pro +++ b/src/quick3d/imports/scene3d/importsscene3d.pro @@ -17,7 +17,8 @@ HEADERS += \ scene3drenderer_p.h \ scene3dsgnode_p.h \ scene3dsgmaterialshader_p.h \ - scene3dsgmaterial_p.h + scene3dsgmaterial_p.h \ + scene3dview_p.h SOURCES += \ qtquickscene3dplugin.cpp \ @@ -27,7 +28,8 @@ SOURCES += \ scene3drenderer.cpp \ scene3dsgnode.cpp \ scene3dsgmaterialshader.cpp \ - scene3dsgmaterial.cpp + scene3dsgmaterial.cpp \ + scene3dview.cpp OTHER_FILES += qmldir diff --git a/src/quick3d/imports/scene3d/qtquickscene3dplugin.cpp b/src/quick3d/imports/scene3d/qtquickscene3dplugin.cpp index f93a8fdd1..00f24070e 100644 --- a/src/quick3d/imports/scene3d/qtquickscene3dplugin.cpp +++ b/src/quick3d/imports/scene3d/qtquickscene3dplugin.cpp @@ -42,6 +42,7 @@ #include <QtQml> #include <scene3ditem_p.h> +#include <scene3dview_p.h> QT_BEGIN_NAMESPACE @@ -49,6 +50,7 @@ void QtQuickScene3DPlugin::registerTypes(const char *uri) { qmlRegisterType<Qt3DRender::Scene3DItem>(uri, 2, 0, "Scene3D"); qmlRegisterType<Qt3DRender::Scene3DItem, 14>(uri, 2, 14, "Scene3D"); + qmlRegisterType<Qt3DRender::Scene3DView>(uri, 2, 14, "Scene3DView"); // Auto-increment the import to stay in sync with ALL future Qt minor versions qmlRegisterModule(uri, 2, QT_VERSION_MINOR); diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index 8f3d0b9a6..098cab032 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -72,6 +72,7 @@ #include <scene3dlogging_p.h> #include <scene3drenderer_p.h> #include <scene3dsgnode_p.h> +#include <scene3dview_p.h> #include <Qt3DCore/private/qaspectengine_p.h> #include <Qt3DCore/private/qaspectmanager_p.h> @@ -122,16 +123,33 @@ namespace Qt3DRender { \endqml to that material. + + It is not recommended to instantiate more than a single Scene3D instance + per application. The reason for this is that a Scene3D instance + instantiates the entire Qt 3D engine (memory managers, thread pool, render + ...) under the scene. You should instead look into using \l Scene3DView + instances in conjunction with a single Scene3D instance. + + When using Scene3D with Scene3DViews the following conditions are expected: + \list + \li The compositingMode is set to FBO + \li The Scene3D is sized to occupy the full window size + \li The Scene3D instance is instantiated prior to any Scene3DView + \li The Scene3D entity property is left unset + \endlist */ Scene3DItem::Scene3DItem(QQuickItem *parent) : QQuickItem(parent) , m_entity(nullptr) + , m_viewHolderEntity(nullptr) + , m_viewHolderFG(nullptr) , m_aspectEngine(new Qt3DCore::QAspectEngine()) , m_renderAspect(nullptr) , m_renderer(nullptr) , m_rendererCleaner(new Scene3DCleaner()) , m_multisample(true) , m_dirty(true) + , m_dirtyViews(false) , m_clearsWindowByDefault(true) , m_disableClearWindow(false) , m_cameraAspectRatioMode(AutomaticAspectRatio) @@ -186,7 +204,7 @@ Qt3DCore::QEntity *Scene3DItem::entity() const void Scene3DItem::setAspects(const QStringList &aspects) { if (!m_aspects.isEmpty()) { - qWarning() << "Aspects already set on the Scene3D, ignoring"; + qCWarning(Scene3D) << "Aspects already set on the Scene3D, ignoring"; return; } @@ -312,6 +330,66 @@ Scene3DItem::CompositingMode Scene3DItem::compositingMode() const return m_compositingMode; } +// MainThread called by Scene3DView +void Scene3DItem::addView(Scene3DView *view) +{ + if (m_views.contains(view)) + return; + + Qt3DRender::QFrameGraphNode *viewFG = view->viewFrameGraph(); + Qt3DCore::QEntity *subtreeRoot = view->viewSubtree(); + + if (m_viewHolderEntity == nullptr) { + m_viewHolderEntity = new Qt3DCore::QEntity; + + if (m_entity != nullptr) { + qCWarning(Scene3D) << "Scene3DView is not supported if the Scene3D entity property has been set"; + } + + Qt3DRender::QRenderSettings *settings = new Qt3DRender::QRenderSettings(); + Qt3DRender::QRenderSurfaceSelector *surfaceSelector = new Qt3DRender::QRenderSurfaceSelector(); + m_viewHolderFG = surfaceSelector; + surfaceSelector->setSurface(window()); + + // Copy setting properties from first View + QVector<Qt3DRender::QRenderSettings *> viewRenderSettings = subtreeRoot->componentsOfType<Qt3DRender::QRenderSettings>(); + if (viewRenderSettings.size() > 0) { + Qt3DRender::QRenderSettings *viewRenderSetting = viewRenderSettings.first(); + settings->setRenderPolicy(viewRenderSetting->renderPolicy()); + settings->pickingSettings()->setPickMethod(viewRenderSetting->pickingSettings()->pickMethod()); + settings->pickingSettings()->setPickResultMode(viewRenderSetting->pickingSettings()->pickResultMode()); + } + settings->setActiveFrameGraph(m_viewHolderFG); + m_viewHolderEntity->addComponent(settings); + + setEntity(m_viewHolderEntity); + } + + // Parent FG and Subtree + viewFG->setParent(m_viewHolderFG); + subtreeRoot->setParent(m_viewHolderEntity); + + m_views.push_back(view); + m_dirtyViews |= true; +} + +// MainThread called by Scene3DView +void Scene3DItem::removeView(Scene3DView *view) +{ + if (!m_views.contains(view)) + return; + + Qt3DRender::QFrameGraphNode *viewFG = view->viewFrameGraph(); + Qt3DCore::QEntity *subtreeRoot = view->viewSubtree(); + + // Unparent FG and Subtree + viewFG->setParent(Q_NODE_NULLPTR); + subtreeRoot->setParent(Q_NODE_NULLPTR); + + m_views.removeOne(view); + m_dirtyViews |= true; +} + void Scene3DItem::applyRootEntityChange() { if (m_aspectEngine->rootEntity() != m_entity) { @@ -391,6 +469,20 @@ void Scene3DItem::onBeforeSync() m_renderer->setCompositingMode(m_compositingMode); const bool usesFBO = m_compositingMode == FBO; + // Make renderer aware of any Scene3DView we are dealing with + if (m_dirtyViews) { + // Scene3DViews checks + if (m_entity != m_viewHolderEntity) { + qCWarning(Scene3D) << "Scene3DView is not supported if the Scene3D entity property has been set"; + } + if (!usesFBO) { + qCWarning(Scene3D) << "Scene3DView is only supported when Scene3D compositingMode is set to FBO"; + } + // The Scene3DRender will take care of providing the texture containing the 3D scene + m_renderer->setScene3DViews(m_views); + m_dirtyViews = false; + } + Q_ASSERT(m_aspectEngine->runMode() == Qt3DCore::QAspectEngine::Manual); m_aspectEngine->processFrame(); // The above essentially sets the number of RV for the RenderQueue and @@ -412,10 +504,13 @@ void Scene3DItem::onBeforeSync() // When using the FBO mode, only the QQuickItem needs to be updated // When using the Underlay mode, the whole windows needs updating - if (usesFBO) + if (usesFBO) { QQuickItem::update(); - else + for (Scene3DView *view : m_views) + view->update(); + } else { window()->update(); + } } void Scene3DItem::setWindowSurface(QObject *rootObject) @@ -539,21 +634,17 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode m_renderer->setCleanerHelper(m_rendererCleaner); } const bool usesFBO = m_compositingMode == FBO; + const bool hasScene3DViews = !m_views.empty(); Scene3DSGNode *fboNode = static_cast<Scene3DSGNode *>(node); // When usin Scene3DViews or Scene3D in Underlay mode // we shouldn't be managing a Scene3DSGNode - if (!usesFBO) { + if (!usesFBO || hasScene3DViews) { if (fboNode != nullptr) { delete fboNode; fboNode = nullptr; m_renderer->setSGNode(fboNode); } - // Record clearBeforeRendering value before we force it to false - m_clearsWindowByDefault = window()->clearBeforeRendering(); - m_disableClearWindow = true; - if (m_clearsWindowByDefault) - window()->setClearBeforeRendering(false); } else { // Regular Scene3D only case // Create SGNode if using FBO and no Scene3DViews @@ -562,11 +653,19 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode m_renderer->setSGNode(fboNode); } fboNode->setRect(boundingRect()); + } + if (usesFBO) { // Reset clear flag if we've set it to false it's still set to that if (m_disableClearWindow && !window()->clearBeforeRendering()) window()->setClearBeforeRendering(m_clearsWindowByDefault); m_disableClearWindow = false; + } else { + // Record clearBeforeRendering value before we force it to false + m_clearsWindowByDefault = window()->clearBeforeRendering(); + m_disableClearWindow = true; + if (m_clearsWindowByDefault) + window()->setClearBeforeRendering(false); } return fboNode; diff --git a/src/quick3d/imports/scene3d/scene3ditem_p.h b/src/quick3d/imports/scene3d/scene3ditem_p.h index ae7a4600e..4a2fc7f44 100644 --- a/src/quick3d/imports/scene3d/scene3ditem_p.h +++ b/src/quick3d/imports/scene3d/scene3ditem_p.h @@ -69,6 +69,8 @@ class QCamera; class QRenderAspect; class Scene3DRenderer; class Scene3DCleaner; +class Scene3DView; +class QFrameGraphNode; class Scene3DItem : public QQuickItem { @@ -106,6 +108,9 @@ public: Q_ENUM(CompositingMode) // LCOV_EXCL_LINE CompositingMode compositingMode() const; + void addView(Scene3DView *view); + void removeView(Scene3DView *view); + public Q_SLOTS: void setAspects(const QStringList &aspects); void setEntity(Qt3DCore::QEntity *entity); @@ -135,6 +140,8 @@ private: QStringList m_aspects; Qt3DCore::QEntity *m_entity; + Qt3DCore::QEntity *m_viewHolderEntity; + Qt3DRender::QFrameGraphNode *m_viewHolderFG; Qt3DCore::QAspectEngine *m_aspectEngine; QRenderAspect *m_renderAspect; @@ -143,6 +150,7 @@ private: bool m_multisample; bool m_dirty; + bool m_dirtyViews; bool m_clearsWindowByDefault; bool m_disableClearWindow; @@ -150,6 +158,7 @@ private: CameraAspectRatioMode m_cameraAspectRatioMode; CompositingMode m_compositingMode; QOffscreenSurface *m_dummySurface; + QVector<Scene3DView *> m_views; }; } // Qt3DRender diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp index bc75e0861..bc4dbd362 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer.cpp +++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp @@ -57,6 +57,7 @@ #include <scene3ditem_p.h> #include <scene3dlogging_p.h> #include <scene3dsgnode_p.h> +#include <scene3dview_p.h> QT_BEGIN_NAMESPACE @@ -159,6 +160,7 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp , m_needsShutdown(true) , m_forceRecreate(false) , m_shouldRender(false) + , m_dirtyViews(false) , m_allowRendering(0) { Q_CHECK_PTR(m_item); @@ -313,9 +315,23 @@ void Scene3DRenderer::beforeSynchronize() m_texture.reset(m_window->createTextureFromId(m_finalFBO->texture(), m_finalFBO->size(), QQuickWindow::TextureHasAlphaChannel)); } + // We can render either the Scene3D or the Scene3DView but not both + // at the same time + Q_ASSERT((m_node == nullptr || m_views.empty()) || + (m_node != nullptr && m_views.empty()) || + (m_node == nullptr && !m_views.empty())); + // Set texture on node if (m_node && (!m_node->texture() || generateNewTexture)) m_node->setTexture(m_texture.data()); + + // Set textures on Scene3DView + if (m_dirtyViews || generateNewTexture) { + for (Scene3DView *view : qAsConst(m_views)) + if (!view->texture() || generateNewTexture) + view->setTexture(m_texture.data()); + m_dirtyViews = false; + } } if (m_aspectEngine->rootEntity() != m_item->entity()) { @@ -326,6 +342,9 @@ void Scene3DRenderer::beforeSynchronize() if (m_node) m_node->markDirty(QSGNode::DirtyMaterial); + for (Scene3DView *view : qAsConst(m_views)) + view->markSGNodeDirty(); + m_item->update(); } } @@ -340,6 +359,13 @@ void Scene3DRenderer::setCompositingMode(Scene3DItem::CompositingMode mode) m_compositingMode = mode; } +// Main Thread, Render Thread locked +void Scene3DRenderer::setScene3DViews(const QVector<Scene3DView *> views) +{ + m_views = views; + m_dirtyViews = true; +} + void Scene3DRenderer::setSGNode(Scene3DSGNode *node) { m_node = node; diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h index 11dfef77d..4f3651cd3 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer_p.h +++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h @@ -72,6 +72,7 @@ namespace Qt3DRender { class QRenderAspect; class Scene3DCleaner; class Scene3DSGNode; +class Scene3DViews; class Scene3DRenderer : public QObject { @@ -87,6 +88,8 @@ public: void allowRender(); void setCompositingMode(Scene3DItem::CompositingMode mode); + void setScene3DViews(const QVector<Scene3DView *> views); + public Q_SLOTS: void render(); void shutdown(); @@ -115,8 +118,10 @@ private: bool m_needsShutdown; bool m_forceRecreate; bool m_shouldRender; + bool m_dirtyViews; QSemaphore m_allowRendering; Scene3DItem::CompositingMode m_compositingMode; + QVector<Scene3DView *> m_views; friend class Scene3DCleaner; }; diff --git a/src/quick3d/imports/scene3d/scene3dsgnode.cpp b/src/quick3d/imports/scene3d/scene3dsgnode.cpp index 8806b59a9..c38e9ffeb 100644 --- a/src/quick3d/imports/scene3d/scene3dsgnode.cpp +++ b/src/quick3d/imports/scene3d/scene3dsgnode.cpp @@ -74,13 +74,12 @@ Scene3DSGNode::~Scene3DSGNode() // is terminated. } -void Scene3DSGNode::setRect(const QRectF &rect) +void Scene3DSGNode::setRect(const QRectF &rect, const QRectF textureRect) { if (rect != m_rect) { m_rect = rect; - // Map the item's bounding rect to normalized texture coordinates - const QRectF sourceRect(0.0f, 1.0f, 1.0f, -1.0f); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_rect, sourceRect); + // By default, map the item's bounding rect to normalized texture coordinates + QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_rect, textureRect); markDirty(DirtyGeometry); } } diff --git a/src/quick3d/imports/scene3d/scene3dsgnode_p.h b/src/quick3d/imports/scene3d/scene3dsgnode_p.h index 68b68eea5..388c8a605 100644 --- a/src/quick3d/imports/scene3d/scene3dsgnode_p.h +++ b/src/quick3d/imports/scene3d/scene3dsgnode_p.h @@ -75,7 +75,7 @@ public: } QSGTexture *texture() const Q_DECL_NOTHROW { return m_material.texture(); } - void setRect(const QRectF &rect); + void setRect(const QRectF &rect, const QRectF textureRect = QRectF(0.0f, 1.0f, 1.0f, -1.0f)); QRectF rect() const Q_DECL_NOTHROW { return m_rect; } private: 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 diff --git a/src/quick3d/imports/scene3d/scene3dview_p.h b/src/quick3d/imports/scene3d/scene3dview_p.h new file mode 100644 index 000000000..ffb80f93b --- /dev/null +++ b/src/quick3d/imports/scene3d/scene3dview_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef SCENE3DVIEW_P_H +#define SCENE3DVIEW_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 <QtQuick/QQuickItem> +#include <QtCore/QFlags> + +QT_BEGIN_NAMESPACE + +class QSGTexture; + +namespace Qt3DCore { +class QEntity; +class QNode; +} + +namespace Qt3DRender { + +class QLayer; +class QLayerFilter; +class Scene3DItem; +class QFrameGraphNode; +class QViewport; + +class Scene3DView : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(Qt3DCore::QEntity* entity READ entity WRITE setEntity NOTIFY entityChanged) + Q_PROPERTY(Qt3DRender::Scene3DItem *scene3D READ scene3D WRITE setScene3D NOTIFY scene3DChanged) + Q_CLASSINFO("DefaultProperty", "entity") + +public: + enum DirtyFlag { + DirtyNode = 1 << 0, + DirtyTexture = 1 << 1 + }; + Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag) + + explicit Scene3DView(QQuickItem *parent = nullptr); + ~Scene3DView(); + + Qt3DCore::QEntity *entity() const; + Scene3DItem *scene3D() const; + + Qt3DCore::QEntity *viewSubtree() const; + Qt3DRender::QFrameGraphNode *viewFrameGraph() const; + + void setTexture(QSGTexture *texture); + QSGTexture *texture() const; + + void markSGNodeDirty(); + +public Q_SLOTS: + void setEntity(Qt3DCore::QEntity *entity); + void setScene3D(Scene3DItem *scene3D); + +Q_SIGNALS: + void entityChanged(); + void scene3DChanged(); + +private: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *nodeData) override; + void adoptSubtree(Qt3DCore::QEntity *subtree); + void abandonSubtree(Qt3DCore::QEntity *subtree); + + Scene3DItem *m_scene3D; + Qt3DCore::QEntity *m_entity; + Qt3DCore::QNode *m_previousFGParent; + + Qt3DCore::QEntity *m_holderEntity; + Qt3DRender::QLayer *m_holderLayer; + Qt3DRender::QLayerFilter *m_holderLayerFilter; + Qt3DRender::QViewport *m_holderViewport; + + QMetaObject::Connection m_scene3DDestroyedConnection; + + DirtyFlags m_dirtyFlags; + QSGTexture *m_texture; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Scene3DView::DirtyFlags) + + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // SCENE3DVIEW_P_H |