summaryrefslogtreecommitdiff
path: root/src/3d/visual_debug.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3d/visual_debug.cc')
-rw-r--r--src/3d/visual_debug.cc247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/3d/visual_debug.cc b/src/3d/visual_debug.cc
new file mode 100644
index 0000000..361372b
--- /dev/null
+++ b/src/3d/visual_debug.cc
@@ -0,0 +1,247 @@
+// This file is part of the 64k demo project.
+// Implementation of visual debugging tools.
+
+#include "3d/visual_debug.h"
+
+#if !defined(STRIP_ALL)
+
+#include <cstring>
+#include <cstdio>
+
+// Simple shader for drawing colored lines
+static const char* kDebugShaderCode = R"(
+struct Uniforms {
+ viewProj : mat4x4<f32>,
+}
+@group(0) @binding(0) var<uniform> uniforms : Uniforms;
+
+struct VertexInput {
+ @location(0) position : vec3<f32>,
+ @location(1) color : vec3<f32>,
+}
+
+struct VertexOutput {
+ @builtin(position) position : vec4<f32>,
+ @location(0) color : vec3<f32>,
+}
+
+@vertex
+fn vs_main(in : VertexInput) -> VertexOutput {
+ var out : VertexOutput;
+ out.position = uniforms.viewProj * vec4<f32>(in.position, 1.0);
+ out.color = in.color;
+ return out;
+}
+
+@fragment
+fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
+ return vec4<f32>(in.color, 1.0);
+}
+)";
+
+void VisualDebug::init(WGPUDevice device, WGPUTextureFormat format) {
+ device_ = device;
+ create_pipeline(format);
+
+ // Initial capacity for vertex buffer (e.g., 1024 lines)
+ vertex_buffer_capacity_ = 1024 * 2 * sizeof(float) * 6; // 2 verts per line, 6 floats per vert
+
+ WGPUBufferDescriptor vb_desc = {};
+ vb_desc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst;
+ vb_desc.size = vertex_buffer_capacity_;
+ vertex_buffer_ = wgpuDeviceCreateBuffer(device_, &vb_desc);
+
+ WGPUBufferDescriptor ub_desc = {};
+ ub_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
+ ub_desc.size = sizeof(mat4);
+ uniform_buffer_ = wgpuDeviceCreateBuffer(device_, &ub_desc);
+}
+
+void VisualDebug::shutdown() {
+ if (pipeline_) wgpuRenderPipelineRelease(pipeline_);
+ if (bind_group_layout_) wgpuBindGroupLayoutRelease(bind_group_layout_);
+ if (vertex_buffer_) wgpuBufferRelease(vertex_buffer_);
+ if (uniform_buffer_) wgpuBufferRelease(uniform_buffer_);
+ if (bind_group_) wgpuBindGroupRelease(bind_group_);
+}
+
+void VisualDebug::create_pipeline(WGPUTextureFormat format) {
+ // Bind Group Layout
+ WGPUBindGroupLayoutEntry bgl_entry = {};
+ bgl_entry.binding = 0;
+ bgl_entry.visibility = WGPUShaderStage_Vertex;
+ bgl_entry.buffer.type = WGPUBufferBindingType_Uniform;
+ bgl_entry.buffer.minBindingSize = sizeof(mat4);
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = 1;
+ bgl_desc.entries = &bgl_entry;
+ bind_group_layout_ = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ // Pipeline Layout
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bind_group_layout_;
+ WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ // Shader
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
+ wgsl_desc.code = kDebugShaderCode;
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#else
+ WGPUShaderSourceWGSL wgsl_desc = {};
+ wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_desc.code = {kDebugShaderCode, strlen(kDebugShaderCode)};
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain;
+#endif
+ WGPUShaderModule shader_module = wgpuDeviceCreateShaderModule(device_, &shader_desc);
+
+ // Vertex State
+ WGPUVertexAttribute attributes[2];
+ // Position
+ attributes[0].format = WGPUVertexFormat_Float32x3;
+ attributes[0].offset = 0;
+ attributes[0].shaderLocation = 0;
+ // Color
+ attributes[1].format = WGPUVertexFormat_Float32x3;
+ attributes[1].offset = sizeof(float) * 3;
+ attributes[1].shaderLocation = 1;
+
+ WGPUVertexBufferLayout vertex_layout = {};
+ vertex_layout.arrayStride = sizeof(float) * 6;
+ vertex_layout.stepMode = WGPUVertexStepMode_Vertex;
+ vertex_layout.attributeCount = 2;
+ vertex_layout.attributes = attributes;
+
+ // Pipeline
+ WGPURenderPipelineDescriptor pipeline_desc = {};
+ pipeline_desc.layout = pipeline_layout;
+ pipeline_desc.vertex.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ pipeline_desc.vertex.entryPoint = "vs_main";
+#else
+ pipeline_desc.vertex.entryPoint = {"vs_main", 7};
+#endif
+ pipeline_desc.vertex.bufferCount = 1;
+ pipeline_desc.vertex.buffers = &vertex_layout;
+
+ WGPUFragmentState fragment_state = {};
+ fragment_state.module = shader_module;
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ fragment_state.entryPoint = "fs_main";
+#else
+ fragment_state.entryPoint = {"fs_main", 7};
+#endif
+ fragment_state.targetCount = 1;
+
+ WGPUColorTargetState color_target = {};
+ color_target.format = format;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ // Enable simple alpha blending if needed, but opaque lines are fine for now
+ fragment_state.targets = &color_target;
+ pipeline_desc.fragment = &fragment_state;
+
+ pipeline_desc.primitive.topology = WGPUPrimitiveTopology_LineList;
+ pipeline_desc.primitive.cullMode = WGPUCullMode_None;
+ pipeline_desc.primitive.frontFace = WGPUFrontFace_CCW;
+
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_False; // Don't write depth
+ depth_stencil.depthCompare = WGPUCompareFunction_Less; // But do test against it
+ pipeline_desc.depthStencil = &depth_stencil;
+
+ pipeline_desc.multisample.count = 1;
+ pipeline_desc.multisample.mask = 0xFFFFFFFF;
+
+ pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_desc);
+
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuShaderModuleRelease(shader_module);
+}
+
+void VisualDebug::add_box(const vec3& c, const vec3& e, const vec3& color) {
+ // 8 corners
+ vec3 p0 = vec3(c.x - e.x, c.y - e.y, c.z - e.z);
+ vec3 p1 = vec3(c.x + e.x, c.y - e.y, c.z - e.z);
+ vec3 p2 = vec3(c.x + e.x, c.y + e.y, c.z - e.z);
+ vec3 p3 = vec3(c.x - e.x, c.y + e.y, c.z - e.z);
+ vec3 p4 = vec3(c.x - e.x, c.y - e.y, c.z + e.z);
+ vec3 p5 = vec3(c.x + e.x, c.y - e.y, c.z + e.z);
+ vec3 p6 = vec3(c.x + e.x, c.y + e.y, c.z + e.z);
+ vec3 p7 = vec3(c.x - e.x, c.y + e.y, c.z + e.z);
+
+ // 12 edges (each 2 vertices)
+ DebugLine edges[] = {
+ {p0, p1, color}, {p1, p2, color}, {p2, p3, color}, {p3, p0, color}, // Front face
+ {p4, p5, color}, {p5, p6, color}, {p6, p7, color}, {p7, p4, color}, // Back face
+ {p0, p4, color}, {p1, p5, color}, {p2, p6, color}, {p3, p7, color} // Connecting edges
+ };
+
+ for (const auto& l : edges) {
+ lines_.push_back(l);
+ }
+}
+
+void VisualDebug::update_buffers(const mat4& view_proj) {
+ // Update Uniforms
+ wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), uniform_buffer_, 0, &view_proj, sizeof(mat4));
+
+ // Update Vertices
+ size_t required_size = lines_.size() * 2 * sizeof(float) * 6;
+ if (required_size > vertex_buffer_capacity_) {
+ // Resize buffer
+ wgpuBufferRelease(vertex_buffer_);
+ vertex_buffer_capacity_ = required_size * 2; // Double capacity
+ WGPUBufferDescriptor vb_desc = {};
+ vb_desc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst;
+ vb_desc.size = vertex_buffer_capacity_;
+ vertex_buffer_ = wgpuDeviceCreateBuffer(device_, &vb_desc);
+ }
+
+ if (required_size > 0) {
+ std::vector<float> vertex_data;
+ vertex_data.reserve(lines_.size() * 12); // 2 verts * 6 floats
+ for (const auto& line : lines_) {
+ vertex_data.push_back(line.start.x); vertex_data.push_back(line.start.y); vertex_data.push_back(line.start.z);
+ vertex_data.push_back(line.color.x); vertex_data.push_back(line.color.y); vertex_data.push_back(line.color.z);
+
+ vertex_data.push_back(line.end.x); vertex_data.push_back(line.end.y); vertex_data.push_back(line.end.z);
+ vertex_data.push_back(line.color.x); vertex_data.push_back(line.color.y); vertex_data.push_back(line.color.z);
+ }
+ wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), vertex_buffer_, 0, vertex_data.data(), vertex_data.size() * sizeof(float));
+ }
+
+ // Re-create bind group if needed (e.g. if uniform buffer changed, though here it's static)
+ if (!bind_group_) {
+ WGPUBindGroupEntry bg_entry = {};
+ bg_entry.binding = 0;
+ bg_entry.buffer = uniform_buffer_;
+ bg_entry.size = sizeof(mat4);
+
+ WGPUBindGroupDescriptor bg_desc = {};
+ bg_desc.layout = bind_group_layout_;
+ bg_desc.entryCount = 1;
+ bg_desc.entries = &bg_entry;
+ bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc);
+ }
+}
+
+void VisualDebug::render(WGPURenderPassEncoder pass, const mat4& view_proj) {
+ if (lines_.empty()) return;
+
+ update_buffers(view_proj);
+
+ wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr);
+ wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vertex_buffer_, 0, lines_.size() * 2 * sizeof(float) * 6);
+ wgpuRenderPassEncoderDraw(pass, (uint32_t)lines_.size() * 2, 1, 0, 0);
+
+ lines_.clear(); // Clear for next frame
+}
+
+#endif // !defined(STRIP_ALL)