// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "graphobjects/qssgrendermodel_p.h" #include "qssgdebugdrawsystem_p.h" #include "qssgrendermesh_p.h" #include "resourcemanager/qssgrenderbuffermanager_p.h" #include "rendererimpl/qssgrenderableobjects_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; QVector indexData; QVector pointsData; for (const auto &line : m_persistentLines) generateLine(line, vertexData, indexData); for (const auto &line : m_lines) generateLine(line, vertexData, indexData); for (const auto &bounds : m_persistentBounds) generateBox(bounds, vertexData, indexData); for (const auto &bounds : m_bounds) generateBox(bounds, vertexData, indexData); pointsData = m_persistentPoints + m_points; if (!vertexData.isEmpty()) { // Lines QByteArray vertexBufferData(reinterpret_cast(vertexData.constData()), qsizetype(vertexData.count() * 6 * sizeof(float))); QByteArray indexBufferData(reinterpret_cast(indexData.constData()), qsizetype(indexData.count() * sizeof(quint32))); if (m_lineVertexBuffer) m_lineVertexBuffer.reset(); if (m_lineIndexBuffer) m_lineIndexBuffer.reset(); m_lineVertexBuffer = std::make_shared(*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(*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(pointsData.constData()), qsizetype(pointsData.count() * 6 * sizeof(float))); if (m_pointVertexBuffer) m_pointVertexBuffer.reset(); m_pointVertexBuffer = std::make_shared(*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 &vertexArray, QVector &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 &vertexArray, QVector &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(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; for (const QSSGMesh::Mesh::VertexBufferEntry &vbe : mesh.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 = model.globalTransform; // Draw original vertex normals as blue lines { // Get Indexes const quint32 *p = reinterpret_cast(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(posPtr); QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]); const char * normalPtr = vp + (index * vertexStride) + normalOffset; const float *fNormalPtr = reinterpret_cast(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(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(posPtr); QVector3D position(fPosPtr[0], fPosPtr[1], fPosPtr[2]); const char * normalPtr = vp + (index * vertexStride) + normalOffset; const float *fNormalPtr = reinterpret_cast(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