1 | // Copyright (C) 2008-2012 NVIDIA Corporation. |
2 | // Copyright (C) 2019 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
4 | |
5 | #ifndef QSSG_RENDER_IMPL_RENDERABLE_OBJECTS_H |
6 | #define QSSG_RENDER_IMPL_RENDERABLE_OBJECTS_H |
7 | |
8 | // |
9 | // W A R N I N G |
10 | // ------------- |
11 | // |
12 | // This file is not part of the Qt API. It exists purely as an |
13 | // implementation detail. This header file may change from version to |
14 | // version without notice, or even be removed. |
15 | // |
16 | // We mean it. |
17 | // |
18 | |
19 | #include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h> |
20 | #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterial_p.h> |
21 | #include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h> |
22 | #include <QtQuick3DRuntimeRender/private/qssgrenderparticles_p.h> |
23 | #include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h> |
24 | #include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h> |
25 | #include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h> |
26 | #include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h> |
27 | #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h> |
28 | #include <QtQuick3DRuntimeRender/private/qssgrenderreflectionprobe_p.h> |
29 | |
30 | #include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h> |
31 | |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | enum class QSSGRenderableObjectFlag : quint32 |
35 | { |
36 | HasTransparency = 1 << 0, |
37 | CompletelyTransparent = 1 << 1, |
38 | Dirty = 1 << 2, |
39 | CastsShadows = 1 << 3, |
40 | ReceivesShadows = 1 << 4, |
41 | HasAttributePosition = 1 << 5, |
42 | HasAttributeNormal = 1 << 6, |
43 | HasAttributeTexCoord0 = 1 << 7, |
44 | HasAttributeTexCoord1 = 1 << 8, |
45 | HasAttributeTangent = 1 << 9, |
46 | HasAttributeBinormal = 1 << 10, |
47 | HasAttributeColor = 1 << 11, |
48 | HasAttributeJointAndWeight = 1 << 12, |
49 | IsPointsTopology = 1 << 13, |
50 | // The number of target models' attributes are too many |
51 | // to store in a renderable flag. |
52 | // They will be recorded in shaderKey. |
53 | HasAttributeMorphTarget = 1 << 14, |
54 | RequiresScreenTexture = 1 << 15, |
55 | ReceivesReflections = 1 << 16, |
56 | UsedInBakedLighting = 1 << 17, |
57 | RendersWithLightmap = 1 << 18, |
58 | HasAttributeTexCoordLightmap = 1 << 19, |
59 | CastsReflections = 1 << 20 |
60 | }; |
61 | |
62 | struct QSSGRenderableObjectFlags : public QFlags<QSSGRenderableObjectFlag> |
63 | { |
64 | void setHasTransparency(bool inHasTransparency) |
65 | { |
66 | setFlag(flag: QSSGRenderableObjectFlag::HasTransparency, on: inHasTransparency); |
67 | } |
68 | bool hasTransparency() const { return this->operator&(other: QSSGRenderableObjectFlag::HasTransparency); } |
69 | void setCompletelyTransparent(bool inTransparent) |
70 | { |
71 | setFlag(flag: QSSGRenderableObjectFlag::CompletelyTransparent, on: inTransparent); |
72 | } |
73 | bool isCompletelyTransparent() const |
74 | { |
75 | return this->operator&(other: QSSGRenderableObjectFlag::CompletelyTransparent); |
76 | } |
77 | void setDirty(bool inDirty) { setFlag(flag: QSSGRenderableObjectFlag::Dirty, on: inDirty); } |
78 | bool isDirty() const { return this->operator&(other: QSSGRenderableObjectFlag::Dirty); } |
79 | |
80 | void setCastsShadows(bool inCastsShadows) { setFlag(flag: QSSGRenderableObjectFlag::CastsShadows, on: inCastsShadows); } |
81 | bool castsShadows() const { return this->operator&(other: QSSGRenderableObjectFlag::CastsShadows); } |
82 | |
83 | void setReceivesShadows(bool inReceivesShadows) { setFlag(flag: QSSGRenderableObjectFlag::ReceivesShadows, on: inReceivesShadows); } |
84 | bool receivesShadows() const { return this->operator&(other: QSSGRenderableObjectFlag::ReceivesShadows); } |
85 | |
86 | void setReceivesReflections(bool inReceivesReflections) { setFlag(flag: QSSGRenderableObjectFlag::ReceivesReflections, on: inReceivesReflections); } |
87 | bool receivesReflections() const { return this->operator&(other: QSSGRenderableObjectFlag::ReceivesReflections); } |
88 | |
89 | void setCastsReflections(bool inCastsReflections) { setFlag(flag: QSSGRenderableObjectFlag::CastsReflections, on: inCastsReflections); } |
90 | bool castsReflections() const { return this->operator&(other: QSSGRenderableObjectFlag::CastsReflections); } |
91 | |
92 | void setUsedInBakedLighting(bool inUsedInBakedLighting) { setFlag(flag: QSSGRenderableObjectFlag::UsedInBakedLighting, on: inUsedInBakedLighting); } |
93 | bool usedInBakedLighting() const { return this->operator&(other: QSSGRenderableObjectFlag::UsedInBakedLighting); } |
94 | |
95 | void setRendersWithLightmap(bool inRendersWithLightmap) { setFlag(flag: QSSGRenderableObjectFlag::RendersWithLightmap, on: inRendersWithLightmap); } |
96 | bool rendersWithLightmap() const { return this->operator&(other: QSSGRenderableObjectFlag::RendersWithLightmap); } |
97 | |
98 | void setHasAttributePosition(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributePosition, on: b); } |
99 | bool hasAttributePosition() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributePosition); } |
100 | |
101 | void setHasAttributeNormal(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeNormal, on: b); } |
102 | bool hasAttributeNormal() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeNormal); } |
103 | |
104 | void setHasAttributeTexCoord0(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeTexCoord0, on: b); } |
105 | bool hasAttributeTexCoord0() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeTexCoord0); } |
106 | |
107 | void setHasAttributeTexCoord1(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeTexCoord1, on: b); } |
108 | bool hasAttributeTexCoord1() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeTexCoord1); } |
109 | |
110 | void setHasAttributeTexCoordLightmap(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeTexCoordLightmap, on: b); } |
111 | bool hasAttributeTexCoordLightmap() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeTexCoordLightmap); } |
112 | |
113 | void setHasAttributeTangent(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeTangent, on: b); } |
114 | bool hasAttributeTangent() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeTangent); } |
115 | |
116 | void setHasAttributeBinormal(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeBinormal, on: b); } |
117 | bool hasAttributeBinormal() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeBinormal); } |
118 | |
119 | void setHasAttributeColor(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeColor, on: b); } |
120 | bool hasAttributeColor() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeColor); } |
121 | |
122 | void setHasAttributeJointAndWeight(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeJointAndWeight, on: b); |
123 | } |
124 | bool hasAttributeJointAndWeight() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeJointAndWeight); } |
125 | |
126 | void setHasAttributeMorphTarget(bool b) { setFlag(flag: QSSGRenderableObjectFlag::HasAttributeMorphTarget, on: b); |
127 | } |
128 | bool hasAttributeMorphTarget() const { return this->operator&(other: QSSGRenderableObjectFlag::HasAttributeMorphTarget); } |
129 | |
130 | void setPointsTopology(bool v) |
131 | { |
132 | setFlag(flag: QSSGRenderableObjectFlag::IsPointsTopology, on: v); |
133 | } |
134 | bool isPointsTopology() const |
135 | { |
136 | return this->operator&(other: QSSGRenderableObjectFlag::IsPointsTopology); |
137 | } |
138 | void setRequiresScreenTexture(bool v) |
139 | { |
140 | setFlag(flag: QSSGRenderableObjectFlag::RequiresScreenTexture, on: v); |
141 | } |
142 | bool requiresScreenTexture() const { |
143 | return this->operator&(other: QSSGRenderableObjectFlag::RequiresScreenTexture); |
144 | } |
145 | }; |
146 | |
147 | struct QSSGShaderLight |
148 | { |
149 | QSSGRenderLight *light = nullptr; |
150 | bool shadows = false; |
151 | QVector3D direction; |
152 | |
153 | inline bool operator < (const QSSGShaderLight &o) const |
154 | { |
155 | // sort by light type |
156 | if (light->type < o.light->type) |
157 | return true; |
158 | // then shadow lights first |
159 | if (shadows > o.shadows) |
160 | return true; |
161 | return false; |
162 | } |
163 | }; |
164 | |
165 | struct QSSGShaderReflectionProbe |
166 | { |
167 | QVector3D probeCubeMapCenter; |
168 | QVector3D probeBoxMax; |
169 | QVector3D probeBoxMin; |
170 | bool enabled = false; |
171 | int parallaxCorrection = 0; |
172 | }; |
173 | |
174 | // Having this as a QVLA is beneficial mainly because QVector would need to |
175 | // detach somewhere in QSSGLayerRenderPreparationData::prepareForRender so the |
176 | // implicit sharing's benefits do not outweigh the cost of allocations in this case. |
177 | typedef QVarLengthArray<QSSGShaderLight, 16> QSSGShaderLightList; |
178 | using QSSGShaderLightListView = QSSGDataView<QSSGShaderLight>; |
179 | |
180 | using QSSGMaterialListView = QSSGDataView<QSSGRenderGraphObject *>; |
181 | |
182 | struct QSSGRenderableObject; |
183 | |
184 | struct QSSGRenderableNodeEntry |
185 | { |
186 | QSSGRenderNode *node = nullptr; |
187 | // TODO: We should have an index here for look-up and store the data in a table, |
188 | // er already have the index from when we collect the nodes. We might cull some items at a later |
189 | // stage but that should be fine. The sort data can be just a float and the index to this... |
190 | mutable QSSGRenderMesh *mesh = nullptr; |
191 | mutable QSSGMaterialListView materials; |
192 | mutable QSSGShaderLightListView lights; |
193 | bool isNull() const { return (node == nullptr); } |
194 | QSSGRenderableNodeEntry() = default; |
195 | QSSGRenderableNodeEntry(QSSGRenderNode &inNode) : node(&inNode) {} |
196 | }; |
197 | |
198 | // Used for sorting |
199 | struct QSSGRenderableObjectHandle |
200 | { |
201 | QSSGRenderableObjectHandle() = default; |
202 | QSSGRenderableObjectHandle(QSSGRenderableObject *o, float camDistSq) |
203 | : obj(o) |
204 | , cameraDistanceSq(camDistSq) |
205 | {} |
206 | QSSGRenderableObject *obj = nullptr; |
207 | float cameraDistanceSq = 0.0f; |
208 | }; |
209 | Q_DECLARE_TYPEINFO(QSSGRenderableObjectHandle, Q_PRIMITIVE_TYPE); |
210 | |
211 | using QSSGRenderableObjectList = QVector<QSSGRenderableObjectHandle>; |
212 | |
213 | struct QSSGRenderableObject |
214 | { |
215 | enum class Type : quint8 |
216 | { |
217 | DefaultMaterialMeshSubset, |
218 | CustomMaterialMeshSubset, |
219 | Particles |
220 | }; |
221 | |
222 | // Variables used for picking |
223 | const QMatrix4x4 &globalTransform; |
224 | const QSSGBounds3 &bounds; |
225 | QSSGBounds3 globalBounds; |
226 | |
227 | QSSGRenderableObjectFlags renderableFlags; |
228 | // For rough sorting for transparency and for depth |
229 | QVector3D worldCenterPoint; |
230 | float depthBiasSq; // Squared as our sorting is based on the square distance! |
231 | float camdistSq = 0.0f; |
232 | QSSGDepthDrawMode depthWriteMode = QSSGDepthDrawMode::OpaqueOnly; |
233 | const Type type; |
234 | float instancingLodMin = -1; |
235 | float instancingLodMax = -1; |
236 | |
237 | QSSGRenderableObject(Type ty, |
238 | QSSGRenderableObjectFlags inFlags, |
239 | const QVector3D &inWorldCenterPt, |
240 | const QMatrix4x4 &inGlobalTransform, |
241 | const QSSGBounds3 &inBounds, |
242 | float inDepthBias, |
243 | float inMinThreshold = -1, |
244 | float inMaxThreshold = -1) |
245 | |
246 | : globalTransform(inGlobalTransform) |
247 | , bounds(inBounds) |
248 | , globalBounds(inBounds) |
249 | , renderableFlags(inFlags) |
250 | , worldCenterPoint(inWorldCenterPt) |
251 | , depthBiasSq(inDepthBias) |
252 | , type(ty) |
253 | , instancingLodMin(inMinThreshold) |
254 | , instancingLodMax(inMaxThreshold) |
255 | { |
256 | } |
257 | }; |
258 | |
259 | Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGRenderableObject>::value); |
260 | |
261 | struct QSSGSubsetRenderable; |
262 | |
263 | // Different subsets from the same model will get the same |
264 | // model context so we can generate the MVP and normal matrix once |
265 | // and only once per subset. |
266 | struct QSSGModelContext |
267 | { |
268 | const QSSGRenderModel &model; |
269 | QMatrix4x4 modelViewProjection; |
270 | QMatrix3x3 normalMatrix; |
271 | |
272 | QSSGModelContext(const QSSGRenderModel &inModel, const QMatrix4x4 &inViewProjection) : model(inModel) |
273 | { |
274 | // For skinning, node's global transformation will be ignored and |
275 | // an identity matrix will be used for the normalMatrix |
276 | if (model.usesBoneTexture()) { |
277 | modelViewProjection = inViewProjection; |
278 | normalMatrix = QMatrix3x3(); |
279 | } else { |
280 | model.calculateMVPAndNormalMatrix(inViewProjection, outMVP&: modelViewProjection, outNormalMatrix&: normalMatrix); |
281 | } |
282 | } |
283 | |
284 | QSSGDataRef<QSSGSubsetRenderable> subsets; |
285 | }; |
286 | |
287 | Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGModelContext>::value); |
288 | |
289 | class QSSGRenderer; |
290 | class QSSGLayerRenderData; |
291 | struct QSSGShadowMapEntry; |
292 | |
293 | struct Q_QUICK3DRUNTIMERENDER_EXPORT QSSGSubsetRenderable : public QSSGRenderableObject |
294 | { |
295 | int reflectionProbeIndex = -1; |
296 | float distanceFromReflectionProbe; |
297 | quint32 subsetLevelOfDetail = 0; |
298 | QSSGShaderReflectionProbe reflectionProbe; |
299 | QSSGRenderer *renderer = nullptr; |
300 | const QSSGModelContext &modelContext; |
301 | const QSSGRenderSubset ⊂ |
302 | QRhiBuffer *instanceBuffer = nullptr; |
303 | float opacity; |
304 | const QSSGRenderGraphObject &material; |
305 | QSSGRenderableImage *firstImage; |
306 | QSSGShaderDefaultMaterialKey shaderDescription; |
307 | const QSSGShaderLightListView &lights; |
308 | |
309 | struct { |
310 | // Transient (due to the subsetRenderable being allocated using a |
311 | // per-frame allocator on every frame), not owned refs from the |
312 | // rhi-prepare step, used by the rhi-render step. |
313 | struct { |
314 | QRhiGraphicsPipeline *pipeline = nullptr; |
315 | QRhiShaderResourceBindings *srb = nullptr; |
316 | } mainPass; |
317 | struct { |
318 | QRhiGraphicsPipeline *pipeline = nullptr; |
319 | QRhiShaderResourceBindings *srb = nullptr; |
320 | } depthPrePass; |
321 | struct { |
322 | QRhiGraphicsPipeline *pipeline = nullptr; |
323 | QRhiShaderResourceBindings *srb[6] = {}; |
324 | } shadowPass; |
325 | struct { |
326 | QRhiGraphicsPipeline *pipeline = nullptr; |
327 | QRhiShaderResourceBindings *srb[6] = {}; |
328 | } reflectionPass; |
329 | } rhiRenderData; |
330 | |
331 | QSSGSubsetRenderable(Type type, |
332 | QSSGRenderableObjectFlags inFlags, |
333 | const QVector3D &inWorldCenterPt, |
334 | QSSGRenderer *rendr, |
335 | const QSSGRenderSubset &inSubset, |
336 | const QSSGModelContext &inModelContext, |
337 | float inOpacity, |
338 | quint32 inSubsetLevelOfDetail, |
339 | const QSSGRenderGraphObject &mat, |
340 | QSSGRenderableImage *inFirstImage, |
341 | QSSGShaderDefaultMaterialKey inShaderKey, |
342 | const QSSGShaderLightListView &inLights); |
343 | |
344 | [[nodiscard]] const QSSGRenderGraphObject &getMaterial() const { return material; } |
345 | }; |
346 | |
347 | Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGSubsetRenderable>::value); |
348 | |
349 | /** |
350 | * A renderable that corresponds to a particles. |
351 | */ |
352 | struct Q_QUICK3DRUNTIMERENDER_EXPORT QSSGParticlesRenderable : public QSSGRenderableObject |
353 | { |
354 | QSSGRenderer *renderer = nullptr; |
355 | const QSSGRenderParticles &particles; |
356 | QSSGRenderableImage *firstImage; |
357 | QSSGRenderableImage *colorTable; |
358 | const QSSGShaderLightListView &lights; |
359 | float opacity; |
360 | |
361 | struct { |
362 | // Transient (due to the subsetRenderable being allocated using a |
363 | // per-frame allocator on every frame), not owned refs from the |
364 | // rhi-prepare step, used by the rhi-render step. |
365 | struct { |
366 | QRhiGraphicsPipeline *pipeline = nullptr; |
367 | QRhiShaderResourceBindings *srb = nullptr; |
368 | } mainPass; |
369 | struct { |
370 | QRhiGraphicsPipeline *pipeline = nullptr; |
371 | QRhiShaderResourceBindings *srb = nullptr; |
372 | } depthPrePass; |
373 | struct { |
374 | QRhiGraphicsPipeline *pipeline = nullptr; |
375 | QRhiShaderResourceBindings *srb[6] = {}; |
376 | } shadowPass; |
377 | struct { |
378 | QRhiGraphicsPipeline *pipeline = nullptr; |
379 | QRhiShaderResourceBindings *srb[6] = {}; |
380 | } reflectionPass; |
381 | } rhiRenderData; |
382 | |
383 | QSSGParticlesRenderable(QSSGRenderableObjectFlags inFlags, |
384 | const QVector3D &inWorldCenterPt, |
385 | QSSGRenderer *rendr, |
386 | const QSSGRenderParticles &inParticles, |
387 | QSSGRenderableImage *inFirstImage, |
388 | QSSGRenderableImage *inColorTable, |
389 | const QSSGShaderLightListView &inLights, |
390 | float inOpacity); |
391 | }; |
392 | |
393 | Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGParticlesRenderable>::value); |
394 | |
395 | QT_END_NAMESPACE |
396 | |
397 | #endif |
398 | |