1// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
4#include "qt3dwindow.h"
5#include "qt3dwindow_p.h"
6
7#include <QtGui/qtguiglobal.h>
8#include <Qt3DCore/qaspectengine.h>
9#include <Qt3DCore/qentity.h>
10#include <Qt3DCore/qcoreaspect.h>
11#include <Qt3DExtras/qforwardrenderer.h>
12#include <Qt3DRender/qrendersettings.h>
13#include <Qt3DRender/qrenderaspect.h>
14#include <Qt3DInput/qinputaspect.h>
15#include <Qt3DInput/qinputsettings.h>
16#include <Qt3DLogic/qlogicaspect.h>
17#include <Qt3DRender/qcamera.h>
18#if QT_CONFIG(vulkan)
19# include <Qt3DRender/private/vulkaninstance_p.h>
20#endif
21#include <Qt3DRender/qt3drender-config.h>
22#include <qopenglcontext.h>
23#include <private/qrendersettings_p.h>
24
25#include <QEvent>
26
27#if QT_CONFIG(qt3d_vulkan)
28#include <QVulkanInstance>
29#endif
30
31static void initResources()
32{
33#ifdef QT_STATIC
34 Q_INIT_RESOURCE(extras);
35#endif
36}
37
38QT_BEGIN_NAMESPACE
39
40namespace Qt3DExtras {
41
42Qt3DWindowPrivate::Qt3DWindowPrivate()
43 : m_aspectEngine(new Qt3DCore::QAspectEngine)
44 , m_renderAspect(new Qt3DRender::QRenderAspect)
45 , m_inputAspect(new Qt3DInput::QInputAspect)
46 , m_logicAspect(new Qt3DLogic::QLogicAspect)
47 , m_renderSettings(new Qt3DRender::QRenderSettings)
48 , m_forwardRenderer(new Qt3DExtras::QForwardRenderer)
49 , m_defaultCamera(new Qt3DRender::QCamera)
50 , m_inputSettings(new Qt3DInput::QInputSettings)
51 , m_root(new Qt3DCore::QEntity)
52 , m_userRoot(nullptr)
53 , m_initialized(false)
54{
55}
56
57Qt3DWindow::Qt3DWindow(QScreen *screen, Qt3DRender::API api)
58 : QWindow(*new Qt3DWindowPrivate(), nullptr)
59{
60 Q_D(Qt3DWindow);
61
62 initResources();
63
64 if (!d->parentWindow)
65 d->connectToScreen(topLevelScreen: screen ? screen : d->topLevelScreen.data());
66
67 setupWindowSurface(window: this, api);
68
69 resize(w: 1024, h: 768);
70 d->m_aspectEngine->registerAspect(aspect: new Qt3DCore::QCoreAspect);
71 d->m_aspectEngine->registerAspect(aspect: d->m_renderAspect);
72 d->m_aspectEngine->registerAspect(aspect: d->m_inputAspect);
73 d->m_aspectEngine->registerAspect(aspect: d->m_logicAspect);
74
75 d->m_defaultCamera->setParent(d->m_root);
76 d->m_forwardRenderer->setCamera(d->m_defaultCamera);
77 d->m_forwardRenderer->setSurface(this);
78 d->m_renderSettings->setActiveFrameGraph(d->m_forwardRenderer);
79 d->m_inputSettings->setEventSource(this);
80}
81
82Qt3DWindow::~Qt3DWindow()
83{
84 Q_D(Qt3DWindow);
85 delete d->m_aspectEngine;
86}
87
88/*!
89 Registers the specified \a aspect.
90*/
91void Qt3DWindow::registerAspect(Qt3DCore::QAbstractAspect *aspect)
92{
93 Q_ASSERT(!isVisible());
94 Q_D(Qt3DWindow);
95 d->m_aspectEngine->registerAspect(aspect);
96}
97
98/*!
99 Registers the specified aspect \a name.
100*/
101void Qt3DWindow::registerAspect(const QString &name)
102{
103 Q_ASSERT(!isVisible());
104 Q_D(Qt3DWindow);
105 d->m_aspectEngine->registerAspect(name);
106}
107
108/*!
109 Sets the specified \a root entity of the scene.
110*/
111void Qt3DWindow::setRootEntity(Qt3DCore::QEntity *root)
112{
113 Q_D(Qt3DWindow);
114 if (d->m_userRoot != root) {
115 if (d->m_userRoot != nullptr)
116 d->m_userRoot->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
117 if (root != nullptr)
118 root->setParent(d->m_root);
119 d->m_userRoot = root;
120 }
121}
122
123/*!
124 Activates the specified \a activeFrameGraph.
125*/
126void Qt3DWindow::setActiveFrameGraph(Qt3DRender::QFrameGraphNode *activeFrameGraph)
127{
128 Q_D(Qt3DWindow);
129 d->m_renderSettings->setActiveFrameGraph(activeFrameGraph);
130}
131
132/*!
133 Returns the node of the active frame graph.
134*/
135Qt3DRender::QFrameGraphNode *Qt3DWindow::activeFrameGraph() const
136{
137 Q_D(const Qt3DWindow);
138 return d->m_renderSettings->activeFrameGraph();
139}
140
141/*!
142 Returns the node of the default framegraph
143*/
144Qt3DExtras::QForwardRenderer *Qt3DWindow::defaultFrameGraph() const
145{
146 Q_D(const Qt3DWindow);
147 return d->m_forwardRenderer;
148}
149
150Qt3DRender::QCamera *Qt3DWindow::camera() const
151{
152 Q_D(const Qt3DWindow);
153 return d->m_defaultCamera;
154}
155
156/*!
157 Returns the render settings of the 3D Window.
158*/
159Qt3DRender::QRenderSettings *Qt3DWindow::renderSettings() const
160{
161 Q_D(const Qt3DWindow);
162 return d->m_renderSettings;
163}
164
165/*!
166 Manages the display events specified in \a e.
167*/
168void Qt3DWindow::showEvent(QShowEvent *e)
169{
170 Q_D(Qt3DWindow);
171 if (!d->m_initialized) {
172 d->m_root->addComponent(comp: d->m_renderSettings);
173 d->m_root->addComponent(comp: d->m_inputSettings);
174 d->m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(d->m_root));
175
176 d->m_initialized = true;
177 }
178 QWindow::showEvent(e);
179}
180
181/*!
182 Resets the aspect ratio of the 3D window.
183*/
184void Qt3DWindow::resizeEvent(QResizeEvent *)
185{
186 Q_D(Qt3DWindow);
187 d->m_defaultCamera->setAspectRatio(float(width()) / std::max(a: 1.f, b: static_cast<float>(height())));
188}
189
190/*!
191 \reimp
192
193 Requests renderer to redraw if we are using OnDemand render policy.
194*/
195bool Qt3DWindow::event(QEvent *e)
196{
197 Q_D(Qt3DWindow);
198 const bool needsRedraw = (e->type() == QEvent::Expose || e->type() == QEvent::UpdateRequest);
199 if (needsRedraw && d->m_renderSettings->renderPolicy() == Qt3DRender::QRenderSettings::OnDemand) {
200 Qt3DRender::QRenderSettingsPrivate *p = static_cast<Qt3DRender::QRenderSettingsPrivate *>(
201 Qt3DCore::QNodePrivate::get(q: d->m_renderSettings));
202 p->invalidateFrame();
203 }
204 return QWindow::event(e);
205}
206
207void setupWindowSurface(QWindow *window, Qt3DRender::API api) noexcept
208{
209 // If the user pass an API through the environment, we use that over the one passed as argument.
210 const auto userRequestedApi = qgetenv(varName: "QSG_RHI_BACKEND").toLower();
211 if (!userRequestedApi.isEmpty()) {
212 if (userRequestedApi == QByteArrayLiteral("opengl") ||
213 userRequestedApi == QByteArrayLiteral("gl") ||
214 userRequestedApi == QByteArrayLiteral("gles2")) {
215 api = Qt3DRender::API::OpenGL;
216 } else if (userRequestedApi == QByteArrayLiteral("vulkan")) {
217 api = Qt3DRender::API::Vulkan;
218 } else if (userRequestedApi == QByteArrayLiteral("metal")) {
219 api = Qt3DRender::API::Metal;
220 } else if (userRequestedApi == QByteArrayLiteral("d3d11")) {
221 api = Qt3DRender::API::DirectX;
222 } else if (userRequestedApi == QByteArrayLiteral("null")) {
223 api = Qt3DRender::API::Null;
224 } else if (userRequestedApi == QByteArrayLiteral("auto")) {
225 api = Qt3DRender::API::RHI;
226 }
227 }
228
229 // Default to using RHI backend is not specified We want to set the
230 // variable to ensure any 3rd party relying on it to detect which rendering
231 // backend is in use will get a valid value.
232 bool useRhi = false;
233 if (qEnvironmentVariableIsEmpty(varName: "QT3D_RENDERER")) {
234#if QT_CONFIG(qt3d_rhi_renderer)
235 qputenv(varName: "QT3D_RENDERER", value: "rhi");
236#else
237 qputenv("QT3D_RENDERER", "opengl");
238#endif
239 }
240#if QT_CONFIG(qt3d_rhi_renderer)
241 useRhi = qEnvironmentVariable(varName: "QT3D_RENDERER") == QStringLiteral("rhi");
242#endif
243
244 if (!useRhi)
245 api = Qt3DRender::API::OpenGL;
246
247 // We have to set the environment so that the backend is able to read it.
248 if (api == Qt3DRender::API::RHI && useRhi) {
249#if defined(Q_OS_WIN)
250 api = Qt3DRender::API::DirectX;
251#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS)
252 api = Qt3DRender::API::Metal;
253#elif QT_CONFIG(opengl)
254 api = Qt3DRender::API::OpenGL;
255#else
256 api = Qt3DRender::API::Vulkan;
257#endif
258 }
259
260 switch (api) {
261 case Qt3DRender::API::OpenGL:
262 qputenv(varName: "QSG_RHI_BACKEND", value: "opengl");
263 window->setSurfaceType(QSurface::OpenGLSurface);
264 break;
265 case Qt3DRender::API::DirectX:
266 qputenv(varName: "QSG_RHI_BACKEND", value: "d3d11");
267 window->setSurfaceType(QSurface::Direct3DSurface);
268 break;
269 case Qt3DRender::API::Null:
270 qputenv(varName: "QSG_RHI_BACKEND", value: "null");
271 window->setSurfaceType(QSurface::OpenGLSurface);
272 break;
273 case Qt3DRender::API::Metal:
274 qputenv(varName: "QSG_RHI_BACKEND", value: "metal");
275 window->setSurfaceType(QSurface::MetalSurface);
276 break;
277#if QT_CONFIG(qt3d_vulkan)
278 case Qt3DRender::API::Vulkan:
279 {
280 qputenv(varName: "QSG_RHI_BACKEND", value: "vulkan");
281 window->setSurfaceType(QSurface::VulkanSurface);
282 window->setVulkanInstance(&Qt3DRender::staticVulkanInstance());
283 break;
284 }
285#endif
286 case Qt3DRender::API::RHI:
287 default:
288 break;
289 }
290
291 QSurfaceFormat format = QSurfaceFormat::defaultFormat();
292 const QByteArray renderingBackend = qgetenv(varName: "QT3D_RENDERER");
293 const bool usesRHI = renderingBackend.isEmpty() || renderingBackend == QByteArrayLiteral("rhi");
294 if (!usesRHI) {
295#if QT_CONFIG(opengles2)
296 format.setRenderableType(QSurfaceFormat::OpenGLES);
297#else
298 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
299 format.setVersion(major: 4, minor: 3);
300 format.setProfile(QSurfaceFormat::CoreProfile);
301 }
302#endif
303 } else {
304 // This is used for RHI
305 format.setVersion(major: 1, minor: 0);
306 }
307
308 format.setDepthBufferSize(24);
309 format.setSamples(4);
310 format.setStencilBufferSize(8);
311 window->setFormat(format);
312 QSurfaceFormat::setDefaultFormat(format);
313}
314
315} // Qt3DExtras
316
317QT_END_NAMESPACE
318
319#include "moc_qt3dwindow.cpp"
320

source code of qt3d/src/extras/defaults/qt3dwindow.cpp