1/****************************************************************************
2**
3** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "scene3dview_p.h"
41#include <Qt3DCore/QEntity>
42#include <Qt3DRender/QRenderSettings>
43#include <Qt3DRender/QFrameGraphNode>
44#include <Qt3DRender/QLayer>
45#include <Qt3DRender/QLayerFilter>
46#include <Qt3DRender/QViewport>
47#include <scene3dsgnode_p.h>
48#include <scene3ditem_p.h>
49#include <QQuickWindow>
50
51QT_BEGIN_NAMESPACE
52
53namespace Qt3DRender {
54
55/*!
56 \qmltype Scene3DView
57 \inherits Item
58 \inqmlmodule QtQuick.Scene3D
59 \since 5.14
60
61 \preliminary
62
63 \brief The Scene3DView type is used to integrate a Qt 3D sub scene into a
64 QtQuick 2 scene using Scene3D. Whereas you should only use a single Scene3D
65 instance per application, you can have multiple Scene3DView instances.
66
67 Essentially, if you need to render multiple scenes each in a separate view,
68 you should use a single Scene3D instance and as many Scene3DView items as
69 you have scenes to render.
70
71 Typical usage looks like:
72 \qml
73 Scene3D {
74 id: mainScene3D
75 anchors.fill: parent
76 }
77
78 Scene3DView {
79 id: view1
80 scene3D: mainScene3D
81 width: 200
82 height: 200
83 Entity {
84 ...
85 }
86 }
87
88 Scene3DView {
89 id: view2
90 scene3D: mainScene3D
91 width: 200
92 height: 200
93 x: 200
94 Entity {
95 ...
96 }
97 }
98 \endqml
99
100 There are a few limitations when using Scene3DView:
101 \list
102 \li The Scene3D compositingMode has to be set to FBO
103 \li The Scene3D is sized to occupy the full window size (at the very least
104 it must be sized as wide as the area occupied by all Scene3DViews)
105 \li The Scene3D instance is instantiated prior to any Scene3DView
106 \li The Scene3D entity property is left unset
107 \endlist
108
109 Scene3D behaves likes a texture atlas from which all Scene3DView instances.
110 For this reason, care should be taken that only the first Scene3DView
111 declared in the scene clears the color/depth. Additionally overlapping
112 Scene3DView instances is discouraged as this might not produce the expected
113 output.
114
115 It is expected that a Scene3DView's Entity provide a RenderSettings with a
116 valid SceneGraph. Please note that only the RenderSettings of the first
117 Scene3DView instantiated will be taken into account.
118
119 There are no restriction on the sharing of elements between different scenes
120 in different Scene3DView instances.
121
122 By default, you are in charge of ensuring the lifetime of the referenced
123 Entity. If you wish to transfer this duty to the Scene3DView, the
124 ownsEntity property can be set to true (defaults to false).
125 */
126
127namespace {
128
129Qt3DRender::QFrameGraphNode *frameGraphFromEntity(Qt3DCore::QEntity *entity)
130{
131 const auto renderSettingsComponents = entity->componentsOfType<Qt3DRender::QRenderSettings>();
132
133 if (renderSettingsComponents.size() > 0) {
134 Qt3DRender::QRenderSettings *renderSettings = renderSettingsComponents.first();
135 return renderSettings->activeFrameGraph();
136 }
137 return nullptr;
138}
139
140}
141
142Scene3DView::Scene3DView(QQuickItem *parent)
143 : QQuickItem(parent)
144 , m_scene3D(nullptr)
145 , m_entity(nullptr)
146 , m_previousFGParent(nullptr)
147 , m_holderEntity(new Qt3DCore::QEntity())
148 , m_holderLayer(new Qt3DRender::QLayer())
149 , m_holderLayerFilter(new Qt3DRender::QLayerFilter())
150 , m_holderViewport(new Qt3DRender::QViewport())
151 , m_dirtyFlags(DirtyNode|DirtyTexture)
152 , m_texture(nullptr)
153 , m_ownsEntity(false)
154{
155 setFlag(flag: QQuickItem::ItemHasContents, enabled: true);\
156
157 m_holderLayer->setRecursive(true);
158 m_holderEntity->addComponent(comp: m_holderLayer);
159 m_holderLayerFilter->setParent(m_holderViewport);
160 m_holderLayerFilter->addLayer(layer: m_holderLayer);
161}
162
163Scene3DView::~Scene3DView()
164{
165 if (m_entity) {
166 abandonSubtree(subtree: m_entity.data());
167 if (m_ownsEntity)
168 m_entity->deleteLater();
169 }
170
171 if (m_scene3D)
172 m_scene3D->removeView(view: this);
173}
174
175Qt3DCore::QEntity *Scene3DView::entity() const
176{
177 return m_entity.data();
178}
179
180Scene3DItem *Scene3DView::scene3D() const
181{
182 return m_scene3D;
183}
184
185Qt3DCore::QEntity *Scene3DView::viewSubtree() const
186{
187 return m_holderEntity;
188}
189
190QFrameGraphNode *Scene3DView::viewFrameGraph() const
191{
192 return m_holderViewport;
193}
194
195// Called by Scene3DRender::beforeSynchronizing in RenderThread
196void Scene3DView::setTexture(QSGTexture *texture)
197{
198 m_dirtyFlags |= DirtyTexture;
199 m_texture = texture;
200 QQuickItem::update();
201}
202
203QSGTexture *Scene3DView::texture() const
204{
205 return m_texture;
206}
207
208bool Scene3DView::ownsEntity() const
209{
210 return m_ownsEntity;
211}
212
213// Called by Scene3DRender::beforeSynchronizing in RenderThread
214void Scene3DView::markSGNodeDirty()
215{
216 m_dirtyFlags |= DirtyNode;
217 QQuickItem::update();
218}
219
220// Main Thread
221void Scene3DView::setEntity(Qt3DCore::QEntity *entity)
222{
223 if (m_entity.data() == entity)
224 return;
225
226 if (m_entity) {
227 abandonSubtree(subtree: m_entity.data());
228 if (m_ownsEntity)
229 m_entity->deleteLater();
230 }
231
232 m_entity = entity;
233 emit entityChanged();
234
235 if (m_entity)
236 adoptSubtree(subtree: m_entity.data());
237}
238
239// Main Thread
240void Scene3DView::setScene3D(Scene3DItem *scene3D)
241{
242 if (m_scene3D == scene3D)
243 return;
244
245 if (m_scene3D) {
246 m_scene3D->removeView(view: this);
247 QObject::disconnect(m_scene3DDestroyedConnection);
248 }
249
250 setTexture(nullptr);
251 m_scene3D = scene3D;
252 emit scene3DChanged();
253
254
255 if (m_scene3D) {
256 m_scene3DDestroyedConnection = QObject::connect(sender: m_scene3D,
257 signal: &Scene3DItem::destroyed,
258 context: this,
259 slot: [this] {
260 m_scene3D = nullptr;
261 });
262 m_scene3D->addView(view: this);
263 }
264}
265
266void Scene3DView::setOwnsEntity(bool ownsEntity)
267{
268 if (ownsEntity == m_ownsEntity)
269 return;
270 m_ownsEntity = ownsEntity;
271 emit ownsEntityChanged();
272}
273
274// Render Thread
275QSGNode *Scene3DView::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
276{
277 Scene3DSGNode *fboNode = static_cast<Scene3DSGNode *>(node);
278 if (fboNode == nullptr)
279 fboNode = new Scene3DSGNode();
280
281 // We only need to draw a sub part of the texture based
282 // on our size, Scene3D essentially acts as a TextureAtlas
283 const QRectF itemRect(mapRectToScene(rect: boundingRect()));
284 const QSize winSize = window() ? window()->size() : QSize();
285 const QRectF normalizedViewportRect(itemRect.x() / winSize.width(),
286 itemRect.y() / winSize.height(),
287 itemRect.width() / winSize.width(),
288 itemRect.height() / winSize.height());
289 // Swap Y axis to match GL coordinates
290 const QRectF textureRect(itemRect.x() / winSize.width(),
291 1.0f - (itemRect.y() / winSize.height()),
292 itemRect.width() / winSize.width(),
293 -(itemRect.height() / winSize.height()));
294
295 // TO DO: Should be done from main thread
296 // updateViewport
297 m_holderViewport->setNormalizedRect(normalizedViewportRect);
298
299 // update node rect and texture coordinates
300 fboNode->setRect(rect: boundingRect(), textureRect);
301
302 if (m_dirtyFlags & DirtyTexture) {
303 fboNode->setTexture(m_texture);
304 m_dirtyFlags.setFlag(flag: DirtyTexture, on: false);
305 // Show FBO Node at this point
306 fboNode->show();
307 }
308 if (m_dirtyFlags & DirtyNode) {
309 fboNode->markDirty(bits: QSGNode::DirtyMaterial);
310 m_dirtyFlags.setFlag(flag: DirtyNode, on: false);
311 }
312
313 return fboNode;
314}
315
316// Main Thread
317void Scene3DView::adoptSubtree(Qt3DCore::QEntity *subtree)
318{
319 // Reparent FrameGraph
320 Qt3DRender::QFrameGraphNode *fgNode = frameGraphFromEntity(entity: subtree);
321 if (fgNode) {
322 m_previousFGParent = fgNode->parentNode();
323 fgNode->setParent(m_holderLayerFilter);
324 }
325
326 // Insert Entity Subtree
327 subtree->setParent(m_holderEntity);
328}
329
330// Main Thread
331void Scene3DView::abandonSubtree(Qt3DCore::QEntity *subtree)
332{
333 // Remove FrameGraph part
334 Qt3DRender::QFrameGraphNode *fgNode = frameGraphFromEntity(entity: subtree);
335 if (fgNode)
336 fgNode->setParent(m_previousFGParent);
337
338 // Remove Entity Subtree
339 subtree->setParent(Q_NODE_NULLPTR);
340}
341
342} // Qt3DRender
343
344QT_END_NAMESPACE
345

source code of qt3d/src/quick3d/imports/scene3d/scene3dview.cpp