1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qssgrendercontextcore.h" |
5 | #include "qssgrenderhelpers.h" |
6 | |
7 | #include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h> |
8 | #include <QtQuick3DRuntimeRender/private/qssgrenderhelpers_p.h> |
9 | #include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h> |
10 | |
11 | #include "graphobjects/qssgrendermodel_p.h" |
12 | #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h> |
13 | |
14 | #include <QtQuick3DUtils/private/qssgassert_p.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | /*! |
19 | \class QSSGRenderHelpers |
20 | \inmodule QtQuick3D |
21 | \since 6.7 |
22 | |
23 | \brief Class containing helper functions for setting up and render QtQuick3D renderables. |
24 | */ |
25 | |
26 | /*! |
27 | \typedef QSSGPrepContextId |
28 | \relates QtQuick3D |
29 | |
30 | Handle to a preparation context. Setting up a preparation context is the first |
31 | step needed before renderables can be created and rendered. |
32 | |
33 | \sa QSSGRenderHelpers::prepareForRender() |
34 | */ |
35 | |
36 | /*! |
37 | \typedef QSSGRenderablesId |
38 | \brief Handle to a set of renderables. |
39 | \relates QtQuick3D |
40 | |
41 | Handle to a set of renderables created for one or more node(s). This \c id can be used |
42 | to for example modify the renderables created for a specific model. |
43 | */ |
44 | |
45 | /*! |
46 | \typedef QSSGPrepResultId |
47 | \relates QtQuick3D |
48 | |
49 | Handle to a preparation result. |
50 | |
51 | Once the \l {QSSGRenderHelpers::createRenderables}{renderables} for a frame are updated and ready |
52 | to be translated into rendering code by the engine, the \l {QSSGRenderHelpers::createRenderables}{renderables} |
53 | and the \l {QSSGRenderHelpers::prepareForRender()}{preparation context} can be \l {QSSGRenderHelpers::commit()}{committed}. |
54 | If the commit succeeds, the returned preparation result can be used to \l {QSSGRenderHelpers::prepareRenderables()}{prepare} |
55 | and \l {QSSGRenderHelpers::renderRenderables}{record} the rendering for the frame. |
56 | */ |
57 | |
58 | /*! |
59 | \enum QSSGRenderHelpers::CreateFlag |
60 | |
61 | \value None The default value. Renderables are created only for the nodes specified. |
62 | \value Recurse Renderables are created for each node and their children. |
63 | \value Steal Renderables are taken from the engine and won't be rendered by QtQuick3D. |
64 | |
65 | \note Calling \l QSSGRenderHelpers::createRenderables() without the {QSSGRenderHelpers::CreateFlag::Steal}{Steal} |
66 | flag set means nodes are duplicated and QtQuick3D will render its copy of the nodes as normal. |
67 | */ |
68 | |
69 | /*! |
70 | Takes a list of node ids and create renderables that can be further processed by the renderer. |
71 | If there are no nodes, or no renderable nodes in the list, the returned id will be invalid. |
72 | |
73 | By default the function does not recurse down and included children of the \a nodes in the list. |
74 | Enabling recursion can be achieved by passing in the \l{CreateFlag::}{Recurse} flag in |
75 | the \a flags argument. |
76 | |
77 | \return an id to the created renderables. |
78 | |
79 | \a frameData, \a prepId |
80 | |
81 | \sa CreateFlags, prepareForRender() |
82 | */ |
83 | QSSGRenderablesId QSSGRenderHelpers::createRenderables(const QSSGFrameData &frameData, |
84 | QSSGPrepContextId prepId, |
85 | const NodeList &nodes, |
86 | CreateFlags flags) |
87 | { |
88 | QSSGRenderablesId rid { QSSGRenderablesId::Invalid }; |
89 | if (nodes.size() > 0) { |
90 | auto *ctx = frameData.contextInterface(); |
91 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
92 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return rid); |
93 | return layer->createRenderables(prepId, nodes, createFlags: flags); |
94 | } |
95 | |
96 | return rid; |
97 | } |
98 | |
99 | /*! |
100 | prepareForRender() creates a context for collecting and storing information about the render-data |
101 | associated with this render extension. |
102 | |
103 | If the same nodes are to be rendered more then once but with different properties, for example |
104 | a different material or camera, then a new context will be needed. To create several contexts for |
105 | one extension the \a slot argument can be used. The default context is created in slot \b 0. |
106 | |
107 | \return an id to the prep context. |
108 | |
109 | \a frameData, \a ext, \a cameraId |
110 | |
111 | \sa commit() |
112 | */ |
113 | |
114 | QSSGPrepContextId QSSGRenderHelpers::prepareForRender(const QSSGFrameData &frameData, |
115 | const QSSGRenderExtension &ext, |
116 | QSSGCameraId cameraId, |
117 | quint32 slot) |
118 | { |
119 | auto *cn = QSSGRenderGraphObjectUtils::getCamera<QSSGRenderCamera>(cameraId); |
120 | QSSG_ASSERT_X(cn && QSSGRenderGraphObject::isCamera(cn->type), "CameraId is not a camera!" , return QSSGPrepContextId::Invalid); |
121 | auto *ctx = frameData.contextInterface(); |
122 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
123 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return QSSGPrepContextId::Invalid); |
124 | return layer->getOrCreateExtensionContext(ext, camera: cn, slot); |
125 | } |
126 | |
127 | /*! |
128 | Once the required changes have been done to the renderables, the data can marked as ready for |
129 | the renderer. |
130 | |
131 | \return an id to the preparation result. |
132 | |
133 | \a frameData, \a prepId, \a renderablesId, \a lodThreshold |
134 | |
135 | \sa prepareRenderables(), renderRenderables() |
136 | */ |
137 | QSSGPrepResultId QSSGRenderHelpers::commit(const QSSGFrameData &frameData, |
138 | QSSGPrepContextId prepId, |
139 | QSSGRenderablesId renderablesId, |
140 | float lodThreshold) |
141 | { |
142 | auto *ctx = frameData.contextInterface(); |
143 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
144 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return QSSGPrepResultId::Invalid); |
145 | return layer->prepareModelsForRender(contextInterface&: *ctx, prepId, renderablesId, lodThreshold); |
146 | } |
147 | |
148 | /*! |
149 | Prepare the draw call data needed for the renderables before calling \l {renderRenderables}. |
150 | |
151 | \return an id to the preparation result. |
152 | |
153 | \a frameData, \a renderPassDescriptor, \a ps, \a prepId, \a filter |
154 | |
155 | \sa renderRenderables() |
156 | */ |
157 | void QSSGRenderHelpers::prepareRenderables(const QSSGFrameData &frameData, |
158 | QSSGPrepResultId prepId, |
159 | QRhiRenderPassDescriptor *renderPassDescriptor, |
160 | QSSGRhiGraphicsPipelineState &ps, |
161 | QSSGRenderablesFilters filter) |
162 | { |
163 | auto *ctx = frameData.contextInterface(); |
164 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
165 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
166 | layer->prepareRenderables(ctx&: *ctx, prepId, renderPassDescriptor, ps, filter); |
167 | } |
168 | |
169 | /*! |
170 | Render the renderables. |
171 | |
172 | \a frameData, \a prepId |
173 | |
174 | \sa prepareRenderables() |
175 | */ |
176 | void QSSGRenderHelpers::renderRenderables(const QSSGFrameData &frameData, |
177 | QSSGPrepResultId prepId) |
178 | { |
179 | QSSGRenderContextInterface *ctx = frameData.contextInterface(); |
180 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
181 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
182 | layer->renderRenderables(ctx&: *ctx, prepId); |
183 | } |
184 | |
185 | QSSGRenderHelpers::QSSGRenderHelpers() |
186 | { |
187 | |
188 | } |
189 | |
190 | /*! |
191 | \class QSSGModelHelpers |
192 | \inmodule QtQuick3D |
193 | \since 6.7 |
194 | |
195 | \brief Class containing helper functions for modifying and setting data for model renderables. |
196 | */ |
197 | |
198 | /*! |
199 | Sets the \a materials to be used on \a model. |
200 | |
201 | \note As with the \l {QtQuick3D::Model::materials}{materials} on the \l {QtQuick3D::Model}{model} item, |
202 | materials are applied in the same manner. |
203 | |
204 | The sub-mesh uses a material from the \l{materials} list, corresponding to its index. If the number |
205 | of materials is less than the sub-meshes, the last material in the list is used for subsequent |
206 | sub-meshes. |
207 | |
208 | \a frameData \a renderablesId |
209 | |
210 | \sa QSSGRenderHelpers::createRenderables() |
211 | */ |
212 | void QSSGModelHelpers::setModelMaterials(const QSSGFrameData &frameData, |
213 | QSSGRenderablesId renderablesId, |
214 | QSSGNodeId model, |
215 | MaterialList materials) |
216 | { |
217 | auto *ctx = frameData.contextInterface(); |
218 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
219 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
220 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
221 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return); |
222 | layer->setModelMaterials(renderablesId, model: *renderModel, materials); |
223 | } |
224 | |
225 | /*! |
226 | Convenience function to apply \a materials to all models in the renderablesId set. |
227 | |
228 | \a frameData, \a renderablesId |
229 | |
230 | \sa QSSGRenderHelpers::createRenderables() |
231 | */ |
232 | void QSSGModelHelpers::setModelMaterials(const QSSGFrameData &frameData, |
233 | QSSGRenderablesId renderablesId, |
234 | MaterialList materials) |
235 | { |
236 | auto *ctx = frameData.contextInterface(); |
237 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
238 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
239 | layer->setModelMaterials(renderablesId, materials); |
240 | } |
241 | |
242 | /*! |
243 | \return Returns the global transform for the \a model in context of the \a prepId. By default the prep context argument is |
244 | QSSGPrepContextId::Uninitialized which returns the model's original global transform. |
245 | |
246 | \a frameData |
247 | |
248 | \sa QSSGRenderHelpers::createRenderables() |
249 | */ |
250 | QMatrix4x4 QSSGModelHelpers::getGlobalTransform(const QSSGFrameData &frameData, |
251 | QSSGNodeId model, |
252 | QSSGPrepContextId prepId) |
253 | { |
254 | auto *ctx = frameData.contextInterface(); |
255 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
256 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return {}); |
257 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
258 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return {}); |
259 | return (prepId != QSSGPrepContextId::Invalid) ? layer->getGlobalTransform(prepId, model: *renderModel) |
260 | : renderModel->globalTransform; |
261 | } |
262 | |
263 | /*! |
264 | \return Returns the local transform for the \a model. |
265 | |
266 | \a frameData |
267 | */ |
268 | QMatrix4x4 QSSGModelHelpers::getLocalTransform(const QSSGFrameData &frameData, QSSGNodeId model) |
269 | { |
270 | Q_UNUSED(frameData); |
271 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
272 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return {}); |
273 | return renderModel->localTransform; |
274 | } |
275 | |
276 | /*! |
277 | \return Returns the global opacity for the \a model. |
278 | |
279 | \a frameData |
280 | */ |
281 | float QSSGModelHelpers::getGlobalOpacity(const QSSGFrameData &frameData, QSSGNodeId model) |
282 | { |
283 | Q_UNUSED(frameData); |
284 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
285 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return {}); |
286 | return renderModel->globalOpacity; |
287 | } |
288 | |
289 | /*! |
290 | \return Returns the global opacity for the \a model in context of the \a prepId. By default the prep context argument is |
291 | QSSGPrepContextId::Uninitialized which returns the model's original global opacity. |
292 | |
293 | \a frameData |
294 | |
295 | \sa QSSGRenderHelpers::createRenderables() |
296 | */ |
297 | float QSSGModelHelpers::getGlobalOpacity(const QSSGFrameData &frameData, QSSGNodeId model, QSSGPrepContextId prepId = QSSGPrepContextId::Invalid) |
298 | { |
299 | auto *ctx = frameData.contextInterface(); |
300 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
301 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return {}); |
302 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
303 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return {}); |
304 | return (prepId != QSSGPrepContextId::Invalid) ? layer->getGlobalOpacity(prepId, model: *renderModel) |
305 | : renderModel->globalOpacity; |
306 | } |
307 | |
308 | /*! |
309 | \return Returns the local opacity for the \a model. |
310 | |
311 | \a frameData |
312 | */ |
313 | float QSSGModelHelpers::getLocalOpacity(const QSSGFrameData &frameData, QSSGNodeId model) |
314 | { |
315 | Q_UNUSED(frameData); |
316 | auto *renderModel = QSSGRenderGraphObjectUtils::getNode<QSSGRenderModel>(nodeId: model); |
317 | QSSG_ASSERT_X(renderModel && renderModel->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return {}); |
318 | return renderModel->localOpacity; |
319 | } |
320 | |
321 | /*! |
322 | Sets the global transform for \a model in the context of the \a renderablesId. |
323 | |
324 | \a frameData, \a transform |
325 | |
326 | \sa QSSGRenderHelpers::createRenderables() |
327 | */ |
328 | void QSSGModelHelpers::setGlobalTransform(const QSSGFrameData &frameData, |
329 | QSSGRenderablesId renderablesId, |
330 | QSSGNodeId model, |
331 | const QMatrix4x4 &transform) |
332 | { |
333 | auto *ctx = frameData.contextInterface(); |
334 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
335 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
336 | auto *node = QSSGRenderGraphObjectUtils::getNode(nodeId: model); |
337 | QSSG_ASSERT_X(node && node->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return); |
338 | const auto &renderModel = static_cast<const QSSGRenderModel &>(*node); |
339 | layer->setGlobalTransform(renderablesId, model: renderModel, mvp: transform); |
340 | } |
341 | |
342 | /*! |
343 | Sets the global opacity for \a model in the context of the \a renderablesId. |
344 | |
345 | \a frameData, \a opacity |
346 | |
347 | \sa QSSGRenderHelpers::createRenderables() |
348 | */ |
349 | void QSSGModelHelpers::setGlobalOpacity(const QSSGFrameData &frameData, QSSGRenderablesId renderablesId, QSSGNodeId model, float opacity) |
350 | { |
351 | auto *ctx = frameData.contextInterface(); |
352 | auto *layer = QSSGLayerRenderData::getCurrent(renderer: *ctx->renderer()); |
353 | QSSG_ASSERT_X(layer, "No active layer for renderer!" , return); |
354 | auto *node = QSSGRenderGraphObjectUtils::getNode(nodeId: model); |
355 | QSSG_ASSERT_X(node && node->type == QSSGRenderGraphObject::Type::Model, "Invalid model-id!" , return); |
356 | const auto &renderModel = static_cast<const QSSGRenderModel &>(*node); |
357 | layer->setGlobalOpacity(renderablesId, model: renderModel, opacity); |
358 | } |
359 | |
360 | /*! |
361 | \class QSSGCameraHelpers |
362 | \inmodule QtQuick3D |
363 | \since 6.7 |
364 | |
365 | \brief Class containing helper functions for getting camera data used for rendering. |
366 | */ |
367 | |
368 | /*! |
369 | Get the projection matrix for \a cameraId. An optional transform argument can be given to be used |
370 | instead of the cameras global transform when calculating the projection matrix. |
371 | |
372 | \return projection matrix for \a cameraId |
373 | |
374 | \a globalTransform |
375 | |
376 | \sa QSSGRenderHelpers::createRenderables() |
377 | */ |
378 | QMatrix4x4 QSSGCameraHelpers::getViewProjectionMatrix(const QSSGCameraId cameraId, |
379 | const QMatrix4x4 *globalTransform) |
380 | { |
381 | auto *renderCamera = QSSGRenderGraphObjectUtils::getCamera<QSSGRenderCamera>(cameraId); |
382 | QSSG_ASSERT(renderCamera && QSSGRenderGraphObject::isCamera(renderCamera->type), return {}); |
383 | |
384 | QMatrix4x4 mat44{Qt::Uninitialized}; |
385 | const auto &projection = renderCamera->projection; |
386 | const auto &transform = (globalTransform != 0) ? *globalTransform : renderCamera->globalTransform; |
387 | QSSGRenderCamera::calculateViewProjectionMatrix(globalTransform: transform, projection, outMatrix&: mat44); |
388 | return mat44; |
389 | } |
390 | |
391 | /*! |
392 | \class QSSGRenderExtensionHelpers |
393 | \inmodule QtQuick3D |
394 | \since 6.7 |
395 | |
396 | \brief Class containing helper functions for the extensions. |
397 | */ |
398 | |
399 | /*! |
400 | Register a render result, in form of a texture, for this \a extension. Once a texture is registered, |
401 | the extension can be uses as a {QtQuick3D::Texture::textureProvider}{texture provider} in QML. |
402 | |
403 | \note To ensure that the \a texture is available for renderables, for example to be used by a {QtQuick3D::Texture} item, |
404 | textures should be registered during the \l QSSGRenderExtension::prepareData call of the extension. |
405 | |
406 | \note Calling this function with a new texture will any previously registered texture. |
407 | \note A texture can be unregistered by registering a nullptr for this extension. |
408 | |
409 | \a frameData |
410 | |
411 | \sa {QtQuick3D::Texture::textureProvider}{textureProvider} |
412 | */ |
413 | void QSSGRenderExtensionHelpers::registerRenderResult(const QSSGFrameData &frameData, |
414 | QSSGExtensionId extension, |
415 | QRhiTexture *texture) |
416 | { |
417 | if (auto *ext = QSSGRenderGraphObjectUtils::getExtension<QSSGRenderExtension>(extensionId: extension)) { |
418 | const QSSGRenderContextInterface *ctx = frameData.contextInterface(); |
419 | ctx->bufferManager()->registerExtensionResult(extensions: *ext, texture); |
420 | } |
421 | } |
422 | |
423 | QT_END_NAMESPACE |
424 | |