summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GEMINI.md1
-rw-r--r--TODO.md2
-rw-r--r--doc/AI_RULES.md1
-rw-r--r--doc/COMPLETED.md1
-rw-r--r--src/3d/renderer.cc14
-rw-r--r--src/3d/visual_debug.cc26
-rw-r--r--src/3d/visual_debug.h3
-rw-r--r--src/tests/test_mesh.cc26
8 files changed, 66 insertions, 8 deletions
diff --git a/GEMINI.md b/GEMINI.md
index c57c12d..dec6c71 100644
--- a/GEMINI.md
+++ b/GEMINI.md
@@ -67,6 +67,7 @@ IMPORTANT:
- Work only on tasks explicitly requested by the user
- Do NOT modify files outside the current scope
- Do NOT perform refactors or cleanups unless explicitly asked
+- **Always use `-j4` for all `cmake --build` commands.**
# Context Maintenance:
- See @doc/CONTEXT_MAINTENANCE.md for keeping context clean
diff --git a/TODO.md b/TODO.md
index 8e02610..5ef2254 100644
--- a/TODO.md
+++ b/TODO.md
@@ -246,7 +246,7 @@ This file tracks prioritized tasks with detailed attack plans.
- Increased particle usage: Added 5 ParticleSprayEffect instances throughout demo (6b, 12b, 17b, 24b, 56b)
- Result: Particles now render as fading transparent circles instead of opaque squares
- [ ] **Task #55: SDF Random Planes Intersection**: Implement `sdPolyhedron` (crystal/gem shapes) via plane intersection.
-- [ ] **Task #68: Visual Debug - Mesh Wireframe Rendering**: Show triangle edges as lines for mesh objects
+- [x] **Task #68: Visual Debug - Mesh Wireframe Rendering**: Show triangle edges as lines for mesh objects
- **Current**: Visual debug mode shows normals for all objects (SDF and meshes)
- **Goal**: Add wireframe overlay for mesh objects to visualize triangle structure
- **Implementation**:
diff --git a/doc/AI_RULES.md b/doc/AI_RULES.md
index c526a39..d18a0cc 100644
--- a/doc/AI_RULES.md
+++ b/doc/AI_RULES.md
@@ -3,4 +3,5 @@
- No refactors unless explicitly requested
- All changes must keep tests passing
- Prefer small, reviewable commits
+- All `cmake --build` commands must use the `-j4` option for parallel building.
- after a task, a 'big' final commit should contain a short handoff tag like "handoff(Gemini):..." if you're gemini-cli, or "handoff(Claude): ..." if you're claude-code.
diff --git a/doc/COMPLETED.md b/doc/COMPLETED.md
index 2b7848f..cd448bf 100644
--- a/doc/COMPLETED.md
+++ b/doc/COMPLETED.md
@@ -269,3 +269,4 @@ This file tracks recently completed tasks, organized by completion date.
- **Unified 3D Shadows**: Implemented robust SDF shadows across all objects using `inv_model` transforms.
- **test_mesh tool**: Implemented a standalone `test_mesh` tool for visualizing OBJ files with debug normal display.
- **Task #39: Visual Debugging System**: Implemented a comprehensive set of wireframe primitives (Sphere, Cone, Cross, Line, Trajectory) in `VisualDebug`. Updated `test_3d_render` to demonstrate usage.
+- **Task #68: Mesh Wireframe Rendering**: Added `add_mesh_wireframe` to `VisualDebug` to visualize triangle edges for mesh objects. Integrated into `Renderer3D` debug path and `test_mesh` tool.
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index eea3ff0..6e8f38a 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -306,11 +306,19 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene,
vec3 extent = obj.local_extent;
if (obj.type == ObjectType::TORUS) {
extent = vec3(1.5f, 0.5f, 1.5f);
- } else if (obj.type != ObjectType::MESH) {
+ } else if (obj.type == ObjectType::MESH) {
+ MeshAsset mesh = GetMeshAsset(obj.mesh_asset_id);
+ if (mesh.num_indices > 0) {
+ visual_debug_.add_mesh_wireframe(obj.get_model_matrix(), mesh.num_vertices, mesh.vertices, mesh.num_indices, mesh.indices, vec3(0.0f, 1.0f, 1.0f)); // Cyan wireframe
+ }
+ } else {
extent = vec3(1.0f, 1.0f, 1.0f);
}
- visual_debug_.add_box(obj.get_model_matrix(), extent,
- vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes
+
+ if (obj.type != ObjectType::MESH) {
+ visual_debug_.add_box(obj.get_model_matrix(), extent,
+ vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes
+ }
}
// Calculate ViewProj matrix for the debug renderer
diff --git a/src/3d/visual_debug.cc b/src/3d/visual_debug.cc
index 3f5778a..78c751b 100644
--- a/src/3d/visual_debug.cc
+++ b/src/3d/visual_debug.cc
@@ -225,6 +225,32 @@ void VisualDebug::add_mesh_normals(const mat4& transform, uint32_t num_vertices,
}
}
+void VisualDebug::add_mesh_wireframe(const mat4& transform, uint32_t num_vertices,
+ const MeshVertex* vertices, uint32_t num_indices,
+ const uint32_t* indices, const vec3& color) {
+ if (!vertices || !indices || num_indices == 0)
+ return;
+
+ for (uint32_t i = 0; i < num_indices; i += 3) {
+ if (i + 2 >= num_indices) break;
+
+ uint32_t idx0 = indices[i];
+ uint32_t idx1 = indices[i + 1];
+ uint32_t idx2 = indices[i + 2];
+
+ if (idx0 >= num_vertices || idx1 >= num_vertices || idx2 >= num_vertices)
+ continue;
+
+ vec4 p0_world = transform * vec4(vertices[idx0].p[0], vertices[idx0].p[1], vertices[idx0].p[2], 1.0f);
+ vec4 p1_world = transform * vec4(vertices[idx1].p[0], vertices[idx1].p[1], vertices[idx1].p[2], 1.0f);
+ vec4 p2_world = transform * vec4(vertices[idx2].p[0], vertices[idx2].p[1], vertices[idx2].p[2], 1.0f);
+
+ add_line(p0_world.xyz(), p1_world.xyz(), color);
+ add_line(p1_world.xyz(), p2_world.xyz(), color);
+ add_line(p2_world.xyz(), p0_world.xyz(), color);
+ }
+}
+
void VisualDebug::add_line(const vec3& start, const vec3& end, const vec3& color) {
lines_.push_back({start, end, color});
}
diff --git a/src/3d/visual_debug.h b/src/3d/visual_debug.h
index 7f1aa8b..c8069e1 100644
--- a/src/3d/visual_debug.h
+++ b/src/3d/visual_debug.h
@@ -30,6 +30,9 @@ class VisualDebug {
void add_aabb(const vec3& min, const vec3& max, const vec3& color);
void add_mesh_normals(const mat4& transform, uint32_t num_vertices, const MeshVertex* vertices);
+ void add_mesh_wireframe(const mat4& transform, uint32_t num_vertices,
+ const MeshVertex* vertices, uint32_t num_indices,
+ const uint32_t* indices, const vec3& color);
void add_line(const vec3& start, const vec3& end, const vec3& color);
void add_cross(const vec3& center, float size, const vec3& color);
diff --git a/src/tests/test_mesh.cc b/src/tests/test_mesh.cc
index 7f898c4..992471a 100644
--- a/src/tests/test_mesh.cc
+++ b/src/tests/test_mesh.cc
@@ -242,8 +242,16 @@ bool load_obj_and_create_buffers(const char* path, Object3D& out_obj) {
g_mesh_gpu_data.vertex_buffer = gpu_create_buffer(g_device, final_vertices.size() * sizeof(MeshVertex), WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst, final_vertices.data()).buffer;
g_mesh_gpu_data.index_buffer = gpu_create_buffer(g_device, final_indices.size() * sizeof(uint32_t), WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst, final_indices.data()).buffer;
+ struct MeshData {
+ std::vector<MeshVertex> vertices;
+ std::vector<uint32_t> indices;
+ };
+ MeshData* mesh_data = new MeshData();
+ mesh_data->vertices = final_vertices;
+ mesh_data->indices = final_indices;
+
out_obj.type = ObjectType::MESH;
- out_obj.user_data = new std::vector<MeshVertex>(final_vertices);
+ out_obj.user_data = mesh_data;
// This test doesn't use the asset system, so we override the renderer's internal cache lookup
// by manually setting the buffers on the renderer object. This is a HACK for this specific tool.
@@ -314,8 +322,14 @@ int main(int argc, char** argv) {
#if !defined(STRIP_ALL)
if (debug_mode) {
- auto* vertices = (std::vector<MeshVertex>*)g_scene.objects[1].user_data;
- g_renderer.GetVisualDebug().add_mesh_normals(g_scene.objects[1].get_model_matrix(), (uint32_t)vertices->size(), vertices->data());
+ struct MeshData {
+ std::vector<MeshVertex> vertices;
+ std::vector<uint32_t> indices;
+ };
+ auto* data = (MeshData*)g_scene.objects[1].user_data;
+ VisualDebug& dbg = g_renderer.GetVisualDebug();
+ dbg.add_mesh_normals(g_scene.objects[1].get_model_matrix(), (uint32_t)data->vertices.size(), data->vertices.data());
+ dbg.add_mesh_wireframe(g_scene.objects[1].get_model_matrix(), (uint32_t)data->vertices.size(), data->vertices.data(), (uint32_t)data->indices.size(), data->indices.data(), vec3(0.0f, 1.0f, 1.0f));
}
#endif /* !defined(STRIP_ALL) */
@@ -334,7 +348,11 @@ int main(int argc, char** argv) {
Renderer3D::SetDebugEnabled(false); // Reset debug mode
#endif
- delete (std::vector<MeshVertex>*)g_scene.objects[1].user_data;
+ struct MeshData {
+ std::vector<MeshVertex> vertices;
+ std::vector<uint32_t> indices;
+ };
+ delete (MeshData*)g_scene.objects[1].user_data;
wgpuBufferRelease(g_mesh_gpu_data.vertex_buffer);
wgpuBufferRelease(g_mesh_gpu_data.index_buffer);