From 19cc2e1073888cd0c68fef035ab1835342f47616 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 15 Oct 2026 21:07:05 +0300 Subject: [PATCH] fix(render): colour-7 solid edges contrast with a layout's paper A solid's edges and silhouettes were drawn in its face colour, and the edge shader read that from the material instead of the edge vertices. Colour 7 is white, so in Wireframe 2D a solid vanished white-on-white in a layout viewport (and on a light model background), while wires and block solids already turned black there. Solids now keep a separate edge colour, colour 7 turned against the background they are drawn on, and the edge shader uses the edge vertices' colour again, which also brings back visual-style edge colours. Shaded faces keep their colour so they still read as lit. (#1446) Co-Authored-By: Claude Opus 5.5 --- src/app/commands/display.rs | 1 + src/scene/mod.rs | 7 +++++++ src/scene/model/mesh_model.rs | 4 ++++ src/scene/pipeline/mesh_gpu.rs | 10 ++++------ src/scene/pipeline/mod.rs | 5 ++++- src/shaders/mesh.wgsl | 5 ++++- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/app/commands/display.rs b/src/app/commands/display.rs index eb23461ba..16cb0f5f2 100644 --- a/src/app/commands/display.rs +++ b/src/app/commands/display.rs @@ -2549,6 +2549,7 @@ mod tests { instance_transform: None, instance_handle: None, instance_color: None, + edge_color: None, instance_aabb: None, } } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 5df8c9f19..ede21eb85 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1991,6 +1991,7 @@ fn transform_block_mesh_lod_set(set: &MeshLodSet, xform: &codec::types::Transfor instance_transform: Some(*xform), instance_handle: None, instance_color: set.display_color(), + edge_color: set.edge_color, instance_aabb: bounds .iter() .all(|value| value.is_finite()) @@ -8994,6 +8995,12 @@ impl Scene { entity, viewport, ); + // Colour 7 lines contrast with what the solid is drawn + // on; shaded faces keep the colour so they still read as + // lit. White edges on a white sheet hid a wireframe solid + // in a layout viewport. (#1446) + let edge = crate::scene::view::render::adapt_to_bg(style.0, self.current_bg()); + set.edge_color = (edge != style.0).then_some(edge); let attached_material = set .material .as_ref() diff --git a/src/scene/model/mesh_model.rs b/src/scene/model/mesh_model.rs index 39aad9b3f..d81130af4 100644 --- a/src/scene/model/mesh_model.rs +++ b/src/scene/model/mesh_model.rs @@ -145,6 +145,9 @@ pub struct MeshLodSet { pub instance_handle: Option, /// Effective colour after INSERT inheritance, without copying the mesh. pub instance_color: Option<[f32; 4]>, + /// Colour of the edge and silhouette lines when it differs from the + /// faces': colour 7 drawn against the background it sits on. + pub edge_color: Option<[f32; 4]>, /// Precise world bounds used by the interaction index. pub instance_aabb: Option<[f64; 6]>, } @@ -249,6 +252,7 @@ impl MeshLodSet { instance_transform: None, instance_handle: None, instance_color: None, + edge_color: None, instance_aabb: None, } } diff --git a/src/scene/pipeline/mesh_gpu.rs b/src/scene/pipeline/mesh_gpu.rs index a27384f8e..bec6b7492 100644 --- a/src/scene/pipeline/mesh_gpu.rs +++ b/src/scene/pipeline/mesh_gpu.rs @@ -1412,13 +1412,12 @@ fn build_instanced_chunks( Vec::new() }; let vertices_at = profile_enabled.then(iced::time::Instant::now); + let edge_base = first.set.edge_color.unwrap_or(first.display_color); let edge_color = first .set .visual_style .as_ref() - .map_or(first.display_color, |style| { - style.edge_color(first.display_color) - }); + .map_or(edge_base, |style| style.edge_color(edge_base)); let edge_key = (source_handle, edge_color.map(f32::to_bits)); let shared_edge_buffer = (first.include_edges && !split_geometry) .then(|| buffers.edges.get(&edge_key).cloned()) @@ -2215,12 +2214,11 @@ pub fn build_mesh_batch_filtered( let uv_mesh = part.uv_mesh; let local_bounds = mesh_bounds(uv_mesh); let model_bounds = mesh_bounds(mesh); + let edge_base = set.edge_color.unwrap_or(part.display_color); let edge_color = set .visual_style .as_ref() - .map_or(part.display_color, |style| { - style.edge_color(part.display_color) - }); + .map_or(edge_base, |style| style.edge_color(edge_base)); let vtx = |vi: usize, local_mapping_normal: Option<[f32; 3]>, model_mapping_normal: Option<[f32; 3]>| { diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 254f55c0b..f669d3267 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -3191,7 +3191,10 @@ analytic={:.1} regular={:.1} blocks={:.1}", let mut slots = rustc_hash::FxHashMap::default(); let mut groups: Vec> = Vec::new(); for (index, set) in sets.iter().enumerate() { - let color = set.display_color().unwrap_or([0.0, 0.0, 0.0, 1.0]); + let color = set + .edge_color + .or_else(|| set.display_color()) + .unwrap_or([0.0, 0.0, 0.0, 1.0]); let key = match (&set.instance_source, set.instance_transform) { (Some(source), Some(transform)) => { let matrix = &transform.matrix.m; diff --git a/src/shaders/mesh.wgsl b/src/shaders/mesh.wgsl index 91751a8aa..dbf2e6299 100644 --- a/src/shaders/mesh.wgsl +++ b/src/shaders/mesh.wgsl @@ -594,7 +594,10 @@ fn fs_main(in: VertexOut) -> @location(0) vec4 { // wireframe and hidden-line edge passes so lines read at their true colour. @fragment fn fs_edge(in: EdgeVertexOut) -> @location(0) vec4 { - return vec4(surface.face_color.rgb, 1.0); + // The edge buffers carry their own colour (keyed by it, so instances + // share a buffer only with the same colour): a visual style's edge + // colour, or colour 7 turned against the background (#1446). + return vec4(in.color.rgb, 1.0); } // Edge fragment for filled render modes: force black so edges frame the shaded