1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30
31
32 #include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
33 #include <QtQuick3DRuntimeRender/private/qssgrendererimpl_p.h>
34 #include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
35 #include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
36 #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
37 #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
38 #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
39 #include <QtQuick3DRuntimeRender/private/qssgrenderresourcemanager_p.h>
40 #include <QtQuick3DRuntimeRender/private/qssgrendereffectsystem_p.h>
41 #include <QtQuick3DRender/private/qssgrenderframebuffer_p.h>
42 #include <QtQuick3DRender/private/qssgrenderrenderbuffer_p.h>
43 #include <QtQuick3DRuntimeRender/private/qssgrenderresourcebufferobjects_p.h>
44 #include <QtQuick3DUtils/private/qssgperftimer_p.h>
45 #include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
46 #include <QtQuick3DRuntimeRender/private/qssgrendercustommaterialsystem_p.h>
47 #include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
48 #include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
49 #include <QtQuick3DUtils/private/qssgutils_p.h>
50 #include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
51
52 #ifdef Q_CC_MSVC
53 #pragma warning(disable : 4355)
54 #endif
55
56 QT_BEGIN_NAMESPACE
57
maybeQueueNodeForRender(QSSGRenderNode & inNode,QVector<QSSGRenderableNodeEntry> & outRenderables,QVector<QSSGRenderCamera * > & outCameras,QVector<QSSGRenderLight * > & outLights,quint32 & ioDFSIndex)58 static void maybeQueueNodeForRender(QSSGRenderNode &inNode,
59 QVector<QSSGRenderableNodeEntry> &outRenderables,
60 QVector<QSSGRenderCamera *> &outCameras,
61 QVector<QSSGRenderLight *> &outLights,
62 quint32 &ioDFSIndex)
63 {
64 ++ioDFSIndex;
65 inNode.dfsIndex = ioDFSIndex;
66 if (inNode.isRenderableType())
67 outRenderables.push_back(inNode);
68 else if (inNode.type == QSSGRenderGraphObject::Type::Camera)
69 outCameras.push_back(static_cast<QSSGRenderCamera *>(&inNode));
70 else if (inNode.type == QSSGRenderGraphObject::Type::Light)
71 outLights.push_back(static_cast<QSSGRenderLight *>(&inNode));
72
73 for (QSSGRenderNode *theChild = inNode.firstChild; theChild != nullptr; theChild = theChild->nextSibling)
74 maybeQueueNodeForRender(*theChild, outRenderables, outCameras, outLights, ioDFSIndex);
75 }
76
hasValidLightProbe(QSSGRenderImage * inLightProbeImage)77 static inline bool hasValidLightProbe(QSSGRenderImage *inLightProbeImage)
78 {
79 return inLightProbeImage && inLightProbeImage->m_textureData.m_texture;
80 }
81
QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inKey)82 QSSGDefaultMaterialPreparationResult::QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inKey)
83 : firstImage(nullptr), opacity(1.0f), materialKey(inKey), dirty(false)
84 {
85 }
86
87 #define MAX_AA_LEVELS 8
88
QSSGLayerRenderPreparationData(QSSGRenderLayer & inLayer,const QSSGRef<QSSGRendererImpl> & inRenderer)89 QSSGLayerRenderPreparationData::QSSGLayerRenderPreparationData(QSSGRenderLayer &inLayer,
90 const QSSGRef<QSSGRendererImpl> &inRenderer)
91 : layer(inLayer)
92 , renderer(inRenderer)
93 , camera(nullptr)
94 , featuresDirty(true)
95 , featureSetHash(0)
96 , tooManyLightsError(false)
97 {
98 }
99
100 QSSGLayerRenderPreparationData::~QSSGLayerRenderPreparationData() = default;
101
setShaderFeature(const char * theStr,bool inValue)102 void QSSGLayerRenderPreparationData::setShaderFeature(const char *theStr, bool inValue)
103 {
104 auto iter = features.cbegin();
105 const auto end = features.cend();
106
107 while (iter != end && iter->name != theStr)
108 ++iter;
109
110 if (iter != end) {
111 if (iter->enabled != inValue) {
112 iter->enabled = inValue;
113 featuresDirty = true;
114 featureSetHash = 0;
115 }
116 } else {
117 features.push_back(QSSGShaderPreprocessorFeature{theStr, inValue});
118 featuresDirty = true;
119 featureSetHash = 0;
120 }
121 }
122
getShaderFeatureSet()123 ShaderFeatureSetList QSSGLayerRenderPreparationData::getShaderFeatureSet()
124 {
125 if (featuresDirty) {
126 std::sort(features.begin(), features.end());
127 featuresDirty = false;
128 }
129 return features;
130 }
131
getShaderFeatureSetHash()132 size_t QSSGLayerRenderPreparationData::getShaderFeatureSetHash()
133 {
134 if (!featureSetHash)
135 featureSetHash = hashShaderFeatureSet(getShaderFeatureSet());
136 return featureSetHash;
137 }
138
createShadowMapManager()139 void QSSGLayerRenderPreparationData::createShadowMapManager()
140 {
141 shadowMapManager = QSSGRenderShadowMap::create(renderer->contextInterface());
142 }
143
getCameraDirection()144 QVector3D QSSGLayerRenderPreparationData::getCameraDirection()
145 {
146 if (!cameraDirection.hasValue()) {
147 if (camera)
148 cameraDirection = camera->getScalingCorrectDirection();
149 else
150 cameraDirection = QVector3D(0, 0, -1);
151 }
152 return *cameraDirection;
153 }
154
155 // Per-frame cache of renderable objects post-sort.
getOpaqueRenderableObjects(bool performSort)156 const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderPreparationData::getOpaqueRenderableObjects(bool performSort)
157 {
158 if (!renderedOpaqueObjects.empty() || camera == nullptr)
159 return renderedOpaqueObjects;
160 if (layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthTest) && !opaqueObjects.empty()) {
161 QVector3D theCameraDirection(getCameraDirection());
162 QVector3D theCameraPosition = camera->getGlobalPos();
163 renderedOpaqueObjects = opaqueObjects;
164 // Setup the object's sorting information
165 for (int idx = 0, end = renderedOpaqueObjects.size(); idx < end; ++idx) {
166 QSSGRenderableObjectHandle &theInfo = renderedOpaqueObjects[idx];
167 const QVector3D difference = theInfo.obj->worldCenterPoint - theCameraPosition;
168 theInfo.cameraDistanceSq = QVector3D::dotProduct(difference, theCameraDirection);
169 }
170
171 static const auto isRenderObjectPtrLessThan = [](const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) {
172 return lhs.cameraDistanceSq < rhs.cameraDistanceSq;
173 };
174 // Render nearest to furthest objects
175 if (performSort)
176 std::sort(renderedOpaqueObjects.begin(), renderedOpaqueObjects.end(), isRenderObjectPtrLessThan);
177 }
178 return renderedOpaqueObjects;
179 }
180
181 // If layer depth test is false, this may also contain opaque objects.
getTransparentRenderableObjects()182 const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderPreparationData::getTransparentRenderableObjects()
183 {
184 if (!renderedTransparentObjects.empty() || camera == nullptr)
185 return renderedTransparentObjects;
186
187 renderedTransparentObjects = transparentObjects;
188
189 if (!layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthTest))
190 renderedTransparentObjects.append(opaqueObjects);
191
192 if (!renderedTransparentObjects.empty()) {
193 QVector3D theCameraDirection(getCameraDirection());
194 QVector3D theCameraPosition = camera->getGlobalPos();
195
196 // Setup the object's sorting information
197 for (quint32 idx = 0, end = renderedTransparentObjects.size(); idx < end; ++idx) {
198 QSSGRenderableObjectHandle &theInfo = renderedTransparentObjects[idx];
199 const QVector3D difference = theInfo.obj->worldCenterPoint - theCameraPosition;
200 theInfo.cameraDistanceSq = QVector3D::dotProduct(difference, theCameraDirection);
201 }
202
203 static const auto iSRenderObjectPtrGreatThan = [](const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) {
204 return lhs.cameraDistanceSq > rhs.cameraDistanceSq;
205 };
206 // render furthest to nearest.
207 std::sort(renderedTransparentObjects.begin(), renderedTransparentObjects.end(), iSRenderObjectPtrGreatThan);
208 }
209
210 return renderedTransparentObjects;
211 }
212
getRenderableItem2Ds()213 const QVector<QSSGRenderableNodeEntry> &QSSGLayerRenderPreparationData::getRenderableItem2Ds()
214 {
215
216 if (!renderedItem2Ds.isEmpty() || camera == nullptr)
217 return renderedItem2Ds;
218
219 renderedItem2Ds = renderableItem2Ds;
220
221 const QVector3D cameraDirection(getCameraDirection());
222 const QVector3D cameraPosition = camera->getGlobalPos();
223
224 const auto isItemNodeDistanceGreatThan = [cameraDirection, cameraPosition]
225 (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) {
226 if (!lhs.node->parent || !rhs.node->parent)
227 return false;
228 const QVector3D lhsDifference = lhs.node->parent->position - cameraPosition;
229 const float lhsCameraDistanceSq = QVector3D::dotProduct(lhsDifference, cameraDirection);
230 const QVector3D rhsDifference = rhs.node->parent->position - cameraPosition;
231 const float rhsCameraDistanceSq = QVector3D::dotProduct(rhsDifference, cameraDirection);
232 return lhsCameraDistanceSq > rhsCameraDistanceSq;
233 };
234
235 const auto isItemZOrderLessThan = []
236 (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) {
237 if (lhs.node->parent && rhs.node->parent && lhs.node->parent == rhs.node->parent) {
238 // Same parent nodes, so sort with item z-ordering
239 QSSGRenderItem2D *lhsItem = static_cast<QSSGRenderItem2D *>(lhs.node);
240 QSSGRenderItem2D *rhsItem = static_cast<QSSGRenderItem2D *>(rhs.node);
241 return lhsItem->zOrder < rhsItem->zOrder;
242 }
243 return false;
244 };
245
246 // Render furthest to nearest items (parent nodes).
247 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemNodeDistanceGreatThan);
248 // Render items inside same node by item z-order.
249 // Note: stable_sort so item order in QML file is respected.
250 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemZOrderLessThan);
251
252 return renderedItem2Ds;
253 }
254
255 /**
256 * Usage: T *ptr = RENDER_FRAME_NEW<T>(context, arg0, arg1, ...); is equivalent to: T *ptr = new T(arg0, arg1, ...);
257 * so RENDER_FRAME_NEW() takes the RCI + T's arguments
258 */
259 template <typename T, typename... Args>
RENDER_FRAME_NEW(const QSSGRef<QSSGRenderContextInterface> & ctx,const Args &...args)260 inline T *RENDER_FRAME_NEW(const QSSGRef<QSSGRenderContextInterface> &ctx, const Args&... args)
261 {
262 return new (ctx->perFrameAllocator().allocate(sizeof(T)))T(const_cast<Args &>(args)...);
263 }
264
generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType,bool receivesShadows)265 QSSGShaderDefaultMaterialKey QSSGLayerRenderPreparationData::generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType, bool receivesShadows)
266 {
267 const uint features = uint(getShaderFeatureSetHash());
268 QSSGShaderDefaultMaterialKey theGeneratedKey(features);
269 const bool lighting = inLightingType != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
270 renderer->defaultMaterialShaderKeyProperties().m_hasLighting.setValue(theGeneratedKey, lighting);
271 if (lighting) {
272 const bool lightProbe = layer.lightProbe && layer.lightProbe->m_textureData.m_texture;
273 renderer->defaultMaterialShaderKeyProperties().m_hasIbl.setValue(theGeneratedKey, lightProbe);
274
275 quint32 numLights = quint32(globalLights.size());
276 if (Q_UNLIKELY(numLights > QSSGShaderDefaultMaterialKeyProperties::LightCount && !tooManyLightsError)) {
277 tooManyLightsError = true;
278 numLights = QSSGShaderDefaultMaterialKeyProperties::LightCount;
279 qCCritical(INVALID_OPERATION, "Too many lights on layer, max is %d", QSSGShaderDefaultMaterialKeyProperties::LightCount);
280 Q_ASSERT(false);
281 }
282 renderer->defaultMaterialShaderKeyProperties().m_lightCount.setValue(theGeneratedKey, numLights);
283
284 for (qint32 lightIdx = 0, lightEnd = globalLights.size(); lightIdx < lightEnd; ++lightIdx) {
285 QSSGRenderLight *theLight(globalLights[lightIdx]);
286 const bool isDirectional = theLight->m_lightType == QSSGRenderLight::Type::Directional;
287 const bool isArea = theLight->m_lightType == QSSGRenderLight::Type::Area;
288 const bool isSpot = theLight->m_lightType == QSSGRenderLight::Type::Spot;
289 const bool castShadowsArea = (theLight->m_lightType != QSSGRenderLight::Type::Area) && (theLight->m_castShadow) && receivesShadows;
290
291 renderer->defaultMaterialShaderKeyProperties().m_lightFlags[lightIdx].setValue(theGeneratedKey, !isDirectional);
292 renderer->defaultMaterialShaderKeyProperties().m_lightAreaFlags[lightIdx].setValue(theGeneratedKey, isArea);
293 renderer->defaultMaterialShaderKeyProperties().m_lightSpotFlags[lightIdx].setValue(theGeneratedKey, isSpot);
294 renderer->defaultMaterialShaderKeyProperties().m_lightShadowFlags[lightIdx].setValue(theGeneratedKey, castShadowsArea);
295 }
296 }
297 return theGeneratedKey;
298 }
299
prepareImageForRender(QSSGRenderImage & inImage,QSSGImageMapTypes inMapType,QSSGRenderableImage * & ioFirstImage,QSSGRenderableImage * & ioNextImage,QSSGRenderableObjectFlags & ioFlags,QSSGShaderDefaultMaterialKey & inShaderKey,quint32 inImageIndex,QSSGRenderDefaultMaterial * inMaterial)300 void QSSGLayerRenderPreparationData::prepareImageForRender(QSSGRenderImage &inImage,
301 QSSGImageMapTypes inMapType,
302 QSSGRenderableImage *&ioFirstImage,
303 QSSGRenderableImage *&ioNextImage,
304 QSSGRenderableObjectFlags &ioFlags,
305 QSSGShaderDefaultMaterialKey &inShaderKey,
306 quint32 inImageIndex,
307 QSSGRenderDefaultMaterial *inMaterial)
308 {
309 const QSSGRef<QSSGRenderContextInterface> &contextInterface(renderer->contextInterface());
310 const QSSGRef<QSSGBufferManager> &bufferManager = contextInterface->bufferManager();
311
312 if (inImage.clearDirty(bufferManager))
313 ioFlags |= QSSGRenderableObjectFlag::Dirty;
314
315 if (inImage.m_textureData.m_texture) {
316 if (inImage.m_textureData.m_textureFlags.hasTransparency()
317 && (inMapType == QSSGImageMapTypes::Diffuse || inMapType == QSSGImageMapTypes::Opacity
318 || inMapType == QSSGImageMapTypes::Translucency)) {
319 ioFlags |= QSSGRenderableObjectFlag::HasTransparency;
320 }
321 // Textures used in general have linear characteristics.
322 // PKC -- The filters are properly set already. Setting them here only overrides what
323 // would
324 // otherwise be a correct setting.
325 // inImage.m_TextureData.m_Texture->SetMinFilter( QSSGRenderTextureMinifyingOp::Linear );
326 // inImage.m_TextureData.m_Texture->SetMagFilter( QSSGRenderTextureMagnifyingOp::Linear );
327
328 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(renderer->contextInterface(), inMapType, inImage);
329 QSSGShaderKeyImageMap &theKeyProp = renderer->defaultMaterialShaderKeyProperties().m_imageMaps[inImageIndex];
330
331 theKeyProp.setEnabled(inShaderKey, true);
332 switch (inImage.m_mappingMode) {
333 default:
334 Q_ASSERT(false);
335 // fallthrough intentional
336 case QSSGRenderImage::MappingModes::Normal:
337 break;
338 case QSSGRenderImage::MappingModes::Environment:
339 theKeyProp.setEnvMap(inShaderKey, true);
340 break;
341 case QSSGRenderImage::MappingModes::LightProbe:
342 theKeyProp.setLightProbe(inShaderKey, true);
343 break;
344 }
345 bool hasA = false;
346 bool hasG = false;
347 bool hasB = false;
348 switch (inImage.m_textureData.m_texture->textureDetails().format.format) {
349 case QSSGRenderTextureFormat::RG8:
350 case QSSGRenderTextureFormat::RG16F:
351 case QSSGRenderTextureFormat::RG32F:
352 hasG = true;
353 break;
354 case QSSGRenderTextureFormat::RGB8:
355 case QSSGRenderTextureFormat::RGB16F:
356 case QSSGRenderTextureFormat::RGB32F:
357 hasG = true;
358 hasB = true;
359 break;
360 case QSSGRenderTextureFormat::Alpha8:
361 hasA = true;
362 break;
363 case QSSGRenderTextureFormat::LuminanceAlpha8:
364 hasA = true;
365 hasG = true;
366 break;
367 default:
368 hasA = true;
369 hasG = true;
370 hasB = true;
371 break;
372 }
373
374 if (inImage.m_textureData.m_textureFlags.isInvertUVCoords())
375 theKeyProp.setInvertUVMap(inShaderKey, true);
376
377 if (inImage.isImageTransformIdentity())
378 theKeyProp.setIdentityTransform(inShaderKey, true);
379
380 if (ioFirstImage == nullptr)
381 ioFirstImage = theImage;
382 else
383 ioNextImage->m_nextImage = theImage;
384
385 // assume offscreen renderer produces non-premultiplied image
386 if (inImage.m_textureData.m_textureFlags.isPreMultiplied())
387 theKeyProp.setPremultiplied(inShaderKey, true);
388
389 QSSGShaderKeyTextureSwizzle &theSwizzleKeyProp = renderer->defaultMaterialShaderKeyProperties().m_textureSwizzle[inImageIndex];
390 theSwizzleKeyProp.setSwizzleMode(inShaderKey, inImage.m_textureData.m_texture->textureSwizzleMode(), true);
391
392 ioNextImage = theImage;
393
394 if (inMaterial && inImageIndex >= QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst) {
395 QSSGRenderDefaultMaterial::TextureChannelMapping value = QSSGRenderDefaultMaterial::R;
396 QSSGRenderDefaultMaterial::TextureChannelMapping defaultValues[5] = {QSSGRenderDefaultMaterial::R, QSSGRenderDefaultMaterial::G, QSSGRenderDefaultMaterial::B, QSSGRenderDefaultMaterial::R, QSSGRenderDefaultMaterial::A};
397 if (inMaterial->type == QSSGRenderGraphObject::Type::DefaultMaterial)
398 defaultValues[1] = defaultValues[2] = QSSGRenderDefaultMaterial::R;
399
400 const quint32 scIndex = inImageIndex - QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst;
401 QSSGShaderKeyTextureChannel &channelKey = renderer->defaultMaterialShaderKeyProperties().m_textureChannels[scIndex];
402 switch (inImageIndex) {
403 case QSSGShaderDefaultMaterialKeyProperties::OpacityMap:
404 value = inMaterial->opacityChannel;
405 break;
406 case QSSGShaderDefaultMaterialKeyProperties::RoughnessMap:
407 value = inMaterial->roughnessChannel;
408 break;
409 case QSSGShaderDefaultMaterialKeyProperties::MetalnessMap:
410 value = inMaterial->metalnessChannel;
411 break;
412 case QSSGShaderDefaultMaterialKeyProperties::OcclusionMap:
413 value = inMaterial->occlusionChannel;
414 break;
415 case QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap:
416 value = inMaterial->translucencyChannel;
417 break;
418 default:
419 break;
420 }
421 bool useDefault = false;
422 switch (value) {
423 case QSSGRenderDefaultMaterial::TextureChannelMapping::G:
424 useDefault = !hasG;
425 break;
426 case QSSGRenderDefaultMaterial::TextureChannelMapping::B:
427 useDefault = !hasB;
428 break;
429 case QSSGRenderDefaultMaterial::TextureChannelMapping::A:
430 useDefault = !hasA;
431 break;
432 default:
433 break;
434 }
435 if (useDefault)
436 value = defaultValues[scIndex];
437 channelKey.setTextureChannel(QSSGShaderKeyTextureChannel::TexturChannelBits(value), inShaderKey);
438 }
439 }
440 }
441
setVertexInputPresence(const QSSGRenderableObjectFlags & renderableFlags,QSSGShaderDefaultMaterialKey & key)442 void QSSGLayerRenderPreparationData::setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags,
443 QSSGShaderDefaultMaterialKey &key)
444 {
445 quint32 vertexAttribs = 0;
446 if (renderableFlags.hasAttributePosition())
447 vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
448 if (renderableFlags.hasAttributeNormal())
449 vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
450 if (renderableFlags.hasAttributeTexCoord0())
451 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
452 if (renderableFlags.hasAttributeTexCoord1())
453 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
454 if (renderableFlags.hasAttributeTangent())
455 vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
456 if (renderableFlags.hasAttributeBinormal())
457 vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
458 if (renderableFlags.hasAttributeColor())
459 vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
460 renderer->defaultMaterialShaderKeyProperties().m_vertexAttributes.setValue(key, vertexAttribs);
461 }
462
prepareDefaultMaterialForRender(QSSGRenderDefaultMaterial & inMaterial,QSSGRenderableObjectFlags & inExistingFlags,float inOpacity)463 QSSGDefaultMaterialPreparationResult QSSGLayerRenderPreparationData::prepareDefaultMaterialForRender(
464 QSSGRenderDefaultMaterial &inMaterial,
465 QSSGRenderableObjectFlags &inExistingFlags,
466 float inOpacity)
467 {
468 QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
469 QSSGDefaultMaterialPreparationResult retval(generateLightingKey(theMaterial->lighting, inExistingFlags.receivesShadows()));
470 retval.renderableFlags = inExistingFlags;
471 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
472 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
473 retval.opacity = inOpacity;
474 float &subsetOpacity(retval.opacity);
475
476 if (theMaterial->dirty.isDirty()) {
477 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
478 }
479 subsetOpacity *= theMaterial->opacity;
480
481 QSSGRenderableImage *firstImage = nullptr;
482
483 // set wireframe mode
484 renderer->defaultMaterialShaderKeyProperties().m_wireframeMode.setValue(theGeneratedKey,
485 renderer->contextInterface()->wireframeMode());
486 // isDoubleSided
487 renderer->defaultMaterialShaderKeyProperties().m_isDoubleSided.setValue(theGeneratedKey, theMaterial->cullMode == QSSGCullFaceMode::Disabled);
488
489 // alpha Mode
490 renderer->defaultMaterialShaderKeyProperties().m_alphaMode.setValue(theGeneratedKey, theMaterial->alphaMode);
491
492 // vertex attribute presence flags
493 quint32 vertexAttribs = 0;
494 if (renderableFlags.hasAttributePosition())
495 vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
496 if (renderableFlags.hasAttributeNormal())
497 vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
498 if (renderableFlags.hasAttributeTexCoord0())
499 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
500 if (renderableFlags.hasAttributeTexCoord1())
501 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
502 if (renderableFlags.hasAttributeTangent())
503 vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
504 if (renderableFlags.hasAttributeBinormal())
505 vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
506 if (renderableFlags.hasAttributeColor())
507 vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
508 renderer->defaultMaterialShaderKeyProperties().m_vertexAttributes.setValue(theGeneratedKey, vertexAttribs);
509
510 if (theMaterial->iblProbe && checkLightProbeDirty(*theMaterial->iblProbe)) {
511 renderer->prepareImageForIbl(*theMaterial->iblProbe);
512 }
513
514 if (!renderer->defaultMaterialShaderKeyProperties().m_hasIbl.getValue(theGeneratedKey)) {
515 bool lightProbeValid = hasValidLightProbe(theMaterial->iblProbe);
516 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe), lightProbeValid);
517 renderer->defaultMaterialShaderKeyProperties().m_hasIbl.setValue(theGeneratedKey, lightProbeValid);
518 // setShaderFeature(ShaderFeatureDefines::enableIblFov(),
519 // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
520 }
521
522 if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
523
524 if (theMaterial->blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver ||
525 theMaterial->opacityMap ||
526 theMaterial->alphaMode == QSSGRenderDefaultMaterial::Blend ||
527 theMaterial->alphaMode == QSSGRenderDefaultMaterial::Mask) {
528 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
529 }
530
531 const bool specularEnabled = theMaterial->isSpecularEnabled();
532 const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
533 renderer->defaultMaterialShaderKeyProperties().m_specularEnabled.setValue(theGeneratedKey, (specularEnabled || metalnessEnabled));
534 if (specularEnabled || metalnessEnabled)
535 renderer->defaultMaterialShaderKeyProperties().m_specularModel.setSpecularModel(theGeneratedKey, theMaterial->specularModel);
536
537 renderer->defaultMaterialShaderKeyProperties().m_fresnelEnabled.setValue(theGeneratedKey, theMaterial->isFresnelEnabled());
538
539 renderer->defaultMaterialShaderKeyProperties().m_vertexColorsEnabled.setValue(theGeneratedKey,
540 theMaterial->isVertexColorsEnabled());
541
542 // Run through the material's images and prepare them for render.
543 // this may in fact set pickable on the renderable flags if one of the images
544 // links to a sub presentation or any offscreen rendered object.
545 QSSGRenderableImage *nextImage = nullptr;
546 #define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \
547 if ((img)) \
548 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \
549 theGeneratedKey, shadercomponent, &inMaterial)
550
551 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
552 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
553 QSSGImageMapTypes::BaseColor,
554 QSSGShaderDefaultMaterialKeyProperties::BaseColorMap);
555 CHECK_IMAGE_AND_PREPARE(theMaterial->metalnessMap,
556 QSSGImageMapTypes::Metalness,
557 QSSGShaderDefaultMaterialKeyProperties::MetalnessMap);
558 CHECK_IMAGE_AND_PREPARE(theMaterial->occlusionMap,
559 QSSGImageMapTypes::Occlusion,
560 QSSGShaderDefaultMaterialKeyProperties::OcclusionMap);
561 } else {
562 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
563 QSSGImageMapTypes::Diffuse,
564 QSSGShaderDefaultMaterialKeyProperties::DiffuseMap);
565 }
566 CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGImageMapTypes::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
567 CHECK_IMAGE_AND_PREPARE(theMaterial->specularReflection,
568 QSSGImageMapTypes::Specular,
569 QSSGShaderDefaultMaterialKeyProperties::SpecularMap);
570 CHECK_IMAGE_AND_PREPARE(theMaterial->roughnessMap,
571 QSSGImageMapTypes::Roughness,
572 QSSGShaderDefaultMaterialKeyProperties::RoughnessMap);
573 CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGImageMapTypes::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
574 CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGImageMapTypes::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
575 CHECK_IMAGE_AND_PREPARE(theMaterial->specularMap,
576 QSSGImageMapTypes::SpecularAmountMap,
577 QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap);
578 CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGImageMapTypes::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
579 CHECK_IMAGE_AND_PREPARE(theMaterial->displacementMap,
580 QSSGImageMapTypes::Displacement,
581 QSSGShaderDefaultMaterialKeyProperties::DisplacementMap);
582 CHECK_IMAGE_AND_PREPARE(theMaterial->translucencyMap,
583 QSSGImageMapTypes::Translucency,
584 QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap);
585 CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapIndirect,
586 QSSGImageMapTypes::LightmapIndirect,
587 QSSGShaderDefaultMaterialKeyProperties::LightmapIndirect);
588 CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapRadiosity,
589 QSSGImageMapTypes::LightmapRadiosity,
590 QSSGShaderDefaultMaterialKeyProperties::LightmapRadiosity);
591 CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapShadow,
592 QSSGImageMapTypes::LightmapShadow,
593 QSSGShaderDefaultMaterialKeyProperties::LightmapShadow);
594 }
595 #undef CHECK_IMAGE_AND_PREPARE
596
597 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
598 subsetOpacity = 0.0f;
599 // You can still pick against completely transparent objects(or rather their bounding
600 // box)
601 // you just don't render them.
602 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
603 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
604 }
605
606 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
607 subsetOpacity = 1.f;
608 else
609 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
610
611 retval.firstImage = firstImage;
612 if (retval.renderableFlags.isDirty())
613 retval.dirty = true;
614 if (retval.dirty)
615 renderer->addMaterialDirtyClear(&inMaterial);
616 return retval;
617 }
618
prepareCustomMaterialForRender(QSSGRenderCustomMaterial & inMaterial,QSSGRenderableObjectFlags & inExistingFlags,float inOpacity,bool alreadyDirty)619 QSSGDefaultMaterialPreparationResult QSSGLayerRenderPreparationData::prepareCustomMaterialForRender(QSSGRenderCustomMaterial &inMaterial,
620 QSSGRenderableObjectFlags &inExistingFlags,
621 float inOpacity, bool alreadyDirty)
622 {
623 QSSGDefaultMaterialPreparationResult retval(generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting::FragmentLighting, inExistingFlags.receivesShadows())); // always fragment lighting
624 retval.renderableFlags = inExistingFlags;
625 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
626 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
627 retval.opacity = inOpacity;
628 float &subsetOpacity(retval.opacity);
629
630 // set wireframe mode
631 renderer->defaultMaterialShaderKeyProperties().m_wireframeMode.setValue(theGeneratedKey,
632 renderer->contextInterface()->wireframeMode());
633
634 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
635 subsetOpacity = 0.0f;
636 // You can still pick against completely transparent objects(or rather their bounding
637 // box)
638 // you just don't render them.
639 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
640 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
641 }
642
643 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
644 subsetOpacity = 1.f;
645 else
646 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
647
648 QSSGRenderableImage *firstImage = nullptr;
649 QSSGRenderableImage *nextImage = nullptr;
650
651 #define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \
652 if ((img)) \
653 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \
654 theGeneratedKey, shadercomponent, nullptr)
655
656 CHECK_IMAGE_AND_PREPARE(inMaterial.m_displacementMap,
657 QSSGImageMapTypes::Displacement,
658 QSSGShaderDefaultMaterialKeyProperties::DisplacementMap);
659 CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapIndirect,
660 QSSGImageMapTypes::LightmapIndirect,
661 QSSGShaderDefaultMaterialKeyProperties::LightmapIndirect);
662 CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapRadiosity,
663 QSSGImageMapTypes::LightmapRadiosity,
664 QSSGShaderDefaultMaterialKeyProperties::LightmapRadiosity);
665 CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapShadow,
666 QSSGImageMapTypes::LightmapShadow,
667 QSSGShaderDefaultMaterialKeyProperties::LightmapShadow);
668 #undef CHECK_IMAGE_AND_PREPARE
669
670 retval.firstImage = firstImage;
671 if (retval.dirty || alreadyDirty)
672 renderer->addMaterialDirtyClear(&inMaterial);
673
674 // register the custom material shaders with the dynamic object system if they
675 // are not registered already
676 const QSSGRef<QSSGDynamicObjectSystem> &theDynamicSystem(renderer->contextInterface()->dynamicObjectSystem());
677 for (auto shaderPath : inMaterial.shaders.keys())
678 theDynamicSystem->setShaderData(shaderPath,
679 inMaterial.shaders[shaderPath],
680 inMaterial.shaderInfo.type,
681 inMaterial.shaderInfo.version,
682 false,
683 false);
684
685 return retval;
686 }
687
prepareModelForRender(QSSGRenderModel & inModel,const QMatrix4x4 & inViewProjection,const QSSGOption<QSSGClippingFrustum> & inClipFrustum,QSSGNodeLightEntryList & inScopedLights)688 bool QSSGLayerRenderPreparationData::prepareModelForRender(QSSGRenderModel &inModel,
689 const QMatrix4x4 &inViewProjection,
690 const QSSGOption<QSSGClippingFrustum> &inClipFrustum,
691 QSSGNodeLightEntryList &inScopedLights)
692 {
693 const QSSGRef<QSSGRenderContextInterface> &contextInterface(renderer->contextInterface());
694 const QSSGRef<QSSGBufferManager> &bufferManager = contextInterface->bufferManager();
695
696 QSSGRenderMesh *theMesh = nullptr;
697 // create custom mesh if set
698 if (inModel.meshPath.isNull() && inModel.geometry)
699 theMesh = inModel.geometry->createOrUpdate(bufferManager);
700 else
701 theMesh = bufferManager->loadMesh(inModel.meshPath);
702
703 if (theMesh == nullptr)
704 return false;
705
706 QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(renderer->contextInterface(), inModel, inViewProjection);
707 modelContexts.push_back(&theModelContext);
708
709 bool subsetDirty = false;
710
711 // Completely transparent models cannot be pickable. But models with completely
712 // transparent materials still are. This allows the artist to control pickability
713 // in a somewhat fine-grained style.
714 const bool canModelBePickable = (inModel.globalOpacity > QSSG_RENDER_MINIMUM_RENDER_OPACITY)
715 && (theModelContext.model.flags.testFlag(QSSGRenderModel::Flag::GloballyPickable));
716 if (canModelBePickable) {
717 // Check if there is BVH data, if not generate it
718 if (!theMesh->bvh && !inModel.meshPath.isNull()) {
719 theMesh->bvh = bufferManager->loadMeshBVH(inModel.meshPath);
720 if (theMesh->bvh) {
721 for (int i = 0; i < theMesh->bvh->roots.count(); ++i)
722 theMesh->subsets[i].bvhRoot = theMesh->bvh->roots.at(i);
723 }
724 }
725 }
726
727 const QSSGScopedLightsListScope lightsScope(globalLights, lightDirections, sourceLightDirections, inScopedLights);
728 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::CgLighting), !globalLights.empty());
729 for (int idx = 0; idx < theMesh->subsets.size(); ++idx) {
730 // If the materials list < size of subsets, then use the last material for the rest
731 QSSGRenderGraphObject *theSourceMaterialObject = nullptr;
732 if (inModel.materials.isEmpty())
733 break;
734 if (idx + 1 > inModel.materials.count())
735 theSourceMaterialObject = inModel.materials.last();
736 else
737 theSourceMaterialObject = inModel.materials.at(idx);
738 QSSGRenderSubset &theOuterSubset(theMesh->subsets[idx]);
739 {
740 QSSGRenderSubset &theSubset(theOuterSubset);
741 QSSGRenderableObjectFlags renderableFlags;
742 float subsetOpacity = inModel.globalOpacity;
743 QVector3D theModelCenter(theSubset.bounds.center());
744 theModelCenter = mat44::transform(inModel.globalTransform, theModelCenter);
745
746 if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY && inClipFrustum.hasValue()) {
747 // Check bounding box against the clipping planes
748 QSSGBounds3 theGlobalBounds = theSubset.bounds;
749 theGlobalBounds.transform(theModelContext.model.globalTransform);
750 if (!inClipFrustum->intersectsWith(theGlobalBounds))
751 subsetOpacity = 0.0f;
752 }
753
754 renderableFlags.setPickable(canModelBePickable);
755
756 // Casting and Receiving Shadows
757 renderableFlags.setCastsShadows(inModel.castsShadows);
758 renderableFlags.setReceivesShadows(inModel.receivesShadows);
759
760 for (const QByteArray &attr : qAsConst(theMesh->inputLayoutInputNames)) {
761 using namespace QSSGMeshUtilities;
762 if (attr == Mesh::getPositionAttrName())
763 renderableFlags.setHasAttributePosition(true);
764 else if (attr == Mesh::getNormalAttrName())
765 renderableFlags.setHasAttributeNormal(true);
766 else if (attr == Mesh::getUVAttrName())
767 renderableFlags.setHasAttributeTexCoord0(true);
768 else if (attr == Mesh::getUV2AttrName())
769 renderableFlags.setHasAttributeTexCoord1(true);
770 else if (attr == Mesh::getTexTanAttrName())
771 renderableFlags.setHasAttributeTangent(true);
772 else if (attr == Mesh::getTexBinormalAttrName())
773 renderableFlags.setHasAttributeBinormal(true);
774 else if (attr == Mesh::getColorAttrName())
775 renderableFlags.setHasAttributeColor(true);
776 }
777
778 QSSGRenderableObject *theRenderableObject = nullptr;
779 QSSGRenderGraphObject *theMaterialObject = theSourceMaterialObject;
780
781 if (theMaterialObject == nullptr)
782 continue;
783
784 // set tessellation
785 if (inModel.tessellationMode != TessellationModeValues::NoTessellation) {
786 theSubset.primitiveType = QSSGRenderDrawMode::Patches;
787 // set tessellation factor
788 theSubset.edgeTessFactor = inModel.edgeTessellation;
789 theSubset.innerTessFactor = inModel.innerTessellation;
790 // update the vertex ver patch count in the input assembler
791 // currently we only support triangle patches so count is always 3
792 theSubset.inputAssembler->setPatchVertexCount(3);
793 theSubset.inputAssemblerDepth->setPatchVertexCount(3);
794 // check wireframe mode
795 theSubset.wireframeMode = contextInterface->wireframeMode();
796
797 subsetDirty = subsetDirty | (theSubset.wireframeMode != inModel.wireframeMode);
798 inModel.wireframeMode = contextInterface->wireframeMode();
799 } else {
800 theSubset.primitiveType = theSubset.inputAssembler->drawMode();
801 theSubset.inputAssembler->setPatchVertexCount(1);
802 theSubset.inputAssemblerDepth->setPatchVertexCount(1);
803 // currently we allow wirframe mode only if tessellation is on
804 theSubset.wireframeMode = false;
805
806 subsetDirty = subsetDirty | (theSubset.wireframeMode != inModel.wireframeMode);
807 inModel.wireframeMode = false;
808 }
809
810 if (theMaterialObject == nullptr)
811 continue;
812
813 if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial || theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
814 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
815 // vertexColor should be supported in both DefaultMaterial and PrincipleMaterial
816 // if the mesh has it.
817 theMaterial.vertexColorsEnabled = renderableFlags.hasAttributeColor();
818 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
819 prepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity));
820 QSSGShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.materialKey;
821 subsetOpacity = theMaterialPrepResult.opacity;
822 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
823 subsetDirty |= theMaterialPrepResult.dirty;
824 renderableFlags = theMaterialPrepResult.renderableFlags;
825
826 renderer->defaultMaterialShaderKeyProperties().m_tessellationMode.setTessellationMode(theGeneratedKey,
827 inModel.tessellationMode,
828 true);
829
830 QSSGDataView<QMatrix4x4> boneGlobals;
831 if (theSubset.joints.size()) {
832 Q_ASSERT(false);
833 }
834
835 theRenderableObject = RENDER_FRAME_NEW<QSSGSubsetRenderable>(renderer->contextInterface(),
836 renderableFlags,
837 theModelCenter,
838 renderer,
839 theSubset,
840 theMaterial,
841 theModelContext,
842 subsetOpacity,
843 firstImage,
844 theGeneratedKey,
845 boneGlobals);
846 subsetDirty = subsetDirty || renderableFlags.isDirty();
847 } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
848 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
849
850 const QSSGRef<QSSGMaterialSystem> &theMaterialSystem(contextInterface->customMaterialSystem());
851 subsetDirty |= theMaterialSystem->prepareForRender(theModelContext.model, theSubset, theMaterial);
852
853 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
854 prepareCustomMaterialForRender(theMaterial, renderableFlags, subsetOpacity, subsetDirty));
855 QSSGShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.materialKey;
856 subsetOpacity = theMaterialPrepResult.opacity;
857 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
858 renderableFlags = theMaterialPrepResult.renderableFlags;
859
860 // prepare for render tells us if the object is transparent
861 if (theMaterial.m_hasTransparency)
862 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
863 // prepare for render tells us if the object is transparent
864 if (theMaterial.m_hasRefraction)
865 renderableFlags |= QSSGRenderableObjectFlag::HasRefraction;
866
867 renderer->defaultMaterialShaderKeyProperties().m_tessellationMode.setTessellationMode(theGeneratedKey,
868 inModel.tessellationMode,
869 true);
870
871 if (theMaterial.m_iblProbe && checkLightProbeDirty(*theMaterial.m_iblProbe)) {
872 renderer->prepareImageForIbl(*theMaterial.m_iblProbe);
873 }
874
875 theRenderableObject = RENDER_FRAME_NEW<QSSGCustomMaterialRenderable>(renderer->contextInterface(),
876 renderableFlags,
877 theModelCenter,
878 renderer,
879 theSubset,
880 theMaterial,
881 theModelContext,
882 subsetOpacity,
883 firstImage,
884 theGeneratedKey);
885 }
886 if (theRenderableObject) {
887 theRenderableObject->scopedLights = inScopedLights;
888 // set tessellation
889 theRenderableObject->tessellationMode = inModel.tessellationMode;
890
891 if (theRenderableObject->renderableFlags.hasTransparency() || theRenderableObject->renderableFlags.hasRefraction()) {
892 transparentObjects.push_back(QSSGRenderableObjectHandle::create(theRenderableObject));
893 } else {
894 opaqueObjects.push_back(QSSGRenderableObjectHandle::create(theRenderableObject));
895 }
896 }
897 }
898 }
899 return subsetDirty;
900 }
901
prepareRenderablesForRender(const QMatrix4x4 & inViewProjection,const QSSGOption<QSSGClippingFrustum> & inClipFrustum,QSSGLayerRenderPreparationResultFlags & ioFlags)902 bool QSSGLayerRenderPreparationData::prepareRenderablesForRender(const QMatrix4x4 &inViewProjection,
903 const QSSGOption<QSSGClippingFrustum> &inClipFrustum,
904 QSSGLayerRenderPreparationResultFlags &ioFlags)
905 {
906 Q_UNUSED(ioFlags)
907 QSSGStackPerfTimer perfTimer(renderer->contextInterface()->performanceTimer(), Q_FUNC_INFO);
908 viewProjection = inViewProjection;
909 bool wasDataDirty = false;
910 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
911 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
912 QSSGRenderNode *theNode = theNodeEntry.node;
913 wasDataDirty = wasDataDirty || theNode->flags.testFlag(QSSGRenderNode::Flag::Dirty);
914 switch (theNode->type) {
915 case QSSGRenderGraphObject::Type::Model: {
916 QSSGRenderModel *theModel = static_cast<QSSGRenderModel *>(theNode);
917 theModel->calculateGlobalVariables();
918 if (theModel->flags.testFlag(QSSGRenderModel::Flag::GloballyActive)) {
919 bool wasModelDirty = prepareModelForRender(*theModel, inViewProjection, inClipFrustum, theNodeEntry.lights);
920 wasDataDirty = wasDataDirty || wasModelDirty;
921 }
922 } break;
923 case QSSGRenderGraphObject::Type::Item2D: {
924 QSSGRenderItem2D *theItem2D = static_cast<QSSGRenderItem2D *>(theNode);
925 theItem2D->calculateGlobalVariables();
926 if (theItem2D->flags.testFlag(QSSGRenderModel::Flag::GloballyActive)) {
927 theItem2D->MVP = inViewProjection * theItem2D->globalTransform;
928 // Pushing front to keep item order inside QML file
929 renderableItem2Ds.push_front(theNodeEntry);
930 }
931 } break;
932 default:
933 Q_ASSERT(false);
934 break;
935 }
936 }
937 return wasDataDirty;
938 }
939
checkLightProbeDirty(QSSGRenderImage & inLightProbe)940 bool QSSGLayerRenderPreparationData::checkLightProbeDirty(QSSGRenderImage &inLightProbe)
941 {
942 const QSSGRef<QSSGRenderContextInterface> &theContext(renderer->contextInterface());
943 const QSSGRef<QSSGBufferManager> &bufferManager = theContext->bufferManager();
944 return inLightProbe.clearDirty(bufferManager, true);
945 }
946
947 struct QSSGLightNodeMarker
948 {
949 QSSGRenderLight *light = nullptr;
950 quint32 lightIndex = 0;
951 quint32 firstValidIndex = 0;
952 quint32 justPastLastValidIndex = 0;
953 bool addOrRemove = false;
954 QSSGLightNodeMarker() = default;
QSSGLightNodeMarkerQSSGLightNodeMarker955 QSSGLightNodeMarker(QSSGRenderLight &inLight, quint32 inLightIndex, QSSGRenderNode &inNode, bool aorm)
956 : light(&inLight), lightIndex(inLightIndex), addOrRemove(aorm)
957 {
958 if (inNode.type == QSSGRenderGraphObject::Type::Layer) {
959 firstValidIndex = 0;
960 justPastLastValidIndex = std::numeric_limits<quint32>::max();
961 } else {
962 firstValidIndex = inNode.dfsIndex;
963 QSSGRenderNode *lastChild = nullptr;
964 QSSGRenderNode *firstChild = inNode.firstChild;
965 // find deepest last child
966 while (firstChild) {
967 for (QSSGRenderNode *childNode = firstChild; childNode; childNode = childNode->nextSibling)
968 lastChild = childNode;
969
970 if (lastChild)
971 firstChild = lastChild->firstChild;
972 else
973 firstChild = nullptr;
974 }
975 if (lastChild)
976 // last valid index would be the last child index + 1
977 justPastLastValidIndex = lastChild->dfsIndex + 1;
978 else // no children.
979 justPastLastValidIndex = firstValidIndex + 1;
980 }
981 }
982 };
983
prepareForRender(const QSize & inViewportDimensions)984 void QSSGLayerRenderPreparationData::prepareForRender(const QSize &inViewportDimensions)
985 {
986 QSSGStackPerfTimer perfTimer(renderer->contextInterface()->performanceTimer(), Q_FUNC_INFO);
987 if (layerPrepResult.hasValue())
988 return;
989
990 features.clear();
991 featureSetHash = 0;
992 QVector2D thePresentationDimensions((float)inViewportDimensions.width(), (float)inViewportDimensions.height());
993 QRect theViewport(renderer->contextInterface()->viewport());
994 QRect theScissor(renderer->contextInterface()->scissorRect());
995 if (theScissor.isNull() || (theScissor == theViewport)) {
996 theScissor = theViewport;
997 renderer->contextInterface()->renderContext()->setScissorTestEnabled(false);
998 } else {
999 renderer->contextInterface()->renderContext()->setScissorTestEnabled(true);
1000 }
1001
1002 bool wasDirty = false;
1003 bool wasDataDirty = false;
1004 wasDirty = layer.flags.testFlag(QSSGRenderLayer::Flag::Dirty);
1005 // The first pass is just to render the data.
1006 quint32 maxNumAAPasses = layer.antialiasingMode == QSSGRenderLayer::AAMode::NoAA ? (quint32)0 : (quint32)(layer.antialiasingQuality) + 1;
1007 maxNumAAPasses = qMin((quint32)(MAX_AA_LEVELS + 1), maxNumAAPasses);
1008 QSSGRenderEffect *theLastEffect = nullptr;
1009 // Uncomment the line below to disable all progressive AA.
1010 // maxNumAAPasses = 0;
1011
1012 QSSGLayerRenderPreparationResult thePrepResult;
1013
1014 bool SSAOEnabled = (layer.aoStrength > 0.0f && layer.aoDistance > 0.0f);
1015 bool SSDOEnabled = (layer.shadowStrength > 0.0f && layer.shadowDist > 0.0f);
1016 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssao), SSAOEnabled);
1017 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssdo), SSDOEnabled);
1018 bool requiresDepthPrepass = (SSAOEnabled || SSDOEnabled);
1019 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssm), false); // by default no shadow map generation
1020
1021 if (layer.flags.testFlag(QSSGRenderLayer::Flag::Active)) {
1022 // Get the layer's width and height.
1023 for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
1024 if (theEffect->flags.testFlag(QSSGRenderEffect::Flag::Dirty)) {
1025 wasDirty = true;
1026 theEffect->flags.setFlag(QSSGRenderEffect::Flag::Dirty, false);
1027 }
1028 if (theEffect->flags.testFlag(QSSGRenderEffect::Flag::Active)) {
1029 theLastEffect = theEffect;
1030 if (theEffect->requiresDepthTexture)
1031 requiresDepthPrepass = true;
1032 }
1033 }
1034 if (layer.flags.testFlag(QSSGRenderLayer::Flag::Dirty)) {
1035 wasDirty = true;
1036 layer.calculateGlobalVariables();
1037 }
1038
1039 thePrepResult = QSSGLayerRenderPreparationResult(
1040 QSSGLayerRenderHelper(theViewport,
1041 theScissor,
1042 layer));
1043
1044 thePrepResult.lastEffect = theLastEffect;
1045 thePrepResult.maxAAPassIndex = maxNumAAPasses;
1046 thePrepResult.flags.setRequiresDepthTexture(requiresDepthPrepass);
1047 if (renderer->context()->renderContextType() != QSSGRenderContextType::GLES2)
1048 thePrepResult.flags.setRequiresSsaoPass(SSAOEnabled);
1049
1050 if (thePrepResult.isLayerVisible()) {
1051 if (layer.lightProbe && checkLightProbeDirty(*layer.lightProbe)) {
1052 renderer->prepareImageForIbl(*layer.lightProbe);
1053 wasDataDirty = true;
1054 }
1055
1056 bool lightProbeValid = hasValidLightProbe(layer.lightProbe);
1057
1058 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe), lightProbeValid);
1059 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::IblFov), layer.probeFov < 180.0f);
1060
1061 if (lightProbeValid && layer.lightProbe2 && checkLightProbeDirty(*layer.lightProbe2)) {
1062 renderer->prepareImageForIbl(*layer.lightProbe2);
1063 wasDataDirty = true;
1064 }
1065
1066 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe2), lightProbeValid && hasValidLightProbe(layer.lightProbe2));
1067
1068 // Push nodes in reverse depth first order
1069 // if (renderableNodes.empty()) {
1070 // camerasAndLights.clear();
1071 // quint32 dfsIndex = 0;
1072 // for (QSSGRenderNode *theChild = layer.firstChild; theChild; theChild = theChild->nextSibling)
1073 // MaybeQueueNodeForRender(*theChild, renderableNodes, camerasAndLights, dfsIndex);
1074 // std::reverse(camerasAndLights.begin(), camerasAndLights.end());
1075 // std::reverse(renderableNodes.begin(), renderableNodes.end());
1076 // lightToNodeMap.clear();
1077 // }
1078 // ### TODO: Really this should only be done if renderableNodes is empty or dirty
1079 // but we don't have a way to say it's dirty yet (new renderables added to the tree)
1080 cameras.clear();
1081 lights.clear();
1082 renderableNodes.clear();
1083 renderableItem2Ds.clear();
1084 quint32 dfsIndex = 0;
1085 for (QSSGRenderNode *theChild = layer.firstChild; theChild; theChild = theChild->nextSibling)
1086 maybeQueueNodeForRender(*theChild, renderableNodes, cameras, lights, dfsIndex);
1087 lightToNodeMap.clear();
1088
1089 globalLights.clear();
1090 for (const auto &oo : qAsConst(opaqueObjects))
1091 delete oo.obj;
1092 opaqueObjects.clear();
1093 for (const auto &to : qAsConst(transparentObjects))
1094 delete to.obj;
1095 transparentObjects.clear();
1096 QVector<QSSGLightNodeMarker> theLightNodeMarkers;
1097 sourceLightDirections.clear();
1098
1099 // Cameras
1100 // First, check the activeCamera is GloballyActive
1101 // and then if not, seek a GloballyActive one from the first
1102 camera = layer.activeCamera;
1103 if (camera != nullptr) {
1104 wasDataDirty = wasDataDirty
1105 || camera->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1106 QSSGCameraGlobalCalculationResult theResult = thePrepResult.setupCameraForRender(*camera);
1107 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1108 if (!theResult.m_computeFrustumSucceeded)
1109 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1110 if (!camera->flags.testFlag(QSSGRenderCamera::Flag::GloballyActive))
1111 camera = nullptr;
1112
1113 }
1114 for (auto iter = cameras.cbegin();
1115 (camera == nullptr) && (iter != cameras.cend()); iter++) {
1116 QSSGRenderCamera *theCamera = *iter;
1117 wasDataDirty = wasDataDirty
1118 || theCamera->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1119 QSSGCameraGlobalCalculationResult theResult = thePrepResult.setupCameraForRender(*theCamera);
1120 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1121 if (!theResult.m_computeFrustumSucceeded)
1122 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1123 if (theCamera->flags.testFlag(QSSGRenderCamera::Flag::GloballyActive))
1124 camera = theCamera;
1125 }
1126 layer.renderedCamera = camera;
1127
1128 // Lights
1129 for (auto rIt = lights.crbegin(); rIt != lights.crend(); rIt++) {
1130 QSSGRenderLight *theLight = *rIt;
1131 wasDataDirty = wasDataDirty || theLight->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1132 bool lightResult = theLight->calculateGlobalVariables();
1133 wasDataDirty = lightResult || wasDataDirty;
1134 // Note we setup the light index such that it is completely invariant of if
1135 // the
1136 // light is active or scoped.
1137 quint32 lightIndex = (quint32)sourceLightDirections.size();
1138 sourceLightDirections.push_back(QVector3D(0.0, 0.0, 0.0));
1139 // Note we still need a light check when building the renderable light list.
1140 // We also cannot cache shader-light bindings based on layers any more
1141 // because
1142 // the number of lights for a given renderable does not depend on the layer
1143 // as it used to but
1144 // additional perhaps on the light's scoping rules.
1145 if (theLight->flags.testFlag(QSSGRenderLight::Flag::GloballyActive)) {
1146 if (theLight->m_scope == nullptr) {
1147 globalLights.push_back(theLight);
1148 if (renderer->context()->renderContextType() != QSSGRenderContextType::GLES2
1149 && theLight->m_castShadow) {
1150 if (!shadowMapManager)
1151 createShadowMapManager();
1152
1153 // PKC -- use of "res" as an exponent of two is an annoying
1154 // artifact of the XML interface
1155 // I'll change this with an enum interface later on, but that's
1156 // less important right now.
1157 quint32 mapSize = 1 << theLight->m_shadowMapRes;
1158 ShadowMapModes mapMode = (theLight->m_lightType != QSSGRenderLight::Type::Directional)
1159 ? ShadowMapModes::CUBE
1160 : ShadowMapModes::VSM;
1161 shadowMapManager->addShadowMapEntry(globalLights.size() - 1,
1162 mapSize,
1163 mapSize,
1164 QSSGRenderTextureFormat::R16F,
1165 1,
1166 mapMode,
1167 ShadowFilterValues::NONE);
1168 thePrepResult.flags.setRequiresShadowMapPass(true);
1169 setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssm), true);
1170 }
1171 }
1172 TLightToNodeMap::iterator iter = lightToNodeMap.insert(theLight, (QSSGRenderNode *)nullptr);
1173 QSSGRenderNode *oldLightScope = iter.value();
1174 QSSGRenderNode *newLightScope = theLight->m_scope;
1175
1176 if (oldLightScope != newLightScope) {
1177 iter.value() = newLightScope;
1178 if (oldLightScope)
1179 theLightNodeMarkers.push_back(QSSGLightNodeMarker(*theLight, lightIndex, *oldLightScope, false));
1180 if (newLightScope)
1181 theLightNodeMarkers.push_back(QSSGLightNodeMarker(*theLight, lightIndex, *newLightScope, true));
1182 }
1183 if (newLightScope) {
1184 sourceLightDirections.back() = theLight->getScalingCorrectDirection();
1185 }
1186 }
1187 }
1188 if (!theLightNodeMarkers.empty()) {
1189 for (auto rIt = renderableNodes.rbegin();
1190 rIt != renderableNodes.rend(); rIt++) {
1191 QSSGRenderableNodeEntry &theNodeEntry(*rIt);
1192 quint32 nodeDFSIndex = theNodeEntry.node->dfsIndex;
1193 for (quint32 markerIdx = 0, markerEnd = theLightNodeMarkers.size(); markerIdx < markerEnd; ++markerIdx) {
1194 QSSGLightNodeMarker &theMarker = theLightNodeMarkers[markerIdx];
1195 if (nodeDFSIndex >= theMarker.firstValidIndex && nodeDFSIndex < theMarker.justPastLastValidIndex) {
1196 if (theMarker.addOrRemove) {
1197 QSSGNodeLightEntry *theNewEntry = new QSSGNodeLightEntry(theMarker.light, theMarker.lightIndex);
1198 theNodeEntry.lights.push_back(*theNewEntry);
1199 } else {
1200 for (QSSGNodeLightEntryList::iterator lightIter = theNodeEntry.lights.begin(),
1201 lightEnd = theNodeEntry.lights.end();
1202 lightIter != lightEnd;
1203 ++lightIter) {
1204 if (lightIter->light == theMarker.light) {
1205 QSSGNodeLightEntry &theEntry = *lightIter;
1206 theNodeEntry.lights.remove(theEntry);
1207 delete &theEntry;
1208 break;
1209 }
1210 }
1211 }
1212 }
1213 }
1214 }
1215 }
1216
1217 if (camera) {
1218 camera->calculateViewProjectionMatrix(viewProjection);
1219 if (camera->enableFrustumClipping) {
1220 QSSGClipPlane nearPlane;
1221 QMatrix3x3 theUpper33(camera->globalTransform.normalMatrix());
1222
1223 QVector3D dir(mat33::transform(theUpper33, QVector3D(0, 0, -1)));
1224 dir.normalize();
1225 nearPlane.normal = dir;
1226 QVector3D theGlobalPos = camera->getGlobalPos() + camera->clipNear * dir;
1227 nearPlane.d = -(QVector3D::dotProduct(dir, theGlobalPos));
1228 // the near plane's bbox edges are calculated in the clipping frustum's
1229 // constructor.
1230 clippingFrustum = QSSGClippingFrustum(viewProjection, nearPlane);
1231 } else if (clippingFrustum.hasValue()) {
1232 clippingFrustum.setEmpty();
1233 }
1234 } else
1235 viewProjection = QMatrix4x4();
1236
1237 // Setup the light directions here.
1238
1239 for (qint32 lightIdx = 0, lightEnd = globalLights.size(); lightIdx < lightEnd; ++lightIdx) {
1240 lightDirections.push_back(globalLights.at(lightIdx)->getScalingCorrectDirection());
1241 }
1242
1243 modelContexts.clear();
1244
1245 bool renderablesDirty = prepareRenderablesForRender(viewProjection,
1246 clippingFrustum,
1247 thePrepResult.flags);
1248 wasDataDirty = wasDataDirty || renderablesDirty;
1249 }
1250 }
1251 wasDirty = wasDirty || wasDataDirty;
1252 thePrepResult.flags.setWasDirty(wasDirty);
1253 thePrepResult.flags.setLayerDataDirty(wasDataDirty);
1254
1255 layerPrepResult = thePrepResult;
1256
1257 // Per-frame cache of renderable objects post-sort.
1258 getOpaqueRenderableObjects();
1259 // If layer depth test is false, this may also contain opaque objects.
1260 getTransparentRenderableObjects();
1261
1262 getCameraDirection();
1263 }
1264
resetForFrame()1265 void QSSGLayerRenderPreparationData::resetForFrame()
1266 {
1267 transparentObjects.clear();
1268 opaqueObjects.clear();
1269 layerPrepResult.setEmpty();
1270 // The check for if the camera is or is not null is used
1271 // to figure out if this layer was rendered at all.
1272 camera = nullptr;
1273 cameraDirection.setEmpty();
1274 lightDirections.clear();
1275 renderedOpaqueObjects.clear();
1276 renderedTransparentObjects.clear();
1277 renderedItem2Ds.clear();
1278 }
1279
1280 QT_END_NAMESPACE
1281