9#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
10#include "../qssgrendercontextcore.h"
11#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
15#include "../qssgrendercontextcore.h"
16#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
23#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
26#include "../qssgshadermapkey_p.h"
27#include "../qssgrenderpickresult_p.h"
28#include "../graphobjects/qssgrenderroot_p.h"
29#include "../graphobjects/qssgrendermodel_p.h"
30#include "../graphobjects/qssgrenderdefaultmaterial_p.h"
31#include "../graphobjects/qssgrendercustommaterial_p.h"
33#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
34#include <QtQuick3DUtils/private/qssgdataref_p.h>
35#include <QtQuick3DUtils/private/qssgutils_p.h>
36#include <QtQuick3DUtils/private/qssgassert_p.h>
37#include <QtQuick3DUtils/private/qssgfrustum_p.h>
38#include <qtquick3d_tracepoints_p.h>
40#include <QtQuick/private/qsgcontext_p.h>
41#include <QtQuick/private/qsgrenderer_p.h>
43#include <QtCore/QMutexLocker>
44#include <QtCore/QBitArray>
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
79struct QSSGRenderableImage;
80class QSSGSubsetRenderable;
82void QSSGRenderer::releaseCachedResources()
84 m_rhiQuadRenderer.reset();
85 m_rhiCubeRenderer.reset();
88void QSSGRenderer::registerItem2DData(QSSGRenderItem2DData &data)
91 for (
const auto *item2DData : m_item2DDatas) {
92 if (item2DData == &data)
96 m_item2DDatas.push_back(&data);
99void QSSGRenderer::unregisterItem2DData(QSSGRenderItem2DData &data)
101 const auto foundIt = std::find(m_item2DDatas.begin(), m_item2DDatas.end(), &data);
102 if (foundIt != m_item2DDatas.end())
103 m_item2DDatas.erase(foundIt);
106void QSSGRenderer::releaseItem2DData(
const QSSGRenderItem2D &item2D)
108 for (
auto *item2DData : m_item2DDatas)
109 item2DData->releaseRenderData(item2D);
112QSSGRenderer::QSSGRenderer() =
default;
114QSSGRenderer::~QSSGRenderer()
116 m_contextInterface =
nullptr;
117 releaseCachedResources();
120void QSSGRenderer::cleanupUnreferencedBuffers(QSSGRenderLayer *inLayer)
123 m_contextInterface->bufferManager()->cleanupUnreferencedBuffers(m_frameCount, inLayer);
126void QSSGRenderer::resetResourceCounters(QSSGRenderLayer *inLayer)
128 m_contextInterface->bufferManager()->resetUsageCounters(m_frameCount, inLayer);
131bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
133 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
134 Q_ASSERT(theRenderData);
138 Q_ASSERT(inLayer.rootNode);
139 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
140 inLayer.rootNode->reindex();
142 beginLayerRender(*theRenderData);
143 theRenderData->resetForFrame();
144 theRenderData->prepareForRender();
146 return theRenderData->layerPrepResult.getFlags().wasDirty();
150void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
152 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
153 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
155 const auto layerPrepResult = theRenderData->layerPrepResult;
156 if (layerPrepResult.isLayerVisible()) {
158 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
159 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
160 beginLayerRender(*theRenderData);
161 theRenderData->maybeProcessLightmapBaking();
166 const auto &activePasses = theRenderData->activePasses;
167 for (
const auto &pass : activePasses) {
168 pass->renderPrep(*
this, *theRenderData);
169 if (pass->passType() == QSSGRenderPass::Type::Standalone)
170 pass->renderPass(*
this);
178void QSSGRenderer::rhiRender(QSSGRenderLayer &inLayer)
180 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
181 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
182 if (theRenderData->layerPrepResult.isLayerVisible()) {
183 beginLayerRender(*theRenderData);
184 const auto &activePasses = theRenderData->activePasses;
185 for (
const auto &pass : activePasses) {
186 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
187 pass->renderPass(*
this);
193template<
typename Container>
196 const auto &rhiCtx = rci.rhiContext();
197 if (!rhiCtx->isValid())
200 const auto &bufferManager = rci.bufferManager();
202 for (
const auto &resource : resources) {
203 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
204 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
205 bufferManager->releaseGeometry(geometry);
206 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
207 auto model =
static_cast<QSSGRenderModel*>(resource);
208 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
209 delete model->particleBuffer;
210 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
211 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
212 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
213 bufferManager->releaseTextureData(textureData);
214 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderExtension) {
215 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
216 bufferManager->releaseExtensionResult(*rext);
217 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
218 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
219 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
220 rhiCtxD->releaseInstanceBuffer(table);
221 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
222 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
223 rci.renderer()->releaseItem2DData(*item2D);
224 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderPass) {
225 auto *userPass =
static_cast<QSSGRenderUserPass *>(resource);
226 bufferManager->releaseUserRenderPass(*userPass);
235void QSSGRenderer::cleanupResources(QList<QSSGRenderGraphObject *> &resources)
237 cleanupResourcesImpl(*m_contextInterface, resources);
241void QSSGRenderer::cleanupResources(QSet<QSSGRenderGraphObject *> &resources)
243 cleanupResourcesImpl(*m_contextInterface, resources);
247QSSGLayerRenderData *QSSGRenderer::getOrCreateLayerRenderData(QSSGRenderLayer &layer)
249 if (layer.renderData ==
nullptr)
250 layer.renderData =
new QSSGLayerRenderData(layer, *
this);
252 return layer.renderData;
255void QSSGRenderer::addMaterialDirtyClear(QSSGRenderGraphObject *material)
257 m_materialClearDirty.insert(material);
263QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
264 QSSGShaderLibraryManager &shaderLibraryManager,
265 QSSGShaderCache &shaderCache,
266 QSSGProgramGenerator &shaderProgramGenerator,
267 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
268 const QSSGShaderFeatures &featureSet,
269 const QSSGUserShaderAugmentation &shaderAugmentation,
270 QByteArray &shaderString)
272 shaderString = rendererLogPrefix();
273 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
279 theKey.toString(shaderString, shaderKeyProperties);
282 for (
const auto &def : shaderAugmentation.defines)
283 shaderString.append(def.name).append(
';').append(def.value).append(
';');
284 shaderString.append(shaderAugmentation.preamble).append(
';');
285 shaderString.append(shaderAugmentation.body).append(
';');
291 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
292 return maybePipeline;
295 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
296 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
297 if (!pregenEntries.isEmpty()) {
298 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
299 if (foundIt != pregenEntries.cend())
300 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
304 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
305 return maybePipeline;
309 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
310 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
314 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(rendererLogPrefix(),
316 renderable.shaderDescription,
320 shaderLibraryManager,
325QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
326 QSSGSubsetRenderable &inRenderable,
327 const QSSGShaderFeatures &inFeatureSet,
328 const QSSGUserShaderAugmentation &shaderAugmentation = {})
330 auto *currentLayer = renderer.m_currentLayer;
331 auto &generatedShaderString = currentLayer->generatedShaderString;
332 const auto &m_contextInterface = renderer.m_contextInterface;
333 const auto &theCache = m_contextInterface->shaderCache();
334 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
335 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
336 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, shaderAugmentation, generatedShaderString);
339void QSSGRenderer::beginFrame(QSSGRenderLayer &layer,
bool allowRecursion)
341 const bool executeBeginFrame = !(allowRecursion && (m_activeFrameRef++ != 0));
342 if (executeBeginFrame) {
343 m_contextInterface->perFrameAllocator()->reset();
344 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(&layer));
345 resetResourceCounters(&layer);
349bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
351 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
352 if (executeEndFrame) {
353 cleanupUnreferencedBuffers(&layer);
356 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
357 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
358 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
359 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
360 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
361 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
362 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
365 m_materialClearDirty.clear();
367 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
372 return executeEndFrame;
375QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
376 const QSSGRenderLayer &layer,
377 const QSSGRenderRay &ray)
379 const auto &bufferManager = ctx.bufferManager();
380 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
381 PickResultList pickResults;
382 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
383 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
385 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
386 return lhs.m_distanceSq < rhs.m_distanceSq;
391QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
392 const QSSGRenderLayer &layer,
393 const QSSGRenderRay &ray,
394 QSSGRenderNode *target)
396 const auto &bufferManager = ctx.bufferManager();
397 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
399 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
400 PickResultList pickResults;
402 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
404 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
406 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
407 return lhs.m_distanceSq < rhs.m_distanceSq;
412using RenderableList = QVarLengthArray<
const QSSGRenderNode *>;
413static void getPickableRecursive(
const QSSGRenderNode &node, RenderableList &renderables,
bool pickEverything =
false)
415 if (QSSGRenderGraphObject::isRenderable(node.type) && (pickEverything || node.getLocalState(QSSGRenderNode::LocalState::Pickable))) {
416 renderables.push_back(&node);
419 for (
const auto &child : node.children)
420 getPickableRecursive(child, renderables, pickEverything);
423std::optional<QSSGRenderPickResult> QSSGRendererPrivate::syncPickClosestPoint(
const QSSGRenderContextInterface &ctx,
424 const QSSGRenderLayer &layer,
425 const QVector3D ¢er,
const float radiusSquared,
426 QSSGRenderNode *target)
428 const auto &bufferManager = ctx.bufferManager();
430 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
431 std::optional<QSSGRenderPickResult> result = std::nullopt;
433 result = closestPointOnSubsetRenderable(layer, *bufferManager, center, radiusSquared, *target);
435 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
436 RenderableList renderables;
437 for (
const auto &childNode : layer.children)
438 getPickableRecursive(childNode, renderables, pickEverything);
439 float bestDistSquared = radiusSquared;
440 for (
const auto &childNode : renderables) {
441 const auto res = closestPointOnSubsetRenderable(layer, *bufferManager, center, bestDistSquared, *childNode);
442 if (res.has_value()) {
443 bestDistSquared = res.value().m_distanceSq;
452QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
453 QSSGBufferManager &bufferManager,
454 const QSSGRenderRay &ray,
455 QVarLengthArray<QSSGRenderNode*> subset)
457 QSSGRendererPrivate::PickResultList pickResults;
458 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
460 for (
auto target : subset)
461 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
463 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
464 return lhs.m_distanceSq < rhs.m_distanceSq;
469void QSSGRendererPrivate::setGlobalPickingEnabled(QSSGRenderer &renderer,
bool isEnabled)
471 renderer.m_globalPickingEnabled = isEnabled;
474void QSSGRendererPrivate::setRenderContextInterface(QSSGRenderer &renderer, QSSGRenderContextInterface *ctx)
476 renderer.m_contextInterface = ctx;
479void QSSGRendererPrivate::setSgRenderContext(QSSGRenderer &renderer, QSGRenderContext *sgRenderCtx)
481 renderer.m_qsgRenderContext = sgRenderCtx;
484QSGRenderContext *QSSGRendererPrivate::getSgRenderContext(
const QSSGRenderer &renderer)
486 return renderer.m_qsgRenderContext.data();
489const std::unique_ptr<QSSGRhiQuadRenderer> &QSSGRenderer::rhiQuadRenderer()
const
491 if (!m_rhiQuadRenderer)
492 m_rhiQuadRenderer = std::make_unique<QSSGRhiQuadRenderer>();
494 return m_rhiQuadRenderer;
497const std::unique_ptr<QSSGRhiCubeRenderer> &QSSGRenderer::rhiCubeRenderer()
const
499 if (!m_rhiCubeRenderer)
500 m_rhiCubeRenderer = std::make_unique<QSSGRhiCubeRenderer>();
502 return m_rhiCubeRenderer;
506void QSSGRenderer::beginSubLayerRender(QSSGLayerRenderData &inLayer)
508 inLayer.saveRenderState(*
this);
509 m_currentLayer =
nullptr;
512void QSSGRenderer::endSubLayerRender(QSSGLayerRenderData &inLayer)
514 inLayer.restoreRenderState(*
this);
515 m_currentLayer = &inLayer;
518void QSSGRenderer::beginLayerRender(QSSGLayerRenderData &inLayer)
520 m_currentLayer = &inLayer;
522void QSSGRenderer::endLayerRender()
524 m_currentLayer =
nullptr;
527static void dfs(
const QSSGRenderNode &node, RenderableList &renderables)
529 if (QSSGRenderGraphObject::isRenderable(node.type))
530 renderables.push_back(&node);
532 for (
const auto &child : node.children)
533 dfs(child, renderables);
536void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
537 QSSGBufferManager &bufferManager,
538 const QSSGRenderRay &ray,
539 bool inPickEverything,
540 PickResultList &outIntersectionResult)
542 RenderableList renderables;
543 for (
const auto &childNode : layer.children)
544 dfs(childNode, renderables);
546 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
547 const auto &pickableObject = renderables.at(idx);
548 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
549 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
555static inline QVector3D multiply(
const QMatrix3x3& M,
const QVector3D& v)
558 M(0,0) * v.x() + M(0,1) * v.y() + M(0,2) * v.z(),
559 M(1,0) * v.x() + M(1,1) * v.y() + M(1,2) * v.z(),
560 M(2,0) * v.x() + M(2,1) * v.y() + M(2,2) * v.z()
565static inline bool isUniformScaleMetric(
const QMatrix3x3& G,
float& s2,
float tolerance = 1e-5f) {
566 const float gxx = G(0,0), gyy = G(1,1), gzz = G(2,2);
567 const float gxy = G(0,1), gxz = G(0,2), gyz = G(1,2);
570 s2 = (gxx + gyy + gzz) / 3.0f;
573 const float scale =
std::max({ std::fabs(gxx), std::fabs(gyy), std::fabs(gzz), 1.0f });
576 const bool offDiagOK = (std::fabs(gxy) <= tolerance * scale) &&
577 (std::fabs(gxz) <= tolerance * scale) &&
578 (std::fabs(gyz) <= tolerance * scale) &&
579 (std::fabs(G(1,0)) <= tolerance * scale) &&
580 (std::fabs(G(2,0)) <= tolerance * scale) &&
581 (std::fabs(G(2,1)) <= tolerance * scale);
583 const bool diagOK = (std::fabs(gxx - s2) <= tolerance * scale) &&
584 (std::fabs(gyy - s2) <= tolerance * scale) &&
585 (std::fabs(gzz - s2) <= tolerance * scale);
587 return offDiagOK && diagOK && (s2 >= 0.0f);
592 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
593 return QVector3D::dotProduct(u, v);
600 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
602 return QVector3D::dotProduct(u, multiply(G, v));
612static QVector3D closestPointOnTriangle(
const QVector3D &p,
617 float &u,
float &v,
float &w)
619 const QVector3D ab = b - a;
620 const QVector3D ac = c - a;
621 const QVector3D ap = p - a;
624 const float d1 = dot(ab, ap);
625 const float d2 = dot(ac, ap);
626 if (d1 <= 0.f && d2 <= 0.f) {
627 u = 1.0f; v = 0.0f; w = 0.0f;
632 const QVector3D bp = p - b;
633 const float d3 = dot(ab, bp);
634 const float d4 = dot(ac, bp);
635 if (d3 >= 0.f && d4 <= d3) {
636 u = 0.0f; v = 1.0f; w = 0.0f;
641 const float vc = d1 * d4 - d3 * d2;
642 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
643 const float v_edge = d1 / (d1 - d3);
644 u = 1.0f - v_edge; v = v_edge; w = 0.0f;
645 return a + v_edge * ab;
649 const QVector3D cp = p - c;
650 const float d5 = dot(ab, cp);
651 const float d6 = dot(ac, cp);
652 if (d6 >= 0.f && d5 <= d6) {
653 u = 0.0f; v = 0.0f; w = 1.0f;
658 const float vb = d5 * d2 - d1 * d6;
659 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
660 const float w_edge = d2 / (d2 - d6);
661 u = 1.0f - w_edge; v = 0.0f; w = w_edge;
662 return a + w_edge * ac;
666 const float va = d3 * d6 - d5 * d4;
667 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
668 const QVector3D bc = c - b;
669 const float w_edge = (d4 - d3) / ((d4 - d3) + (d5 - d6));
670 u = 0.0f; v = 1.0f - w_edge; w = w_edge;
671 return b + w_edge * bc;
675 const float denom = va + vb + vc;
678 if (
std::abs(denom) < 1e-20f) {
680 const float da = dot(ap, ap);
681 const float db = dot(bp, bp);
682 const float dc = dot(cp, cp);
683 if (da <= db && da <= dc) {
684 u = 1.0f; v = 0.0f; w = 0.0f;
688 u = 0.0f; v = 1.0f; w = 0.0f;
691 u = 0.0f; v = 0.0f; w = 1.0f;
695 const float invDenom = 1.0f / denom;
699 return a + v * ab + w * ac;
704 QMatrix4x4 globalTransform;
705 QMatrix3x3 pullbackMetric;
706 QVector3D centerLocal;
712static inline SphereData createSphereData(
const QMatrix4x4 &globalTransform,
713 const QVector3D ¢erWorld)
715 QMatrix4x4 inv = globalTransform.inverted();
718 const QVector3D centerLocal = QSSGUtils::mat44::transform(inv, centerWorld);
720 const QMatrix3x3 A = QSSGUtils::mat44::getUpper3x3(globalTransform);
721 const QMatrix3x3 G = A.transposed() * A;
723 return SphereData{ globalTransform, G, centerLocal };
727static inline float distanceSqPointTransformedAABB(
const QVector3D &localPoint,
728 const QSSGBounds3 &localAABB,
732 QVector3D closestLocal(
733 qBound(localAABB.minimum.x(), localPoint.x(), localAABB.maximum.x()),
734 qBound(localAABB.minimum.y(), localPoint.y(), localAABB.maximum.y()),
735 qBound(localAABB.minimum.z(), localPoint.z(), localAABB.maximum.z())
739 QVector3D localDiff = localPoint - closestLocal;
744 return dot(localDiff, localDiff);
747struct ClosestPointResult
750 float distSq =
std::numeric_limits<
float>::max();
751 QVector3D localPoint;
752 QVector3D scenePoint;
753 QVector3D faceNormal;
754 QVector3D sceneNormal;
757 int instanceIndex = -1;
760static void closestPointBVHLeafNode(
const SphereData &data,
761 const QSSGMeshBVHNode *node,
762 const QSSGRenderMesh *mesh,
765 ClosestPointResult &best)
767 const int begin = node->offset;
768 const int end = begin + node->count;
769 const auto &triangles = mesh->bvh->triangles();
772 float uniformScaleFactor = 1.0f;
773 const bool isUniformScale = isUniformScaleMetric(data.pullbackMetric, uniformScaleFactor);
774 const MetricDot metricDot { data.pullbackMetric };
776 for (
int i = begin; i < end; ++i) {
777 const auto &triangle = triangles[i];
780 const float triangleDistSq = distanceSqPointTransformedAABB(data.centerLocal, triangle.bounds, data.pullbackMetric);
781 if (triangleDistSq >= best.distSq)
786 QVector3D closestPoint;
788 if (isUniformScale) {
789 closestPoint = closestPointOnTriangle(data.centerLocal,
790 triangle.vertex1, triangle.vertex2, triangle.vertex3,
794 closestPoint = closestPointOnTriangle(data.centerLocal,
795 triangle.vertex1, triangle.vertex2, triangle.vertex3,
801 const QVector3D delta = data.centerLocal - closestPoint;
802 const float distSq = metricDot(delta, delta);
805 if (distSq < best.distSq) {
806 best.distSq = distSq;
807 best.localPoint = closestPoint;
808 best.scenePoint = QSSGUtils::mat44::transform(data.globalTransform, closestPoint);
809 best.subset = subset;
810 best.instanceIndex = instanceIndex;
815 best.uv = u * triangle.uvCoord1 + v * triangle.uvCoord2 + w * triangle.uvCoord3;
818 const QVector3D edge1 = triangle.vertex2 - triangle.vertex1;
819 const QVector3D edge2 = triangle.vertex3 - triangle.vertex1;
820 best.faceNormal = QVector3D::normal(edge1, edge2).normalized();
821 const QMatrix3x3 normalMatrix = data.globalTransform.normalMatrix();
822 best.sceneNormal = QSSGUtils::mat33::transform(normalMatrix, best.faceNormal);
827static void closestPointBVH(
const SphereData &data,
828 const QSSGMeshBVHNode *node,
829 const QSSGRenderMesh *mesh,
832 ClosestPointResult &best)
834 if (!node || !mesh || !mesh->bvh)
838 const float aabbDistSq = distanceSqPointTransformedAABB(data.centerLocal, node->boundingData, data.pullbackMetric);
839 if (aabbDistSq >= best.distSq)
843 if (node->count != 0) {
844 closestPointBVHLeafNode(data, node, mesh, subset, instanceIndex, best);
849 const auto *leftChild =
static_cast<
const QSSGMeshBVHNode *>(node->left);
850 const auto *rightChild =
static_cast<
const QSSGMeshBVHNode *>(node->right);
853 const float leftDistSq = leftChild
854 ? distanceSqPointTransformedAABB(data.centerLocal, leftChild->boundingData, data.pullbackMetric)
855 : std::numeric_limits<
float>::max();
856 const float rightDistSq = rightChild
857 ? distanceSqPointTransformedAABB(data.centerLocal, rightChild->boundingData, data.pullbackMetric)
858 : std::numeric_limits<
float>::max();
861 if (leftDistSq < rightDistSq) {
862 if (leftDistSq < best.distSq)
863 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
864 if (rightDistSq < best.distSq)
865 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
867 if (rightDistSq < best.distSq)
868 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
869 if (leftDistSq < best.distSq)
870 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
875std::optional<QSSGRenderPickResult>
876QSSGRendererPrivate::closestPointOnSubsetRenderable(
const QSSGRenderLayer& layer,
877 QSSGBufferManager& bufferManager,
878 const QVector3D& center,
879 const float radiusSquared,
880 const QSSGRenderNode& node)
882 if (!layer.renderData)
885 const auto *renderData = layer.renderData;
892 if (node.type != QSSGRenderGraphObject::Type::Model)
895 const auto &model =
static_cast<
const QSSGRenderModel &>(node);
903 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
905 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
910 QSSGBounds3 modelBounds;
911 for (
const auto &subset : std::as_const(mesh->subsets))
912 modelBounds.include(subset.bounds);
914 if (modelBounds.isEmpty())
917 const bool instancing = model.instancing();
918 int instanceCount = instancing ? model.instanceTable->count() : 1;
919 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
921 ClosestPointResult best;
922 best.distSq = radiusSquared;
924 for (
int i = 0; i < instanceCount; ++i) {
925 int instanceIndex = 0;
926 QMatrix4x4 modelTransform;
929 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
931 modelTransform = renderData->getGlobalTransform(model);
933 const SphereData data = createSphereData(modelTransform, center);
935 if (distanceSqPointTransformedAABB(data.centerLocal, modelBounds, data.pullbackMetric) > best.distSq)
938 for (
int subsetIndex = 0; subsetIndex < mesh->subsets.size(); ++subsetIndex) {
939 const auto &subset = mesh->subsets[subsetIndex];
942 if (distanceSqPointTransformedAABB(data.centerLocal, subset.bounds, data.pullbackMetric) >= best.distSq)
945 if (!subset.bvhRoot.isNull()) {
946 const auto *bvhRoot =
static_cast<
const QSSGMeshBVHNode *>(subset.bvhRoot);
947 closestPointBVH(data, bvhRoot, mesh, subsetIndex, instanceIndex, best);
952 return QSSGRenderPickResult{
972 if (model.materials.isEmpty())
973 return QSSGCullFaceMode::Back;
974 const int idx = qMin(subset,
int(model.materials.size()) - 1);
975 const QSSGRenderGraphObject *material = model.materials.at(idx);
977 return QSSGCullFaceMode::Back;
978 switch (material->type) {
979 case QSSGRenderGraphObject::Type::DefaultMaterial:
980 case QSSGRenderGraphObject::Type::PrincipledMaterial:
981 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
982 return static_cast<
const QSSGRenderDefaultMaterial *>(material)->cullMode;
983 case QSSGRenderGraphObject::Type::CustomMaterial:
984 return static_cast<
const QSSGRenderCustomMaterial *>(material)->m_cullMode;
986 return QSSGCullFaceMode::Back;
995 case QSSGCullFaceMode::Unknown:
996 case QSSGCullFaceMode::Back:
997 return QSSGCullFaceMode::Front;
998 case QSSGCullFaceMode::Front:
999 return QSSGCullFaceMode::Back;
1000 case QSSGCullFaceMode::Disabled:
1001 case QSSGCullFaceMode::FrontAndBack:
1007void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
1008 QSSGBufferManager &bufferManager,
1009 const QSSGRenderRay &inRay,
1010 const QSSGRenderNode &node,
1011 PickResultList &outIntersectionResultList)
1013 if (!layer.renderData)
1016 const auto *renderData = layer.renderData;
1019 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
1020 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
1021 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
1025 if (node.type != QSSGRenderGraphObject::Type::Model)
1028 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
1035 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
1036 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
1040 const auto &subMeshes = mesh->subsets;
1041 QSSGBounds3 modelBounds;
1042 for (
const auto &subMesh : subMeshes)
1043 modelBounds.include(subMesh.bounds);
1045 if (modelBounds.isEmpty())
1048 const bool instancing = model.instancing();
1049 int instanceCount = instancing ? model.instanceTable->count() : 1;
1051 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
1053 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
1055 QMatrix4x4 modelTransform;
1057 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
1059 modelTransform = renderData->getGlobalTransform(model);
1061 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
1063 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
1066 if (!hit.intersects())
1069 const bool mirrored = modelTransform.determinant() < 0.0f;
1072 float minRayLength = std::numeric_limits<
float>::max();
1073 QSSGRenderRay::IntersectionResult intersectionResult;
1074 QVector<QSSGRenderRay::IntersectionResult> results;
1077 int resultSubset = 0;
1078 for (
const auto &subMesh : subMeshes) {
1079 QSSGRenderRay::IntersectionResult result;
1080 if (!subMesh.bvhRoot.isNull()) {
1081 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
1082 if (hit.intersects()) {
1084 QSSGCullFaceMode cullMode = cullModeForSubset(model, subset);
1086 cullMode = mirroredCullMode(cullMode);
1087 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results, cullMode);
1088 float subMeshMinRayLength = std::numeric_limits<
float>::max();
1089 for (
const auto &subMeshResult : std::as_const(results)) {
1090 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
1091 result = subMeshResult;
1092 subMeshMinRayLength = result.rayLengthSquared;
1097 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
1098 if (hit.intersects())
1099 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
1101 if (result.intersects && result.rayLengthSquared < minRayLength) {
1102 intersectionResult = result;
1103 minRayLength = intersectionResult.rayLengthSquared;
1104 resultSubset = subset;
1109 if (intersectionResult.intersects)
1110 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
1111 intersectionResult.rayLengthSquared,
1112 intersectionResult.relXY,
1113 intersectionResult.scenePosition,
1114 intersectionResult.localPosition,
1115 intersectionResult.faceNormal,
1116 intersectionResult.sceneFaceNormal,
1123void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
1124 const QSSGRenderRay &inRay,
1125 const QSSGRenderItem2D &item2D,
1126 PickResultList &outIntersectionResultList)
1128 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
1131 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
1132 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
1134 const float d = QVector3D::dotProduct(inRay.direction, normal);
1135 float intersectionTime = 0;
1137 const QVector3D p0l0 = p0 - inRay.origin;
1138 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
1139 if (intersectionTime >= 0) {
1141 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
1142 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
1143 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
1144 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
1145 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
1146 intersectionTime * intersectionTime,
1149 localIntersectionPoint,
1150 -normal, -normal });
1155QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
1156 QSSGSubsetRenderable &inRenderable,
1157 const QSSGShaderFeatures &inFeatureSet,
1158 const QSSGUserShaderAugmentation &shaderAugmentation)
1160 auto *m_currentLayer = renderer.m_currentLayer;
1161 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
1170 auto &shaderMap = m_currentLayer->shaderMap;
1172 QElapsedTimer timer;
1175 QSSGRhiShaderPipelinePtr shaderPipeline;
1180 QByteArray name = shaderAugmentation.preamble + shaderAugmentation.body;
1181 for (
const auto &def : shaderAugmentation.defines)
1182 name.append(def.name).append(def.value);
1183 QSSGShaderMapKey skey = QSSGShaderMapKey(name,
1185 inRenderable.shaderDescription);
1186 auto it = shaderMap.find(skey);
1187 if (it == shaderMap.end()) {
1188 Q_TRACE_SCOPE(QSSG_generateShader);
1189 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1190 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet, shaderAugmentation);
1191 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
1195 shaderMap.insert(skey, shaderPipeline);
1197 shaderPipeline = it.value();
1200 if (shaderPipeline !=
nullptr) {
1201 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1202 m_currentLayer->ensureCachedCameraDatas();
1205 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1206 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1208 return shaderPipeline;
1211QList<
const QSSGRenderNode *> QSSGRendererPrivate::syncPickInFrustum(
const QSSGRenderContextInterface &ctx,
1212 const QSSGRenderLayer &layer,
1213 const QSSGFrustum &frustum)
1215 if (!layer.renderData)
1218 auto &bufferManager = ctx.bufferManager();
1219 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
1221 RenderableList nodes;
1222 for (
const auto &child : layer.children)
1223 getPickableRecursive(child, nodes, pickEverything);
1225 QList<
const QSSGRenderNode *> ret;
1226 for (
const auto node : nodes) {
1227 auto aabb = node->getBounds(*bufferManager);
1228 if (!aabb.isEmpty()) {
1229 const auto &transform = layer.renderData->getGlobalTransform(*node);
1230 aabb.transform(transform);
1231 if (frustum.contains(aabb))
friend class QSSGRenderContextInterface
static void cleanupResourcesImpl(const QSSGRenderContextInterface &rci, const Container &resources)
static void getPickableRecursive(const QSSGRenderNode &node, RenderableList &renderables, bool pickEverything=false)
static void dfs(const QSSGRenderNode &node, RenderableList &renderables)
static QByteArray rendererLogPrefix()
static QSSGCullFaceMode mirroredCullMode(QSSGCullFaceMode cullMode)
static QSSGCullFaceMode cullModeForSubset(const QSSGRenderModel &model, int subset)