7#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
8#include "../qssgrendercontextcore.h"
9#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
13#include "../qssgrendercontextcore.h"
14#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
21#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
24#include "../qssgshadermapkey_p.h"
25#include "../qssgrenderpickresult_p.h"
26#include "../graphobjects/qssgrenderroot_p.h"
28#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
29#include <QtQuick3DUtils/private/qssgdataref_p.h>
30#include <QtQuick3DUtils/private/qssgutils_p.h>
31#include <QtQuick3DUtils/private/qssgassert_p.h>
32#include <qtquick3d_tracepoints_p.h>
34#include <QtQuick/private/qsgcontext_p.h>
35#include <QtQuick/private/qsgrenderer_p.h>
37#include <QtCore/QMutexLocker>
38#include <QtCore/QBitArray>
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
73struct QSSGRenderableImage;
74class QSSGSubsetRenderable;
76void QSSGRenderer::releaseCachedResources()
78 m_rhiQuadRenderer.reset();
79 m_rhiCubeRenderer.reset();
82void QSSGRenderer::registerItem2DData(QSSGRenderItem2DData &data)
85 for (
const auto *item2DData : m_item2DDatas) {
86 if (item2DData == &data)
90 m_item2DDatas.push_back(&data);
93void QSSGRenderer::unregisterItem2DData(QSSGRenderItem2DData &data)
95 const auto foundIt = std::find(m_item2DDatas.begin(), m_item2DDatas.end(), &data);
96 if (foundIt != m_item2DDatas.end())
97 m_item2DDatas.erase(foundIt);
100void QSSGRenderer::releaseItem2DData(
const QSSGRenderItem2D &item2D)
102 for (
auto *item2DData : m_item2DDatas)
103 item2DData->releaseRenderData(item2D);
106QSSGRenderer::QSSGRenderer() =
default;
108QSSGRenderer::~QSSGRenderer()
110 m_contextInterface =
nullptr;
111 releaseCachedResources();
114void QSSGRenderer::cleanupUnreferencedBuffers(QSSGRenderLayer *inLayer)
117 m_contextInterface->bufferManager()->cleanupUnreferencedBuffers(m_frameCount, inLayer);
120void QSSGRenderer::resetResourceCounters(QSSGRenderLayer *inLayer)
122 m_contextInterface->bufferManager()->resetUsageCounters(m_frameCount, inLayer);
125bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
127 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
128 Q_ASSERT(theRenderData);
132 Q_ASSERT(inLayer.rootNode);
133 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
134 inLayer.rootNode->reindex();
136 beginLayerRender(*theRenderData);
137 theRenderData->resetForFrame();
138 theRenderData->prepareForRender();
140 return theRenderData->layerPrepResult.getFlags().wasDirty();
144void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
146 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
147 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
149 const auto layerPrepResult = theRenderData->layerPrepResult;
150 if (layerPrepResult.isLayerVisible()) {
152 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
153 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
154 beginLayerRender(*theRenderData);
155 theRenderData->maybeProcessLightmapBaking();
160 const auto &activePasses = theRenderData->activePasses;
161 for (
const auto &pass : activePasses) {
162 pass->renderPrep(*
this, *theRenderData);
163 if (pass->passType() == QSSGRenderPass::Type::Standalone)
164 pass->renderPass(*
this);
172void QSSGRenderer::rhiRender(QSSGRenderLayer &inLayer)
174 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
175 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
176 if (theRenderData->layerPrepResult.isLayerVisible()) {
177 beginLayerRender(*theRenderData);
178 const auto &activePasses = theRenderData->activePasses;
179 for (
const auto &pass : activePasses) {
180 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
181 pass->renderPass(*
this);
187template<
typename Container>
190 const auto &rhiCtx = rci.rhiContext();
191 if (!rhiCtx->isValid())
194 const auto &bufferManager = rci.bufferManager();
196 for (
const auto &resource : resources) {
197 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
198 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
199 bufferManager->releaseGeometry(geometry);
200 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
201 auto model =
static_cast<QSSGRenderModel*>(resource);
202 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
203 delete model->particleBuffer;
204 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
205 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
206 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
207 bufferManager->releaseTextureData(textureData);
208 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderExtension) {
209 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
210 bufferManager->releaseExtensionResult(*rext);
211 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
212 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
213 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
214 rhiCtxD->releaseInstanceBuffer(table);
215 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
216 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
217 rci.renderer()->releaseItem2DData(*item2D);
226void QSSGRenderer::cleanupResources(QList<QSSGRenderGraphObject *> &resources)
228 cleanupResourcesImpl(*m_contextInterface, resources);
232void QSSGRenderer::cleanupResources(QSet<QSSGRenderGraphObject *> &resources)
234 cleanupResourcesImpl(*m_contextInterface, resources);
238QSSGLayerRenderData *QSSGRenderer::getOrCreateLayerRenderData(QSSGRenderLayer &layer)
240 if (layer.renderData ==
nullptr)
241 layer.renderData =
new QSSGLayerRenderData(layer, *
this);
243 return layer.renderData;
246void QSSGRenderer::addMaterialDirtyClear(QSSGRenderGraphObject *material)
248 m_materialClearDirty.insert(material);
254QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
255 QSSGShaderLibraryManager &shaderLibraryManager,
256 QSSGShaderCache &shaderCache,
257 QSSGProgramGenerator &shaderProgramGenerator,
258 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
259 const QSSGShaderFeatures &featureSet,
260 QByteArray &shaderString)
262 shaderString = logPrefix();
263 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
269 theKey.toString(shaderString, shaderKeyProperties);
275 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
276 return maybePipeline;
279 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
280 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
281 if (!pregenEntries.isEmpty()) {
282 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
283 if (foundIt != pregenEntries.cend())
284 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
288 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
289 return maybePipeline;
293 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
294 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
298 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(logPrefix(),
300 renderable.shaderDescription,
304 shaderLibraryManager,
308QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
309 QSSGSubsetRenderable &inRenderable,
310 const QSSGShaderFeatures &inFeatureSet)
312 auto *currentLayer = renderer.m_currentLayer;
313 auto &generatedShaderString = currentLayer->generatedShaderString;
314 const auto &m_contextInterface = renderer.m_contextInterface;
315 const auto &theCache = m_contextInterface->shaderCache();
316 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
317 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
318 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, generatedShaderString);
321void QSSGRenderer::beginFrame(QSSGRenderLayer &layer,
bool allowRecursion)
323 const bool executeBeginFrame = !(allowRecursion && (m_activeFrameRef++ != 0));
324 if (executeBeginFrame) {
325 m_contextInterface->perFrameAllocator()->reset();
326 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(&layer));
327 resetResourceCounters(&layer);
331bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
333 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
334 if (executeEndFrame) {
335 cleanupUnreferencedBuffers(&layer);
338 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
339 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
340 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
341 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
342 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
343 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
344 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
347 m_materialClearDirty.clear();
349 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
354 return executeEndFrame;
357QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
358 const QSSGRenderLayer &layer,
359 const QSSGRenderRay &ray)
361 const auto &bufferManager = ctx.bufferManager();
362 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
363 PickResultList pickResults;
364 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
365 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
367 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
368 return lhs.m_distanceSq < rhs.m_distanceSq;
373QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
374 const QSSGRenderLayer &layer,
375 const QSSGRenderRay &ray,
376 QSSGRenderNode *target)
378 const auto &bufferManager = ctx.bufferManager();
379 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
381 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
382 PickResultList pickResults;
384 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
386 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
388 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
389 return lhs.m_distanceSq < rhs.m_distanceSq;
394using RenderableList = QVarLengthArray<
const QSSGRenderNode *>;
395static void getPickableRecursive(
const QSSGRenderNode &node, RenderableList &renderables,
bool pickEverything =
false)
397 if (QSSGRenderGraphObject::isRenderable(node.type) && (pickEverything || node.getLocalState(QSSGRenderNode::LocalState::Pickable))) {
398 renderables.push_back(&node);
401 for (
const auto &child : node.children)
402 getPickableRecursive(child, renderables, pickEverything);
405std::optional<QSSGRenderPickResult> QSSGRendererPrivate::syncPickClosestPoint(
const QSSGRenderContextInterface &ctx,
406 const QSSGRenderLayer &layer,
407 const QVector3D ¢er,
const float radiusSquared,
408 QSSGRenderNode *target)
410 const auto &bufferManager = ctx.bufferManager();
412 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
413 std::optional<QSSGRenderPickResult> result = std::nullopt;
415 result = closestPointOnSubsetRenderable(layer, *bufferManager, center, radiusSquared, *target);
417 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
418 RenderableList renderables;
419 for (
const auto &childNode : layer.children)
420 getPickableRecursive(childNode, renderables, pickEverything);
421 float bestDistSquared = radiusSquared;
422 for (
const auto &childNode : renderables) {
423 const auto res = closestPointOnSubsetRenderable(layer, *bufferManager, center, bestDistSquared, *childNode);
424 if (res.has_value()) {
425 bestDistSquared = res.value().m_distanceSq;
434QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
435 QSSGBufferManager &bufferManager,
436 const QSSGRenderRay &ray,
437 QVarLengthArray<QSSGRenderNode*> subset)
439 QSSGRendererPrivate::PickResultList pickResults;
440 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
442 for (
auto target : subset)
443 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
445 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
446 return lhs.m_distanceSq < rhs.m_distanceSq;
451void QSSGRendererPrivate::setGlobalPickingEnabled(QSSGRenderer &renderer,
bool isEnabled)
453 renderer.m_globalPickingEnabled = isEnabled;
456void QSSGRendererPrivate::setRenderContextInterface(QSSGRenderer &renderer, QSSGRenderContextInterface *ctx)
458 renderer.m_contextInterface = ctx;
461void QSSGRendererPrivate::setSgRenderContext(QSSGRenderer &renderer, QSGRenderContext *sgRenderCtx)
463 renderer.m_qsgRenderContext = sgRenderCtx;
466QSGRenderContext *QSSGRendererPrivate::getSgRenderContext(
const QSSGRenderer &renderer)
468 return renderer.m_qsgRenderContext.data();
471const std::unique_ptr<QSSGRhiQuadRenderer> &QSSGRenderer::rhiQuadRenderer()
const
473 if (!m_rhiQuadRenderer)
474 m_rhiQuadRenderer = std::make_unique<QSSGRhiQuadRenderer>();
476 return m_rhiQuadRenderer;
479const std::unique_ptr<QSSGRhiCubeRenderer> &QSSGRenderer::rhiCubeRenderer()
const
481 if (!m_rhiCubeRenderer)
482 m_rhiCubeRenderer = std::make_unique<QSSGRhiCubeRenderer>();
484 return m_rhiCubeRenderer;
488void QSSGRenderer::beginSubLayerRender(QSSGLayerRenderData &inLayer)
490 inLayer.saveRenderState(*
this);
491 m_currentLayer =
nullptr;
494void QSSGRenderer::endSubLayerRender(QSSGLayerRenderData &inLayer)
496 inLayer.restoreRenderState(*
this);
497 m_currentLayer = &inLayer;
500void QSSGRenderer::beginLayerRender(QSSGLayerRenderData &inLayer)
502 m_currentLayer = &inLayer;
504void QSSGRenderer::endLayerRender()
506 m_currentLayer =
nullptr;
509static void dfs(
const QSSGRenderNode &node, RenderableList &renderables)
511 if (QSSGRenderGraphObject::isRenderable(node.type))
512 renderables.push_back(&node);
514 for (
const auto &child : node.children)
515 dfs(child, renderables);
518void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
519 QSSGBufferManager &bufferManager,
520 const QSSGRenderRay &ray,
521 bool inPickEverything,
522 PickResultList &outIntersectionResult)
524 RenderableList renderables;
525 for (
const auto &childNode : layer.children)
526 dfs(childNode, renderables);
528 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
529 const auto &pickableObject = renderables.at(idx);
530 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
531 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
537static inline QVector3D multiply(
const QMatrix3x3& M,
const QVector3D& v)
540 M(0,0) * v.x() + M(0,1) * v.y() + M(0,2) * v.z(),
541 M(1,0) * v.x() + M(1,1) * v.y() + M(1,2) * v.z(),
542 M(2,0) * v.x() + M(2,1) * v.y() + M(2,2) * v.z()
547static inline bool isUniformScaleMetric(
const QMatrix3x3& G,
float& s2,
float tolerance = 1e-5f) {
548 const float gxx = G(0,0), gyy = G(1,1), gzz = G(2,2);
549 const float gxy = G(0,1), gxz = G(0,2), gyz = G(1,2);
552 s2 = (gxx + gyy + gzz) / 3.0f;
555 const float scale = std::max({ std::fabs(gxx), std::fabs(gyy), std::fabs(gzz), 1.0f });
558 const bool offDiagOK = (std::fabs(gxy) <= tolerance * scale) &&
559 (std::fabs(gxz) <= tolerance * scale) &&
560 (std::fabs(gyz) <= tolerance * scale) &&
561 (std::fabs(G(1,0)) <= tolerance * scale) &&
562 (std::fabs(G(2,0)) <= tolerance * scale) &&
563 (std::fabs(G(2,1)) <= tolerance * scale);
565 const bool diagOK = (std::fabs(gxx - s2) <= tolerance * scale) &&
566 (std::fabs(gyy - s2) <= tolerance * scale) &&
567 (std::fabs(gzz - s2) <= tolerance * scale);
569 return offDiagOK && diagOK && (s2 >= 0.0f);
574 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
575 return QVector3D::dotProduct(u, v);
582 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
584 return QVector3D::dotProduct(u, multiply(G, v));
594static QVector3D closestPointOnTriangle(
const QVector3D &p,
599 float &u,
float &v,
float &w)
601 const QVector3D ab = b - a;
602 const QVector3D ac = c - a;
603 const QVector3D ap = p - a;
606 const float d1 = dot(ab, ap);
607 const float d2 = dot(ac, ap);
608 if (d1 <= 0.f && d2 <= 0.f) {
609 u = 1.0f; v = 0.0f; w = 0.0f;
614 const QVector3D bp = p - b;
615 const float d3 = dot(ab, bp);
616 const float d4 = dot(ac, bp);
617 if (d3 >= 0.f && d4 <= d3) {
618 u = 0.0f; v = 1.0f; w = 0.0f;
623 const float vc = d1 * d4 - d3 * d2;
624 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
625 const float v_edge = d1 / (d1 - d3);
626 u = 1.0f - v_edge; v = v_edge; w = 0.0f;
627 return a + v_edge * ab;
631 const QVector3D cp = p - c;
632 const float d5 = dot(ab, cp);
633 const float d6 = dot(ac, cp);
634 if (d6 >= 0.f && d5 <= d6) {
635 u = 0.0f; v = 0.0f; w = 1.0f;
640 const float vb = d5 * d2 - d1 * d6;
641 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
642 const float w_edge = d2 / (d2 - d6);
643 u = 1.0f - w_edge; v = 0.0f; w = w_edge;
644 return a + w_edge * ac;
648 const float va = d3 * d6 - d5 * d4;
649 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
650 const QVector3D bc = c - b;
651 const float w_edge = (d4 - d3) / ((d4 - d3) + (d5 - d6));
652 u = 0.0f; v = 1.0f - w_edge; w = w_edge;
653 return b + w_edge * bc;
657 const float denom = va + vb + vc;
660 if (
std::abs(denom) < 1e-20f) {
662 const float da = dot(ap, ap);
663 const float db = dot(bp, bp);
664 const float dc = dot(cp, cp);
665 if (da <= db && da <= dc) {
666 u = 1.0f; v = 0.0f; w = 0.0f;
670 u = 0.0f; v = 1.0f; w = 0.0f;
673 u = 0.0f; v = 0.0f; w = 1.0f;
677 const float invDenom = 1.0f / denom;
681 return a + v * ab + w * ac;
686 QMatrix4x4 globalTransform;
687 QMatrix3x3 pullbackMetric;
688 QVector3D centerLocal;
694static inline SphereData createSphereData(
const QMatrix4x4 &globalTransform,
695 const QVector3D ¢erWorld)
697 QMatrix4x4 inv = globalTransform.inverted();
700 const QVector3D centerLocal = QSSGUtils::mat44::transform(inv, centerWorld);
702 const QMatrix3x3 A = QSSGUtils::mat44::getUpper3x3(globalTransform);
703 const QMatrix3x3 G = A.transposed() * A;
705 return SphereData{ globalTransform, G, centerLocal };
709static inline float distanceSqPointTransformedAABB(
const QVector3D &localPoint,
710 const QSSGBounds3 &localAABB,
714 QVector3D closestLocal(
715 qBound(localAABB.minimum.x(), localPoint.x(), localAABB.maximum.x()),
716 qBound(localAABB.minimum.y(), localPoint.y(), localAABB.maximum.y()),
717 qBound(localAABB.minimum.z(), localPoint.z(), localAABB.maximum.z())
721 QVector3D localDiff = localPoint - closestLocal;
726 return dot(localDiff, localDiff);
729struct ClosestPointResult
732 float distSq =
std::numeric_limits<
float>::max();
733 QVector3D localPoint;
734 QVector3D scenePoint;
735 QVector3D faceNormal;
736 QVector3D sceneNormal;
739 int instanceIndex = -1;
742static void closestPointBVHLeafNode(
const SphereData &data,
743 const QSSGMeshBVHNode *node,
744 const QSSGRenderMesh *mesh,
747 ClosestPointResult &best)
749 const int begin = node->offset;
750 const int end = begin + node->count;
751 const auto &triangles = mesh->bvh->triangles();
754 float uniformScaleFactor = 1.0f;
755 const bool isUniformScale = isUniformScaleMetric(data.pullbackMetric, uniformScaleFactor);
756 const MetricDot metricDot { data.pullbackMetric };
758 for (
int i = begin; i < end; ++i) {
759 const auto &triangle = triangles[i];
762 const float triangleDistSq = distanceSqPointTransformedAABB(data.centerLocal, triangle.bounds, data.pullbackMetric);
763 if (triangleDistSq >= best.distSq)
768 QVector3D closestPoint;
770 if (isUniformScale) {
771 closestPoint = closestPointOnTriangle(data.centerLocal,
772 triangle.vertex1, triangle.vertex2, triangle.vertex3,
776 closestPoint = closestPointOnTriangle(data.centerLocal,
777 triangle.vertex1, triangle.vertex2, triangle.vertex3,
783 const QVector3D delta = data.centerLocal - closestPoint;
784 const float distSq = metricDot(delta, delta);
787 if (distSq < best.distSq) {
788 best.distSq = distSq;
789 best.localPoint = closestPoint;
790 best.scenePoint = QSSGUtils::mat44::transform(data.globalTransform, closestPoint);
791 best.subset = subset;
792 best.instanceIndex = instanceIndex;
797 best.uv = u * triangle.uvCoord1 + v * triangle.uvCoord2 + w * triangle.uvCoord3;
800 const QVector3D edge1 = triangle.vertex2 - triangle.vertex1;
801 const QVector3D edge2 = triangle.vertex3 - triangle.vertex1;
802 best.faceNormal = QVector3D::normal(edge1, edge2).normalized();
803 const QMatrix3x3 normalMatrix = data.globalTransform.normalMatrix();
804 best.sceneNormal = QSSGUtils::mat33::transform(normalMatrix, best.faceNormal);
809static void closestPointBVH(
const SphereData &data,
810 const QSSGMeshBVHNode *node,
811 const QSSGRenderMesh *mesh,
814 ClosestPointResult &best)
816 if (!node || !mesh || !mesh->bvh)
820 const float aabbDistSq = distanceSqPointTransformedAABB(data.centerLocal, node->boundingData, data.pullbackMetric);
821 if (aabbDistSq >= best.distSq)
825 if (node->count != 0) {
826 closestPointBVHLeafNode(data, node, mesh, subset, instanceIndex, best);
831 const auto *leftChild =
static_cast<
const QSSGMeshBVHNode *>(node->left);
832 const auto *rightChild =
static_cast<
const QSSGMeshBVHNode *>(node->right);
835 const float leftDistSq = leftChild
836 ? distanceSqPointTransformedAABB(data.centerLocal, leftChild->boundingData, data.pullbackMetric)
837 : std::numeric_limits<
float>::max();
838 const float rightDistSq = rightChild
839 ? distanceSqPointTransformedAABB(data.centerLocal, rightChild->boundingData, data.pullbackMetric)
840 : std::numeric_limits<
float>::max();
843 if (leftDistSq < rightDistSq) {
844 if (leftDistSq < best.distSq)
845 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
846 if (rightDistSq < best.distSq)
847 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
849 if (rightDistSq < best.distSq)
850 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
851 if (leftDistSq < best.distSq)
852 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
857std::optional<QSSGRenderPickResult>
858QSSGRendererPrivate::closestPointOnSubsetRenderable(
const QSSGRenderLayer& layer,
859 QSSGBufferManager& bufferManager,
860 const QVector3D& center,
861 const float radiusSquared,
862 const QSSGRenderNode& node)
864 if (!layer.renderData)
867 const auto *renderData = layer.renderData;
874 if (node.type != QSSGRenderGraphObject::Type::Model)
877 const auto &model =
static_cast<
const QSSGRenderModel &>(node);
885 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
887 auto mesh = bufferManager.getMeshForPicking(model);
892 QSSGBounds3 modelBounds;
893 for (
const auto &subset : mesh->subsets)
894 modelBounds.include(subset.bounds);
896 if (modelBounds.isEmpty())
899 const bool instancing = model.instancing();
900 int instanceCount = instancing ? model.instanceTable->count() : 1;
901 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
903 ClosestPointResult best;
904 best.distSq = radiusSquared;
906 for (
int i = 0; i < instanceCount; ++i) {
907 int instanceIndex = 0;
908 QMatrix4x4 modelTransform;
911 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
913 modelTransform = renderData->getGlobalTransform(model);
915 const SphereData data = createSphereData(modelTransform, center);
917 if (distanceSqPointTransformedAABB(data.centerLocal, modelBounds, data.pullbackMetric) > best.distSq)
920 for (
int subsetIndex = 0; subsetIndex < mesh->subsets.size(); ++subsetIndex) {
921 const auto &subset = mesh->subsets[subsetIndex];
924 if (distanceSqPointTransformedAABB(data.centerLocal, subset.bounds, data.pullbackMetric) >= best.distSq)
927 if (!subset.bvhRoot.isNull()) {
928 const auto *bvhRoot =
static_cast<
const QSSGMeshBVHNode *>(subset.bvhRoot);
929 closestPointBVH(data, bvhRoot, mesh, subsetIndex, instanceIndex, best);
934 return QSSGRenderPickResult{
950void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
951 QSSGBufferManager &bufferManager,
952 const QSSGRenderRay &inRay,
953 const QSSGRenderNode &node,
954 PickResultList &outIntersectionResultList)
956 if (!layer.renderData)
959 const auto *renderData = layer.renderData;
962 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
963 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
964 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
968 if (node.type != QSSGRenderGraphObject::Type::Model)
971 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
978 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
979 auto mesh = bufferManager.getMeshForPicking(model);
983 const auto &subMeshes = mesh->subsets;
984 QSSGBounds3 modelBounds;
985 for (
const auto &subMesh : subMeshes)
986 modelBounds.include(subMesh.bounds);
988 if (modelBounds.isEmpty())
991 const bool instancing = model.instancing();
992 int instanceCount = instancing ? model.instanceTable->count() : 1;
994 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
996 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
998 QMatrix4x4 modelTransform;
1000 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
1002 modelTransform = renderData->getGlobalTransform(model);
1004 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
1006 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
1009 if (!hit.intersects())
1013 float minRayLength = std::numeric_limits<
float>::max();
1014 QSSGRenderRay::IntersectionResult intersectionResult;
1015 QVector<QSSGRenderRay::IntersectionResult> results;
1018 int resultSubset = 0;
1019 for (
const auto &subMesh : subMeshes) {
1020 QSSGRenderRay::IntersectionResult result;
1021 if (!subMesh.bvhRoot.isNull()) {
1022 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
1023 if (hit.intersects()) {
1025 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results);
1026 float subMeshMinRayLength = std::numeric_limits<
float>::max();
1027 for (
const auto &subMeshResult : std::as_const(results)) {
1028 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
1029 result = subMeshResult;
1030 subMeshMinRayLength = result.rayLengthSquared;
1035 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
1036 if (hit.intersects())
1037 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
1039 if (result.intersects && result.rayLengthSquared < minRayLength) {
1040 intersectionResult = result;
1041 minRayLength = intersectionResult.rayLengthSquared;
1042 resultSubset = subset;
1047 if (intersectionResult.intersects)
1048 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
1049 intersectionResult.rayLengthSquared,
1050 intersectionResult.relXY,
1051 intersectionResult.scenePosition,
1052 intersectionResult.localPosition,
1053 intersectionResult.faceNormal,
1054 intersectionResult.sceneFaceNormal,
1061void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
1062 const QSSGRenderRay &inRay,
1063 const QSSGRenderItem2D &item2D,
1064 PickResultList &outIntersectionResultList)
1066 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
1069 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
1070 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
1072 const float d = QVector3D::dotProduct(inRay.direction, normal);
1073 float intersectionTime = 0;
1075 const QVector3D p0l0 = p0 - inRay.origin;
1076 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
1077 if (intersectionTime >= 0) {
1079 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
1080 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
1081 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
1082 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
1083 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
1084 intersectionTime * intersectionTime,
1087 localIntersectionPoint,
1088 -normal, -normal });
1093QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
1094 QSSGSubsetRenderable &inRenderable,
1095 const QSSGShaderFeatures &inFeatureSet)
1097 auto *m_currentLayer = renderer.m_currentLayer;
1098 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
1107 auto &shaderMap = m_currentLayer->shaderMap;
1109 QElapsedTimer timer;
1112 QSSGRhiShaderPipelinePtr shaderPipeline;
1116 QSSGShaderMapKey skey = QSSGShaderMapKey(QByteArray(),
1118 inRenderable.shaderDescription);
1119 auto it = shaderMap.find(skey);
1120 if (it == shaderMap.end()) {
1121 Q_TRACE_SCOPE(QSSG_generateShader);
1122 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1123 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet);
1124 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
1128 shaderMap.insert(skey, shaderPipeline);
1130 shaderPipeline = it.value();
1133 if (shaderPipeline !=
nullptr) {
1134 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1135 m_currentLayer->ensureCachedCameraDatas();
1138 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1139 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1141 return shaderPipeline;
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 logPrefix()