summaryrefslogtreecommitdiff
path: root/src/3d
diff options
context:
space:
mode:
Diffstat (limited to 'src/3d')
-rw-r--r--src/3d/renderer.cc25
-rw-r--r--src/3d/renderer.h13
-rw-r--r--src/3d/visual_debug.cc247
-rw-r--r--src/3d/visual_debug.h49
4 files changed, 334 insertions, 0 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index db9d73d..adc9a5f 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -7,6 +7,10 @@
#include <cstring>
#include <iostream>
+#if !defined(STRIP_ALL)
+bool Renderer3D::s_debug_enabled_ = false;
+#endif
+
static const char* kShaderCode = R"(
struct GlobalUniforms {
view_proj: mat4x4<f32>,
@@ -242,9 +246,17 @@ void Renderer3D::init(WGPUDevice device, WGPUQueue queue,
create_default_resources();
create_pipeline();
+
+#if !defined(STRIP_ALL)
+ visual_debug_.init(device_, format_);
+#endif
}
void Renderer3D::shutdown() {
+#if !defined(STRIP_ALL)
+ visual_debug_.shutdown();
+#endif
+
if (default_sampler_)
wgpuSamplerRelease(default_sampler_);
if (pipeline_)
@@ -482,6 +494,19 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene,
if (instance_count > 0) {
wgpuRenderPassEncoderDraw(pass, 36, instance_count, 0, 0);
}
+
+#if !defined(STRIP_ALL)
+ if (s_debug_enabled_) {
+ for (const auto& obj : scene.objects) {
+ // Simple AABB approximation from scale
+ visual_debug_.add_box(obj.position, obj.scale, vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes
+ }
+
+ // Calculate ViewProj matrix for the debug renderer
+ mat4 view_proj = camera.get_projection_matrix() * camera.get_view_matrix();
+ visual_debug_.render(pass, view_proj);
+ }
+#endif
}
void Renderer3D::render(const Scene& scene, const Camera& camera, float time,
diff --git a/src/3d/renderer.h b/src/3d/renderer.h
index 8cb379b..453daf2 100644
--- a/src/3d/renderer.h
+++ b/src/3d/renderer.h
@@ -9,6 +9,10 @@
#include "gpu/gpu.h"
#include <vector>
+#if !defined(STRIP_ALL)
+#include "3d/visual_debug.h"
+#endif
+
// Matches the GPU struct layout
struct GlobalUniforms {
mat4 view_proj;
@@ -28,6 +32,10 @@ class Renderer3D {
void init(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format);
void shutdown();
+#if !defined(STRIP_ALL)
+ static void SetDebugEnabled(bool enabled) { s_debug_enabled_ = enabled; }
+#endif
+
// Renders the scene to the given texture view (Convenience: creates a pass)
void render(const Scene& scene, const Camera& camera, float time,
@@ -75,4 +83,9 @@ class Renderer3D {
// Max objects capacity
static const int kMaxObjects = 100;
+
+#if !defined(STRIP_ALL)
+ VisualDebug visual_debug_;
+ static bool s_debug_enabled_;
+#endif
};
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)
diff --git a/src/3d/visual_debug.h b/src/3d/visual_debug.h
new file mode 100644
index 0000000..c757de3
--- /dev/null
+++ b/src/3d/visual_debug.h
@@ -0,0 +1,49 @@
+// This file is part of the 64k demo project.
+// It provides visual debugging tools, such as wireframe rendering.
+// This entire module is excluded when STRIP_ALL is defined.
+
+#pragma once
+
+#if !defined(STRIP_ALL)
+
+#include "util/mini_math.h"
+#include "gpu/gpu.h"
+#include <vector>
+
+struct DebugLine {
+ vec3 start;
+ vec3 end;
+ vec3 color;
+};
+
+class VisualDebug {
+public:
+ void init(WGPUDevice device, WGPUTextureFormat format);
+ void shutdown();
+
+ // Queue a wireframe box for rendering this frame
+ void add_box(const vec3& center, const vec3& extent, const vec3& color);
+
+ // Render all queued primitives and clear the queue
+ void render(WGPURenderPassEncoder pass, const mat4& view_proj);
+
+private:
+ WGPUDevice device_ = nullptr;
+ WGPURenderPipeline pipeline_ = nullptr;
+ WGPUBindGroupLayout bind_group_layout_ = nullptr;
+
+ std::vector<DebugLine> lines_;
+
+ // Uniform buffer for ViewProjection matrix
+ WGPUBuffer uniform_buffer_ = nullptr;
+ WGPUBindGroup bind_group_ = nullptr;
+
+ // Vertex buffer for line segments
+ WGPUBuffer vertex_buffer_ = nullptr;
+ size_t vertex_buffer_capacity_ = 0;
+
+ void create_pipeline(WGPUTextureFormat format);
+ void update_buffers(const mat4& view_proj);
+};
+
+#endif // !defined(STRIP_ALL)