diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index 6647bd6ce..82abcc51e 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -744,6 +744,9 @@ impl Scene { let mut uniforms = Uniforms::new(&inst.camera, full_bounds, self.document.header.lineweight_display); uniforms.view_proj = crop_view_proj(uniforms.view_proj, uo, vo, us, vs); + // The RTE matrix is a view-projection too, so the same clip-space crop + // applies — keep it in lock-step with view_proj for tiled / sub-rect panes. + uniforms.view_rot = crop_view_proj(uniforms.view_rot, uo, vo, us, vs); uniforms.viewport_size = [visible_w, visible_h]; uniforms.flat_shade = if flags.flat_shade { 1.0 } else { 0.0 }; uniforms.transparency_enable = if self.transparency_display { 1.0 } else { 0.0 }; diff --git a/src/shaders/wire.wgsl b/src/shaders/wire.wgsl index 50273a455..63ac1daa7 100644 --- a/src/shaders/wire.wgsl +++ b/src/shaders/wire.wgsl @@ -28,6 +28,17 @@ struct Uniforms { // Transparency-display toggle: 1.0 = honour baked alpha, 0.0 = force // every line opaque. transparency_enable: f32, + _pad: vec2, + // ── Relative-to-eye (double-single) ────────────────────────────────── + // view_rot is the rotation-only view-projection; vertices subtract the eye + // (eye_high + eye_low, two f32 emulating f64) before transforming, so the + // large eye translation never enters the f32 matrix → no large-coordinate + // jitter on pan / zoom / rotate. + view_rot: mat4x4, + eye_high: vec3, + _pad_eh: f32, + eye_low: vec3, + _pad_el: f32, } @group(0) @binding(0) var u: Uniforms; @@ -74,8 +85,15 @@ struct VertexOut { let which_end = which_end_arr[vid]; let side = side_arr[vid]; - let clip_a = u.view_proj * vec4(in.pos_a, 1.0); - let clip_b = u.view_proj * vec4(in.pos_b, 1.0); + // Relative-to-eye: subtract the eye (high+low) from each endpoint, then + // transform by the rotation-only view-projection. (pos − eye_high) is exact + // in f32 when both are the same magnitude (Sterbenz), and eye_low corrects + // the eye's sub-f32 residual — so the position tracks the camera smoothly + // at large coordinates instead of snapping onto the f32 grid each frame. + let rel_a = (in.pos_a - u.eye_high) - u.eye_low; + let rel_b = (in.pos_b - u.eye_high) - u.eye_low; + let clip_a = u.view_rot * vec4(rel_a, 1.0); + let clip_b = u.view_rot * vec4(rel_b, 1.0); // NDC of both endpoints. let ndc_a = clip_a.xy / clip_a.w;