Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qssglayerrenderdata.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
6
7#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrenderskeleton_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderjoint_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrendermorphtarget_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrenderparticles_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
24#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
25
26//#define QT_QUICK3D_MESH_LOD_DEBUG
27//#define QT_QUICK3D_MESH_LOD_NORMALS_DEBUG
28
29#if defined(QT_QUICK3D_MESH_LOD_DEBUG) || defined(QT_QUICK3D_MESH_LOD_NORMALS_DEBUG)
30#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
31#endif
32
33#include <QtQuick3DUtils/private/qssgutils_p.h>
34#include <QtQuick3DUtils/private/qssgassert_p.h>
35
36#include <QtQuick/private/qsgtexture_p.h>
37#include <QtQuick/private/qsgrenderer_p.h>
38
39#include <QtCore/QCoreApplication>
40#include <QtCore/QBitArray>
41#include <array>
42
43#include "qssgrenderpass_p.h"
44
46
47Q_LOGGING_CATEGORY(lcQuick3DRender, "qt.quick3d.render");
48
49#define POS4BONETRANS(x) (sizeof(float) * 16 * (x) * 2)
50#define POS4BONENORM(x) (sizeof(float) * 16 * ((x) * 2 + 1))
51#define BONEDATASIZE4ID(x) POS4BONETRANS(x + 1)
52
53static bool checkParticleSupport(QRhi *rhi)
54{
55 QSSG_ASSERT(rhi, return false);
56
57 bool ret = true;
58 const bool supportRgba32f = rhi->isTextureFormatSupported(QRhiTexture::RGBA32F);
59 const bool supportRgba16f = rhi->isTextureFormatSupported(QRhiTexture::RGBA16F);
60 if (!supportRgba32f && !supportRgba16f) {
61 static bool warningShown = false;
62 if (!warningShown) {
63 qWarning () << "Particles not supported due to missing RGBA32F and RGBA16F texture format support";
64 warningShown = true;
65 }
66 ret = false;
67 }
68
69 return ret;
70}
71
72// These are meant to be pixel offsets, so you need to divide them by the width/height
73// of the layer respectively.
75 QVector2D(-0.170840f, -0.553840f), // 1x
76 QVector2D(0.162960f, -0.319340f), // 2x
77 QVector2D(0.360260f, -0.245840f), // 3x
78 QVector2D(-0.561340f, -0.149540f), // 4x
79 QVector2D(0.249460f, 0.453460f), // 5x
80 QVector2D(-0.336340f, 0.378260f), // 6x
81 QVector2D(0.340000f, 0.166260f), // 7x
82 QVector2D(0.235760f, 0.527760f), // 8x
83};
84
86{
87 QSSG_ASSERT(visibleRenderables.isEmpty(), visibleRenderables.clear());
88 visibleRenderables.reserve(renderables.size());
89 for (quint32 end = renderables.size(), idx = quint32(0); idx != end; ++idx) {
90 auto handle = renderables.at(idx);
91 const auto &b = handle.obj->globalBounds;
92 if (clipFrustum.intersectsWith(b))
93 visibleRenderables.push_back(handle);
94 }
95
96 return visibleRenderables.size();
97}
98
100{
101 const qint32 end = renderables.size();
102 qint32 front = 0;
103 qint32 back = end - 1;
104
105 while (front <= back) {
106 const auto &b = renderables.at(front).obj->globalBounds;
107 if (clipFrustum.intersectsWith(b))
108 ++front;
109 else
110 renderables.swapItemsAt(front, back--);
111 }
112
113 return back + 1;
114}
115
116[[nodiscard]] constexpr static inline bool nearestToFurthestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
117{
118 return lhs.cameraDistanceSq < rhs.cameraDistanceSq;
119}
120
121[[nodiscard]] constexpr static inline bool furthestToNearestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
122{
123 return lhs.cameraDistanceSq > rhs.cameraDistanceSq;
124}
125
126static void collectBoneTransforms(QSSGRenderNode *node, QSSGRenderSkeleton *skeletonNode, const QVector<QMatrix4x4> &poses)
127{
128 if (node->type == QSSGRenderGraphObject::Type::Joint) {
129 QSSGRenderJoint *jointNode = static_cast<QSSGRenderJoint *>(node);
130 jointNode->calculateGlobalVariables();
131 QMatrix4x4 globalTrans = jointNode->globalTransform;
132 // if user doesn't give the inverseBindPose, identity matrices are used.
133 if (poses.size() > jointNode->index)
134 globalTrans *= poses[jointNode->index];
135 memcpy(skeletonNode->boneData.data() + POS4BONETRANS(jointNode->index),
136 reinterpret_cast<const void *>(globalTrans.constData()),
137 sizeof(float) * 16);
138 // only upper 3x3 is meaningful
139 memcpy(skeletonNode->boneData.data() + POS4BONENORM(jointNode->index),
140 reinterpret_cast<const void *>(QMatrix4x4(globalTrans.normalMatrix()).constData()),
141 sizeof(float) * 11);
142 } else {
143 skeletonNode->containsNonJointNodes = true;
144 }
145 for (auto &child : node->children)
146 collectBoneTransforms(&child, skeletonNode, poses);
147}
148
149static bool hasDirtyNonJointNodes(QSSGRenderNode *node, bool &hasChildJoints)
150{
151 if (!node)
152 return false;
153 // we might be non-joint dirty node, but if we do not have child joints we need to return false
154 // Note! The frontend clears TransformDirty. Use dirty instead.
155 bool dirtyNonJoint = ((node->type != QSSGRenderGraphObject::Type::Joint)
156 && node->isDirty());
157
158 // Tell our parent we are joint
159 if (node->type == QSSGRenderGraphObject::Type::Joint)
160 hasChildJoints = true;
161 bool nodeHasChildJoints = false;
162 for (auto &child : node->children) {
163 bool ret = hasDirtyNonJointNodes(&child, nodeHasChildJoints);
164 // return if we have child joints and non-joint dirty nodes, else check other children
165 hasChildJoints |= nodeHasChildJoints;
166 if (ret && nodeHasChildJoints)
167 return true;
168 }
169 // return true if we have child joints and we are dirty non-joint
170 hasChildJoints |= nodeHasChildJoints;
171 return dirtyNonJoint && nodeHasChildJoints;
172}
173
174template<typename T, typename V>
175inline void collectNode(V node, QVector<T> &dst, int &dstPos)
176{
177 if (dstPos < dst.size())
178 dst[dstPos] = node;
179 else
180 dst.push_back(node);
181
182 ++dstPos;
183}
184template <typename T, typename V>
185static inline void collectNodeFront(V node, QVector<T> &dst, int &dstPos)
186{
187 if (dstPos < dst.size())
188 dst[dst.size() - dstPos - 1] = node;
189 else
190 dst.push_front(node);
191
192 ++dstPos;
193}
194
195#define MAX_MORPH_TARGET 8
196#define MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS 3
197#define MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS 1
198
200 QVector<QSSGRenderableNodeEntry> &outRenderableModels,
201 int &ioRenderableModelsCount,
202 QVector<QSSGRenderableNodeEntry> &outRenderableParticles,
203 int &ioRenderableParticlesCount,
204 QVector<QSSGRenderItem2D *> &outRenderableItem2Ds,
205 int &ioRenderableItem2DsCount,
206 QVector<QSSGRenderCamera *> &outCameras,
207 int &ioCameraCount,
209 int &ioLightCount,
210 QVector<QSSGRenderReflectionProbe *> &outReflectionProbes,
211 int &ioReflectionProbeCount,
212 quint32 &ioDFSIndex)
213{
216 ++ioDFSIndex;
217 inNode.dfsIndex = ioDFSIndex;
219 if (inNode.type == QSSGRenderNode::Type::Model)
220 collectNode(QSSGRenderableNodeEntry(inNode), outRenderableModels, ioRenderableModelsCount);
221 else if (inNode.type == QSSGRenderNode::Type::Particles)
222 collectNode(QSSGRenderableNodeEntry(inNode), outRenderableParticles, ioRenderableParticlesCount);
223 else if (inNode.type == QSSGRenderNode::Type::Item2D) // Pushing front to keep item order inside QML file
224 collectNodeFront(static_cast<QSSGRenderItem2D *>(&inNode), outRenderableItem2Ds, ioRenderableItem2DsCount);
225 } else if (QSSGRenderGraphObject::isCamera(inNode.type)) {
226 collectNode(static_cast<QSSGRenderCamera *>(&inNode), outCameras, ioCameraCount);
227 } else if (QSSGRenderGraphObject::isLight(inNode.type)) {
228 if (auto &light = static_cast<QSSGRenderLight &>(inNode); light.isEnabled())
229 collectNode(&light, outLights, ioLightCount);
230 } else if (inNode.type == QSSGRenderGraphObject::Type::ReflectionProbe) {
231 collectNode(static_cast<QSSGRenderReflectionProbe *>(&inNode), outReflectionProbes, ioReflectionProbeCount);
232 }
233
234 for (auto &theChild : inNode.children)
235 wasDirty |= maybeQueueNodeForRender(theChild,
236 outRenderableModels,
237 ioRenderableModelsCount,
238 outRenderableParticles,
239 ioRenderableParticlesCount,
240 outRenderableItem2Ds,
241 ioRenderableItem2DsCount,
242 outCameras,
243 ioCameraCount,
244 outLights,
245 ioLightCount,
246 outReflectionProbes,
247 ioReflectionProbeCount,
248 ioDFSIndex);
249 }
250 return wasDirty;
251}
252
254 : firstImage(nullptr), opacity(1.0f), materialKey(inKey), dirty(false)
255{
256}
257
259{
261 if (camera) {
262 // Calculate viewProjection and clippingFrustum for Render Camera
263 QMatrix4x4 viewProjection(Qt::Uninitialized);
264 camera->calculateViewProjectionMatrix(viewProjection);
265 std::optional<QSSGClippingFrustum> clippingFrustum;
266 if (camera->enableFrustumClipping) {
267 QSSGClipPlane nearPlane;
268 QMatrix3x3 theUpper33(camera->globalTransform.normalMatrix());
269 QVector3D dir(mat33::transform(theUpper33, QVector3D(0, 0, -1)));
270 dir.normalize();
271 nearPlane.normal = dir;
272 QVector3D theGlobalPos = camera->getGlobalPos() + camera->clipNear * dir;
273 nearPlane.d = -(QVector3D::dotProduct(dir, theGlobalPos));
274 // the near plane's bbox edges are calculated in the clipping frustum's
275 // constructor.
276 clippingFrustum = QSSGClippingFrustum{viewProjection, nearPlane};
277 }
278 ret = { viewProjection, clippingFrustum, camera->getScalingCorrectDirection(), camera->getGlobalPos() };
279 }
280
281 return ret;
282}
283
284// Returns the cached data for the active render camera (if any)
285QSSGCameraRenderData QSSGLayerRenderData::getCachedCameraData()
286{
287 if (!cameraData.has_value())
289
290 return *cameraData;
291}
292
293[[nodiscard]] static inline float getCameraDistanceSq(const QSSGRenderableObject &obj,
294 const QSSGCameraRenderData &camera) noexcept
295{
296 const QVector3D difference = obj.worldCenterPoint - camera.position;
297 return QVector3D::dotProduct(difference, camera.direction) + obj.depthBiasSq;
298}
299
300// Per-frame cache of renderable objects post-sort.
302{
303 if (!renderedOpaqueObjects.empty() || camera == nullptr)
305
308 // Render nearest to furthest objects
310 }
312}
313
314// If layer depth test is false, this may also contain opaque objects.
316{
317 if (!renderedTransparentObjects.empty() || camera == nullptr)
319
321
322 if (!layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest))
324
326 // render furthest to nearest.
328 }
329
331}
332
334{
335 if (!renderedScreenTextureObjects.empty() || camera == nullptr)
339 // render furthest to nearest.
341 }
343}
344
346{
347 if (!renderedBakedLightingModels.empty() || camera == nullptr)
349 if (layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest) && !bakedLightingModels.empty()) {
352 // sort nearest to furthest (front to back)
353 std::sort(lm.renderables.begin(), lm.renderables.end(), nearestToFurthestCompare);
354 }
355 }
357}
358
360{
361 if (!renderedItem2Ds.isEmpty() || camera == nullptr)
362 return renderedItem2Ds;
363
365
366 if (!renderedItem2Ds.isEmpty()) {
367 const auto cameraDirectionAndPosition = getCachedCameraData();
368 const QVector3D &cameraDirection = cameraDirectionAndPosition.direction;
369 const QVector3D &cameraPosition = cameraDirectionAndPosition.position;
370
371 const auto isItemNodeDistanceGreatThan = [cameraDirection, cameraPosition]
372 (const QSSGRenderItem2D *lhs, const QSSGRenderItem2D *rhs) {
373 if (!lhs->parent || !rhs->parent)
374 return false;
375 const QVector3D lhsDifference = lhs->parent->getGlobalPos() - cameraPosition;
376 const float lhsCameraDistanceSq = QVector3D::dotProduct(lhsDifference, cameraDirection);
377 const QVector3D rhsDifference = rhs->parent->getGlobalPos() - cameraPosition;
378 const float rhsCameraDistanceSq = QVector3D::dotProduct(rhsDifference, cameraDirection);
379 return lhsCameraDistanceSq > rhsCameraDistanceSq;
380 };
381
382 const auto isItemZOrderLessThan = []
383 (const QSSGRenderItem2D *lhs, const QSSGRenderItem2D *rhs) {
384 if (lhs->parent && rhs->parent && lhs->parent == rhs->parent) {
385 // Same parent nodes, so sort with item z-ordering
386 return lhs->zOrder < rhs->zOrder;
387 }
388 return false;
389 };
390
391 // Render furthest to nearest items (parent nodes).
392 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemNodeDistanceGreatThan);
393 // Render items inside same node by item z-order.
394 // Note: stable_sort so item order in QML file is respected.
395 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemZOrderLessThan);
396 }
397
398 return renderedItem2Ds;
399}
400
401// Depth Write List
402void QSSGLayerRenderData::updateSortedDepthObjectsListImp()
403{
405 return;
406
407 const auto &sortedOpaqueObjects = getSortedOpaqueRenderableObjects(); // front to back
408 const auto &sortedTransparentObjects = getSortedTransparentRenderableObjects(); // back to front
409 const auto &sortedScreenTextureObjects = getSortedScreenTextureRenderableObjects(); // back to front
410 if (layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest)) {
411 for (const auto &opaqueObject : sortedOpaqueObjects) {
412 const auto depthMode = opaqueObject.obj->depthWriteMode;
413 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
414 renderedDepthWriteObjects.append(opaqueObject);
415 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
417 }
418 for (const auto &transparentObject : sortedTransparentObjects) {
419 const auto depthMode = transparentObject.obj->depthWriteMode;
420 if (depthMode == QSSGDepthDrawMode::Always)
421 renderedDepthWriteObjects.append(transparentObject);
422 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
423 renderedOpaqueDepthPrepassObjects.append(transparentObject);
424 }
425 for (const auto &screenTextureObject : sortedScreenTextureObjects) {
426 const auto depthMode = screenTextureObject.obj->depthWriteMode;
427 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
428 renderedDepthWriteObjects.append(screenTextureObject);
429 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
430 renderedOpaqueDepthPrepassObjects.append(screenTextureObject);
431 }
432 }
433}
434
436{
437 updateSortedDepthObjectsListImp();
439}
440
442{
443 updateSortedDepthObjectsListImp();
445}
446
451template <typename T, typename... Args>
453{
454 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
455 return new (ctx.perFrameAllocator().allocate(sizeof(T)))T(std::forward<Args>(args)...);
456}
457
458template <typename T>
460{
461 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
462 const size_t asize = sizeof(T) * count;
463 return { reinterpret_cast<T *>(ctx.perFrameAllocator().allocate(asize)), qsizetype(count) };
464}
465
467 QSSGRenderDefaultMaterial::MaterialLighting inLightingType, const QSSGShaderLightListView &lights, bool receivesShadows)
468{
469 QSSGShaderDefaultMaterialKey theGeneratedKey(qHash(features));
470 const bool lighting = inLightingType != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
472 if (lighting) {
473 renderer->defaultMaterialShaderKeyProperties().m_hasIbl.setValue(theGeneratedKey, layer.lightProbe != nullptr);
474
475 quint32 numLights = quint32(lights.size());
478
479 int shadowMapCount = 0;
480 for (int lightIdx = 0, lightEnd = lights.size(); lightIdx < lightEnd; ++lightIdx) {
481 QSSGRenderLight *theLight(lights[lightIdx].light);
482 const bool isDirectional = theLight->type == QSSGRenderLight::Type::DirectionalLight;
483 const bool isSpot = theLight->type == QSSGRenderLight::Type::SpotLight;
484 const bool castsShadows = theLight->m_castShadow
485 && !theLight->m_fullyBaked
486 && receivesShadows
487 && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS;
488 if (castsShadows)
489 ++shadowMapCount;
490
491 renderer->defaultMaterialShaderKeyProperties().m_lightFlags[lightIdx].setValue(theGeneratedKey, !isDirectional);
492 renderer->defaultMaterialShaderKeyProperties().m_lightSpotFlags[lightIdx].setValue(theGeneratedKey, isSpot);
493 renderer->defaultMaterialShaderKeyProperties().m_lightShadowFlags[lightIdx].setValue(theGeneratedKey, castsShadows);
494 }
495 }
496 return theGeneratedKey;
497}
498
501 QSSGRenderableImage *&ioFirstImage,
502 QSSGRenderableImage *&ioNextImage,
504 QSSGShaderDefaultMaterialKey &inShaderKey,
505 quint32 inImageIndex,
506 QSSGRenderDefaultMaterial *inMaterial)
507{
509 const auto &bufferManager = contextInterface.bufferManager();
510
511 if (inImage.clearDirty())
513
514 // This is where the QRhiTexture gets created, if not already done. Note
515 // that the bufferManager is per-QQuickWindow, and so per-render-thread.
516 // Hence using the same Texture (backed by inImage as the backend node) in
517 // multiple windows will work by each scene in each window getting its own
518 // QRhiTexture. And that's why the QSSGRenderImageTexture cannot be a
519 // member of the QSSGRenderImage. Conceptually this matches what we do for
520 // models (QSSGRenderModel -> QSSGRenderMesh retrieved from the
521 // bufferManager in each prepareModelForRender, etc.).
522
523 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(&inImage);
524
525 if (texture.m_texture) {
526 if (texture.m_flags.hasTransparency()
527 && (inMapType == QSSGRenderableImage::Type::Diffuse // note: Type::BaseColor is skipped here intentionally
528 || inMapType == QSSGRenderableImage::Type::Opacity
529 || inMapType == QSSGRenderableImage::Type::Translucency))
530 {
532 }
533
534 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, inMapType, inImage, texture);
536
537 theKeyProp.setEnabled(inShaderKey, true);
538 switch (inImage.m_mappingMode) {
540 break;
542 theKeyProp.setEnvMap(inShaderKey, true);
543 break;
545 theKeyProp.setLightProbe(inShaderKey, true);
546 break;
547 }
548
549 bool hasA = false;
550 bool hasG = false;
551 bool hasB = false;
552
553
554 //### TODO: More formats
555 switch (texture.m_texture->format()) {
557 hasA = !renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
558 break;
560 // Leave BGA as false
561 break;
562 default:
563 hasA = true;
564 hasG = true;
565 hasB = true;
566 break;
567 }
568
569 if (inImage.isImageTransformIdentity())
570 theKeyProp.setIdentityTransform(inShaderKey, true);
571
572 if (inImage.m_indexUV == 1)
573 theKeyProp.setUsesUV1(inShaderKey, true);
574
575 if (ioFirstImage == nullptr)
576 ioFirstImage = theImage;
577 else
578 ioNextImage->m_nextImage = theImage;
579
580 ioNextImage = theImage;
581
584
587 switch (inImageIndex) {
589 value = inMaterial->opacityChannel;
590 break;
592 value = inMaterial->roughnessChannel;
593 break;
595 value = inMaterial->metalnessChannel;
596 break;
598 value = inMaterial->occlusionChannel;
599 break;
601 value = inMaterial->translucencyChannel;
602 break;
604 value = inMaterial->heightChannel;
605 break;
607 value = inMaterial->clearcoatChannel;
608 break;
610 value = inMaterial->clearcoatRoughnessChannel;
611 break;
613 value = inMaterial->transmissionChannel;
614 break;
616 value = inMaterial->thicknessChannel;
617 break;
618 default:
619 break;
620 }
621 bool useDefault = false;
622 switch (value) {
624 useDefault = !hasG;
625 break;
627 useDefault = !hasB;
628 break;
630 useDefault = !hasA;
631 break;
632 default:
633 break;
634 }
635 if (useDefault)
636 value = QSSGRenderDefaultMaterial::R; // Always Fallback to Red
638 }
639 }
640}
641
645{
646 quint32 vertexAttribs = 0;
647 if (renderableFlags.hasAttributePosition())
649 if (renderableFlags.hasAttributeNormal())
651 if (renderableFlags.hasAttributeTexCoord0())
653 if (renderableFlags.hasAttributeTexCoord1())
655 if (renderableFlags.hasAttributeTexCoordLightmap())
657 if (renderableFlags.hasAttributeTangent())
659 if (renderableFlags.hasAttributeBinormal())
661 if (renderableFlags.hasAttributeColor())
663 if (renderableFlags.hasAttributeJointAndWeight())
666}
667
668QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareDefaultMaterialForRender(
669 QSSGRenderDefaultMaterial &inMaterial,
670 QSSGRenderableObjectFlags &inExistingFlags,
671 float inOpacity,
672 const QSSGShaderLightListView &lights,
674{
675 QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
677 retval.renderableFlags = inExistingFlags;
678 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
679 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
680 retval.opacity = inOpacity;
681 float &subsetOpacity(retval.opacity);
682
683 if (theMaterial->isDirty())
684 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
685
686 subsetOpacity *= theMaterial->opacity;
687
688 QSSGRenderableImage *firstImage = nullptr;
689
691
692 // isDoubleSided
694
695 // default materials never define their on position
697
698 // default materials dont make use of raw projection or inverse projection matrices
701 // nor they do rely on VAR_COLOR
703
704 // alpha Mode
706
707 // vertex attribute presence flags
708 setVertexInputPresence(renderableFlags, theGeneratedKey, renderer);
709
710 // set the flag indicating the need for gl_PointSize
711 renderer->defaultMaterialShaderKeyProperties().m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
712
713 // propagate the flag indicating the presence of a lightmap
714 renderer->defaultMaterialShaderKeyProperties().m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
715
716 renderer->defaultMaterialShaderKeyProperties().m_specularGlossyEnabled.setValue(theGeneratedKey, theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
717
718 // debug modes
719 renderer->defaultMaterialShaderKeyProperties().m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
720
721 // fog
723
724 if (!renderer->defaultMaterialShaderKeyProperties().m_hasIbl.getValue(theGeneratedKey) && theMaterial->iblProbe) {
727 // features.set(ShaderFeatureDefines::enableIblFov(),
728 // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
729 }
730
731 if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
732
733 // Set the semi-transparency flag as specified in PrincipledMaterial's
734 // blendMode and alphaMode:
735 // - the default SourceOver blendMode does not imply alpha blending on
736 // its own,
737 // - but other blendMode values do,
738 // - an alphaMode of Blend guarantees blending to be enabled regardless
739 // of anything else.
740 // Additionally:
741 // - Opacity and texture map alpha are handled elsewhere (that's when a
742 // blendMode of SourceOver or an alphaMode of Default/Opaque can in the
743 // end still result in HasTransparency),
744 // - the presence of an opacityMap guarantees alpha blending regardless
745 // of its content.
746
748 || theMaterial->opacityMap
750 {
752 }
753
754 const bool specularEnabled = theMaterial->isSpecularEnabled();
755 const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
756 renderer->defaultMaterialShaderKeyProperties().m_specularEnabled.setValue(theGeneratedKey, (specularEnabled || metalnessEnabled));
757 if (specularEnabled || metalnessEnabled)
759
761
763 theMaterial->isVertexColorsEnabled());
765 theMaterial->isClearcoatEnabled());
767 theMaterial->isTransmissionEnabled());
768
769 // Run through the material's images and prepare them for render.
770 // this may in fact set pickable on the renderable flags if one of the images
771 // links to a sub presentation or any offscreen rendered object.
772 QSSGRenderableImage *nextImage = nullptr;
773#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \
774 if ((img)) \
775 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \
776 theGeneratedKey, shadercomponent, &inMaterial)
777
778 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
779 theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
781 QSSGRenderableImage::Type::BaseColor,
784 QSSGRenderableImage::Type::Occlusion,
787 QSSGRenderableImage::Type::Height,
790 QSSGRenderableImage::Type::Clearcoat,
793 QSSGRenderableImage::Type::ClearcoatRoughness,
796 QSSGRenderableImage::Type::ClearcoatNormal,
799 QSSGRenderableImage::Type::Transmission,
802 QSSGRenderableImage::Type::Thickness,
804 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
806 QSSGRenderableImage::Type::Metalness,
808 }
809 } else {
811 QSSGRenderableImage::Type::Diffuse,
813 }
814 CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGRenderableImage::Type::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
816 QSSGRenderableImage::Type::Specular,
819 QSSGRenderableImage::Type::Roughness,
821 CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGRenderableImage::Type::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
822 CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGRenderableImage::Type::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
824 QSSGRenderableImage::Type::SpecularAmountMap,
826 CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGRenderableImage::Type::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
828 QSSGRenderableImage::Type::Translucency,
830 }
831#undef CHECK_IMAGE_AND_PREPARE
832
833 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
834 subsetOpacity = 0.0f;
835 // You can still pick against completely transparent objects(or rather their bounding
836 // box)
837 // you just don't render them.
840 }
841
842 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
843 subsetOpacity = 1.f;
844 else
846
847 if (inMaterial.isTransmissionEnabled()) {
848 ioFlags.setRequiresScreenTexture(true);
851 }
852
853 retval.firstImage = firstImage;
854 if (retval.renderableFlags.isDirty())
855 retval.dirty = true;
856 if (retval.dirty)
857 renderer->addMaterialDirtyClear(&inMaterial);
858 return retval;
859}
860
861QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareCustomMaterialForRender(
862 QSSGRenderCustomMaterial &inMaterial, QSSGRenderableObjectFlags &inExistingFlags,
863 float inOpacity, bool alreadyDirty, const QSSGShaderLightListView &lights,
865{
868 lights, inExistingFlags.receivesShadows()));
869 retval.renderableFlags = inExistingFlags;
870 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
871 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
872 retval.opacity = inOpacity;
873 float &subsetOpacity(retval.opacity);
874
875 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
876 subsetOpacity = 0.0f;
877 // You can still pick against completely transparent objects(or rather their bounding
878 // box)
879 // you just don't render them.
882 }
883
884 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
885 subsetOpacity = 1.f;
886 else
888
890
891 // isDoubleSided
894
895 // Does the material override the position output
896 const bool overridesPosition = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::OverridesPosition);
897 renderer->defaultMaterialShaderKeyProperties().m_overridesPosition.setValue(theGeneratedKey, overridesPosition);
898
899 // Optional usage of PROJECTION_MATRIX and/or INVERSE_PROJECTION_MATRIX
900 const bool usesProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
901 renderer->defaultMaterialShaderKeyProperties().m_usesProjectionMatrix.setValue(theGeneratedKey, usesProjectionMatrix);
902 const bool usesInvProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
903 renderer->defaultMaterialShaderKeyProperties().m_usesInverseProjectionMatrix.setValue(theGeneratedKey, usesInvProjectionMatrix);
904
905 // vertex attribute presence flags
906 setVertexInputPresence(renderableFlags, theGeneratedKey, renderer);
907
908 // set the flag indicating the need for gl_PointSize
909 renderer->defaultMaterialShaderKeyProperties().m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
910
911 // propagate the flag indicating the presence of a lightmap
912 renderer->defaultMaterialShaderKeyProperties().m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
913
914 // debug modes
915 renderer->defaultMaterialShaderKeyProperties().m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
916
917 // fog
919
920 // Knowing whether VAR_COLOR is used becomes relevant when there is no
921 // custom vertex shader, but VAR_COLOR is present in the custom fragment
922 // snippet, because that case needs special care.
923 const bool usesVarColor = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor);
924 renderer->defaultMaterialShaderKeyProperties().m_usesVarColor.setValue(theGeneratedKey, usesVarColor);
925
928
930 ioFlags.setRequiresScreenTexture(true);
932 }
933
935 ioFlags.setRequiresScreenTexture(true);
938 }
939
941 ioFlags.setRequiresDepthTexture(true);
942
944 ioFlags.setRequiresDepthTexture(true);
945 ioFlags.setRequiresSsaoPass(true);
946 }
947
948 retval.firstImage = nullptr;
949
950 if (retval.dirty || alreadyDirty)
951 renderer->addMaterialDirtyClear(&inMaterial);
952 return retval;
953}
954
956 RenderableNodeEntries &renderableModels)
957{
958 prepareModelMeshesForRenderInternal(contextInterface, renderableModels, false);
959}
960
961void QSSGLayerRenderData::prepareModelMeshesForRenderInternal(const QSSGRenderContextInterface &contextInterface,
962 RenderableNodeEntries &renderableModels,
963 bool globalPickingEnabled)
964{
965 const auto &bufferManager = contextInterface.bufferManager();
966
967 const auto originalModelCount = renderableModels.size();
968 auto end = originalModelCount;
969
970 for (int idx = 0; idx < end; ++idx) {
971 // It's up to the BufferManager to employ the appropriate caching mechanisms, so
972 // loadMesh() is expected to be fast if already loaded. Note that preparing
973 // the same QSSGRenderModel in different QQuickWindows (possible when a
974 // scene is shared between View3Ds where the View3Ds belong to different
975 // windows) leads to a different QSSGRenderMesh since the BufferManager is,
976 // very correctly, per window, and so per scenegraph render thread.
977
978 const auto &renderable = renderableModels.at(idx);
979 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
980 // Ensure we have a mesh and at least 1 material
981 if (auto theMesh = bufferManager->loadMesh(&model); theMesh && model.materials.size() > 0) {
982 renderable.mesh = theMesh;
983 renderable.materials = QSSGMaterialListView(model.materials);
984 // Completely transparent models cannot be pickable. But models with completely
985 // transparent materials still are. This allows the artist to control pickability
986 // in a somewhat fine-grained style.
987 const bool canModelBePickable = (model.globalOpacity > QSSG_RENDER_MINIMUM_RENDER_OPACITY)
988 && (globalPickingEnabled
990 if (canModelBePickable) {
991 // Check if there is BVH data, if not generate it
992 if (!theMesh->bvh) {
993 if (!model.meshPath.isNull())
994 theMesh->bvh = bufferManager->loadMeshBVH(model.meshPath);
995 else if (model.geometry)
996 theMesh->bvh = bufferManager->loadMeshBVH(model.geometry);
997
998 if (theMesh->bvh) {
999 for (int i = 0; i < theMesh->bvh->roots.size(); ++i)
1000 theMesh->subsets[i].bvhRoot = theMesh->bvh->roots.at(i);
1001 }
1002 }
1003 }
1004 } else {
1005 // Swap current (idx) and last item (--end).
1006 // Note, post-decrement idx to ensure we recheck the new current item on next iteration
1007 // and pre-decrement the end move the end of the list to not include the culled renderable.
1009 }
1010 }
1011
1012 // Any models without a mesh get dropped right here
1013 if (end != originalModelCount)
1015
1016 // Now is the time to kick off the vertex/index buffer updates for all the
1017 // new meshes (and their submeshes). This here is the last possible place
1018 // to kick this off because the rest of the rendering pipeline will only
1019 // see the individual sub-objects as "renderable objects".
1020 bufferManager->commitBufferResourceUpdates();
1021}
1022
1024{
1025 lightmapTextures[&modelContext] = lightmapTexture;
1026}
1027
1029{
1030 QRhiTexture *ret = nullptr;
1031 if (modelContext.model.hasLightmap()) {
1032 const auto it = lightmapTextures.constFind(&modelContext);
1033 ret = (it != lightmapTextures.cend()) ? *it : nullptr;
1034 }
1035
1036 return ret;
1037}
1038
1040{
1041 bonemapTextures[&modelContext] = bonemapTexture;
1042}
1043
1045{
1046 QRhiTexture *ret = nullptr;
1047 if (modelContext.model.usesBoneTexture()) {
1048 const auto it = bonemapTextures.constFind(&modelContext);
1049 ret = (it != bonemapTextures.cend()) ? *it : nullptr;
1050 }
1051
1052 return ret;
1053}
1054
1055// inModel is const to emphasize the fact that its members cannot be written
1056// here: in case there is a scene shared between multiple View3Ds in different
1057// QQuickWindows, each window may run this in their own render thread, while
1058// inModel is the same.
1061 const QSSGCameraRenderData &cameraData,
1063 float lodThreshold)
1064{
1065 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
1067 const auto &bufferManager = contextInterface.bufferManager();
1068
1069 bool wasDirty = false;
1070
1071 for (const QSSGRenderableNodeEntry &renderable : renderableModels) {
1072 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1073 const auto &lights = renderable.lights;
1074 QSSGRenderMesh *theMesh = renderable.mesh;
1075
1076 QSSG_ASSERT_X(theMesh != nullptr, "Only renderables with a mesh will be processed!", continue);
1077
1078 QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(contextInterface, model, cameraData.viewProjection);
1079 modelContexts.push_back(&theModelContext);
1080 // We might over-allocate here, as the material list technically can contain an invalid (nullptr) material.
1081 // We'll fix that by adjusting the size at the end for now...
1082 const auto &meshSubsets = theMesh->subsets;
1083 const auto meshSubsetCount = meshSubsets.size();
1084 theModelContext.subsets = RENDER_FRAME_NEW_BUFFER<QSSGSubsetRenderable>(contextInterface, meshSubsetCount);
1085
1086 // Prepare boneTexture for skinning
1087 if (model.skin) {
1088 auto boneTexture = bufferManager->loadSkinmap(model.skin);
1089 setBonemapTexture(theModelContext, boneTexture.m_texture);
1090 } else if (model.skeleton) {
1091 auto boneTexture = bufferManager->loadSkinmap(&(model.skeleton->boneTexData));
1092 setBonemapTexture(theModelContext, boneTexture.m_texture);
1093 } else {
1094 setBonemapTexture(theModelContext, nullptr);
1095 }
1096
1097 // many renderableFlags are the same for all the subsets
1098 QSSGRenderableObjectFlags renderableFlagsForModel;
1099
1100 if (meshSubsetCount > 0) {
1101 const QSSGRenderSubset &theSubset = meshSubsets.at(0);
1102
1103 renderableFlagsForModel.setCastsShadows(model.castsShadows);
1104 renderableFlagsForModel.setReceivesShadows(model.receivesShadows);
1105 renderableFlagsForModel.setReceivesReflections(model.receivesReflections);
1106 renderableFlagsForModel.setCastsReflections(model.castsReflections);
1107
1108 renderableFlagsForModel.setUsedInBakedLighting(model.usedInBakedLighting);
1109 if (model.hasLightmap()) {
1110 QSSGRenderImageTexture lmImageTexture = bufferManager->loadLightmap(model);
1111 if (lmImageTexture.m_texture) {
1112 renderableFlagsForModel.setRendersWithLightmap(true);
1113 setLightmapTexture(theModelContext, lmImageTexture.m_texture);
1114 }
1115 }
1116
1117 // TODO: This should be a oneshot thing, move the flags over!
1118 // With the RHI we need to be able to tell the material shader
1119 // generator to not generate vertex input attributes that are not
1120 // provided by the mesh. (because unlike OpenGL, other graphics
1121 // APIs may treat unbound vertex inputs as a fatal error)
1122 bool hasJoint = false;
1123 bool hasWeight = false;
1124 bool hasMorphTarget = theSubset.rhi.targetsTexture != nullptr;
1125 for (const QSSGRhiInputAssemblerState::InputSemantic &sem : std::as_const(theSubset.rhi.ia.inputs)) {
1127 renderableFlagsForModel.setHasAttributePosition(true);
1129 renderableFlagsForModel.setHasAttributeNormal(true);
1131 renderableFlagsForModel.setHasAttributeTexCoord0(true);
1133 renderableFlagsForModel.setHasAttributeTexCoord1(true);
1135 renderableFlagsForModel.setHasAttributeTexCoordLightmap(true);
1137 renderableFlagsForModel.setHasAttributeTangent(true);
1139 renderableFlagsForModel.setHasAttributeBinormal(true);
1141 renderableFlagsForModel.setHasAttributeColor(true);
1142 // For skinning, we will set the HasAttribute only
1143 // if the mesh has both joint and weight
1145 hasJoint = true;
1147 hasWeight = true;
1148 }
1149 }
1150 renderableFlagsForModel.setHasAttributeJointAndWeight(hasJoint && hasWeight);
1151 renderableFlagsForModel.setHasAttributeMorphTarget(hasMorphTarget);
1152 }
1153
1154 QSSGRenderableObjectList bakedLightingObjects;
1155 bool usesBlendParticles = particlesEnabled && theModelContext.model.particleBuffer != nullptr
1156 && model.particleBuffer->particleCount();
1157
1158 // Subset(s)
1159 auto &renderableSubsets = theModelContext.subsets;
1160 const auto &materials = renderable.materials;
1161 const auto materialCount = materials.size();
1162 QSSGRenderGraphObject *lastMaterial = !materials.isEmpty() ? materials.last() : nullptr;
1163 int idx = 0, subsetIdx = 0;
1164 for (; idx < meshSubsetCount; ++idx) {
1165 // If the materials list < size of subsets, then use the last material for the rest
1166 QSSGRenderGraphObject *theMaterialObject = (idx >= materialCount) ? lastMaterial : materials[idx];
1167 QSSG_ASSERT_X(theMaterialObject != nullptr, "No material found for model!", continue);
1168
1169 const QSSGRenderSubset &theSubset = meshSubsets.at(idx);
1170 QSSGRenderableObjectFlags renderableFlags = renderableFlagsForModel;
1171 float subsetOpacity = model.globalOpacity;
1172
1173 renderableFlags.setPointsTopology(theSubset.rhi.ia.topology == QRhiGraphicsPipeline::Points);
1174 QSSGRenderableObject *theRenderableObject = &renderableSubsets[subsetIdx++];
1175
1176 bool usesInstancing = theModelContext.model.instancing()
1177 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1178 if (usesInstancing && theModelContext.model.instanceTable->hasTransparency())
1180 if (theModelContext.model.hasTransparency)
1182
1183 // Level Of Detail
1184 quint32 subsetLevelOfDetail = 0;
1185 if (!theSubset.lods.isEmpty() && lodThreshold > 0.0f) {
1186 // Accounts for FOV
1187 float lodDistanceMultiplier = camera->getLevelOfDetailMultiplier();
1188 float distanceThreshold = 0.0f;
1189 const auto scale = mat44::getScale(model.globalTransform);
1190 float modelScale = qMax(scale.x(), qMax(scale.y(), scale.z()));
1191 QSSGBounds3 transformedBounds = theSubset.bounds;
1192 if (camera->type != QSSGRenderGraphObject::Type::OrthographicCamera) {
1193 transformedBounds.transform(model.globalTransform);
1194#ifdef QT_QUICK3D_MESH_LOD_DEBUG
1195 renderer->contextInterface()->debugDrawSystem()->drawBounds(transformedBounds, QColor(Qt::red));
1196#endif
1197 const QVector3D cameraNormal = camera->getScalingCorrectDirection();
1198 const QVector3D cameraPosition = camera->getGlobalPos();
1199 const QSSGPlane cameraPlane = QSSGPlane(cameraPosition, cameraNormal);
1200 const QVector3D lodSupportMin = transformedBounds.getSupport(-cameraNormal);
1201 const QVector3D lodSupportMax = transformedBounds.getSupport(cameraNormal);
1202#ifdef QT_QUICK3D_MESH_LOD_DEBUG
1203 renderer->contextInterface()->debugDrawSystem()->drawPoint(lodSupportMin, QColor("orange"));
1204#endif
1205
1206 const float distanceMin = cameraPlane.distance(lodSupportMin);
1207 const float distanceMax = cameraPlane.distance(lodSupportMax);
1208
1209 if (distanceMin * distanceMax < 0.0)
1210 distanceThreshold = 0.0;
1211 else if (distanceMin >= 0.0)
1212 distanceThreshold = distanceMin;
1213 else if (distanceMax <= 0.0)
1214 distanceThreshold = -distanceMax;
1215
1216 } else {
1217 // Orthographic Projection
1218 distanceThreshold = 1.0;
1219 }
1220
1221 int currentLod = -1;
1222 if (model.levelOfDetailBias > 0.0f) {
1223 const float threshold = distanceThreshold * lodDistanceMultiplier;
1224 const float modelBias = 1 / model.levelOfDetailBias;
1225 for (qsizetype i = 0; i < theSubset.lods.count(); ++i) {
1226 float subsetDistance = theSubset.lods[i].distance * modelScale * modelBias;
1227 float screenSize = subsetDistance / threshold;
1228 if (screenSize > lodThreshold)
1229 break;
1230 currentLod = i;
1231 }
1232 }
1233 if (currentLod == -1)
1234 subsetLevelOfDetail = 0;
1235 else
1236 subsetLevelOfDetail = currentLod + 1;
1237#ifdef QT_QUICK3D_MESH_LOD_DEBUG
1238 auto levelOfDetailColor = [](int lod) -> QColor {
1239 static QVector<QColor> colors = {
1241 QColor(Qt::red),
1253 };
1254
1255 if (lod >= colors.count()) {
1256 return QColor(Qt::darkGray);
1257 }
1258
1259 return colors[lod];
1260 };
1261 renderer->contextInterface()->debugDrawSystem()->drawBounds(transformedBounds, levelOfDetailColor(subsetLevelOfDetail));
1262#endif
1263 }
1264
1265#ifdef QT_QUICK3D_MESH_LOD_NORMALS_DEBUG
1266 auto debugNormals = [=](const QSSGRenderModel &model, const QSSGRenderSubset &theSubset, quint32 subsetLevelOfDetail, float lineLength) {
1267 QSSGMesh::Mesh mesh;
1268 if (model.geometry)
1269 mesh = bufferManager->loadMeshData(model.geometry);
1270 else
1271 mesh = bufferManager->loadMeshData(model.meshPath);
1272
1273 if (!mesh.isValid())
1274 return; // invalid mesh
1275
1276 QByteArray vertexData = mesh.vertexBuffer().data;
1277 if (vertexData.isEmpty())
1278 return; // no vertex dat
1279 quint32 vertexStride = mesh.vertexBuffer().stride;
1280 QByteArray indexData = mesh.indexBuffer().data;
1281 if (indexData.isEmpty())
1282 return; // no index data, not what we're after
1283 if (mesh.indexBuffer().componentType != QSSGMesh::Mesh::ComponentType::UnsignedInt32)
1284 return; // not uint3d, not what we're after either
1285
1286 quint32 positionOffset = UINT_MAX;
1287 quint32 normalOffset = UINT_MAX;
1288
1289 for (const QSSGMesh::Mesh::VertexBufferEntry &vbe : mesh.vertexBuffer().entries) {
1291 positionOffset = vbe.offset;
1292 if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
1293 vbe.componentCount != 3)
1294 return; // not a vec3, some weird stuff
1295 } else if (vbe.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
1296 normalOffset = vbe.offset;
1297 if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
1298 vbe.componentCount != 3)
1299 return; // not a vec3, really weird normals I guess
1300 }
1301 }
1302
1303 const auto globalTransform = model.globalTransform;
1304 // Draw original vertex normals as blue lines
1305 {
1306 // Get Indexes
1307 const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
1308 const char *vp = vertexData.constData();
1309 p += theSubset.offset;
1310 for (uint i = 0; i < theSubset.count; ++i) {
1311 const quint32 index = *(p + i);
1312 const char * posPtr = vp + (index * vertexStride) + positionOffset;
1313 const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
1314 QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
1315 const char * normalPtr = vp + (index * vertexStride) + normalOffset;
1316 const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
1317 QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
1318 position = globalTransform.map(position);
1319 normal = mat33::transform(theModelContext.normalMatrix, normal);
1320 normal = normal.normalized();
1321 renderer->contextInterface()->debugDrawSystem()->drawLine(position, position + (normal * lineLength), QColor(Qt::blue));
1322 }
1323 }
1324
1325 // Draw lod vertex normals as red lines
1326 if (subsetLevelOfDetail != 0) {
1327 // Get Indexes
1328 const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
1329 const char *vp = vertexData.constData();
1330 p += theSubset.lodOffset(subsetLevelOfDetail);
1331 const quint32 indexCount = theSubset.lodCount(subsetLevelOfDetail);
1332 for (uint i = 0; i < indexCount; ++i) {
1333 const quint32 index = *(p + i);
1334 const char * posPtr = vp + (index * vertexStride) + positionOffset;
1335 const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
1336 QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
1337 const char * normalPtr = vp + (index * vertexStride) + normalOffset;
1338 const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
1339 QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
1340 position = globalTransform.map(position);
1341 normal = mat33::transform(theModelContext.normalMatrix, normal);
1342 normal = normal.normalized();
1343 renderer->contextInterface()->debugDrawSystem()->drawLine(position, position + (normal * lineLength), QColor(Qt::red));
1344 }
1345 }
1346 };
1347#endif
1348
1349 QVector3D theModelCenter(theSubset.bounds.center());
1350 theModelCenter = mat44::transform(model.globalTransform, theModelCenter);
1351#ifdef QT_QUICK3D_MESH_LOD_NORMALS_DEBUG
1352 debugNormals(model, theSubset, subsetLevelOfDetail, (theModelCenter - camera->getGlobalPos()).length() * 0.01);
1353#endif
1354
1355 if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
1356 theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1357 theMaterialObject->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1358 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
1359 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(prepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity, lights, ioFlags));
1360 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1361 subsetOpacity = theMaterialPrepResult.opacity;
1362 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1363 wasDirty |= theMaterialPrepResult.dirty;
1364 renderableFlags = theMaterialPrepResult.renderableFlags;
1365
1366 // Blend particles
1367 renderer->defaultMaterialShaderKeyProperties().m_blendParticles.setValue(theGeneratedKey, usesBlendParticles);
1368
1369 // Skin
1370 const auto boneCount = model.skin ? model.skin->boneCount :
1371 model.skeleton ? model.skeleton->boneCount : 0;
1372 renderer->defaultMaterialShaderKeyProperties().m_boneCount.setValue(theGeneratedKey, boneCount);
1374 theGeneratedKey, !rhiCtx->rhi()->isFeatureSupported(QRhi::IntAttributes));
1375 // Instancing
1376 renderer->defaultMaterialShaderKeyProperties().m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1377 // Morphing
1379 theSubset.rhi.ia.targetCount);
1394
1395 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset,
1396 renderableFlags,
1397 theModelCenter,
1398 renderer,
1399 theSubset,
1400 theModelContext,
1401 subsetOpacity,
1402 subsetLevelOfDetail,
1403 theMaterial,
1404 firstImage,
1405 theGeneratedKey,
1406 lights);
1407 wasDirty = wasDirty || renderableFlags.isDirty();
1408 } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
1409 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
1410
1411 const auto &theMaterialSystem(contextInterface.customMaterialSystem());
1412 wasDirty |= theMaterialSystem->prepareForRender(theModelContext.model, theSubset, theMaterial);
1413
1414 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
1415 prepareCustomMaterialForRender(theMaterial, renderableFlags, subsetOpacity, wasDirty,
1416 lights, ioFlags));
1417 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1418 subsetOpacity = theMaterialPrepResult.opacity;
1419 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1420 renderableFlags = theMaterialPrepResult.renderableFlags;
1421
1422 if (model.particleBuffer && model.particleBuffer->particleCount())
1424 else
1426
1427 // Skin
1428 const auto boneCount = model.skin ? model.skin->boneCount :
1429 model.skeleton ? model.skeleton->boneCount : 0;
1430 renderer->defaultMaterialShaderKeyProperties().m_boneCount.setValue(theGeneratedKey, boneCount);
1432 theGeneratedKey, !rhiCtx->rhi()->isFeatureSupported(QRhi::IntAttributes));
1433
1434 // Instancing
1435 bool usesInstancing = theModelContext.model.instancing()
1436 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1437 renderer->defaultMaterialShaderKeyProperties().m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1438 // Morphing
1440 theSubset.rhi.ia.targetCount);
1455
1456 if (theMaterial.m_iblProbe)
1457 theMaterial.m_iblProbe->clearDirty();
1458
1459 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::CustomMaterialMeshSubset,
1460 renderableFlags,
1461 theModelCenter,
1462 renderer,
1463 theSubset,
1464 theModelContext,
1465 subsetOpacity,
1466 subsetLevelOfDetail,
1467 theMaterial,
1468 firstImage,
1469 theGeneratedKey,
1470 lights);
1471 }
1472 if (theRenderableObject) // NOTE: Should just go in with the ctor args
1473 theRenderableObject->camdistSq = getCameraDistanceSq(*theRenderableObject, cameraData);
1474 }
1475
1476 // If the indices don't match then something's off and we need to adjust the subset renderable list size.
1477 if (Q_UNLIKELY(idx != subsetIdx))
1478 renderableSubsets.mSize = subsetIdx + 1;
1479
1480 bool handled = false;
1481 if (filter)
1482 handled = filter(&theModelContext);
1483
1484 if (!handled) {
1485 for (auto &ro : renderableSubsets) {
1486 if (ro.renderableFlags.requiresScreenTexture())
1487 screenTextureObjects.push_back({&ro, ro.camdistSq});
1488 else if (ro.renderableFlags.hasTransparency())
1489 transparentObjects.push_back({&ro, ro.camdistSq});
1490 else
1491 opaqueObjects.push_back({&ro, ro.camdistSq});
1492
1493 if (ro.renderableFlags.usedInBakedLighting())
1494 bakedLightingObjects.push_back({&ro, ro.camdistSq});
1495 }
1496 }
1497
1498 if (!bakedLightingObjects.isEmpty())
1499 bakedLightingModels.push_back(QSSGBakedLightingModel(&model, bakedLightingObjects));
1500 }
1501
1502 return wasDirty;
1503}
1504
1506{
1507 QSSG_ASSERT(particlesEnabled, return false);
1508
1510
1511 bool dirty = false;
1512
1513 for (const auto &renderable : renderableParticles) {
1514 const QSSGRenderParticles &particles = *static_cast<QSSGRenderParticles *>(renderable.node);
1515 const auto &lights = renderable.lights;
1516
1517 QSSGRenderableObjectFlags renderableFlags;
1518 renderableFlags.setCastsShadows(false);
1519 renderableFlags.setReceivesShadows(false);
1520 renderableFlags.setHasAttributePosition(true);
1521 renderableFlags.setHasAttributeNormal(true);
1522 renderableFlags.setHasAttributeTexCoord0(true);
1523 renderableFlags.setHasAttributeColor(true);
1524 renderableFlags.setHasTransparency(particles.m_hasTransparency);
1525 renderableFlags.setCastsReflections(particles.m_castsReflections);
1526
1527 float opacity = particles.globalOpacity;
1528 QVector3D center(particles.m_particleBuffer.bounds().center());
1529 center = mat44::transform(particles.globalTransform, center);
1530
1531 QSSGRenderableImage *firstImage = nullptr;
1532 if (particles.m_sprite) {
1533 const auto &bufferManager = contextInterface.bufferManager();
1534
1535 if (particles.m_sprite->clearDirty())
1536 dirty = true;
1537
1538 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_sprite);
1539 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_sprite, texture);
1540 firstImage = theImage;
1541 }
1542
1543 QSSGRenderableImage *colorTable = nullptr;
1544 if (particles.m_colorTable) {
1545 const auto &bufferManager = contextInterface.bufferManager();
1546
1547 if (particles.m_colorTable->clearDirty())
1548 dirty = true;
1549
1550 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_colorTable);
1551
1552 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_colorTable, texture);
1553 colorTable = theImage;
1554 }
1555
1556 if (opacity > 0.0f && particles.m_particleBuffer.particleCount()) {
1557 auto *theRenderableObject = RENDER_FRAME_NEW<QSSGParticlesRenderable>(contextInterface,
1558 renderableFlags,
1559 center,
1560 renderer,
1561 particles,
1562 firstImage,
1563 colorTable,
1564 lights,
1565 opacity);
1566 if (theRenderableObject) {
1567 if (theRenderableObject->renderableFlags.requiresScreenTexture())
1568 screenTextureObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1569 else if (theRenderableObject->renderableFlags.hasTransparency())
1570 transparentObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1571 else
1572 opaqueObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1573 }
1574 }
1575 }
1576
1577 return dirty;
1578}
1579
1581 const RenderableItem2DEntries &renderableItem2Ds)
1582{
1583 const bool hasItems = (renderableItem2Ds.size() != 0);
1584 if (hasItems) {
1585 const auto &clipSpaceCorrMatrix = ctxIfc.rhiContext()->rhi()->clipSpaceCorrMatrix();
1586 auto cameraData = getCachedCameraData();
1587 for (const auto &theItem2D : renderableItem2Ds) {
1588 theItem2D->MVP = cameraData.viewProjection * theItem2D->globalTransform;
1589 static const QMatrix4x4 flipMatrix(1.0f, 0.0f, 0.0f, 0.0f,
1590 0.0f, -1.0f, 0.0f, 0.0f,
1591 0.0f, 0.0f, 1.0f, 0.0f,
1592 0.0f, 0.0f, 0.0f, 1.0f);
1593 theItem2D->MVP = clipSpaceCorrMatrix * theItem2D->MVP * flipMatrix;
1594 }
1595 }
1596
1597 return hasItems;
1598}
1599
1601{
1603 const auto &bufferManager = contextInterface.bufferManager();
1604
1605 for (const auto resourceLoader : std::as_const(layer.resourceLoaders))
1606 bufferManager->processResourceLoader(static_cast<QSSGRenderResourceLoader *>(resourceLoader));
1607}
1608
1610{
1611 const auto probeCount = reflectionProbes.size();
1612 requestReflectionMapManager(); // ensure that we have a reflection map manager
1613
1614 for (int i = 0; i < probeCount; i++) {
1616
1617 int reflectionObjectCount = 0;
1618 QVector3D probeExtent = probe->boxSize / 2;
1619 QSSGBounds3 probeBound = QSSGBounds3::centerExtents(probe->getGlobalPos() + probe->boxOffset, probeExtent);
1620
1621 const auto injectProbe = [&](const QSSGRenderableObjectHandle &handle) {
1622 if (handle.obj->renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections)
1623 && !(handle.obj->type == QSSGRenderableObject::Type::Particles)) {
1624 QSSGSubsetRenderable* renderableObj = static_cast<QSSGSubsetRenderable*>(handle.obj);
1625 QSSGBounds3 nodeBound = renderableObj->bounds;
1626 QVector4D vmin(nodeBound.minimum, 1.0);
1627 QVector4D vmax(nodeBound.maximum, 1.0);
1628 vmin = renderableObj->globalTransform * vmin;
1629 vmax = renderableObj->globalTransform * vmax;
1630 nodeBound.minimum = vmin.toVector3D();
1631 nodeBound.maximum = vmax.toVector3D();
1632 if (probeBound.intersects(nodeBound)) {
1633 QVector3D nodeBoundCenter = nodeBound.center();
1634 QVector3D probeBoundCenter = probeBound.center();
1635 float distance = nodeBoundCenter.distanceToPoint(probeBoundCenter);
1636 if (renderableObj->reflectionProbeIndex == -1 || distance < renderableObj->distanceFromReflectionProbe) {
1637 renderableObj->reflectionProbeIndex = i;
1638 renderableObj->distanceFromReflectionProbe = distance;
1640 renderableObj->reflectionProbe.probeCubeMapCenter = probe->getGlobalPos();
1641 renderableObj->reflectionProbe.probeBoxMax = probeBound.maximum;
1642 renderableObj->reflectionProbe.probeBoxMin = probeBound.minimum;
1643 renderableObj->reflectionProbe.enabled = true;
1644 reflectionObjectCount++;
1645 }
1646 }
1647 }
1648 };
1649
1650 for (const auto &handle : std::as_const(transparentObjects))
1651 injectProbe(handle);
1652
1653 for (const auto &handle : std::as_const(opaqueObjects))
1654 injectProbe(handle);
1655
1656 if (probe->texture)
1657 reflectionMapManager->addTexturedReflectionMapEntry(i, *probe);
1658 else if (reflectionObjectCount > 0)
1659 reflectionMapManager->addReflectionMapEntry(i, *probe);
1660 }
1661}
1662
1663static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
1664{
1665 // check if the node is parent of the lightScope
1666 while (node) {
1667 if (node == lightScope)
1668 return true;
1669 node = node->parent;
1670 }
1671 return false;
1672}
1673
1674static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES = 4096; // 256 vec4
1675
1676static inline int effectiveMaxLightCount(const QSSGShaderFeatures &features)
1677{
1680
1681 return QSSG_MAX_NUM_LIGHTS;
1682}
1683
1685{
1686 // First model using skeleton clears the dirty flag so we need another mechanism
1687 // to tell to the other models the skeleton is dirty.
1688 QSet<QSSGRenderSkeleton *> dirtySkeletons;
1689 for (const auto &node : std::as_const(renderableNodes)) {
1690 if (node.node->type == QSSGRenderGraphObject::Type::Model) {
1691 auto modelNode = static_cast<QSSGRenderModel *>(node.node);
1692 auto skeletonNode = modelNode->skeleton;
1693 bool hcj = false;
1694 if (skeletonNode) {
1695 const bool dirtySkeleton = dirtySkeletons.contains(skeletonNode);
1696 const bool hasDirtyNonJoints = (skeletonNode->containsNonJointNodes
1697 && (hasDirtyNonJointNodes(skeletonNode, hcj) || dirtySkeleton));
1698 const bool dirtyTransform = skeletonNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty);
1699 if (skeletonNode->skinningDirty || hasDirtyNonJoints || dirtyTransform) {
1700 skeletonNode->boneTransformsDirty = false;
1701 if (hasDirtyNonJoints && !dirtySkeleton)
1702 dirtySkeletons.insert(skeletonNode);
1703 skeletonNode->skinningDirty = false;
1704 const qsizetype dataSize = BONEDATASIZE4ID(skeletonNode->maxIndex);
1705 if (skeletonNode->boneData.size() < dataSize)
1706 skeletonNode->boneData.resize(dataSize);
1707 skeletonNode->calculateGlobalVariables();
1708 skeletonNode->containsNonJointNodes = false;
1709 for (auto &child : skeletonNode->children)
1710 collectBoneTransforms(&child, skeletonNode, modelNode->inverseBindPoses);
1711 }
1712 skeletonNode->boneCount = skeletonNode->boneData.size() / 2 / 4 / 16;
1713 const int boneTexWidth = qCeil(qSqrt(skeletonNode->boneCount * 4 * 2));
1714 skeletonNode->boneTexData.setSize(QSize(boneTexWidth, boneTexWidth));
1715 skeletonNode->boneData.resize(boneTexWidth * boneTexWidth * 16);
1716 skeletonNode->boneTexData.setTextureData(skeletonNode->boneData);
1717 }
1718 const int numMorphTarget = modelNode->morphTargets.size();
1719 for (int i = 0; i < numMorphTarget; ++i) {
1720 auto morphTarget = static_cast<const QSSGRenderMorphTarget *>(modelNode->morphTargets.at(i));
1721 modelNode->morphWeights[i] = morphTarget->weight;
1722 modelNode->morphAttributes[i] = morphTarget->attributes;
1724 modelNode->morphAttributes[i] &= 0x1; // MorphTarget.Position
1726 modelNode->morphAttributes[i] &= 0x3; // MorphTarget.Position | MorphTarget.Normal
1727 }
1728 }
1729 }
1730
1731 dirtySkeletons.clear();
1732}
1733
1735{
1736 if (layerPrepResult.has_value())
1737 return;
1738
1739 // Verify that the depth write list(s) were cleared between frames
1742
1743 QRect theViewport(renderer->contextInterface()->viewport());
1744
1745 // NOTE: The renderer won't change in practice (after being set the first time), but just update
1746 // it anyways.
1747 frameData.m_renderer = renderer;
1748 frameData.clear();
1749
1750 // Create base pipeline state
1751 ps = {}; // Reset
1752 ps.viewport = { float(theViewport.x()), float(theViewport.y()), float(theViewport.width()), float(theViewport.height()), 0.0f, 1.0f };
1753 if (layer.scissorRect.isValid()) {
1754 ps.scissorEnable = true;
1755 ps.scissor = { layer.scissorRect.x(),
1756 theViewport.height() - (layer.scissorRect.y() + layer.scissorRect.height()),
1757 layer.scissorRect.width(),
1758 layer.scissorRect.height() };
1759 }
1760
1761 bool wasDirty = false;
1762 bool wasDataDirty = false;
1763 wasDirty = layer.isDirty();
1764
1765 QSSGLayerRenderPreparationResult thePrepResult(theViewport, layer);
1766
1767 // SSAO
1768 const bool SSAOEnabled = layer.ssaoEnabled();
1769 thePrepResult.flags.setRequiresSsaoPass(SSAOEnabled);
1770 features.set(QSSGShaderFeatures::Feature::Ssao, SSAOEnabled);
1771
1772 // Effects
1773 bool requiresDepthTexture = SSAOEnabled;
1774 for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
1775 if (theEffect->isDirty()) {
1776 wasDirty = true;
1777 theEffect->clearDirty();
1778 }
1779 if (theEffect->requiresDepthTexture)
1780 requiresDepthTexture = true;
1781 }
1782 thePrepResult.flags.setRequiresDepthTexture(requiresDepthTexture);
1783
1784 // Tonemapping. Except when there are effects, then it is up to the
1785 // last pass of the last effect to perform tonemapping.
1786 if (!layer.firstEffect)
1787 QSSGRenderer::setTonemapFeatures(features, layer.tonemapMode);
1788
1789 // We may not be able to have an array of 15 light struct elements in
1790 // the shaders. Switch on the reduced-max-number-of-lights feature
1791 // if necessary. In practice this is relevant with OpenGL ES 3.0 or
1792 // 2.0, because there are still implementations in use that only
1793 // support the spec mandated minimum of 224 vec4s (so 3584 bytes).
1794 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
1795 if (rhiCtx->maxUniformBufferRange() < REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES) {
1797 static bool notified = false;
1798 if (!notified) {
1799 notified = true;
1800 qCDebug(lcQuick3DRender, "Qt Quick 3D maximum number of lights has been reduced from %d to %d due to the graphics driver's limitations",
1802 }
1803 }
1804
1805 // IBL Lightprobe Image
1806 QSSGRenderImageTexture lightProbeTexture;
1807 if (layer.lightProbe) {
1808 if (layer.lightProbe->m_format == QSSGRenderTextureFormat::Unknown) {
1809 // Choose on a format that makes sense for a light probe
1810 // At this point it's just a suggestion
1811 if (renderer->contextInterface()->rhiContext()->rhi()->isTextureFormatSupported(QRhiTexture::RGBA16F))
1812 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBA16F;
1813 else
1814 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBE8;
1815 }
1816
1817 if (layer.lightProbe->clearDirty())
1818 wasDataDirty = true;
1819
1820 // NOTE: This call can lead to rendering (of envmap) and a texture upload
1821 lightProbeTexture = renderer->contextInterface()->bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
1822 if (lightProbeTexture.m_texture) {
1823
1825 features.set(QSSGShaderFeatures::Feature::IblOrientation, !layer.probeOrientation.isIdentity());
1826
1827 // By this point we will know what the actual texture format of the light probe is
1828 // Check if using RGBE format light probe texture (the Rhi format will be RGBA8)
1829 if (lightProbeTexture.m_flags.isRgbe8())
1831 } else {
1832 layer.lightProbe = nullptr;
1833 }
1834 }
1835
1836 // Gather Spatial Nodes from Render Tree
1837 // Do not just clear() renderableNodes and friends. Rather, reuse
1838 // the space (even if clear does not actually deallocate, it still
1839 // costs time to run dtors and such). In scenes with a static node
1840 // count in the range of thousands this may matter.
1841 int renderableModelsCount = 0;
1842 int renderableParticlesCount = 0;
1843 int renderableItem2DsCount = 0;
1844 int cameraNodeCount = 0;
1845 int lightNodeCount = 0;
1846 int reflectionProbeCount = 0;
1847 quint32 dfsIndex = 0;
1848 for (auto &theChild : layer.children)
1849 wasDataDirty |= maybeQueueNodeForRender(theChild,
1851 renderableModelsCount,
1853 renderableParticlesCount,
1855 renderableItem2DsCount,
1856 cameras,
1857 cameraNodeCount,
1858 lights,
1859 lightNodeCount,
1861 reflectionProbeCount,
1862 dfsIndex);
1863
1864 if (renderableModels.size() != renderableModelsCount)
1865 renderableModels.resize(renderableModelsCount);
1866 if (renderableParticles.size() != renderableParticlesCount)
1867 renderableParticles.resize(renderableParticlesCount);
1868 if (renderableItem2Ds.size() != renderableItem2DsCount)
1869 renderableItem2Ds.resize(renderableItem2DsCount);
1870
1871 if (cameras.size() != cameraNodeCount)
1872 cameras.resize(cameraNodeCount);
1873 if (lights.size() != lightNodeCount)
1874 lights.resize(lightNodeCount);
1875 if (reflectionProbes.size() != reflectionProbeCount)
1876 reflectionProbes.resize(reflectionProbeCount);
1877
1878 // Cameras
1879 // 1. If there's an explicit camera set and it's active (visible) we'll use that.
1880 // 2. ... if the explicitly set camera is not visible, no further attempts will be done.
1881 // 3. If no explicit camera is set, we'll search and pick the first active camera.
1882 camera = layer.explicitCamera;
1883 if (camera != nullptr) {
1884 // 1.
1885 wasDataDirty = wasDataDirty || camera->isDirty();
1887 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1888 if (!theResult.m_computeFrustumSucceeded)
1889 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1890
1891 // 2.
1893 camera = nullptr;
1894 } else {
1895 // 3.
1896 for (auto iter = cameras.cbegin();
1897 (camera == nullptr) && (iter != cameras.cend()); iter++) {
1898 QSSGRenderCamera *theCamera = *iter;
1899 wasDataDirty = wasDataDirty
1900 || theCamera->isDirty();
1901 QSSGCameraGlobalCalculationResult theResult = thePrepResult.setupCameraForRender(*theCamera);
1902 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1903 if (!theResult.m_computeFrustumSucceeded)
1904 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1906 camera = theCamera;
1907 }
1908 }
1909
1910 float meshLodThreshold = 1.0f;
1911 if (camera) {
1913 meshLodThreshold = camera->levelOfDetailPixelThreshold / theViewport.width();
1914 }
1915
1916 layer.renderedCamera = camera;
1917
1918 // ResourceLoaders
1920
1921 // Skeletons
1923
1924 // Lights
1925 int shadowMapCount = 0;
1926 bool hasScopedLights = false;
1927 // Determine which lights will actually Render
1928 // Determine how many lights will need shadow maps
1929 // NOTE: This culling is specific to our Forward renderer
1930 const int maxLightCount = effectiveMaxLightCount(features);
1931 const bool showLightCountWarning = !tooManyLightsWarningShown && (lights.size() > maxLightCount);
1932 if (showLightCountWarning) {
1933 qWarning("Too many lights in scene, maximum is %d", maxLightCount);
1935 }
1936
1937 QSSGShaderLightList renderableLights; // All lights (upto 'maxLightCount')
1938
1939 // List should contain only enabled lights (active && birghtness > 0).
1940 {
1941 auto it = lights.crbegin();
1942 const auto end = it + qMin(maxLightCount, lights.size());
1943
1944 for (; it != end; ++it) {
1945 QSSGRenderLight *renderLight = (*it);
1946 hasScopedLights |= (renderLight->m_scope != nullptr);
1947 const bool mightCastShadows = renderLight->m_castShadow && !renderLight->m_fullyBaked;
1948 const bool shadows = mightCastShadows && (shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS);
1949 shadowMapCount += int(shadows);
1950 const auto &direction = renderLight->getScalingCorrectDirection();
1951 renderableLights.push_back(QSSGShaderLight{ renderLight, shadows, direction });
1952 }
1953
1954 if ((shadowMapCount >= QSSG_MAX_NUM_SHADOW_MAPS) && !tooManyShadowLightsWarningShown) {
1955 qWarning("Too many shadow casting lights in scene, maximum is %d", QSSG_MAX_NUM_SHADOW_MAPS);
1957 }
1958 }
1959
1960 if (shadowMapCount > 0) { // Setup Shadow Maps Entries for Lights casting shadows
1961 requestShadowMapManager(); // Ensure we have a shadow map manager
1962
1963 for (int i = 0, end = renderableLights.size(); i != end; ++i) {
1964 const auto &shaderLight = renderableLights.at(i);
1965 if (shaderLight.shadows) {
1966 quint32 mapSize = 1 << shaderLight.light->m_shadowMapRes;
1967 ShadowMapModes mapMode = (shaderLight.light->type != QSSGRenderLight::Type::DirectionalLight)
1970 shadowMapManager->addShadowMapEntry(i,
1971 mapSize,
1972 mapSize,
1973 mapMode,
1974 shaderLight.light->debugObjectName);
1975 thePrepResult.flags.setRequiresShadowMapPass(true);
1976 // Any light with castShadow=true triggers shadow mapping
1977 // in the generated shaders. The fact that some (or even
1978 // all) objects may opt out from receiving shadows plays no
1979 // role here whatsoever.
1980 features.set(QSSGShaderFeatures::Feature::Ssm, true);
1981 }
1982 }
1983 }
1984
1985 // Give each renderable a copy of the lights available
1986 // Also setup scoping for scoped lights
1987
1989 if (hasScopedLights) { // Filter out scoped lights from the global lights list
1990 for (const auto &shaderLight : std::as_const(renderableLights)) {
1991 if (!shaderLight.light->m_scope)
1992 globalLights.push_back(shaderLight);
1993 }
1994
1995 const auto prepareLightsWithScopedLights = [&renderableLights, this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
1996 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
1997 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
1998 QSSGShaderLightList filteredLights;
1999 for (const auto &light : std::as_const(renderableLights)) {
2000 if (light.light->m_scope && !scopeLight(theNodeEntry.node, light.light->m_scope))
2001 continue;
2002 filteredLights.push_back(light);
2003 }
2004
2005 if (filteredLights.isEmpty()) { // Node without scoped lights, just reference the global light list.
2006 theNodeEntry.lights = QSSGDataView(globalLights);
2007 } else {
2008 // This node has scoped lights, i.e., it's lights differ from the global list
2009 // we therefore create a bespoke light list for it. Technically this might be the same for
2010 // more then this one node, but the overhead for tracking that is not worth it.
2011 auto customLightList = RENDER_FRAME_NEW_BUFFER<QSSGShaderLight>(*renderer->contextInterface(), filteredLights.size());
2012 std::copy(filteredLights.cbegin(), filteredLights.cend(), customLightList.begin());
2013 theNodeEntry.lights = customLightList;
2014 }
2015 }
2016 };
2017
2018 prepareLightsWithScopedLights(renderableModels);
2019 prepareLightsWithScopedLights(renderableParticles);
2020 } else { // Just a simple copy
2021 globalLights = renderableLights;
2022 // No scoped lights, all nodes can just reference the global light list.
2023 const auto prepareLights = [this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2024 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2025 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2026 theNodeEntry.lights = QSSGDataView(globalLights);
2027 }
2028 };
2029
2030 prepareLights(renderableModels);
2031 prepareLights(renderableParticles);
2032 }
2033
2034 {
2035 // Give user provided passes a chance to modify the renderable data before starting
2036 // Note: All non-active extensions should be filtered out by now
2038 for (int i = 0; i != QSSGRenderLayer::RenderExtensionMode::Count; ++i) {
2039 const auto &renderExtensions = layer.renderExtensions[i];
2040 auto &userPass = userPasses[i];
2041 for (auto rit = renderExtensions.crbegin(), rend = renderExtensions.crend(); rit != rend; ++rit) {
2042 wasDirty |= (*rit)->prepareData(frameData);
2043 userPass.extensions.push_back(*rit);
2044 }
2045 }
2046 }
2047
2048 // Ensure meshes for models
2049 prepareModelMeshesForRenderInternal(*renderer->contextInterface(), renderableModels, renderer->isGlobalPickingEnabled());
2050
2051 if (camera) { // NOTE: We shouldn't really get this far without a camera...
2052 const auto &cameraData = getCachedCameraData();
2053 wasDirty |= prepareModelsForRender(renderableModels, thePrepResult.flags, cameraData, {}, meshLodThreshold);
2054 if (particlesEnabled)
2057 }
2058
2060
2061 wasDirty = wasDirty || wasDataDirty;
2062 thePrepResult.flags.setWasDirty(wasDirty);
2063 thePrepResult.flags.setLayerDataDirty(wasDataDirty);
2064
2065 layerPrepResult = thePrepResult;
2066
2067 //
2068 const bool animating = wasDirty;
2069 if (animating)
2070 layer.progAAPassIndex = 0;
2071
2072 const bool progressiveAA = layer.antialiasingMode == QSSGRenderLayer::AAMode::ProgressiveAA && !animating;
2073 layer.progressiveAAIsActive = progressiveAA;
2074 const bool temporalAA = layer.temporalAAEnabled && !progressiveAA && layer.antialiasingMode != QSSGRenderLayer::AAMode::MSAA;
2075
2076 layer.temporalAAIsActive = temporalAA;
2077
2078 QVector2D vertexOffsetsAA;
2079
2080 if (progressiveAA && layer.progAAPassIndex > 0 && layer.progAAPassIndex < quint32(layer.antialiasingQuality)) {
2081 int idx = layer.progAAPassIndex - 1;
2082 vertexOffsetsAA = s_ProgressiveAAVertexOffsets[idx] / QVector2D{ float(theViewport.width()/2.0), float(theViewport.height()/2.0) };
2083 }
2084
2085 if (temporalAA) {
2086 const int t = 1 - 2 * (layer.tempAAPassIndex % 2);
2087 const float f = t * layer.temporalAAStrength;
2088 vertexOffsetsAA = { f / float(theViewport.width()/2.0), f / float(theViewport.height()/2.0) };
2089 }
2090
2091 if (camera) {
2092 if (temporalAA || progressiveAA /*&& !vertexOffsetsAA.isNull()*/) {
2093 QMatrix4x4 offsetProjection = camera->projection;
2094 QMatrix4x4 invProjection = camera->projection.inverted();
2095 if (camera->type == QSSGRenderCamera::Type::OrthographicCamera) {
2096 offsetProjection(0, 3) -= vertexOffsetsAA.x();
2097 offsetProjection(1, 3) -= vertexOffsetsAA.y();
2098 } else if (camera->type == QSSGRenderCamera::Type::PerspectiveCamera) {
2099 offsetProjection(0, 2) += vertexOffsetsAA.x();
2100 offsetProjection(1, 2) += vertexOffsetsAA.y();
2101 }
2102 for (auto &modelContext : std::as_const(modelContexts))
2103 modelContext->modelViewProjection = offsetProjection * invProjection * modelContext->modelViewProjection;
2104 }
2105 }
2106
2107 // Prepare passes
2109 // If needed, generate a depth texture with the opaque objects. This
2110 // and the SSAO texture must come first since other passes may want to
2111 // expose these textures to their shaders.
2112 if (thePrepResult.flags.requiresDepthTexture())
2114
2115 // Screen space ambient occlusion. Relies on the depth texture and generates an AO map.
2116 if (thePrepResult.flags.requiresSsaoPass())
2118
2119 // Shadows. Generates a 2D or cube shadow map. (opaque + pre-pass transparent objects)
2120 if (thePrepResult.flags.requiresShadowMapPass())
2122
2125
2126 // Screen texture with opaque objects.
2127 if (thePrepResult.flags.requiresScreenTexture())
2129
2131 if (underlayPass.hasData())
2132 activePasses.push_back(&underlayPass);
2133
2135
2137 if (overlayPass.hasData())
2138 activePasses.push_back(&overlayPass);
2139}
2140
2142{
2143 for (const auto &pass : activePasses)
2144 pass->release();
2149 bakedLightingModels.clear();
2150 layerPrepResult.reset();
2151 // The check for if the camera is or is not null is used
2152 // to figure out if this layer was rendered at all.
2153 camera = nullptr;
2154 cameraData.reset();
2155 clippingFrustum.reset();
2163 renderableItem2Ds.clear();
2164 lightmapTextures.clear();
2165 bonemapTextures.clear();
2168 features = QSSGShaderFeatures();
2169 plainSkyBoxPrepared = false;
2170}
2171
2173 : layer(&inLayer)
2174{
2175 viewport = inViewport;
2176}
2177
2179{
2180 return viewport.height() >= 2.0f && viewport.width() >= 2.0f;
2181}
2182
2184{
2185 const auto size = viewport.size().toSize();
2187}
2188
2190{
2191 // When using ssaa we need to zoom with the ssaa multiplier since otherwise the
2192 // orthographic camera will be zoomed out due to the bigger viewport. We therefore
2193 // scale the magnification before calulating the camera variables and then revert.
2194 // Since the same camera can be used in several View3Ds with or without ssaa we
2195 // cannot store the magnification permanently.
2196 const float horizontalMagnification = inCamera.horizontalMagnification;
2197 const float verticalMagnification = inCamera.verticalMagnification;
2198 inCamera.horizontalMagnification *= layer->ssaaEnabled ? layer->ssaaMultiplier : 1.0f;
2199 inCamera.verticalMagnification *= layer->ssaaEnabled ? layer->ssaaMultiplier : 1.0f;
2200 const auto result = inCamera.calculateGlobalVariables(viewport);
2201 inCamera.horizontalMagnification = horizontalMagnification;
2202 inCamera.verticalMagnification = verticalMagnification;
2203 return result;
2204}
2205
2207 : layer(inLayer)
2208 , renderer(&inRenderer)
2209 , particlesEnabled(checkParticleSupport(inRenderer.contextInterface()->rhi()))
2210{
2211}
2212
2214{
2215 delete m_lightmapper;
2216 for (auto &pass : activePasses)
2217 pass->release();
2218}
2219
2220static void sortInstances(QByteArray &sortedData, QList<QSSGRhiSortData> &sortData, const void *instances,
2221 int stride, int count, const QVector3D &cameraDirection)
2222{
2223 sortData.resize(count);
2225 // create sort data
2226 {
2227 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2228 for (int i = 0; i < count; i++) {
2229 const QVector3D pos = QVector3D(instance->row0.w(), instance->row1.w(), instance->row2.w());
2230 sortData[i] = {QVector3D::dotProduct(pos, cameraDirection), i};
2231 instance++;
2232 }
2233 }
2234
2235 // sort
2236 std::sort(sortData.begin(), sortData.end(), [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
2237 return a.d > b.d;
2238 });
2239
2240 // copy instances
2241 {
2242 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2243 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(sortedData.data());
2244 for (auto &s : sortData)
2245 *dest++ = instance[s.indexOrOffset];
2246 }
2247}
2248
2249static void cullLodInstances(QByteArray &lodData, const void *instances, int count,
2250 const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
2251{
2252 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2253 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(lodData.data());
2254 for (int i = 0; i < count; ++i) {
2255 const float x = cameraPosition.x() - instance->row0.w();
2256 const float y = cameraPosition.y() - instance->row1.w();
2257 const float z = cameraPosition.z() - instance->row2.w();
2258 const float distanceSq = x * x + y * y + z * z;
2259 if (distanceSq >= minThreshold * minThreshold && (maxThreshold < 0 || distanceSq < maxThreshold * maxThreshold))
2260 *dest = *instance;
2261 else
2262 *dest= {};
2263 dest++;
2264 instance++;
2265 }
2266}
2267
2268bool QSSGSubsetRenderable::prepareInstancing(QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
2269{
2271 return instanceBuffer;
2273 bool usesLod = minThreshold >= 0 || maxThreshold >= 0;
2274 QSSGRhiInstanceBufferData &instanceData(usesLod ? rhiCtx->instanceBufferData(&modelContext.model) : rhiCtx->instanceBufferData(table));
2275 quint32 instanceBufferSize = table->dataSize();
2276 // Create or resize the instance buffer ### if (instanceData.owned)
2277 bool sortingChanged = table->isDepthSortingEnabled() != instanceData.sorting;
2278 bool cameraDirectionChanged = !qFuzzyCompare(instanceData.sortedCameraDirection, cameraDirection);
2279 bool cameraPositionChanged = !qFuzzyCompare(instanceData.cameraPosition, cameraPosition);
2280 bool updateInstanceBuffer = table->serial() != instanceData.serial || sortingChanged || (cameraDirectionChanged && table->isDepthSortingEnabled());
2281 bool updateForLod = cameraPositionChanged && usesLod;
2282 if (sortingChanged && !table->isDepthSortingEnabled()) {
2283 instanceData.sortedData.clear();
2284 instanceData.sortData.clear();
2285 instanceData.sortedCameraDirection = {};
2286 }
2287 instanceData.sorting = table->isDepthSortingEnabled();
2288 if (instanceData.buffer && instanceData.buffer->size() < instanceBufferSize) {
2289 updateInstanceBuffer = true;
2290 // qDebug() << "Resizing instance buffer";
2291 instanceData.buffer->setSize(instanceBufferSize);
2292 instanceData.buffer->create();
2293 }
2294 if (!instanceData.buffer) {
2295 // qDebug() << "Creating instance buffer";
2296 updateInstanceBuffer = true;
2297 instanceData.buffer = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, instanceBufferSize);
2298 instanceData.buffer->create();
2299 }
2300 if (updateInstanceBuffer || updateForLod) {
2301 const void *data = nullptr;
2302 if (table->isDepthSortingEnabled()) {
2303 if (updateInstanceBuffer) {
2304 QMatrix4x4 invGlobalTransform = modelContext.model.globalTransform.inverted();
2305 instanceData.sortedData.resize(table->dataSize());
2306 sortInstances(instanceData.sortedData,
2307 instanceData.sortData,
2308 table->constData(),
2309 table->stride(),
2310 table->count(),
2311 invGlobalTransform.map(cameraDirection).normalized());
2312 }
2313 data = instanceData.sortedData.constData();
2314 instanceData.sortedCameraDirection = cameraDirection;
2315 } else {
2316 data = table->constData();
2317 }
2318 if (data) {
2319 if (updateForLod) {
2320 if (table->isDepthSortingEnabled()) {
2321 instanceData.lodData.resize(table->dataSize());
2322 cullLodInstances(instanceData.lodData, instanceData.sortedData.constData(), instanceData.sortedData.size(), cameraPosition, minThreshold, maxThreshold);
2323 data = instanceData.lodData.constData();
2324 } else {
2325 instanceData.lodData.resize(table->dataSize());
2326 cullLodInstances(instanceData.lodData, table->constData(), table->count(), cameraPosition, minThreshold, maxThreshold);
2327 data = instanceData.lodData.constData();
2328 }
2329 }
2331 rub->updateDynamicBuffer(instanceData.buffer, 0, instanceBufferSize, data);
2332 rhiCtx->commandBuffer()->resourceUpdate(rub);
2333 //qDebug() << "****** UPDATING INST BUFFER. Size" << instanceBufferSize;
2334 } else {
2335 qWarning() << "NO DATA IN INSTANCE TABLE";
2336 }
2337 instanceData.serial = table->serial();
2338 instanceData.cameraPosition = cameraPosition;
2339 }
2340 instanceBuffer = instanceData.buffer;
2341 return instanceBuffer;
2342}
2343
2345{
2347 static bool bakeRequested = false;
2348 static bool bakeFlagChecked = false;
2349 if (!bakeFlagChecked) {
2350 bakeFlagChecked = true;
2351 const bool cmdLineReq = QCoreApplication::arguments().contains(QStringLiteral("--bake-lightmaps"));
2352 const bool envReq = qEnvironmentVariableIntValue("QT_QUICK3D_BAKE_LIGHTMAPS");
2353 bakeRequested = cmdLineReq || envReq;
2354 }
2355 if (!bakeRequested)
2356 return;
2357 }
2358
2359 const auto &sortedBakedLightingModels = getSortedBakedLightingModels(); // front to back
2360
2361 QSSGRhiContext *rhiCtx = renderer->contextInterface()->rhiContext().get();
2362
2363 if (!m_lightmapper)
2364 m_lightmapper = new QSSGLightmapper(rhiCtx, renderer);
2365
2366 // sortedBakedLightingModels contains all models with
2367 // usedInBakedLighting: true. These, together with lights that
2368 // have a bakeMode set to either Indirect or All, form the
2369 // lightmapped scene. A lightmap is stored persistently only
2370 // for models that have their lightmapKey set.
2371
2373 m_lightmapper->setOptions(layer.lmOptions);
2375
2376 for (int i = 0, ie = sortedBakedLightingModels.size(); i != ie; ++i)
2377 m_lightmapper->add(sortedBakedLightingModels[i]);
2378
2379 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2380 cb->debugMarkBegin("Quick3D lightmap baking");
2382 cb->debugMarkEnd();
2383
2385 qDebug("Lightmap baking done, exiting application");
2387 }
2388
2390}
2391
2393{
2394 return frameData;
2395}
2396
2398{
2400 if (auto node = reinterpret_cast<QSSGRenderNode *>(id)) {
2401 // NOTE: We only look-up models for now.
2402 if (node->type == QSSGRenderNode::Type::Model) {
2403 const auto cbegin = renderableModels.cbegin();
2404 const auto cend = renderableModels.cend();
2405 const auto foundIt = std::find_if(cbegin, cend, [node](const QSSGRenderableNodeEntry &e){ return (e.node == node); });
2406 if (foundIt != cend)
2407 ret = *foundIt;
2408 }
2409 }
2410
2411 return ret;
2412}
2413
2415{
2417 if (auto node = reinterpret_cast<QSSGRenderNode *>(id)) {
2418 // NOTE: We only look-up models for now.
2419 if (node->type == QSSGRenderNode::Type::Model) {
2420 const auto cbegin = renderableModels.cbegin();
2421 const auto cend = renderableModels.cend();
2422 const auto foundIt = std::find_if(cbegin, cend, [node](const QSSGRenderableNodeEntry &e){ return (e.node == node); });
2423 if (foundIt != cend) {
2424 ret = *foundIt;
2425 renderableModels.erase(foundIt);
2426 }
2427 }
2428 }
2429
2430 return ret;
2431}
2432
2434{
2435 QSSGRenderGraphObject *ret = nullptr;
2436 if (auto res = reinterpret_cast<QSSGRenderGraphObject *>(id))
2437 ret = res;
2438
2439 return ret;
2440}
2441
2443{
2445 if (!camera_ || camera_ == camera)
2446 data = getCachedCameraData();
2447 else if (camera_)
2448 data = getCameraDataImpl(camera_);
2449
2450 return data;
2451}
2452
2454{
2456 if ((!camera_ || camera_ == camera) && cameraData.has_value())
2457 data = cameraData.value();
2458 else if (camera_)
2459 data = getCameraDataImpl(camera_);
2460
2461 return data;
2462}
2463
2465{
2466 return renderer ? renderer->contextInterface() : nullptr;
2467}
2468
2470{
2471 if (!shadowMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
2472 shadowMapManager.reset(new QSSGRenderShadowMap(*renderer->contextInterface()));
2473 return shadowMapManager;
2474}
2475
2477{
2478 if (!reflectionMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
2479 reflectionMapManager.reset(new QSSGRenderReflectionMap(*renderer->contextInterface()));
2480 return reflectionMapManager;
2481}
2482
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QStringList arguments()
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator cend() const noexcept
Definition qhash.h:1208
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:949
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:882
bool empty() const noexcept
Definition qlist.h:682
void swapItemsAt(qsizetype i, qsizetype j)
Definition qlist.h:664
void push_back(parameter_type t)
Definition qlist.h:672
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator begin()
Definition qlist.h:608
void reserve(qsizetype size)
Definition qlist.h:746
void resize(qsizetype size)
Definition qlist.h:392
const_iterator cend() const noexcept
Definition qlist.h:614
void append(parameter_type t)
Definition qlist.h:441
const_iterator cbegin() const noexcept
Definition qlist.h:613
void clear()
Definition qlist.h:417
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
QMatrix3x3 normalMatrix() const
Returns the normal matrix corresponding to this 4x4 transformation.
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
QPoint map(const QPoint &point) const
Maps point by multiplying this matrix by point.
Definition qmatrix4x4.h:908
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:721
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:238
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:184
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:187
@ Dynamic
Definition qrhi.h:839
@ VertexBuffer
Definition qrhi.h:843
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:1614
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
Sometimes committing resource updates is necessary or just more convenient without starting a render ...
Definition qrhi.cpp:8986
\inmodule QtGui
Definition qrhi.h:1694
void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
Enqueues updating a region of a QRhiBuffer buf created with the type QRhiBuffer::Dynamic.
Definition qrhi.cpp:8595
\inmodule QtGui
Definition qrhi.h:883
@ RGBA32F
Definition qrhi.h:914
@ RED_OR_ALPHA8
Definition qrhi.h:911
@ RGBA16F
Definition qrhi.h:913
\inmodule QtGui
Definition qrhi.h:1767
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
Definition qrhi.cpp:10079
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition qrhi.cpp:9673
@ IntAttributes
Definition qrhi.h:1816
@ RedOrAlpha8IsRed
Definition qrhi.h:1804
@ Instancing
Definition qrhi.h:1798
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:8854
Class representing 3D range or axis aligned bounding box.
static Q_ALWAYS_INLINE QSSGBounds3 centerExtents(const QVector3D &center, const QVector3D &extent)
returns the AABB from center and extents vectors.
Q_ALWAYS_INLINE QVector3D center() const
returns the center of this axis aligned box.
QVector3D getSupport(const QVector3D &direction) const
Q_ALWAYS_INLINE bool intersects(const QSSGBounds3 &b) const
indicates whether the intersection of this and b is empty or not.
QVector3D minimum
QVector3D maximum
static QSSGBounds3 transform(const QMatrix3x3 &matrix, const QSSGBounds3 &bounds)
gets the transformed bounds of the passed AABB (resulting in a bigger AABB).
\inmodule QtQuick3DRuntimeRender
QVector< QSSGBakedLightingModel > renderedBakedLightingModels
QRhiTexture * getLightmapTexture(const QSSGModelContext &modelContext) const
static qsizetype frustumCullingInline(const QSSGClippingFrustum &clipFrustum, QSSGRenderableObjectList &renderables)
bool prepareModelsForRender(const RenderableNodeEntries &renderableModels, QSSGLayerRenderPreparationResultFlags &ioFlags, const QSSGCameraRenderData &cameraData, RenderableFilter filter, float lodThreshold=0.0f)
bool prepareItem2DsForRender(const QSSGRenderContextInterface &ctxIfc, const RenderableItem2DEntries &renderableItem2Ds)
QSSGLightmapper::Callback lightmapBakingOutputCallback
QSSGRenderableObjectList renderedOpaqueObjects
QSSGRenderableObjectList renderedTransparentObjects
QVector< QSSGBakedLightingModel > bakedLightingModels
QSSGFrameData & getFrameData()
void setLightmapTexture(const QSSGModelContext &modelContext, QRhiTexture *lightmapTexture)
std::optional< QSSGClippingFrustum > clippingFrustum
void setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags, QSSGShaderDefaultMaterialKey &key, QSSGRenderer *renderer)
QSSGRenderableObjectList renderedDepthWriteObjects
const QSSGRenderableObjectList & getSortedRenderedDepthWriteObjects()
QSSGRenderableNodeEntry takeNode(QSSGNodeId id)
QSSGRenderCamera * camera
QSSGLayerRenderData(QSSGRenderLayer &inLayer, QSSGRenderer &inRenderer)
void prepareImageForRender(QSSGRenderImage &inImage, QSSGRenderableImage::Type inMapType, QSSGRenderableImage *&ioFirstImage, QSSGRenderableImage *&ioNextImage, QSSGRenderableObjectFlags &ioFlags, QSSGShaderDefaultMaterialKey &ioGeneratedShaderKey, quint32 inImageIndex, QSSGRenderDefaultMaterial *inMaterial=nullptr)
QVarLengthArray< QSSGRenderPass *, 12 > activePasses
QSSGRenderableObjectList opaqueObjects
static void prepareModelMeshes(const QSSGRenderContextInterface &contextInterface, RenderableNodeEntries &renderableModels)
const QSSGRenderableObjectList & getSortedOpaqueRenderableObjects()
const QSSGRenderReflectionMapPtr & requestReflectionMapManager()
QSSGLightmapper * m_lightmapper
const QSSGRenderableObjectList & getSortedTransparentRenderableObjects()
QSSGShaderDefaultMaterialKey generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType, const QSSGShaderLightListView &lights, bool receivesShadows=true)
ReflectionMapPass reflectionMapPass
const QSSGRenderableObjectList & getSortedrenderedOpaqueDepthPrepassObjects()
RenderableNodeEntries renderableModels
const QSSGRenderableObjectList & getSortedScreenTextureRenderableObjects()
const QVector< QSSGBakedLightingModel > & getSortedBakedLightingModels()
const RenderableItem2DEntries & getRenderableItem2Ds()
QRhiTexture * getBonemapTexture(const QSSGModelContext &modelContext) const
static qsizetype frustumCulling(const QSSGClippingFrustum &clipFrustum, const QSSGRenderableObjectList &renderables, QSSGRenderableObjectList &visibleRenderables)
QSSGRenderableObjectList renderedOpaqueDepthPrepassObjects
QSSGRenderableObjectList transparentObjects
RenderableItem2DEntries renderedItem2Ds
QSSGCameraRenderData getCameraRenderData(const QSSGRenderCamera *camera)
std::optional< QSSGLayerRenderPreparationResult > layerPrepResult
QSSGRenderContextInterface * contextInterface() const
void setBonemapTexture(const QSSGModelContext &modelContext, QRhiTexture *bonemapTexture)
QSSGRenderGraphObject * getResource(QSSGResourceId id) const
QSSGRenderableObjectList renderedScreenTextureObjects
QSSGRenderableNodeEntry getNode(QSSGNodeId id) const
const QSSGRenderShadowMapPtr & requestShadowMapManager()
QVector< QSSGRenderLight * > lights
QSSGRenderableObjectList screenTextureObjects
UserPass userPasses[USERPASSES]
QVector< QSSGRenderItem2D * > renderableItem2Ds
bool prepareParticlesForRender(const RenderableNodeEntries &renderableParticles, const QSSGCameraRenderData &cameraData)
QVector< QSSGRenderReflectionProbe * > reflectionProbes
QVector< QSSGRenderCamera * > cameras
std::function< bool(QSSGModelContext *)> RenderableFilter
QSSGShaderLightList globalLights
TModelContextPtrList modelContexts
static constexpr size_t USERPASSES
RenderableNodeEntries renderableParticles
std::optional< QSSGCameraRenderData > cameraData
qsizetype add(const QSSGBakedLightingModel &model)
void setOptions(const QSSGLightmapperOptions &options)
void setOutputCallback(Callback callback)
bool isValid() const
Definition qssgmesh_p.h:116
VertexBuffer vertexBuffer() const
Definition qssgmesh_p.h:97
IndexBuffer indexBuffer() const
Definition qssgmesh_p.h:98
Representation of a plane.
Definition qssgplane_p.h:31
Q_ALWAYS_INLINE float distance(const QVector3D &p) const
Definition qssgplane_p.h:62
const std::unique_ptr< QSSGRhiContext > & rhiContext() const
const std::unique_ptr< QSSGDebugDrawSystem > & debugDrawSystem() const
const std::unique_ptr< QSSGCustomMaterialSystem > & customMaterialSystem() const
const std::unique_ptr< QSSGBufferManager > & bufferManager() const
static void setTonemapFeatures(QSSGShaderFeatures &features, QSSGRenderLayer::TonemapMode tonemapMode)
bool isGlobalPickingEnabled() const
QSSGShaderDefaultMaterialKeyProperties & defaultMaterialShaderKeyProperties()
void addMaterialDirtyClear(QSSGRenderGraphObject *material)
QSSGRenderContextInterface * contextInterface() const
QSSGRhiInstanceBufferData & instanceBufferData(QSSGRenderInstanceTable *instanceTable)
QRhiCommandBuffer * commandBuffer() const
QRhi * rhi() const
Definition qset.h:18
void clear()
Definition qset.h:61
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:390
\inmodule QtCore
Definition qsize.h:25
constexpr size_type size() const noexcept
bool isEmpty() const
const T & at(qsizetype idx) const
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
void push_back(const T &t)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
QT_WARNING_POP float distanceToPoint(QVector3D point) const noexcept
Definition qvectornd.h:792
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
constexpr float w() const noexcept
Returns the w coordinate of this point.
Definition qvectornd.h:881
constexpr QVector3D toVector3D() const noexcept
Returns the 3D vector form of this 4D vector, dropping the w coordinate.
Definition qvectornd.h:1011
QList< QSSGRenderExtension * > extensions
EGLContext ctx
QCamera * camera
Definition camera.cpp:19
double e
QSet< QString >::iterator it
direction
constexpr quint32 nextMultipleOf4(quint32 value)
Combined button and popup list for selecting options.
@ darkRed
Definition qnamespace.h:40
@ darkCyan
Definition qnamespace.h:43
@ cyan
Definition qnamespace.h:37
@ white
Definition qnamespace.h:30
@ blue
Definition qnamespace.h:36
@ magenta
Definition qnamespace.h:38
@ yellow
Definition qnamespace.h:39
@ darkBlue
Definition qnamespace.h:42
@ darkGray
Definition qnamespace.h:31
@ darkMagenta
Definition qnamespace.h:44
@ green
Definition qnamespace.h:35
@ red
Definition qnamespace.h:34
@ darkGreen
Definition qnamespace.h:41
@ darkYellow
Definition qnamespace.h:45
constexpr Initialization Uninitialized
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix3x3 &m, const QVector3D &v)
Definition qssgutils.cpp:43
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v)
Definition qssgutils.cpp:86
QVector3D Q_QUICK3DUTILS_EXPORT getScale(const QMatrix4x4 &m)
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:105
#define Q_UNLIKELY(x)
#define Q_REQUIRED_RESULT
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT layer
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:243
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCDebug(category,...)
return ret
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLenum GLenum GLsizei count
GLfloat GLfloat f
const void GLsizei GLsizei stride
GLsizei GLsizei GLfloat distance
GLenum GLenum dst
GLenum GLuint texture
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint y
GLhandleARB obj
[2]
GLuint res
GLint lod
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
GLenum GLenum GLsizei void * table
quintptr QSSGResourceId
quintptr QSSGNodeId
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static QT_BEGIN_NAMESPACE const QRgb colors[][14]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT_X(cond, msg, action)
#define QSSG_ASSERT(cond, action)
#define QSSG_GUARD(cond)
void updateDirtySkeletons(const QVector< QSSGRenderableNodeEntry > &renderableNodes)
static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
static void collectNodeFront(V node, QVector< T > &dst, int &dstPos)
#define POS4BONETRANS(x)
static constexpr bool furthestToNearestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
Q_REQUIRED_RESULT QSSGDataRef< T > RENDER_FRAME_NEW_BUFFER(QSSGRenderContextInterface &ctx, size_t count)
#define MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS
static QSSGCameraRenderData getCameraDataImpl(const QSSGRenderCamera *camera)
#define MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS
static const QVector2D s_ProgressiveAAVertexOffsets[QSSGLayerRenderData::MAX_AA_LEVELS]
#define POS4BONENORM(x)
static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES
static bool checkParticleSupport(QRhi *rhi)
#define BONEDATASIZE4ID(x)
static void sortInstances(QByteArray &sortedData, QList< QSSGRhiSortData > &sortData, const void *instances, int stride, int count, const QVector3D &cameraDirection)
#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent)
static constexpr bool nearestToFurthestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
static void cullLodInstances(QByteArray &lodData, const void *instances, int count, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
Q_REQUIRED_RESULT T * RENDER_FRAME_NEW(QSSGRenderContextInterface &ctx, Args &&... args)
static void collectBoneTransforms(QSSGRenderNode *node, QSSGRenderSkeleton *skeletonNode, const QVector< QMatrix4x4 > &poses)
static bool hasDirtyNonJointNodes(QSSGRenderNode *node, bool &hasChildJoints)
static int effectiveMaxLightCount(const QSSGShaderFeatures &features)
static bool maybeQueueNodeForRender(QSSGRenderNode &inNode, QVector< QSSGRenderableNodeEntry > &outRenderableModels, int &ioRenderableModelsCount, QVector< QSSGRenderableNodeEntry > &outRenderableParticles, int &ioRenderableParticlesCount, QVector< QSSGRenderItem2D * > &outRenderableItem2Ds, int &ioRenderableItem2DsCount, QVector< QSSGRenderCamera * > &outCameras, int &ioCameraCount, QVector< QSSGRenderLight * > &outLights, int &ioLightCount, QVector< QSSGRenderReflectionProbe * > &outReflectionProbes, int &ioReflectionProbeCount, quint32 &ioDFSIndex)
void collectNode(V node, QVector< T > &dst, int &dstPos)
static float getCameraDistanceSq(const QSSGRenderableObject &obj, const QSSGCameraRenderData &camera) noexcept
#define QSSG_RENDER_MINIMUM_RENDER_OPACITY
QSSGDataView< QSSGRenderGraphObject * > QSSGMaterialListView
std::shared_ptr< QSSGRenderReflectionMap > QSSGRenderReflectionMapPtr
std::shared_ptr< QSSGRenderShadowMap > QSSGRenderShadowMapPtr
@ VSM
variance shadow mapping
@ CUBE
cubemap omnidirectional shadows
#define QSSG_REDUCED_MAX_NUM_LIGHTS
#define QSSG_MAX_NUM_LIGHTS
#define QSSG_MAX_NUM_SHADOW_MAPS
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define QStringLiteral(str)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
unsigned int quint32
Definition qtypes.h:45
int qint32
Definition qtypes.h:44
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
QSqlQueryModel * model
[16]
QObject::connect nullptr
QSemaphore sem(5)
[0]
QString dir
[11]
QLayoutItem * child
[0]
QJSValueList args
QSvgRenderer * renderer
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
bool intersectsWith(const QSSGBounds3 &bounds) const
qsizetype size() const
QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inMaterialKey)
QSSGShaderDefaultMaterialKey materialKey
QSSGLayerRenderPreparationResultFlags flags
QSSGCameraGlobalCalculationResult setupCameraForRender(QSSGRenderCamera &inCamera)
static const char * getNormalAttrName()
Definition qssgmesh_p.h:350
static const char * getPositionAttrName()
Definition qssgmesh_p.h:349
ComponentType componentType
Definition qssgmesh_p.h:66
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:61
const QSSGRenderModel & model
QSSGDataRef< QSSGSubsetRenderable > subsets
QSSGBounds3 bounds() const
float getLevelOfDetailMultiplier() const
QSSGCameraGlobalCalculationResult calculateGlobalVariables(const QRectF &inViewport)
bool isDirty(DirtyFlag dirtyFlag=DirtyMask) const
QSSGRenderDefaultMaterial::MaterialSpecularModel specularModel
QSSGRenderDefaultMaterial::MaterialBlendMode blendMode
QSSGRenderDefaultMaterial::MaterialAlphaMode alphaMode
TextureChannelMapping clearcoatRoughnessChannel
static Q_REQUIRED_RESULT constexpr bool isLight(Type type) Q_DECL_NOTHROW
static Q_REQUIRED_RESULT constexpr bool isCamera(Type type) Q_DECL_NOTHROW
static Q_REQUIRED_RESULT constexpr bool isRenderable(Type type) Q_DECL_NOTHROW
QSSGRenderImageTextureFlags m_flags
bool isImageTransformIdentity() const
MappingModes m_mappingMode
bool isEnabled() const
QSSGRenderNode * m_scope
QVector< QSSGRenderSubset > subsets
QSSGParticleBuffer * particleBuffer
bool instancing() const
QSSGRenderSkeleton * skeleton
bool usesBoneTexture() const
bool hasLightmap() const
QSSGRenderInstanceTable * instanceTable
QVector< QSSGRenderGraphObject * > materials
bool calculateGlobalVariables()
QMatrix4x4 globalTransform
QVector3D getScalingCorrectDirection() const
QSSGRenderNode * parent
QVector3D getGlobalPos() const
constexpr bool getGlobalState(GlobalState stateFlag) const
constexpr bool isDirty(DirtyFlag dirtyFlag=DirtyFlag::DirtyMask) const
QSSGRenderImage * m_colorTable
QSSGParticleBuffer m_particleBuffer
QSSGRhiInputAssemblerState ia
quint32 lodOffset(int lodLevel) const
quint32 lodCount(int lodLevel) const
QVector< Lod > lods
QRhiTexture * targetsTexture
struct QSSGRenderSubset::@751 rhi
QSSGRenderableImage * m_nextImage
QSSGShaderLightListView lights
void setRendersWithLightmap(bool inRendersWithLightmap)
void setCastsReflections(bool inCastsReflections)
void setCastsShadows(bool inCastsShadows)
void setReceivesReflections(bool inReceivesReflections)
void setUsedInBakedLighting(bool inUsedInBakedLighting)
void setHasTransparency(bool inHasTransparency)
void setReceivesShadows(bool inReceivesShadows)
const QMatrix4x4 & globalTransform
std::array< quint8, MaxTargetSemantic+1 > targetOffsets
QVarLengthArray< InputSemantic, 8 > inputs
QRhiGraphicsPipeline::Topology topology
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord1Offset
QSSGShaderKeyBoolean m_lightSpotFlags[LightCount]
QSSGShaderKeyTextureChannel m_textureChannels[SingleChannelImageCount]
QSSGShaderKeyImageMap m_imageMaps[ImageMapCount]
QSSGShaderKeyUnsigned< 8 > m_targetPositionOffset
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord0Offset
QSSGShaderKeyVertexAttribute m_vertexAttributes
QSSGShaderKeyBoolean m_lightFlags[LightCount]
QSSGShaderKeyUnsigned< 8 > m_targetBinormalOffset
QSSGShaderKeyBoolean m_lightShadowFlags[LightCount]
constexpr bool isSet(Feature feature) const
void set(Feature feature, bool val)
void setValue(QSSGDataRef< quint32 > inDataStore, bool inValue) const
bool getValue(QSSGDataView< quint32 > inDataStore) const
void setIdentityTransform(QSSGDataRef< quint32 > inKeySet, bool val)
void setEnabled(QSSGDataRef< quint32 > inKeySet, bool val)
void setUsesUV1(QSSGDataRef< quint32 > inKeySet, bool val)
void setLightProbe(QSSGDataRef< quint32 > inKeySet, bool val)
void setEnvMap(QSSGDataRef< quint32 > inKeySet, bool val)
void setSpecularModel(QSSGDataRef< quint32 > inKeySet, QSSGRenderDefaultMaterial::MaterialSpecularModel inModel)
void setTextureChannel(TexturChannelBits channel, QSSGDataRef< quint32 > inKeySet)
void setValue(QSSGDataRef< quint32 > inDataStore, quint32 inValue) const
QSSGRenderLight * light
bool prepareInstancing(QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
QSSGShaderReflectionProbe reflectionProbe
const QSSGModelContext & modelContext