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 | |
31 | static void initResources() |
32 | { |
33 | #ifdef QT_STATIC |
34 | Q_INIT_RESOURCE(extras); |
35 | #endif |
36 | } |
37 | |
38 | QT_BEGIN_NAMESPACE |
39 | |
40 | namespace Qt3DExtras { |
41 | |
42 | 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 | |
57 | 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 | |
82 | Qt3DWindow::() |
83 | { |
84 | Q_D(Qt3DWindow); |
85 | delete d->m_aspectEngine; |
86 | } |
87 | |
88 | /*! |
89 | Registers the specified \a aspect. |
90 | */ |
91 | void Qt3DWindow::(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 | */ |
101 | void Qt3DWindow::(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 | */ |
111 | void Qt3DWindow::(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 | */ |
126 | void Qt3DWindow::(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 | */ |
135 | Qt3DRender::QFrameGraphNode *Qt3DWindow::() 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 | */ |
144 | Qt3DExtras::QForwardRenderer *Qt3DWindow::() const |
145 | { |
146 | Q_D(const Qt3DWindow); |
147 | return d->m_forwardRenderer; |
148 | } |
149 | |
150 | Qt3DRender::QCamera *Qt3DWindow::() 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 | */ |
159 | Qt3DRender::QRenderSettings *Qt3DWindow::() 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 | */ |
168 | void Qt3DWindow::(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 | */ |
184 | void Qt3DWindow::(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 | */ |
195 | bool Qt3DWindow::(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 | |
207 | void (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 | |
317 | QT_END_NAMESPACE |
318 | |
319 | #include "moc_qt3dwindow.cpp" |
320 | |