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"
30#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
31#include <QtQuick3DUtils/private/qssgdataref_p.h>
32#include <QtQuick3DUtils/private/qssgutils_p.h>
33#include <QtQuick3DUtils/private/qssgassert_p.h>
34#include <QtQuick3DUtils/private/qssgfrustum_p.h>
35#include <qtquick3d_tracepoints_p.h>
37#include <QtQuick/private/qsgcontext_p.h>
38#include <QtQuick/private/qsgrenderer_p.h>
40#include <QtCore/QMutexLocker>
41#include <QtCore/QBitArray>
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
76struct QSSGRenderableImage;
77class QSSGSubsetRenderable;
79void QSSGRenderer::releaseCachedResources()
81 m_rhiQuadRenderer.reset();
82 m_rhiCubeRenderer.reset();
85void QSSGRenderer::registerItem2DData(QSSGRenderItem2DData &data)
88 for (
const auto *item2DData : m_item2DDatas) {
89 if (item2DData == &data)
93 m_item2DDatas.push_back(&data);
96void QSSGRenderer::unregisterItem2DData(QSSGRenderItem2DData &data)
98 const auto foundIt = std::find(m_item2DDatas.begin(), m_item2DDatas.end(), &data);
99 if (foundIt != m_item2DDatas.end())
100 m_item2DDatas.erase(foundIt);
103void QSSGRenderer::releaseItem2DData(
const QSSGRenderItem2D &item2D)
105 for (
auto *item2DData : m_item2DDatas)
106 item2DData->releaseRenderData(item2D);
109QSSGRenderer::QSSGRenderer() =
default;
111QSSGRenderer::~QSSGRenderer()
113 m_contextInterface =
nullptr;
114 releaseCachedResources();
117void QSSGRenderer::cleanupUnreferencedBuffers(QSSGRenderLayer *inLayer)
120 m_contextInterface->bufferManager()->cleanupUnreferencedBuffers(m_frameCount, inLayer);
123void QSSGRenderer::resetResourceCounters(QSSGRenderLayer *inLayer)
125 m_contextInterface->bufferManager()->resetUsageCounters(m_frameCount, inLayer);
128bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
130 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
131 Q_ASSERT(theRenderData);
135 Q_ASSERT(inLayer.rootNode);
136 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
137 inLayer.rootNode->reindex();
139 beginLayerRender(*theRenderData);
140 theRenderData->resetForFrame();
141 theRenderData->prepareForRender();
143 return theRenderData->layerPrepResult.getFlags().wasDirty();
147void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
149 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
150 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
152 const auto layerPrepResult = theRenderData->layerPrepResult;
153 if (layerPrepResult.isLayerVisible()) {
155 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
156 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
157 beginLayerRender(*theRenderData);
158 theRenderData->maybeProcessLightmapBaking();
163 const auto &activePasses = theRenderData->activePasses;
164 for (
const auto &pass : activePasses) {
165 pass->renderPrep(*
this, *theRenderData);
166 if (pass->passType() == QSSGRenderPass::Type::Standalone)
167 pass->renderPass(*
this);
175void QSSGRenderer::rhiRender(QSSGRenderLayer &inLayer)
177 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
178 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
179 if (theRenderData->layerPrepResult.isLayerVisible()) {
180 beginLayerRender(*theRenderData);
181 const auto &activePasses = theRenderData->activePasses;
182 for (
const auto &pass : activePasses) {
183 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
184 pass->renderPass(*
this);
190template<
typename Container>
193 const auto &rhiCtx = rci.rhiContext();
194 if (!rhiCtx->isValid())
197 const auto &bufferManager = rci.bufferManager();
199 for (
const auto &resource : resources) {
200 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
201 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
202 bufferManager->releaseGeometry(geometry);
203 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
204 auto model =
static_cast<QSSGRenderModel*>(resource);
205 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
206 delete model->particleBuffer;
207 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
208 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
209 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
210 bufferManager->releaseTextureData(textureData);
211 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderExtension) {
212 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
213 bufferManager->releaseExtensionResult(*rext);
214 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
215 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
216 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
217 rhiCtxD->releaseInstanceBuffer(table);
218 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
219 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
220 rci.renderer()->releaseItem2DData(*item2D);
221 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderPass) {
222 auto *userPass =
static_cast<QSSGRenderUserPass *>(resource);
223 bufferManager->releaseUserRenderPass(*userPass);
232void QSSGRenderer::cleanupResources(QList<QSSGRenderGraphObject *> &resources)
234 cleanupResourcesImpl(*m_contextInterface, resources);
238void QSSGRenderer::cleanupResources(QSet<QSSGRenderGraphObject *> &resources)
240 cleanupResourcesImpl(*m_contextInterface, resources);
244QSSGLayerRenderData *QSSGRenderer::getOrCreateLayerRenderData(QSSGRenderLayer &layer)
246 if (layer.renderData ==
nullptr)
247 layer.renderData =
new QSSGLayerRenderData(layer, *
this);
249 return layer.renderData;
252void QSSGRenderer::addMaterialDirtyClear(QSSGRenderGraphObject *material)
254 m_materialClearDirty.insert(material);
260QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
261 QSSGShaderLibraryManager &shaderLibraryManager,
262 QSSGShaderCache &shaderCache,
263 QSSGProgramGenerator &shaderProgramGenerator,
264 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
265 const QSSGShaderFeatures &featureSet,
266 const QSSGUserShaderAugmentation &shaderAugmentation,
267 QByteArray &shaderString)
269 shaderString = rendererLogPrefix();
270 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
276 theKey.toString(shaderString, shaderKeyProperties);
279 for (
const auto &def : shaderAugmentation.defines)
280 shaderString.append(def.name).append(
';').append(def.value).append(
';');
281 shaderString.append(shaderAugmentation.preamble).append(
';');
282 shaderString.append(shaderAugmentation.body).append(
';');
288 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
289 return maybePipeline;
292 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
293 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
294 if (!pregenEntries.isEmpty()) {
295 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
296 if (foundIt != pregenEntries.cend())
297 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
301 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
302 return maybePipeline;
306 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
307 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
311 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(rendererLogPrefix(),
313 renderable.shaderDescription,
317 shaderLibraryManager,
322QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
323 QSSGSubsetRenderable &inRenderable,
324 const QSSGShaderFeatures &inFeatureSet,
325 const QSSGUserShaderAugmentation &shaderAugmentation = {})
327 auto *currentLayer = renderer.m_currentLayer;
328 auto &generatedShaderString = currentLayer->generatedShaderString;
329 const auto &m_contextInterface = renderer.m_contextInterface;
330 const auto &theCache = m_contextInterface->shaderCache();
331 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
332 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
333 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, shaderAugmentation, generatedShaderString);
336void QSSGRenderer::beginFrame(QSSGRenderLayer &layer,
bool allowRecursion)
338 const bool executeBeginFrame = !(allowRecursion && (m_activeFrameRef++ != 0));
339 if (executeBeginFrame) {
340 m_contextInterface->perFrameAllocator()->reset();
341 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(&layer));
342 resetResourceCounters(&layer);
346bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
348 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
349 if (executeEndFrame) {
350 cleanupUnreferencedBuffers(&layer);
353 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
354 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
355 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
356 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
357 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
358 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
359 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
362 m_materialClearDirty.clear();
364 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
369 return executeEndFrame;
372QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
373 const QSSGRenderLayer &layer,
374 const QSSGRenderRay &ray)
376 const auto &bufferManager = ctx.bufferManager();
377 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
378 PickResultList pickResults;
379 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
380 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
382 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
383 return lhs.m_distanceSq < rhs.m_distanceSq;
388QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
389 const QSSGRenderLayer &layer,
390 const QSSGRenderRay &ray,
391 QSSGRenderNode *target)
393 const auto &bufferManager = ctx.bufferManager();
394 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
396 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
397 PickResultList pickResults;
399 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
401 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
403 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
404 return lhs.m_distanceSq < rhs.m_distanceSq;
409using RenderableList = QVarLengthArray<
const QSSGRenderNode *>;
410static void getPickableRecursive(
const QSSGRenderNode &node, RenderableList &renderables,
bool pickEverything =
false)
412 if (QSSGRenderGraphObject::isRenderable(node.type) && (pickEverything || node.getLocalState(QSSGRenderNode::LocalState::Pickable))) {
413 renderables.push_back(&node);
416 for (
const auto &child : node.children)
417 getPickableRecursive(child, renderables, pickEverything);
420std::optional<QSSGRenderPickResult> QSSGRendererPrivate::syncPickClosestPoint(
const QSSGRenderContextInterface &ctx,
421 const QSSGRenderLayer &layer,
422 const QVector3D ¢er,
const float radiusSquared,
423 QSSGRenderNode *target)
425 const auto &bufferManager = ctx.bufferManager();
427 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
428 std::optional<QSSGRenderPickResult> result = std::nullopt;
430 result = closestPointOnSubsetRenderable(layer, *bufferManager, center, radiusSquared, *target);
432 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
433 RenderableList renderables;
434 for (
const auto &childNode : layer.children)
435 getPickableRecursive(childNode, renderables, pickEverything);
436 float bestDistSquared = radiusSquared;
437 for (
const auto &childNode : renderables) {
438 const auto res = closestPointOnSubsetRenderable(layer, *bufferManager, center, bestDistSquared, *childNode);
439 if (res.has_value()) {
440 bestDistSquared = res.value().m_distanceSq;
449QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
450 QSSGBufferManager &bufferManager,
451 const QSSGRenderRay &ray,
452 QVarLengthArray<QSSGRenderNode*> subset)
454 QSSGRendererPrivate::PickResultList pickResults;
455 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
457 for (
auto target : subset)
458 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
460 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
461 return lhs.m_distanceSq < rhs.m_distanceSq;
466void QSSGRendererPrivate::setGlobalPickingEnabled(QSSGRenderer &renderer,
bool isEnabled)
468 renderer.m_globalPickingEnabled = isEnabled;
471void QSSGRendererPrivate::setRenderContextInterface(QSSGRenderer &renderer, QSSGRenderContextInterface *ctx)
473 renderer.m_contextInterface = ctx;
476void QSSGRendererPrivate::setSgRenderContext(QSSGRenderer &renderer, QSGRenderContext *sgRenderCtx)
478 renderer.m_qsgRenderContext = sgRenderCtx;
481QSGRenderContext *QSSGRendererPrivate::getSgRenderContext(
const QSSGRenderer &renderer)
483 return renderer.m_qsgRenderContext.data();
486const std::unique_ptr<QSSGRhiQuadRenderer> &QSSGRenderer::rhiQuadRenderer()
const
488 if (!m_rhiQuadRenderer)
489 m_rhiQuadRenderer = std::make_unique<QSSGRhiQuadRenderer>();
491 return m_rhiQuadRenderer;
494const std::unique_ptr<QSSGRhiCubeRenderer> &QSSGRenderer::rhiCubeRenderer()
const
496 if (!m_rhiCubeRenderer)
497 m_rhiCubeRenderer = std::make_unique<QSSGRhiCubeRenderer>();
499 return m_rhiCubeRenderer;
503void QSSGRenderer::beginSubLayerRender(QSSGLayerRenderData &inLayer)
505 inLayer.saveRenderState(*
this);
506 m_currentLayer =
nullptr;
509void QSSGRenderer::endSubLayerRender(QSSGLayerRenderData &inLayer)
511 inLayer.restoreRenderState(*
this);
512 m_currentLayer = &inLayer;
515void QSSGRenderer::beginLayerRender(QSSGLayerRenderData &inLayer)
517 m_currentLayer = &inLayer;
519void QSSGRenderer::endLayerRender()
521 m_currentLayer =
nullptr;
524static void dfs(
const QSSGRenderNode &node, RenderableList &renderables)
526 if (QSSGRenderGraphObject::isRenderable(node.type))
527 renderables.push_back(&node);
529 for (
const auto &child : node.children)
530 dfs(child, renderables);
533void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
534 QSSGBufferManager &bufferManager,
535 const QSSGRenderRay &ray,
536 bool inPickEverything,
537 PickResultList &outIntersectionResult)
539 RenderableList renderables;
540 for (
const auto &childNode : layer.children)
541 dfs(childNode, renderables);
543 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
544 const auto &pickableObject = renderables.at(idx);
545 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
546 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
552static inline QVector3D multiply(
const QMatrix3x3& M,
const QVector3D& v)
555 M(0,0) * v.x() + M(0,1) * v.y() + M(0,2) * v.z(),
556 M(1,0) * v.x() + M(1,1) * v.y() + M(1,2) * v.z(),
557 M(2,0) * v.x() + M(2,1) * v.y() + M(2,2) * v.z()
562static inline bool isUniformScaleMetric(
const QMatrix3x3& G,
float& s2,
float tolerance = 1e-5f) {
563 const float gxx = G(0,0), gyy = G(1,1), gzz = G(2,2);
564 const float gxy = G(0,1), gxz = G(0,2), gyz = G(1,2);
567 s2 = (gxx + gyy + gzz) / 3.0f;
570 const float scale =
std::max({ std::fabs(gxx), std::fabs(gyy), std::fabs(gzz), 1.0f });
573 const bool offDiagOK = (std::fabs(gxy) <= tolerance * scale) &&
574 (std::fabs(gxz) <= tolerance * scale) &&
575 (std::fabs(gyz) <= tolerance * scale) &&
576 (std::fabs(G(1,0)) <= tolerance * scale) &&
577 (std::fabs(G(2,0)) <= tolerance * scale) &&
578 (std::fabs(G(2,1)) <= tolerance * scale);
580 const bool diagOK = (std::fabs(gxx - s2) <= tolerance * scale) &&
581 (std::fabs(gyy - s2) <= tolerance * scale) &&
582 (std::fabs(gzz - s2) <= tolerance * scale);
584 return offDiagOK && diagOK && (s2 >= 0.0f);
589 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
590 return QVector3D::dotProduct(u, v);
597 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
599 return QVector3D::dotProduct(u, multiply(G, v));
609static QVector3D closestPointOnTriangle(
const QVector3D &p,
614 float &u,
float &v,
float &w)
616 const QVector3D ab = b - a;
617 const QVector3D ac = c - a;
618 const QVector3D ap = p - a;
621 const float d1 = dot(ab, ap);
622 const float d2 = dot(ac, ap);
623 if (d1 <= 0.f && d2 <= 0.f) {
624 u = 1.0f; v = 0.0f; w = 0.0f;
629 const QVector3D bp = p - b;
630 const float d3 = dot(ab, bp);
631 const float d4 = dot(ac, bp);
632 if (d3 >= 0.f && d4 <= d3) {
633 u = 0.0f; v = 1.0f; w = 0.0f;
638 const float vc = d1 * d4 - d3 * d2;
639 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
640 const float v_edge = d1 / (d1 - d3);
641 u = 1.0f - v_edge; v = v_edge; w = 0.0f;
642 return a + v_edge * ab;
646 const QVector3D cp = p - c;
647 const float d5 = dot(ab, cp);
648 const float d6 = dot(ac, cp);
649 if (d6 >= 0.f && d5 <= d6) {
650 u = 0.0f; v = 0.0f; w = 1.0f;
655 const float vb = d5 * d2 - d1 * d6;
656 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
657 const float w_edge = d2 / (d2 - d6);
658 u = 1.0f - w_edge; v = 0.0f; w = w_edge;
659 return a + w_edge * ac;
663 const float va = d3 * d6 - d5 * d4;
664 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
665 const QVector3D bc = c - b;
666 const float w_edge = (d4 - d3) / ((d4 - d3) + (d5 - d6));
667 u = 0.0f; v = 1.0f - w_edge; w = w_edge;
668 return b + w_edge * bc;
672 const float denom = va + vb + vc;
675 if (
std::abs(denom) < 1e-20f) {
677 const float da = dot(ap, ap);
678 const float db = dot(bp, bp);
679 const float dc = dot(cp, cp);
680 if (da <= db && da <= dc) {
681 u = 1.0f; v = 0.0f; w = 0.0f;
685 u = 0.0f; v = 1.0f; w = 0.0f;
688 u = 0.0f; v = 0.0f; w = 1.0f;
692 const float invDenom = 1.0f / denom;
696 return a + v * ab + w * ac;
701 QMatrix4x4 globalTransform;
702 QMatrix3x3 pullbackMetric;
703 QVector3D centerLocal;
709static inline SphereData createSphereData(
const QMatrix4x4 &globalTransform,
710 const QVector3D ¢erWorld)
712 QMatrix4x4 inv = globalTransform.inverted();
715 const QVector3D centerLocal = QSSGUtils::mat44::transform(inv, centerWorld);
717 const QMatrix3x3 A = QSSGUtils::mat44::getUpper3x3(globalTransform);
718 const QMatrix3x3 G = A.transposed() * A;
720 return SphereData{ globalTransform, G, centerLocal };
724static inline float distanceSqPointTransformedAABB(
const QVector3D &localPoint,
725 const QSSGBounds3 &localAABB,
729 QVector3D closestLocal(
730 qBound(localAABB.minimum.x(), localPoint.x(), localAABB.maximum.x()),
731 qBound(localAABB.minimum.y(), localPoint.y(), localAABB.maximum.y()),
732 qBound(localAABB.minimum.z(), localPoint.z(), localAABB.maximum.z())
736 QVector3D localDiff = localPoint - closestLocal;
741 return dot(localDiff, localDiff);
744struct ClosestPointResult
747 float distSq =
std::numeric_limits<
float>::max();
748 QVector3D localPoint;
749 QVector3D scenePoint;
750 QVector3D faceNormal;
751 QVector3D sceneNormal;
754 int instanceIndex = -1;
757static void closestPointBVHLeafNode(
const SphereData &data,
758 const QSSGMeshBVHNode *node,
759 const QSSGRenderMesh *mesh,
762 ClosestPointResult &best)
764 const int begin = node->offset;
765 const int end = begin + node->count;
766 const auto &triangles = mesh->bvh->triangles();
769 float uniformScaleFactor = 1.0f;
770 const bool isUniformScale = isUniformScaleMetric(data.pullbackMetric, uniformScaleFactor);
771 const MetricDot metricDot { data.pullbackMetric };
773 for (
int i = begin; i < end; ++i) {
774 const auto &triangle = triangles[i];
777 const float triangleDistSq = distanceSqPointTransformedAABB(data.centerLocal, triangle.bounds, data.pullbackMetric);
778 if (triangleDistSq >= best.distSq)
783 QVector3D closestPoint;
785 if (isUniformScale) {
786 closestPoint = closestPointOnTriangle(data.centerLocal,
787 triangle.vertex1, triangle.vertex2, triangle.vertex3,
791 closestPoint = closestPointOnTriangle(data.centerLocal,
792 triangle.vertex1, triangle.vertex2, triangle.vertex3,
798 const QVector3D delta = data.centerLocal - closestPoint;
799 const float distSq = metricDot(delta, delta);
802 if (distSq < best.distSq) {
803 best.distSq = distSq;
804 best.localPoint = closestPoint;
805 best.scenePoint = QSSGUtils::mat44::transform(data.globalTransform, closestPoint);
806 best.subset = subset;
807 best.instanceIndex = instanceIndex;
812 best.uv = u * triangle.uvCoord1 + v * triangle.uvCoord2 + w * triangle.uvCoord3;
815 const QVector3D edge1 = triangle.vertex2 - triangle.vertex1;
816 const QVector3D edge2 = triangle.vertex3 - triangle.vertex1;
817 best.faceNormal = QVector3D::normal(edge1, edge2).normalized();
818 const QMatrix3x3 normalMatrix = data.globalTransform.normalMatrix();
819 best.sceneNormal = QSSGUtils::mat33::transform(normalMatrix, best.faceNormal);
824static void closestPointBVH(
const SphereData &data,
825 const QSSGMeshBVHNode *node,
826 const QSSGRenderMesh *mesh,
829 ClosestPointResult &best)
831 if (!node || !mesh || !mesh->bvh)
835 const float aabbDistSq = distanceSqPointTransformedAABB(data.centerLocal, node->boundingData, data.pullbackMetric);
836 if (aabbDistSq >= best.distSq)
840 if (node->count != 0) {
841 closestPointBVHLeafNode(data, node, mesh, subset, instanceIndex, best);
846 const auto *leftChild =
static_cast<
const QSSGMeshBVHNode *>(node->left);
847 const auto *rightChild =
static_cast<
const QSSGMeshBVHNode *>(node->right);
850 const float leftDistSq = leftChild
851 ? distanceSqPointTransformedAABB(data.centerLocal, leftChild->boundingData, data.pullbackMetric)
852 : std::numeric_limits<
float>::max();
853 const float rightDistSq = rightChild
854 ? distanceSqPointTransformedAABB(data.centerLocal, rightChild->boundingData, data.pullbackMetric)
855 : std::numeric_limits<
float>::max();
858 if (leftDistSq < rightDistSq) {
859 if (leftDistSq < best.distSq)
860 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
861 if (rightDistSq < best.distSq)
862 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
864 if (rightDistSq < best.distSq)
865 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
866 if (leftDistSq < best.distSq)
867 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
872std::optional<QSSGRenderPickResult>
873QSSGRendererPrivate::closestPointOnSubsetRenderable(
const QSSGRenderLayer& layer,
874 QSSGBufferManager& bufferManager,
875 const QVector3D& center,
876 const float radiusSquared,
877 const QSSGRenderNode& node)
879 if (!layer.renderData)
882 const auto *renderData = layer.renderData;
889 if (node.type != QSSGRenderGraphObject::Type::Model)
892 const auto &model =
static_cast<
const QSSGRenderModel &>(node);
900 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
902 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
907 QSSGBounds3 modelBounds;
908 for (
const auto &subset : std::as_const(mesh->subsets))
909 modelBounds.include(subset.bounds);
911 if (modelBounds.isEmpty())
914 const bool instancing = model.instancing();
915 int instanceCount = instancing ? model.instanceTable->count() : 1;
916 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
918 ClosestPointResult best;
919 best.distSq = radiusSquared;
921 for (
int i = 0; i < instanceCount; ++i) {
922 int instanceIndex = 0;
923 QMatrix4x4 modelTransform;
926 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
928 modelTransform = renderData->getGlobalTransform(model);
930 const SphereData data = createSphereData(modelTransform, center);
932 if (distanceSqPointTransformedAABB(data.centerLocal, modelBounds, data.pullbackMetric) > best.distSq)
935 for (
int subsetIndex = 0; subsetIndex < mesh->subsets.size(); ++subsetIndex) {
936 const auto &subset = mesh->subsets[subsetIndex];
939 if (distanceSqPointTransformedAABB(data.centerLocal, subset.bounds, data.pullbackMetric) >= best.distSq)
942 if (!subset.bvhRoot.isNull()) {
943 const auto *bvhRoot =
static_cast<
const QSSGMeshBVHNode *>(subset.bvhRoot);
944 closestPointBVH(data, bvhRoot, mesh, subsetIndex, instanceIndex, best);
949 return QSSGRenderPickResult{
965void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
966 QSSGBufferManager &bufferManager,
967 const QSSGRenderRay &inRay,
968 const QSSGRenderNode &node,
969 PickResultList &outIntersectionResultList)
971 if (!layer.renderData)
974 const auto *renderData = layer.renderData;
977 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
978 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
979 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
983 if (node.type != QSSGRenderGraphObject::Type::Model)
986 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
993 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
994 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
998 const auto &subMeshes = mesh->subsets;
999 QSSGBounds3 modelBounds;
1000 for (
const auto &subMesh : subMeshes)
1001 modelBounds.include(subMesh.bounds);
1003 if (modelBounds.isEmpty())
1006 const bool instancing = model.instancing();
1007 int instanceCount = instancing ? model.instanceTable->count() : 1;
1009 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
1011 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
1013 QMatrix4x4 modelTransform;
1015 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
1017 modelTransform = renderData->getGlobalTransform(model);
1019 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
1021 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
1024 if (!hit.intersects())
1028 float minRayLength = std::numeric_limits<
float>::max();
1029 QSSGRenderRay::IntersectionResult intersectionResult;
1030 QVector<QSSGRenderRay::IntersectionResult> results;
1033 int resultSubset = 0;
1034 for (
const auto &subMesh : subMeshes) {
1035 QSSGRenderRay::IntersectionResult result;
1036 if (!subMesh.bvhRoot.isNull()) {
1037 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
1038 if (hit.intersects()) {
1040 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results);
1041 float subMeshMinRayLength = std::numeric_limits<
float>::max();
1042 for (
const auto &subMeshResult : std::as_const(results)) {
1043 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
1044 result = subMeshResult;
1045 subMeshMinRayLength = result.rayLengthSquared;
1050 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
1051 if (hit.intersects())
1052 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
1054 if (result.intersects && result.rayLengthSquared < minRayLength) {
1055 intersectionResult = result;
1056 minRayLength = intersectionResult.rayLengthSquared;
1057 resultSubset = subset;
1062 if (intersectionResult.intersects)
1063 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
1064 intersectionResult.rayLengthSquared,
1065 intersectionResult.relXY,
1066 intersectionResult.scenePosition,
1067 intersectionResult.localPosition,
1068 intersectionResult.faceNormal,
1069 intersectionResult.sceneFaceNormal,
1076void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
1077 const QSSGRenderRay &inRay,
1078 const QSSGRenderItem2D &item2D,
1079 PickResultList &outIntersectionResultList)
1081 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
1084 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
1085 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
1087 const float d = QVector3D::dotProduct(inRay.direction, normal);
1088 float intersectionTime = 0;
1090 const QVector3D p0l0 = p0 - inRay.origin;
1091 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
1092 if (intersectionTime >= 0) {
1094 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
1095 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
1096 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
1097 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
1098 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
1099 intersectionTime * intersectionTime,
1102 localIntersectionPoint,
1103 -normal, -normal });
1108QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
1109 QSSGSubsetRenderable &inRenderable,
1110 const QSSGShaderFeatures &inFeatureSet,
1111 const QSSGUserShaderAugmentation &shaderAugmentation)
1113 auto *m_currentLayer = renderer.m_currentLayer;
1114 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
1123 auto &shaderMap = m_currentLayer->shaderMap;
1125 QElapsedTimer timer;
1128 QSSGRhiShaderPipelinePtr shaderPipeline;
1133 QByteArray name = shaderAugmentation.preamble + shaderAugmentation.body;
1134 for (
const auto &def : shaderAugmentation.defines)
1135 name.append(def.name).append(def.value);
1136 QSSGShaderMapKey skey = QSSGShaderMapKey(name,
1138 inRenderable.shaderDescription);
1139 auto it = shaderMap.find(skey);
1140 if (it == shaderMap.end()) {
1141 Q_TRACE_SCOPE(QSSG_generateShader);
1142 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1143 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet, shaderAugmentation);
1144 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
1148 shaderMap.insert(skey, shaderPipeline);
1150 shaderPipeline = it.value();
1153 if (shaderPipeline !=
nullptr) {
1154 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1155 m_currentLayer->ensureCachedCameraDatas();
1158 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1159 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1161 return shaderPipeline;
1164QList<
const QSSGRenderNode *> QSSGRendererPrivate::syncPickInFrustum(
const QSSGRenderContextInterface &ctx,
1165 const QSSGRenderLayer &layer,
1166 const QSSGFrustum &frustum)
1168 if (!layer.renderData)
1171 auto &bufferManager = ctx.bufferManager();
1172 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
1174 RenderableList nodes;
1175 for (
const auto &child : layer.children)
1176 getPickableRecursive(child, nodes, pickEverything);
1178 QList<
const QSSGRenderNode *> ret;
1179 for (
const auto node : nodes) {
1180 auto aabb = node->getBounds(*bufferManager);
1181 if (!aabb.isEmpty()) {
1182 const auto &transform = layer.renderData->getGlobalTransform(*node);
1183 aabb.transform(transform);
1184 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()