1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "graphobjects/qssgrendermodel_p.h"
5#include "qssgdebugdrawsystem_p.h"
6
7#include "qssgrendermesh_p.h"
8#include "resourcemanager/qssgrenderbuffermanager_p.h"
9#include "rendererimpl/qssgrenderableobjects_p.h"
10
11QT_BEGIN_NAMESPACE
12
13QSSGDebugDrawSystem::QSSGDebugDrawSystem()
14{
15
16}
17
18QSSGDebugDrawSystem::~QSSGDebugDrawSystem()
19{
20
21}
22
23bool QSSGDebugDrawSystem::hasContent() const
24{
25 return !m_lines.isEmpty() || !m_persistentLines.isEmpty() || !m_bounds.isEmpty() || !m_persistentBounds.isEmpty() || !m_persistentPoints.isEmpty() || !m_points.isEmpty();
26}
27
28void QSSGDebugDrawSystem::drawLine(const QVector3D &startPoint,
29 const QVector3D &endPoint,
30 const QColor &color,
31 bool isPersistent)
32{
33 LineData line = {.startPoint: startPoint, .endPoint: endPoint, .color: color};
34 if (isPersistent)
35 m_persistentLines.append(t: line);
36 else
37 m_lines.append(t: line);
38}
39
40void QSSGDebugDrawSystem::drawBounds(const QSSGBounds3 &bounds,
41 const QColor &color,
42 bool isPersistent)
43{
44 BoundsData bound = {.bounds: bounds, .color: color};
45 if (isPersistent)
46 m_persistentBounds.append(t: bound);
47 else
48 m_bounds.append(t: bound);
49}
50
51void QSSGDebugDrawSystem::drawPoint(const QVector3D &vertex, const QColor &color, bool isPersistent)
52{
53 VertexData point = {.position: vertex, .color: {color.redF(), color.greenF(), color.blueF()}};
54 if (isPersistent)
55 m_persistentPoints.append(t: point);
56 else
57 m_points.append(t: point);
58}
59
60
61void QSSGDebugDrawSystem::prepareGeometry(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *rub)
62{
63
64 QVector<VertexData> vertexData;
65 QVector<quint32> indexData;
66 QVector<VertexData> pointsData;
67 for (const auto &line : m_persistentLines)
68 generateLine(line, vertexArray&: vertexData, indexArray&: indexData);
69 for (const auto &line : m_lines)
70 generateLine(line, vertexArray&: vertexData, indexArray&: indexData);
71 for (const auto &bounds : m_persistentBounds)
72 generateBox(bounds, vertexArray&: vertexData, indexArray&: indexData);
73 for (const auto &bounds : m_bounds)
74 generateBox(bounds, vertexArray&: vertexData, indexArray&: indexData);
75 pointsData = m_persistentPoints + m_points;
76
77 if (!vertexData.isEmpty()) {
78 // Lines
79 QByteArray vertexBufferData(reinterpret_cast<const char*>(vertexData.constData()), qsizetype(vertexData.count() * 6 * sizeof(float)));
80 QByteArray indexBufferData(reinterpret_cast<const char*>(indexData.constData()), qsizetype(indexData.count() * sizeof(quint32)));
81
82 if (m_lineVertexBuffer)
83 m_lineVertexBuffer.reset();
84 if (m_lineIndexBuffer)
85 m_lineIndexBuffer.reset();
86
87 m_lineVertexBuffer = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx,
88 args: QRhiBuffer::Immutable,
89 args: QRhiBuffer::VertexBuffer,
90 args: quint32(6 * sizeof(float)),
91 args: 6 * sizeof(float) * vertexData.count());
92 m_lineVertexBuffer->buffer()->setName(QByteArrayLiteral("debug lines vertex buffer"));
93 rub->uploadStaticBuffer(buf: m_lineVertexBuffer->buffer(), data: vertexBufferData.constData());
94
95 m_lineIndexBuffer = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx,
96 args: QRhiBuffer::Immutable,
97 args: QRhiBuffer::IndexBuffer,
98 args: 0,
99 args: indexBufferData.size(),
100 args: QRhiCommandBuffer::IndexUInt32);
101 m_lineIndexBuffer->buffer()->setName(QByteArrayLiteral("debug lines index buffer"));
102 rub->uploadStaticBuffer(buf: m_lineIndexBuffer->buffer(), data: indexBufferData.constData());
103
104 m_indexSize = indexData.count();
105 }
106
107 if (!pointsData.isEmpty()) {
108 // Points
109 QByteArray vertexBufferData(reinterpret_cast<const char*>(pointsData.constData()), qsizetype(pointsData.count() * 6 * sizeof(float)));
110
111 if (m_pointVertexBuffer)
112 m_pointVertexBuffer.reset();
113
114 m_pointVertexBuffer = std::make_shared<QSSGRhiBuffer>(args&: *rhiCtx,
115 args: QRhiBuffer::Immutable,
116 args: QRhiBuffer::VertexBuffer,
117 args: quint32(6 * sizeof(float)),
118 args: vertexBufferData.size());
119 m_pointVertexBuffer->buffer()->setName(QByteArrayLiteral("debug points vertex buffer"));
120 rub->uploadStaticBuffer(buf: m_pointVertexBuffer->buffer(), data: vertexBufferData.constData());
121 m_pointsSize = pointsData.count();
122 }
123}
124
125void QSSGDebugDrawSystem::recordRenderDebugObjects(QSSGRhiContext *rhiCtx,
126 QSSGRhiGraphicsPipelineState *ps,
127 QRhiShaderResourceBindings *srb,
128 QRhiRenderPassDescriptor *rpDesc)
129{
130 ps->ia.inputLayout.setAttributes({
131 { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
132 { 0, 1, QRhiVertexInputAttribute::Float3, 3 * sizeof(float) }
133 });
134 ps->ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic
135 << QSSGRhiInputAssemblerState::ColorSemantic;
136 ps->ia.inputLayout.setBindings({6 * sizeof(float)});
137 ps->ia.topology = QRhiGraphicsPipeline::Lines;
138 ps->depthWriteEnable = true;
139 ps->depthTestEnable = true;
140 ps->cullMode = QRhiGraphicsPipeline::None;
141
142 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
143 if (m_indexSize > 0) {
144 auto graphicsPipeline = rhiCtx->pipeline(key: QSSGGraphicsPipelineStateKey::create(state: *ps, rpDesc, srb), rpDesc, srb);
145 cb->setGraphicsPipeline(graphicsPipeline);
146 cb->setShaderResources(srb);
147 cb->setViewport(ps->viewport);
148
149 // Lines
150 QRhiCommandBuffer::VertexInput vb(m_lineVertexBuffer->buffer(), 0);
151 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb, indexBuf: m_lineIndexBuffer->buffer(), indexOffset: 0, indexFormat: m_lineIndexBuffer->indexFormat());
152 cb->drawIndexed(indexCount: m_indexSize);
153 }
154
155 // Points
156 if (m_pointsSize > 0) {
157 ps->ia.topology = QRhiGraphicsPipeline::Points;
158 auto graphicsPipeline = rhiCtx->pipeline(key: QSSGGraphicsPipelineStateKey::create(state: *ps, rpDesc, srb), rpDesc, srb);
159 cb->setGraphicsPipeline(graphicsPipeline);
160 cb->setShaderResources(srb);
161 cb->setViewport(ps->viewport);
162
163 QRhiCommandBuffer::VertexInput vb(m_pointVertexBuffer->buffer(), 0);
164 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb);
165 cb->draw(vertexCount: m_pointsSize);
166 }
167
168 m_lines.clear();
169 m_bounds.clear();
170 m_points.clear();
171 m_indexSize = 0;
172 m_pointsSize = 0;
173}
174
175void QSSGDebugDrawSystem::setEnabled(bool v)
176{
177 modes = v ? (modes | ModeFlagT(Mode::Other)) : (modes & ~ModeFlagT(Mode::Other));
178}
179
180void QSSGDebugDrawSystem::generateLine(const LineData &line, QVector<VertexData> &vertexArray, QVector<quint32> &indexArray)
181{
182 const QVector3D color = {line.color.redF(), line.color.greenF(), line.color.blueF()};
183 indexArray.append(t: vertexArray.count());
184 vertexArray.append(t: {.position: line.startPoint, .color: color});
185 indexArray.append(t: vertexArray.count());
186 vertexArray.append(t: {.position: line.endPoint, .color: color});
187}
188
189void QSSGDebugDrawSystem::generateBox(const BoundsData &bounds, QVector<VertexData> &vertexArray, QVector<quint32> &indexArray)
190{
191 const QVector3D color = {bounds.color.redF(), bounds.color.greenF(), bounds.color.blueF()};
192
193 quint32 offset = vertexArray.count();
194 for (const QVector3D point : bounds.bounds.toQSSGBoxPoints())
195 vertexArray.append(t: {.position: point, .color: color});
196
197 indexArray.append(t: offset + 0);
198 indexArray.append(t: offset + 3);
199
200 indexArray.append(t: offset + 3);
201 indexArray.append(t: offset + 6);
202
203 indexArray.append(t: offset + 6);
204 indexArray.append(t: offset + 1);
205
206 indexArray.append(t: offset + 1);
207 indexArray.append(t: offset + 0);
208
209 indexArray.append(t: offset + 2);
210 indexArray.append(t: offset + 5);
211
212 indexArray.append(t: offset + 5);
213 indexArray.append(t: offset + 4);
214
215 indexArray.append(t: offset + 4);
216 indexArray.append(t: offset + 7);
217
218 indexArray.append(t: offset + 7);
219 indexArray.append(t: offset + 2);
220
221 indexArray.append(t: offset + 0);
222 indexArray.append(t: offset + 2);
223
224 indexArray.append(t: offset + 3);
225 indexArray.append(t: offset + 5);
226
227 indexArray.append(t: offset + 6);
228 indexArray.append(t: offset + 4);
229
230 indexArray.append(t: offset + 1);
231 indexArray.append(t: offset + 7);
232
233}
234
235QColor QSSGDebugDrawSystem::levelOfDetailColor(quint32 lod)
236{
237 static const QColor colors[] {
238 QColor(Qt::white),
239 QColor(Qt::red),
240 QColor(Qt::green),
241 QColor(Qt::blue),
242 QColor(Qt::yellow),
243 QColor(Qt::cyan),
244 QColor(Qt::magenta),
245 QColor(Qt::darkRed),
246 QColor(Qt::darkGreen),
247 QColor(Qt::darkBlue),
248 QColor(Qt::darkCyan),
249 QColor(Qt::darkMagenta),
250 QColor(Qt::darkYellow),
251 QColor(Qt::darkGray)
252 };
253
254 const size_t idx = qBound<size_t>(min: 0, val: lod, max: std::size(colors) - 1);
255 return colors[idx];
256}
257
258void QSSGDebugDrawSystem::debugNormals(QSSGBufferManager &bufferManager, const QSSGModelContext &theModelContext, const QSSGRenderSubset &theSubset, quint32 subsetLevelOfDetail, float lineLength)
259{
260 const auto &model = theModelContext.model;
261
262 QSSGMesh::Mesh mesh;
263 if (model.geometry)
264 mesh = bufferManager.loadMeshData(geometry: model.geometry);
265 else
266 mesh = bufferManager.loadMeshData(inSourcePath: model.meshPath);
267
268 if (!mesh.isValid())
269 return; // invalid mesh
270
271 QByteArray vertexData = mesh.vertexBuffer().data;
272 if (vertexData.isEmpty())
273 return; // no vertex dat
274 quint32 vertexStride = mesh.vertexBuffer().stride;
275 QByteArray indexData = mesh.indexBuffer().data;
276 if (indexData.isEmpty())
277 return; // no index data, not what we're after
278 if (mesh.indexBuffer().componentType != QSSGMesh::Mesh::ComponentType::UnsignedInt32)
279 return; // not uint3d, not what we're after either
280
281 quint32 positionOffset = UINT_MAX;
282 quint32 normalOffset = UINT_MAX;
283
284 for (const QSSGMesh::Mesh::VertexBufferEntry &vbe : mesh.vertexBuffer().entries) {
285 if (vbe.name == QSSGMesh::MeshInternal::getPositionAttrName()) {
286 positionOffset = vbe.offset;
287 if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
288 vbe.componentCount != 3)
289 return; // not a vec3, some weird stuff
290 } else if (vbe.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
291 normalOffset = vbe.offset;
292 if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
293 vbe.componentCount != 3)
294 return; // not a vec3, really weird normals I guess
295 }
296 }
297
298 const auto globalTransform = model.globalTransform;
299 // Draw original vertex normals as blue lines
300 {
301 // Get Indexes
302 const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
303 const char *vp = vertexData.constData();
304 p += theSubset.offset;
305 for (uint i = 0; i < theSubset.count; ++i) {
306 const quint32 index = *(p + i);
307 const char * posPtr = vp + (index * vertexStride) + positionOffset;
308 const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
309 QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
310 const char * normalPtr = vp + (index * vertexStride) + normalOffset;
311 const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
312 QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
313 position = globalTransform.map(point: position);
314 normal = QSSGUtils::mat33::transform(m: theModelContext.normalMatrix, v: normal);
315 normal = normal.normalized();
316 drawLine(startPoint: position, endPoint: position + (normal * lineLength), color: QColor(Qt::blue));
317 }
318 }
319
320 // Draw lod vertex normals as red lines
321 if (subsetLevelOfDetail != 0) {
322 // Get Indexes
323 const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
324 const char *vp = vertexData.constData();
325 p += theSubset.lodOffset(lodLevel: subsetLevelOfDetail);
326 const quint32 indexCount = theSubset.lodCount(lodLevel: subsetLevelOfDetail);
327 for (uint i = 0; i < indexCount; ++i) {
328 const quint32 index = *(p + i);
329 const char * posPtr = vp + (index * vertexStride) + positionOffset;
330 const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
331 QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
332 const char * normalPtr = vp + (index * vertexStride) + normalOffset;
333 const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
334 QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
335 position = globalTransform.map(point: position);
336 normal = QSSGUtils::mat33::transform(m: theModelContext.normalMatrix, v: normal);
337 normal = normal.normalized();
338 drawLine(startPoint: position, endPoint: position + (normal * lineLength), color: QColor(Qt::red));
339 }
340 }
341}
342
343QT_END_NAMESPACE
344

source code of qtquick3d/src/runtimerender/qssgdebugdrawsystem.cpp