diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-08 07:07:24 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-08 07:07:24 +0100 |
| commit | 59c563630ed15fa60b393a9d11c25f4d35668f5a (patch) | |
| tree | 4b94ebb8b36cf0acbe1cb07c8bb8e46e77855f96 | |
| parent | 1bc1cf8cd2c66bbae615a5ddba883b7cd55bd67f (diff) | |
feat(3d): Implement Visual Debug primitives (Sphere, Cone, Cross, Trajectory)
| -rw-r--r-- | doc/VISUAL_DEBUG.md | 48 | ||||
| -rw-r--r-- | src/3d/visual_debug.cc | 75 | ||||
| -rw-r--r-- | src/3d/visual_debug.h | 6 | ||||
| -rw-r--r-- | src/tests/test_3d_render.cc | 16 |
4 files changed, 145 insertions, 0 deletions
diff --git a/doc/VISUAL_DEBUG.md b/doc/VISUAL_DEBUG.md new file mode 100644 index 0000000..f0adcba --- /dev/null +++ b/doc/VISUAL_DEBUG.md @@ -0,0 +1,48 @@ +# Visual Debugging System + +The `VisualDebug` class provides immediate-mode style 3D wireframe rendering for debugging purposes. It is stripped from the final binary when `STRIP_ALL` is defined. + +## Features + +- **Wireframe Primitives**: Boxes, AABBs, Spheres, Cones, Crosses, Lines. +- **Trajectories**: Visualize paths with `add_trajectory`. +- **Mesh Normals**: Visualize vertex normals. +- **Zero Overhead**: Code is compiled out in release builds. + +## Usage + +Access the instance via `Renderer3D::GetVisualDebug()` (only available if `!STRIP_ALL`). + +```cpp +#if !defined(STRIP_ALL) + VisualDebug& dbg = renderer.GetVisualDebug(); + + // Draw a red box at origin + dbg.add_box(mat4::identity(), vec3(1,1,1), vec3(1,0,0)); + + // Draw a trajectory + std::vector<vec3> path = { ... }; + dbg.add_trajectory(path, vec3(0,1,0)); + + // Draw a light cone + dbg.add_cone(light_pos, light_dir, range, radius, vec3(1,1,0)); +#endif +``` + +## Primitives + +- `add_line(start, end, color)`: Basic line segment. +- `add_cross(center, size, color)`: 3D cross (useful for points). +- `add_sphere(center, radius, color)`: Wireframe sphere (3 axis circles). +- `add_cone(apex, dir, height, radius, color)`: Wireframe cone (useful for spotlights). +- `add_box(transform, half_extent, color)`: OBB. +- `add_aabb(min, max, color)`: Axis-aligned box. +- `add_trajectory(points, color)`: Polyline. + +## Integration + +The `VisualDebug::render` method is called automatically by `Renderer3D::draw` if `s_debug_enabled_` is true. +To enable globally: +```cpp +Renderer3D::SetDebugEnabled(true); +``` diff --git a/src/3d/visual_debug.cc b/src/3d/visual_debug.cc index 9667d87..3f5778a 100644 --- a/src/3d/visual_debug.cc +++ b/src/3d/visual_debug.cc @@ -225,6 +225,81 @@ void VisualDebug::add_mesh_normals(const mat4& transform, uint32_t num_vertices, } } +void VisualDebug::add_line(const vec3& start, const vec3& end, const vec3& color) { + lines_.push_back({start, end, color}); +} + +void VisualDebug::add_cross(const vec3& center, float size, const vec3& color) { + float s = size * 0.5f; + add_line(center - vec3(s, 0, 0), center + vec3(s, 0, 0), color); + add_line(center - vec3(0, s, 0), center + vec3(0, s, 0), color); + add_line(center - vec3(0, 0, s), center + vec3(0, 0, s), color); +} + +void VisualDebug::add_sphere(const vec3& center, float radius, const vec3& color) { + const int kSegments = 16; + const float kStep = 6.2831853f / kSegments; + + auto draw_circle = [&](int axis1, int axis2) { + for (int i = 0; i < kSegments; ++i) { + float a1 = i * kStep; + float a2 = (i + 1) * kStep; + vec3 p1 = center; + vec3 p2 = center; + + // axis mapping: 0=x, 1=y, 2=z + p1[axis1] += std::cos(a1) * radius; + p1[axis2] += std::sin(a1) * radius; + + p2[axis1] += std::cos(a2) * radius; + p2[axis2] += std::sin(a2) * radius; + + add_line(p1, p2, color); + } + }; + + draw_circle(0, 1); // XY + draw_circle(0, 2); // XZ + draw_circle(1, 2); // YZ +} + +void VisualDebug::add_cone(const vec3& apex, const vec3& dir, float height, float radius, const vec3& color) { + vec3 d = dir.normalize(); + vec3 base_center = apex + d * height; + + // Rotation to align (0, 1, 0) with d + quat q = quat::from_to(vec3(0, 1, 0), d); + + const int kSegments = 16; + const float kStep = 6.2831853f / kSegments; + + for (int i = 0; i < kSegments; ++i) { + float a1 = i * kStep; + float a2 = (i + 1) * kStep; + + // Circle points in XZ plane (local space, y=0) + vec3 p1_local(std::cos(a1) * radius, 0, std::sin(a1) * radius); + vec3 p2_local(std::cos(a2) * radius, 0, std::sin(a2) * radius); + + // Rotate and translate to base_center + vec3 p1 = base_center + q.rotate(p1_local); + vec3 p2 = base_center + q.rotate(p2_local); + + add_line(p1, p2, color); // Base circle + + // Connect to apex (draw every 4th segment to avoid clutter) + if (i % 4 == 0) { + add_line(apex, p1, color); + } + } +} + +void VisualDebug::add_trajectory(const std::vector<vec3>& points, const vec3& color) { + if (points.size() < 2) return; + for (size_t i = 0; i < points.size() - 1; ++i) { + add_line(points[i], points[i + 1], color); + } +} void VisualDebug::update_buffers(const mat4& view_proj) { // Update Uniforms diff --git a/src/3d/visual_debug.h b/src/3d/visual_debug.h index 505a799..7f1aa8b 100644 --- a/src/3d/visual_debug.h +++ b/src/3d/visual_debug.h @@ -31,6 +31,12 @@ class VisualDebug { void add_mesh_normals(const mat4& transform, uint32_t num_vertices, const MeshVertex* vertices); + void add_line(const vec3& start, const vec3& end, const vec3& color); + void add_cross(const vec3& center, float size, const vec3& color); + void add_sphere(const vec3& center, float radius, const vec3& color); + void add_cone(const vec3& apex, const vec3& dir, float height, float radius, const vec3& color); + void add_trajectory(const std::vector<vec3>& points, const vec3& color); + // Render all queued primitives and clear the queue void render(WGPURenderPassEncoder pass, const mat4& view_proj); diff --git a/src/tests/test_3d_render.cc b/src/tests/test_3d_render.cc index d9fb118..a7c74e1 100644 --- a/src/tests/test_3d_render.cc +++ b/src/tests/test_3d_render.cc @@ -270,6 +270,22 @@ int main(int argc, char** argv) { #if !defined(STRIP_ALL) Renderer3D::SetDebugEnabled(true); + VisualDebug& dbg = g_renderer.GetVisualDebug(); + dbg.add_cross(vec3(0, 0, 0), 1.0f, vec3(1, 0, 0)); + dbg.add_sphere(vec3(std::sin(time) * 2.0f, 3.0f, std::cos(time) * 2.0f), 0.5f, + vec3(0, 1, 1)); + dbg.add_line(vec3(0, 0, 0), vec3(0, 5, 0), vec3(1, 0, 1)); + + // Cone (Spotlight visualization) + dbg.add_cone(vec3(0, 5, 0), vec3(0, -1, 0), 2.0f, 1.0f, vec3(1, 1, 0)); + + // Trajectory path + std::vector<vec3> path; + for(int i=0; i<=32; ++i) { + float a = i * 6.28318f / 32.0f; + path.push_back(vec3(std::sin(a)*4.0f, 0.5f, std::cos(a)*4.0f)); + } + dbg.add_trajectory(path, vec3(0, 0.5f, 1.0f)); #endif WGPUSurfaceTexture surface_tex; |
