Renderer: Add DebugDrawer
This commit is contained in:
parent
099528758c
commit
4a5f866754
|
|
@ -2,6 +2,7 @@
|
|||
#include <Nazara/Math.hpp>
|
||||
#include <Nazara/Platform.hpp>
|
||||
#include <Nazara/Renderer.hpp>
|
||||
#include <Nazara/Renderer/DebugDrawer.hpp>
|
||||
#include <NZSL/FilesystemModuleResolver.hpp>
|
||||
#include <NZSL/LangWriter.hpp>
|
||||
#include <NZSL/Parser.hpp>
|
||||
|
|
@ -249,6 +250,8 @@ int main()
|
|||
|
||||
Nz::Mouse::SetRelativeMouseMode(true);
|
||||
|
||||
Nz::DebugDrawer debugDrawer(*renderDevice);
|
||||
|
||||
while (window.IsOpen())
|
||||
{
|
||||
Nz::WindowEvent event;
|
||||
|
|
@ -329,6 +332,14 @@ int main()
|
|||
continue;
|
||||
}
|
||||
|
||||
debugDrawer.Reset(frame);
|
||||
debugDrawer.SetViewerData(ubo.viewMatrix * ubo.projectionMatrix);
|
||||
|
||||
Nz::Boxf aabb = spaceship->GetAABB();
|
||||
aabb.Transform(ubo.modelMatrix);
|
||||
|
||||
debugDrawer.DrawBox(aabb, Nz::Color::Green);
|
||||
|
||||
ubo.viewMatrix = Nz::Matrix4f::TransformInverse(viewerPos, camAngles);
|
||||
|
||||
if (uboUpdate)
|
||||
|
|
@ -351,6 +362,8 @@ int main()
|
|||
uboUpdate = false;
|
||||
}
|
||||
|
||||
debugDrawer.Prepare(frame);
|
||||
|
||||
const Nz::RenderTarget* windowRT = window.GetRenderTarget();
|
||||
frame.Execute([&](Nz::CommandBufferBuilder& builder)
|
||||
{
|
||||
|
|
@ -375,6 +388,8 @@ int main()
|
|||
builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
|
||||
|
||||
builder.DrawIndexed(meshIB->GetIndexCount());
|
||||
|
||||
debugDrawer.Draw(builder);
|
||||
}
|
||||
builder.EndRenderPass();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,53 +9,91 @@
|
|||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Math/BoundingVolume.hpp>
|
||||
#include <Nazara/Math/Box.hpp>
|
||||
#include <Nazara/Math/Frustum.hpp>
|
||||
#include <Nazara/Math/OrientedBox.hpp>
|
||||
#include <Nazara/Math/Matrix4.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Renderer/Config.hpp>
|
||||
#include <Nazara/Renderer/UploadPool.hpp>
|
||||
#include <Nazara/Utility/VertexStruct.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class CommandBufferBuilder;
|
||||
class RenderBuffer;
|
||||
class RenderFrame;
|
||||
class RenderDevice;
|
||||
class RenderPipeline;
|
||||
class RenderPipelineLayout;
|
||||
class ShaderBinding;
|
||||
class Skeleton;
|
||||
class StaticMesh;
|
||||
|
||||
class NAZARA_RENDERER_API DebugDrawer
|
||||
{
|
||||
public:
|
||||
static void Draw(const BoundingVolumef& volume);
|
||||
static void Draw(const Boxf& box);
|
||||
static void Draw(const Boxi& box);
|
||||
static void Draw(const Boxui& box);
|
||||
static void Draw(const Frustumf& frustum);
|
||||
static void Draw(const OrientedBoxf& orientedBox);
|
||||
static void Draw(const Skeleton* skeleton);
|
||||
static void Draw(const Vector3f& position, float size = 0.1f);
|
||||
static void DrawAxes(const Vector3f& position = Vector3f::Zero(), float size = 1.f);
|
||||
static void DrawBinormals(const StaticMesh* subMesh);
|
||||
static void DrawCone(const Vector3f& origin, const Quaternionf& rotation, float angle, float length);
|
||||
static void DrawLine(const Vector3f& p1, const Vector3f& p2);
|
||||
static void DrawPoints(const Vector3f* ptr, unsigned int pointCount);
|
||||
static void DrawNormals(const StaticMesh* subMesh, float normalLength = 0.01f);
|
||||
static void DrawTangents(const StaticMesh* subMesh);
|
||||
DebugDrawer(RenderDevice& renderDevice, std::size_t maxVertexPerDraw = DefaultVertexBlockSize);
|
||||
DebugDrawer(const DebugDrawer&) = delete;
|
||||
DebugDrawer(DebugDrawer&&) = delete;
|
||||
~DebugDrawer();
|
||||
|
||||
static void EnableDepthBuffer(bool depthBuffer);
|
||||
void Draw(CommandBufferBuilder& builder);
|
||||
|
||||
static float GetLineWidth();
|
||||
static float GetPointSize();
|
||||
static Color GetPrimaryColor();
|
||||
static Color GetSecondaryColor();
|
||||
inline void DrawBox(const Boxf& box, const Color& color);
|
||||
inline void DrawLine(const Vector3f& start, const Vector3f& end, const Color& color);
|
||||
inline void DrawLine(const Vector3f& start, const Vector3f& end, const Color& startColor, const Color& endColor);
|
||||
void DrawSkeleton(const Skeleton& skeleton, const Color& color);
|
||||
|
||||
static bool Initialize();
|
||||
static bool IsDepthBufferEnabled();
|
||||
void Prepare(RenderFrame& renderFrame);
|
||||
|
||||
static void SetLineWidth(float width);
|
||||
static void SetPointSize(float size);
|
||||
static void SetPrimaryColor(const Color& color);
|
||||
static void SetSecondaryColor(const Color& color);
|
||||
void Reset(RenderFrame& renderFrame);
|
||||
|
||||
static void Uninitialize();
|
||||
void SetViewerData(const Matrix4f& viewProjMatrix);
|
||||
|
||||
DebugDrawer& operator=(const DebugDrawer&) = delete;
|
||||
DebugDrawer& operator=(DebugDrawer&&) = delete;
|
||||
|
||||
static constexpr std::size_t DefaultVertexBlockSize = 4096;
|
||||
|
||||
private:
|
||||
struct ViewerData
|
||||
{
|
||||
std::shared_ptr<RenderBuffer> buffer;
|
||||
std::shared_ptr<ShaderBinding> binding;
|
||||
};
|
||||
|
||||
struct DataPool
|
||||
{
|
||||
std::vector<std::shared_ptr<RenderBuffer>> vertexBuffers;
|
||||
std::vector<ViewerData> viewerData;
|
||||
};
|
||||
|
||||
struct DrawCall
|
||||
{
|
||||
std::shared_ptr<RenderBuffer> vertexBuffer;
|
||||
std::uint64_t vertexCount;
|
||||
};
|
||||
|
||||
struct PendingUpload
|
||||
{
|
||||
UploadPool::Allocation* allocation;
|
||||
RenderBuffer* vertexBuffer;
|
||||
};
|
||||
|
||||
std::shared_ptr<DataPool> m_dataPool;
|
||||
std::shared_ptr<RenderPipeline> m_renderPipeline;
|
||||
std::shared_ptr<RenderPipelineLayout> m_renderPipelineLayout;
|
||||
std::size_t m_vertexPerBlock;
|
||||
std::vector<DrawCall> m_drawCalls;
|
||||
std::vector<PendingUpload> m_pendingUploads;
|
||||
std::vector<UInt8> m_viewerData;
|
||||
std::vector<VertexStruct_XYZ_Color> m_lineVertices;
|
||||
RenderDevice& m_renderDevice;
|
||||
ViewerData m_currentViewerData;
|
||||
bool m_viewerDataUpdated;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Renderer/DebugDrawer.inl>
|
||||
|
||||
#endif // NAZARA_RENDERER_DEBUGDRAWER_HPP
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/DebugDrawer.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline void DebugDrawer::DrawBox(const Boxf& box, const Color& color)
|
||||
{
|
||||
Vector3f max = box.GetMaximum();
|
||||
Vector3f min = box.GetMinimum();
|
||||
|
||||
DrawLine({ min.x, min.y, min.z }, { max.x, min.y, min.z }, color);
|
||||
DrawLine({ min.x, min.y, min.z }, { min.x, max.y, min.z }, color);
|
||||
DrawLine({ min.x, min.y, min.z }, { min.x, min.y, max.z }, color);
|
||||
DrawLine({ max.x, max.y, max.z }, { min.x, max.y, max.z }, color);
|
||||
DrawLine({ max.x, max.y, max.z }, { max.x, min.y, max.z }, color);
|
||||
DrawLine({ max.x, max.y, max.z }, { max.x, max.y, min.z }, color);
|
||||
DrawLine({ min.x, min.y, max.z }, { max.x, min.y, max.z }, color);
|
||||
DrawLine({ min.x, min.y, max.z }, { min.x, max.y, max.z }, color);
|
||||
DrawLine({ min.x, max.y, min.z }, { max.x, max.y, min.z }, color);
|
||||
DrawLine({ min.x, max.y, min.z }, { min.x, max.y, max.z }, color);
|
||||
DrawLine({ max.x, min.y, min.z }, { max.x, max.y, min.z }, color);
|
||||
DrawLine({ max.x, min.y, min.z }, { max.x, min.y, max.z }, color);
|
||||
}
|
||||
|
||||
inline void DebugDrawer::DrawLine(const Vector3f& start, const Vector3f& end, const Color& color)
|
||||
{
|
||||
return DrawLine(start, end, color, color);
|
||||
}
|
||||
|
||||
inline void DebugDrawer::DrawLine(const Vector3f& start, const Vector3f& end, const Color& startColor, const Color& endColor)
|
||||
{
|
||||
auto& startVertex = m_lineVertices.emplace_back();
|
||||
startVertex.color = startColor;
|
||||
startVertex.position = start;
|
||||
|
||||
auto& endVertex = m_lineVertices.emplace_back();
|
||||
endVertex.color = endColor;
|
||||
endVertex.position = end;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Renderer/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/DebugDrawer.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Renderer/RenderDevice.hpp>
|
||||
#include <Nazara/Renderer/RenderFrame.hpp>
|
||||
#include <Nazara/Renderer/RenderPipeline.hpp>
|
||||
#include <Nazara/Renderer/RenderPipelineLayout.hpp>
|
||||
#include <Nazara/Utility/Joint.hpp>
|
||||
#include <Nazara/Utility/Skeleton.hpp>
|
||||
#include <NZSL/Serializer.hpp>
|
||||
#include <NZSL/Ast/AstSerializer.hpp>
|
||||
#include <NZSL/Math/FieldOffsets.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const UInt8 r_debugDrawShader[] = {
|
||||
#include <Nazara/Renderer/Resources/Shaders/DebugDraw.nzslb.h>
|
||||
};
|
||||
}
|
||||
|
||||
DebugDrawer::DebugDrawer(RenderDevice& renderDevice, std::size_t maxVertexPerDraw) :
|
||||
m_vertexPerBlock(maxVertexPerDraw),
|
||||
m_renderDevice(renderDevice),
|
||||
m_viewerDataUpdated(false)
|
||||
{
|
||||
nzsl::Unserializer unserializer(r_debugDrawShader, sizeof(r_debugDrawShader));
|
||||
nzsl::Ast::ModulePtr shaderModule = nzsl::Ast::UnserializeShader(unserializer);
|
||||
|
||||
auto debugDrawShader = m_renderDevice.InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, {});
|
||||
if (!debugDrawShader)
|
||||
throw std::runtime_error("failed to instantiate debug draw shader");
|
||||
|
||||
RenderPipelineLayoutInfo layoutInfo;
|
||||
layoutInfo.bindings.assign({
|
||||
{
|
||||
0, 0,
|
||||
ShaderBindingType::UniformBuffer,
|
||||
nzsl::ShaderStageType::Vertex
|
||||
}
|
||||
});
|
||||
|
||||
m_renderPipelineLayout = m_renderDevice.InstantiateRenderPipelineLayout(std::move(layoutInfo));
|
||||
if (!m_renderPipelineLayout)
|
||||
throw std::runtime_error("failed to instantiate render pipeline layout");
|
||||
|
||||
RenderPipelineInfo pipelineInfo;
|
||||
pipelineInfo.pipelineLayout = m_renderPipelineLayout;
|
||||
pipelineInfo.shaderModules.push_back(std::move(debugDrawShader));
|
||||
pipelineInfo.depthBuffer = true;
|
||||
pipelineInfo.depthWrite = false;
|
||||
|
||||
pipelineInfo.blending = true;
|
||||
pipelineInfo.blend.srcColor = BlendFunc::SrcAlpha;
|
||||
pipelineInfo.blend.dstColor = BlendFunc::InvSrcAlpha;
|
||||
|
||||
pipelineInfo.primitiveMode = PrimitiveMode::LineList;
|
||||
pipelineInfo.vertexBuffers.push_back({
|
||||
0,
|
||||
VertexDeclaration::Get(Nz::VertexLayout::XYZ_Color)
|
||||
});
|
||||
|
||||
m_renderPipeline = m_renderDevice.InstantiateRenderPipeline(pipelineInfo);
|
||||
|
||||
m_dataPool = std::make_shared<DataPool>();
|
||||
}
|
||||
|
||||
DebugDrawer::~DebugDrawer()
|
||||
{
|
||||
m_drawCalls.clear();
|
||||
m_dataPool.reset();
|
||||
m_currentViewerData = {};
|
||||
}
|
||||
|
||||
void DebugDrawer::Draw(CommandBufferBuilder& builder)
|
||||
{
|
||||
if (m_drawCalls.empty())
|
||||
return;
|
||||
|
||||
builder.BindShaderBinding(0, *m_currentViewerData.binding);
|
||||
builder.BindPipeline(*m_renderPipeline);
|
||||
|
||||
for (auto& drawCall : m_drawCalls)
|
||||
{
|
||||
builder.BindVertexBuffer(0, *drawCall.vertexBuffer);
|
||||
builder.Draw(drawCall.vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDrawer::DrawSkeleton(const Skeleton& skeleton, const Color& color)
|
||||
{
|
||||
std::size_t jointCount = skeleton.GetJointCount();
|
||||
for (std::size_t i = 0; i < jointCount; ++i)
|
||||
{
|
||||
const Joint* joint = skeleton.GetJoint(i);
|
||||
const Node* parent = joint->GetParent();
|
||||
if (parent)
|
||||
DrawLine(joint->GetPosition(CoordSys::Global), parent->GetPosition(CoordSys::Global), color);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDrawer::Prepare(RenderFrame& renderFrame)
|
||||
{
|
||||
UploadPool& uploadPool = renderFrame.GetUploadPool();
|
||||
|
||||
if (!m_lineVertices.empty())
|
||||
{
|
||||
std::size_t vertexCount = m_lineVertices.size();
|
||||
m_drawCalls.clear();
|
||||
m_drawCalls.reserve(vertexCount / m_vertexPerBlock + 1);
|
||||
|
||||
m_pendingUploads.clear();
|
||||
m_pendingUploads.reserve(vertexCount / m_vertexPerBlock + 1);
|
||||
|
||||
// Handle vertex buffers
|
||||
const VertexStruct_XYZ_Color* lineVertices = m_lineVertices.data();
|
||||
while (vertexCount > 0)
|
||||
{
|
||||
auto& drawCall = m_drawCalls.emplace_back();
|
||||
|
||||
// Try to reuse vertex buffers from pool if any
|
||||
if (!m_dataPool->vertexBuffers.empty())
|
||||
{
|
||||
drawCall.vertexBuffer = std::move(m_dataPool->vertexBuffers.back());
|
||||
m_dataPool->vertexBuffers.pop_back();
|
||||
}
|
||||
else
|
||||
drawCall.vertexBuffer = m_renderDevice.InstantiateBuffer(BufferType::Vertex, m_vertexPerBlock * sizeof(VertexStruct_XYZ_Color), BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
|
||||
|
||||
drawCall.vertexCount = std::min(vertexCount, m_vertexPerBlock);
|
||||
|
||||
auto& pendingUpload = m_pendingUploads.emplace_back();
|
||||
pendingUpload.allocation = &uploadPool.Allocate(drawCall.vertexCount * sizeof(VertexStruct_XYZ_Color));
|
||||
pendingUpload.vertexBuffer = drawCall.vertexBuffer.get();
|
||||
std::memcpy(pendingUpload.allocation->mappedPtr, lineVertices, drawCall.vertexCount * sizeof(VertexStruct_XYZ_Color));
|
||||
|
||||
lineVertices += drawCall.vertexCount;
|
||||
vertexCount -= drawCall.vertexCount;
|
||||
}
|
||||
m_lineVertices.clear();
|
||||
}
|
||||
|
||||
// Handle viewer data
|
||||
if (m_viewerDataUpdated)
|
||||
{
|
||||
if (!m_dataPool->viewerData.empty())
|
||||
{
|
||||
m_currentViewerData = std::move(m_dataPool->viewerData.back());
|
||||
m_dataPool->viewerData.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentViewerData.buffer = m_renderDevice.InstantiateBuffer(BufferType::Uniform, m_viewerData.size(), BufferUsage::DeviceLocal | BufferUsage::Dynamic | BufferUsage::Write);
|
||||
m_currentViewerData.binding = m_renderPipelineLayout->AllocateShaderBinding(0);
|
||||
m_currentViewerData.binding->Update({
|
||||
{
|
||||
0,
|
||||
Nz::ShaderBinding::UniformBufferBinding {
|
||||
m_currentViewerData.buffer.get(),
|
||||
0, m_currentViewerData.buffer->GetSize()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (m_viewerDataUpdated || !m_pendingUploads.empty())
|
||||
{
|
||||
renderFrame.Execute([&](CommandBufferBuilder& builder)
|
||||
{
|
||||
builder.BeginDebugRegion("Debug drawer upload", Color::Yellow);
|
||||
{
|
||||
if (m_viewerDataUpdated)
|
||||
{
|
||||
const UploadPool::Allocation& viewerDataAllocation = uploadPool.Allocate(m_viewerData.size());
|
||||
std::memcpy(viewerDataAllocation.mappedPtr, m_viewerData.data(), m_viewerData.size());
|
||||
|
||||
builder.CopyBuffer(viewerDataAllocation, m_currentViewerData.buffer.get());
|
||||
}
|
||||
|
||||
for (auto& pendingUpload : m_pendingUploads)
|
||||
builder.CopyBuffer(*pendingUpload.allocation, pendingUpload.vertexBuffer);
|
||||
m_pendingUploads.clear();
|
||||
|
||||
builder.PostTransferBarrier();
|
||||
}
|
||||
builder.EndDebugRegion();
|
||||
}, QueueType::Graphics);
|
||||
}
|
||||
|
||||
m_viewerDataUpdated = false;
|
||||
}
|
||||
|
||||
void DebugDrawer::Reset(RenderFrame& renderFrame)
|
||||
{
|
||||
if (m_currentViewerData.binding)
|
||||
{
|
||||
// keep pipeline layout alive as needs to stay alive until all shader bindings have been freed
|
||||
renderFrame.PushReleaseCallback([pool = m_dataPool, data = std::move(m_currentViewerData), pipelineLayout = m_renderPipelineLayout]() mutable
|
||||
{
|
||||
pool->viewerData.push_back(std::move(data));
|
||||
});
|
||||
}
|
||||
m_currentViewerData.binding = {};
|
||||
|
||||
for (auto& drawCall : m_drawCalls)
|
||||
{
|
||||
renderFrame.PushReleaseCallback([pool = m_dataPool, buffer = std::move(drawCall.vertexBuffer)]() mutable
|
||||
{
|
||||
pool->vertexBuffers.push_back(std::move(buffer));
|
||||
});
|
||||
}
|
||||
m_drawCalls.clear();
|
||||
|
||||
m_lineVertices.clear();
|
||||
}
|
||||
|
||||
void DebugDrawer::SetViewerData(const Matrix4f& viewProjMatrix)
|
||||
{
|
||||
// Setup viewer data buffer for current frame
|
||||
nzsl::FieldOffsets viewerDataFields(nzsl::StructLayout::Std140);
|
||||
std::size_t viewProjOffset = viewerDataFields.AddMatrix(nzsl::StructFieldType::Float1, 4, 4, true);
|
||||
|
||||
m_viewerData.resize(viewerDataFields.GetSize());
|
||||
AccessByOffset<Matrix4f&>(m_viewerData.data(), viewProjOffset) = viewProjMatrix;
|
||||
|
||||
m_viewerDataUpdated = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
[nzsl_version("1.0")]
|
||||
module DebugDraw;
|
||||
|
||||
[export]
|
||||
[layout(std140)]
|
||||
struct ViewerData
|
||||
{
|
||||
viewProjMatrix: mat4[f32]
|
||||
}
|
||||
|
||||
external
|
||||
{
|
||||
[binding(0)] viewerData: uniform[ViewerData],
|
||||
}
|
||||
|
||||
// Fragment stage
|
||||
struct FragIn
|
||||
{
|
||||
[location(0)] color: vec4[f32]
|
||||
}
|
||||
|
||||
struct FragOut
|
||||
{
|
||||
[location(0)] color: vec4[f32]
|
||||
}
|
||||
|
||||
[entry(frag)]
|
||||
fn main(input: FragIn) -> FragOut
|
||||
{
|
||||
let output: FragOut;
|
||||
output.color = input.color;
|
||||
return output;
|
||||
}
|
||||
|
||||
// Vertex stage
|
||||
struct VertIn
|
||||
{
|
||||
[location(0)]
|
||||
pos: vec3[f32],
|
||||
|
||||
[location(1)]
|
||||
color: vec4[f32]
|
||||
}
|
||||
|
||||
struct VertOut
|
||||
{
|
||||
[location(0)] color: vec4[f32],
|
||||
[builtin(position)] position: vec4[f32]
|
||||
}
|
||||
|
||||
[entry(vert)]
|
||||
fn main(input: VertIn) -> VertOut
|
||||
{
|
||||
let output: VertOut;
|
||||
output.position = viewerData.viewProjMatrix * vec4[f32](input.pos, 1.0);
|
||||
output.color = input.color;
|
||||
|
||||
return output;
|
||||
}
|
||||
Loading…
Reference in New Issue