Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include "graphobjects/qssgrendermodel_p.h"
++ #include "qssgdebugdrawsystem_p.h"
++
++ #include "qssgrendermesh_p.h"
++ #include "resourcemanager/qssgrenderbuffermanager_p.h"
++ #include "rendererimpl/qssgrenderableobjects_p.h"
++ #include "rendererimpl/qssglayerrenderdata_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ QSSGDebugDrawSystem::QSSGDebugDrawSystem()
++ {
++
++ }
++
++ QSSGDebugDrawSystem::~QSSGDebugDrawSystem()
++ {
++
++ }
++
++ bool QSSGDebugDrawSystem::hasContent() const
++ {
++ return !m_lines.isEmpty() || !m_persistentLines.isEmpty() || !m_bounds.isEmpty() || !m_persistentBounds.isEmpty() || !m_persistentPoints.isEmpty() || !m_points.isEmpty();
++ }
++
++ void QSSGDebugDrawSystem::drawLine(const QVector3D &startPoint,
++ const QVector3D &endPoint,
++ const QColor &color,
++ bool isPersistent)
++ {
++ LineData line = {startPoint, endPoint, color};
++ if (isPersistent)
++ m_persistentLines.append(line);
++ else
++ m_lines.append(line);
++ }
++
++ void QSSGDebugDrawSystem::drawBounds(const QSSGBounds3 &bounds,
++ const QColor &color,
++ bool isPersistent)
++ {
++ BoundsData bound = {bounds, color};
++ if (isPersistent)
++ m_persistentBounds.append(bound);
++ else
++ m_bounds.append(bound);
++ }
++
++ void QSSGDebugDrawSystem::drawPoint(const QVector3D &vertex, const QColor &color, bool isPersistent)
++ {
++ VertexData point = {vertex, {color.redF(), color.greenF(), color.blueF()}};
++ if (isPersistent)
++ m_persistentPoints.append(point);
++ else
++ m_points.append(point);
++ }
++
++
++ void QSSGDebugDrawSystem::prepareGeometry(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *rub)
++ {
++
++ QVector<VertexData> vertexData;
++ QVector<quint32> indexData;
++ QVector<VertexData> pointsData;
++ for (const auto &line : std::as_const(m_persistentLines))
++ generateLine(line, vertexData, indexData);
++ for (const auto &line : std::as_const(m_lines))
++ generateLine(line, vertexData, indexData);
++ for (const auto &bounds : std::as_const(m_persistentBounds))
++ generateBox(bounds, vertexData, indexData);
++ for (const auto &bounds : std::as_const(m_bounds))
++ generateBox(bounds, vertexData, indexData);
++ pointsData = m_persistentPoints + m_points;
++
++ if (!vertexData.isEmpty()) {
++ // Lines
++ QByteArray vertexBufferData(reinterpret_cast<const char*>(vertexData.constData()), qsizetype(vertexData.count() * 6 * sizeof(float)));
++ QByteArray indexBufferData(reinterpret_cast<const char*>(indexData.constData()), qsizetype(indexData.count() * sizeof(quint32)));
++
++ if (m_lineVertexBuffer)
++ m_lineVertexBuffer.reset();
++ if (m_lineIndexBuffer)
++ m_lineIndexBuffer.reset();
++
++ m_lineVertexBuffer = std::make_shared<QSSGRhiBuffer>(*rhiCtx,
++ QRhiBuffer::Immutable,
++ QRhiBuffer::VertexBuffer,
++ quint32(6 * sizeof(float)),
++ 6 * sizeof(float) * vertexData.count());
++ m_lineVertexBuffer->buffer()->setName(QByteArrayLiteral("debug lines vertex buffer"));
++ rub->uploadStaticBuffer(m_lineVertexBuffer->buffer(), vertexBufferData.constData());
++
++ m_lineIndexBuffer = std::make_shared<QSSGRhiBuffer>(*rhiCtx,
++ QRhiBuffer::Immutable,
++ QRhiBuffer::IndexBuffer,
++ 0,
++ indexBufferData.size(),
++ QRhiCommandBuffer::IndexUInt32);
++ m_lineIndexBuffer->buffer()->setName(QByteArrayLiteral("debug lines index buffer"));
++ rub->uploadStaticBuffer(m_lineIndexBuffer->buffer(), indexBufferData.constData());
++
++ m_indexSize = indexData.count();
++ }
++
++ if (!pointsData.isEmpty()) {
++ // Points
++ QByteArray vertexBufferData(reinterpret_cast<const char*>(pointsData.constData()), qsizetype(pointsData.count() * 6 * sizeof(float)));
++
++ if (m_pointVertexBuffer)
++ m_pointVertexBuffer.reset();
++
++ m_pointVertexBuffer = std::make_shared<QSSGRhiBuffer>(*rhiCtx,
++ QRhiBuffer::Immutable,
++ QRhiBuffer::VertexBuffer,
++ quint32(6 * sizeof(float)),
++ vertexBufferData.size());
++ m_pointVertexBuffer->buffer()->setName(QByteArrayLiteral("debug points vertex buffer"));
++ rub->uploadStaticBuffer(m_pointVertexBuffer->buffer(), vertexBufferData.constData());
++ m_pointsSize = pointsData.count();
++ }
++ }
++
++ void QSSGDebugDrawSystem::recordRenderDebugObjects(QSSGRhiContext *rhiCtx,
++ QSSGRhiGraphicsPipelineState *ps,
++ QRhiShaderResourceBindings *srb,
++ QRhiRenderPassDescriptor *rpDesc)
++ {
++ auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
++ ia.inputLayout.setAttributes({
++ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
++ { 0, 1, QRhiVertexInputAttribute::Float3, 3 * sizeof(float) }
++ });
++ ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic
++ << QSSGRhiInputAssemblerState::ColorSemantic;
++ ia.inputLayout.setBindings({6 * sizeof(float)});
++ ia.topology = QRhiGraphicsPipeline::Lines;
++ ps->flags |= QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled;
++ ps->flags |= QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled;
++ ps->cullMode = QRhiGraphicsPipeline::None;
++
++ QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
++ QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
++ if (m_indexSize > 0) {
++ auto graphicsPipeline = rhiCtxD->pipeline(*ps, rpDesc, srb);
++ cb->setGraphicsPipeline(graphicsPipeline);
++ cb->setShaderResources(srb);
++ cb->setViewport(ps->viewport);
++
++ // Lines
++ QRhiCommandBuffer::VertexInput vb(m_lineVertexBuffer->buffer(), 0);
++ cb->setVertexInput(0, 1, &vb, m_lineIndexBuffer->buffer(), 0, m_lineIndexBuffer->indexFormat());
++ cb->drawIndexed(m_indexSize);
++ }
++
++ // Points
++ if (m_pointsSize > 0) {
++ ia.topology = QRhiGraphicsPipeline::Points;
++ auto graphicsPipeline = rhiCtxD->pipeline(*ps, rpDesc, srb);
++ cb->setGraphicsPipeline(graphicsPipeline);
++ cb->setShaderResources(srb);
++ cb->setViewport(ps->viewport);
++
++ QRhiCommandBuffer::VertexInput vb(m_pointVertexBuffer->buffer(), 0);
++ cb->setVertexInput(0, 1, &vb);
++ cb->draw(m_pointsSize);
++ }
++
++ m_lines.clear();
++ m_bounds.clear();
++ m_points.clear();
++ m_indexSize = 0;
++ m_pointsSize = 0;
++ }
++
++ void QSSGDebugDrawSystem::setEnabled(bool v)
++ {
++ modes = v ? (modes | ModeFlagT(Mode::Other)) : (modes & ~ModeFlagT(Mode::Other));
++ }
++
++ void QSSGDebugDrawSystem::generateLine(const LineData &line, QVector<VertexData> &vertexArray, QVector<quint32> &indexArray)
++ {
++ const QVector3D color = {line.color.redF(), line.color.greenF(), line.color.blueF()};
++ indexArray.append(vertexArray.count());
++ vertexArray.append({line.startPoint, color});
++ indexArray.append(vertexArray.count());
++ vertexArray.append({line.endPoint, color});
++ }
++
++ void QSSGDebugDrawSystem::generateBox(const BoundsData &bounds, QVector<VertexData> &vertexArray, QVector<quint32> &indexArray)
++ {
++ const QVector3D color = {bounds.color.redF(), bounds.color.greenF(), bounds.color.blueF()};
++
++ quint32 offset = vertexArray.count();
++ for (const QVector3D point : bounds.bounds.toQSSGBoxPoints())
++ vertexArray.append({point, color});
++
++ indexArray.append(offset + 0);
++ indexArray.append(offset + 3);
++
++ indexArray.append(offset + 3);
++ indexArray.append(offset + 6);
++
++ indexArray.append(offset + 6);
++ indexArray.append(offset + 1);
++
++ indexArray.append(offset + 1);
++ indexArray.append(offset + 0);
++
++ indexArray.append(offset + 2);
++ indexArray.append(offset + 5);
++
++ indexArray.append(offset + 5);
++ indexArray.append(offset + 4);
++
++ indexArray.append(offset + 4);
++ indexArray.append(offset + 7);
++
++ indexArray.append(offset + 7);
++ indexArray.append(offset + 2);
++
++ indexArray.append(offset + 0);
++ indexArray.append(offset + 2);
++
++ indexArray.append(offset + 3);
++ indexArray.append(offset + 5);
++
++ indexArray.append(offset + 6);
++ indexArray.append(offset + 4);
++
++ indexArray.append(offset + 1);
++ indexArray.append(offset + 7);
++
++ }
++
++ QColor QSSGDebugDrawSystem::levelOfDetailColor(quint32 lod)
++ {
++ static const QColor colors[] {
++ QColor(Qt::white),
++ QColor(Qt::red),
++ QColor(Qt::green),
++ QColor(Qt::blue),
++ QColor(Qt::yellow),
++ QColor(Qt::cyan),
++ QColor(Qt::magenta),
++ QColor(Qt::darkRed),
++ QColor(Qt::darkGreen),
++ QColor(Qt::darkBlue),
++ QColor(Qt::darkCyan),
++ QColor(Qt::darkMagenta),
++ QColor(Qt::darkYellow),
++ QColor(Qt::darkGray)
++ };
++
++ const size_t idx = qBound<size_t>(0, lod, std::size(colors) - 1);
++ return colors[idx];
++ }
++
++ void QSSGDebugDrawSystem::debugNormals(QSSGBufferManager &bufferManager, const QSSGModelContext &theModelContext, const QSSGRenderSubset &theSubset, quint32 subsetLevelOfDetail, float lineLength)
++ {
++ const auto &model = theModelContext.model;
++
++ QSSGMesh::Mesh mesh;
++ if (model.geometry)
++ mesh = bufferManager.loadMeshData(model.geometry);
++ else
++ mesh = bufferManager.loadMeshData(model.meshPath);
++
++ if (!mesh.isValid())
++ return; // invalid mesh
++
++ QByteArray vertexData = mesh.vertexBuffer().data;
++ if (vertexData.isEmpty())
++ return; // no vertex dat
++ quint32 vertexStride = mesh.vertexBuffer().stride;
++ QByteArray indexData = mesh.indexBuffer().data;
++ if (indexData.isEmpty())
++ return; // no index data, not what we're after
++ if (mesh.indexBuffer().componentType != QSSGMesh::Mesh::ComponentType::UnsignedInt32)
++ return; // not uint3d, not what we're after either
++
++ quint32 positionOffset = UINT_MAX;
++ quint32 normalOffset = UINT_MAX;
++
++ const auto vertexBuffer = mesh.vertexBuffer();
++ for (const QSSGMesh::Mesh::VertexBufferEntry &vbe : vertexBuffer.entries) {
++ if (vbe.name == QSSGMesh::MeshInternal::getPositionAttrName()) {
++ positionOffset = vbe.offset;
++ if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
++ vbe.componentCount != 3)
++ return; // not a vec3, some weird stuff
++ } else if (vbe.name == QSSGMesh::MeshInternal::getNormalAttrName()) {
++ normalOffset = vbe.offset;
++ if (vbe.componentType != QSSGMesh::Mesh::ComponentType::Float32 &&
++ vbe.componentCount != 3)
++ return; // not a vec3, really weird normals I guess
++ }
++ }
++
++ const auto &globalTransform = theModelContext.globalTransform;
++ // Draw original vertex normals as blue lines
++ {
++ // Get Indexes
++ const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
++ const char *vp = vertexData.constData();
++ p += theSubset.offset;
++ for (uint i = 0; i < theSubset.count; ++i) {
++ const quint32 index = *(p + i);
++ const char * posPtr = vp + (index * vertexStride) + positionOffset;
++ const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
++ QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
++ const char * normalPtr = vp + (index * vertexStride) + normalOffset;
++ const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
++ QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
++ position = globalTransform.map(position);
++ normal = QSSGUtils::mat33::transform(theModelContext.normalMatrix, normal);
++ normal = normal.normalized();
++ drawLine(position, position + (normal * lineLength), QColor(Qt::blue));
++ }
++ }
++
++ // Draw lod vertex normals as red lines
++ if (subsetLevelOfDetail != 0) {
++ // Get Indexes
++ const quint32 *p = reinterpret_cast<const quint32 *>(indexData.constData());
++ const char *vp = vertexData.constData();
++ p += theSubset.lodOffset(subsetLevelOfDetail);
++ const quint32 indexCount = theSubset.lodCount(subsetLevelOfDetail);
++ for (uint i = 0; i < indexCount; ++i) {
++ const quint32 index = *(p + i);
++ const char * posPtr = vp + (index * vertexStride) + positionOffset;
++ const float *fPosPtr = reinterpret_cast<const float *>(posPtr);
++ QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]);
++ const char * normalPtr = vp + (index * vertexStride) + normalOffset;
++ const float *fNormalPtr = reinterpret_cast<const float *>(normalPtr);
++ QVector3D normal(fNormalPtr[0], fNormalPtr[1], fNormalPtr[2]);
++ position = globalTransform.map(position);
++ normal = QSSGUtils::mat33::transform(theModelContext.normalMatrix, normal);
++ normal = normal.normalized();
++ drawLine(position, position + (normal * lineLength), QColor(Qt::red));
++ }
++ }
++ }
++
++ QT_END_NAMESPACE