mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 02:55:54 +00:00
Viewport: hidden edges are drawn before the visible ones, so a visible edge is never covered by a light hidden copy
An edge along a face seen edge on lies within a pixel of the nearer face beside it, which hides part of its width; its hidden copy, drawn after the visible edges, laid a light line over the visible edge of that nearer face (some 100 px on a box at yaw 1.565). Drawn first, it is covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3369,14 +3369,19 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
tris(pass, &res.tri_hl, &h.tris);
|
||||
pass.set_bind_group(0, scene_bind(m, &res.bind), &[]);
|
||||
lines(pass, &res.line, &m.lines);
|
||||
pass.set_bind_group(0, scene_bind(sk, &res.bind), &[]);
|
||||
lines(pass, &res.line, &sk.lines);
|
||||
// Hidden edges before the visible ones, so a visible edge is drawn over them. An edge
|
||||
// within a pixel of a nearer face's outline (a face seen edge on beside it) is hidden
|
||||
// under part of its own width; drawn after, its light hidden copy covered the visible
|
||||
// edge beside it there with a light line.
|
||||
pass.set_bind_group(0, scene_bind(m, &res.bind), &[]);
|
||||
lines(pass, &res.line_hidden, &m.hidden);
|
||||
pass.set_bind_group(0, scene_bind(h, &res.bind), &[]);
|
||||
lines(pass, &res.line_hidden, &h.hidden);
|
||||
pass.set_bind_group(0, scene_bind(m, &res.bind), &[]);
|
||||
lines(pass, &res.line, &m.lines);
|
||||
pass.set_bind_group(0, scene_bind(sk, &res.bind), &[]);
|
||||
lines(pass, &res.line, &sk.lines);
|
||||
pass.set_bind_group(0, scene_bind(h, &res.bind), &[]);
|
||||
lines(pass, &res.line, &h.lines);
|
||||
lines(pass, &res.guide_line, &h.guide_lines);
|
||||
pass.set_bind_group(0, scene_bind(pv, &res.bind), &[]);
|
||||
|
||||
@@ -575,3 +575,84 @@ fn touching_caps_are_hatched_apart_and_keep_their_hatch() {
|
||||
assert!(!oa.1, "{mode} depth: one hatch runs at 45 degrees");
|
||||
}
|
||||
}
|
||||
|
||||
/// A 40 × 20 × 30 mm box centred on the origin, a flat-shaded face per side, and its 12 edges
|
||||
/// with the outward normals of the two faces beside each.
|
||||
fn shaded_box() -> (Mesh, Vec<(Vec3, Vec3, [Vec3; 2])>) {
|
||||
let h = Vec3::new(20.0, 10.0, 15.0);
|
||||
let corner = |i: usize| Vec3::new(if i & 1 == 0 { -h.x } else { h.x }, if i & 2 == 0 { -h.y } else { h.y }, if i & 4 == 0 { -h.z } else { h.z });
|
||||
// Each side: its normal and its corners, counter-clockwise seen from outside.
|
||||
let sides = [
|
||||
(Vec3::new(-1.0, 0.0, 0.0), [0, 4, 6, 2]),
|
||||
(Vec3::new(1.0, 0.0, 0.0), [1, 3, 7, 5]),
|
||||
(Vec3::new(0.0, -1.0, 0.0), [0, 1, 5, 4]),
|
||||
(Vec3::new(0.0, 1.0, 0.0), [2, 6, 7, 3]),
|
||||
(Vec3::new(0.0, 0.0, -1.0), [0, 2, 3, 1]),
|
||||
(Vec3::new(0.0, 0.0, 1.0), [4, 5, 7, 6]),
|
||||
];
|
||||
let mut mesh = Mesh::default();
|
||||
for (f, (n, c)) in sides.iter().enumerate() {
|
||||
let base = mesh.positions.len() as u32;
|
||||
mesh.positions.extend(c.iter().map(|i| corner(*i)));
|
||||
mesh.normals.extend([*n; 4]);
|
||||
mesh.triangles.extend([[base, base + 1, base + 2], [base, base + 2, base + 3]]);
|
||||
mesh.tri_face.extend([f as u32; 2]);
|
||||
}
|
||||
let mut edges = Vec::new();
|
||||
for (i, (n, c)) in sides.iter().enumerate() {
|
||||
for k in 0..4 {
|
||||
let (a, b) = (c[k], c[(k + 1) % 4]);
|
||||
// Each edge once: from the side that runs it from the lower corner.
|
||||
if a > b {
|
||||
continue;
|
||||
}
|
||||
let other = sides.iter().enumerate().find(|(j, (_, d))| *j != i && (0..4).any(|m| d[m] == b && d[(m + 1) % 4] == a));
|
||||
if let Some((_, (m, _))) = other {
|
||||
edges.push((corner(a), corner(b), [*n, *m]));
|
||||
}
|
||||
}
|
||||
}
|
||||
(mesh, edges)
|
||||
}
|
||||
|
||||
/// The shaded-with-hidden-edges style: every edge drawn depth-tested and, in a light colour,
|
||||
/// where the model hides it (`GpuScene::hidden_line`). A box turned so one side is seen almost
|
||||
/// edge on (yaw 1.565): the side is under a pixel wide, so the edges along it lie within a pixel
|
||||
/// of the nearer face beside it, which hides part of their width. Their hidden copy shows nowhere:
|
||||
/// drawn after the visible edges, it covered the visible edge of that nearer face with a light
|
||||
/// line along some 100 px. The box's back edges, which are hidden, still show.
|
||||
#[test]
|
||||
fn a_visible_edge_beside_a_face_seen_edge_on_has_no_hidden_copy() {
|
||||
let (mesh, edges) = shaded_box();
|
||||
let (w, h) = (400, 300);
|
||||
for (mode, c) in capturers() {
|
||||
for fov in [0.0, 0.6] {
|
||||
for yaw in [1.565_f64, 0.35] {
|
||||
let cam = Camera { yaw, distance: 120.0, fov, ..Default::default() };
|
||||
let toward = |p: Vec3| if fov > 0.0 { cam.eye() - p } else { cam.back() };
|
||||
let back = |(a, b, n): &(Vec3, Vec3, [Vec3; 2])| n.iter().all(|n| n.dot(toward((*a + *b) * 0.5)) < 0.0);
|
||||
let lines = edges
|
||||
.iter()
|
||||
.map(|(a, b, _)| SceneLine { points: vec![*a, *b], color: Rgb(20, 20, 20), alpha: 1.0, width: 1.3, on_top: false })
|
||||
.collect();
|
||||
let scene =
|
||||
Scene { meshes: vec![SceneMesh::solid(Arc::new(mesh.clone()), Rgb(160, 165, 175))], lines, radius: 40.0, ..Default::default() };
|
||||
let hidden = |keep: &dyn Fn(&(Vec3, Vec3, [Vec3; 2])) -> bool| {
|
||||
let mut sc = solidcraft_gpu::view::GpuScene::default();
|
||||
let col = sc.color([0, 255, 0, 255]);
|
||||
for e in edges.iter().filter(|e| keep(e)) {
|
||||
sc.hidden_line(e.0.to_f32(), e.1.to_f32(), col, 1.0);
|
||||
}
|
||||
c.rgba_highlighted(&scene, Some(sc), &cam, w, h).unwrap()
|
||||
};
|
||||
let green = |p: [u8; 4]| p[1] > 150 && p[0] < 120 && p[2] < 120;
|
||||
let (all, back_only, none) = (hidden(&|_| true), hidden(&back), hidden(&|_| false));
|
||||
let doubled = differing(&all, &back_only);
|
||||
assert_eq!(doubled, 0, "{mode} depth, fov {fov}, yaw {yaw}: a visible edge's hidden copy shows in {doubled} px");
|
||||
let behind = pixels(&back_only).filter(|p| green(*p)).count();
|
||||
assert!(behind > 100, "{mode} depth, fov {fov}, yaw {yaw}: the back edges show in {behind} px");
|
||||
assert_eq!(pixels(&none).filter(|p| green(*p)).count(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user