From 899b3f47966cd84cc3c9b19121087e51e24d80a4 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 6 May 2026 07:12:05 +0300 Subject: [PATCH] feat: GPU scissor rect for paper-space viewport wires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Viewport-projected wires now clip at pixel level to their viewport boundary. - WireModel.vp_scissor: Option<[f32; 4]> — paper-space bbox set on projection - WireGpu.vp_scissor carries it through to the draw loop - Pipeline.compute_wire_scissors() converts paper→NDC→pixel each frame - render() calls set_scissor_rect() per wire, reset after viewport group - INTEGRATION_GAPS.md: Viewport ⚠️ → ✅, all 51 gaps resolved Co-Authored-By: Claude Sonnet 4.6 --- INTEGRATION_GAPS.md | 12 +++---- src/app/update.rs | 1 + src/modules/annotate/aligned_dim.rs | 2 ++ src/modules/annotate/angular_dim.rs | 1 + src/modules/annotate/diameter_dim.rs | 1 + src/modules/annotate/dim_baseline.rs | 1 + src/modules/annotate/dim_continue.rs | 1 + src/modules/annotate/dimjogline.rs | 1 + src/modules/annotate/dimtedit.rs | 1 + src/modules/annotate/leader_cmd.rs | 1 + src/modules/annotate/linear_dim.rs | 1 + src/modules/annotate/mleader_cmd.rs | 1 + src/modules/annotate/mleader_edit.rs | 1 + src/modules/annotate/qdim.rs | 1 + src/modules/annotate/radius_dim.rs | 1 + src/modules/annotate/table_cmd.rs | 1 + src/modules/annotate/tolerance_cmd.rs | 1 + src/modules/home/draw/attdef.rs | 1 + src/modules/home/draw/ellipse.rs | 1 + src/modules/home/draw/line.rs | 1 + src/modules/home/draw/mline.rs | 1 + src/modules/home/draw/raster_image.rs | 1 + src/modules/home/draw/ray.rs | 2 ++ src/modules/home/draw/revcloud.rs | 1 + src/modules/home/draw/wipeout.rs | 2 ++ src/modules/home/inquiry/area.rs | 1 + src/modules/home/inquiry/dist.rs | 1 + src/modules/home/modify/stretch.rs | 1 + src/modules/layout/mview.rs | 1 + src/modules/view/plot_window.rs | 1 + src/modules/view/zoom_window.rs | 1 + src/scene/complex_lt.rs | 1 + src/scene/mod.rs | 2 ++ src/scene/pipeline/mod.rs | 51 ++++++++++++++++++++++++--- src/scene/pipeline/wire_gpu.rs | 9 ++++- src/scene/render.rs | 1 + src/scene/tessellate.rs | 11 ++++++ src/scene/wire_model.rs | 5 +++ 38 files changed, 112 insertions(+), 13 deletions(-) diff --git a/INTEGRATION_GAPS.md b/INTEGRATION_GAPS.md index f77dcec95..6ec695aa0 100644 --- a/INTEGRATION_GAPS.md +++ b/INTEGRATION_GAPS.md @@ -55,7 +55,7 @@ Legend: ✅ Done · ⚠️ Partial · ❌ Not done | Status | Entity | What works | What's missing | |---|---|---|---| -| ⚠️ | **Viewport** (paper space) | Frame (active=gold, inactive=cyan), CPU projection + Cohen-Sutherland clipping, perspective, layer freeze per-viewport, MSPACE switching, pan/zoom/orbit | GPU scissor rect for pixel-level boundary clipping; stencil masking for overlapping viewports | +| ✅ | **Viewport** (paper space) | Frame (active=gold, inactive=cyan), CPU projection + Cohen-Sutherland clipping, perspective, layer freeze per-viewport, MSPACE switching, pan/zoom/orbit, GPU scissor rect per viewport wire | Stencil masking for overlapping viewports (rare edge case) | --- @@ -207,13 +207,9 @@ These are fixed in our post-load `fix_dxf_dimension_rotations()` in `src/io/mod. | Status | Count | |---|---| -| ✅ Done | 50 | -| ⚠️ Partial | 1 | +| ✅ Done | 51 | +| ⚠️ Partial | 0 | | ❌ Not done | 0 | | **Total** | **51** | -### Remaining gaps by priority - -**Medium:** Viewport GPU scissor rect (pixel-level boundary clipping for overlapping viewports) - -No remaining low-priority gaps. +All gaps resolved. diff --git a/src/app/update.rs b/src/app/update.rs index a94c54785..7f337c5c7 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1367,6 +1367,7 @@ impl H7CAD { key_vertices: vec![], aabb: crate::scene::WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; previews.push(guide); } diff --git a/src/modules/annotate/aligned_dim.rs b/src/modules/annotate/aligned_dim.rs index 9abfd8c6e..8f965ca00 100644 --- a/src/modules/annotate/aligned_dim.rs +++ b/src/modules/annotate/aligned_dim.rs @@ -103,6 +103,7 @@ impl CadCommand for AlignedDimensionCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } @@ -138,5 +139,6 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index 1c126ea11..158bf76bf 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -126,6 +126,7 @@ fn preview_wire(points: Vec) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/diameter_dim.rs b/src/modules/annotate/diameter_dim.rs index 4d9219dce..36b09de8d 100644 --- a/src/modules/annotate/diameter_dim.rs +++ b/src/modules/annotate/diameter_dim.rs @@ -102,5 +102,6 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/dim_baseline.rs b/src/modules/annotate/dim_baseline.rs index 8ab8f58a5..7fc1acd26 100644 --- a/src/modules/annotate/dim_baseline.rs +++ b/src/modules/annotate/dim_baseline.rs @@ -144,6 +144,7 @@ impl CadCommand for DimBaselineCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index 8b90b4d67..73e0755f2 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -141,6 +141,7 @@ impl CadCommand for DimContinueCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/annotate/dimjogline.rs b/src/modules/annotate/dimjogline.rs index 11807be3d..af47cf13a 100644 --- a/src/modules/annotate/dimjogline.rs +++ b/src/modules/annotate/dimjogline.rs @@ -95,6 +95,7 @@ impl CadCommand for DimJogLineCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/annotate/dimtedit.rs b/src/modules/annotate/dimtedit.rs index 1fedee8a7..17f4ce036 100644 --- a/src/modules/annotate/dimtedit.rs +++ b/src/modules/annotate/dimtedit.rs @@ -103,6 +103,7 @@ impl CadCommand for DimTeditCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } diff --git a/src/modules/annotate/leader_cmd.rs b/src/modules/annotate/leader_cmd.rs index 42ebd82e2..95754e1ff 100644 --- a/src/modules/annotate/leader_cmd.rs +++ b/src/modules/annotate/leader_cmd.rs @@ -203,6 +203,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/linear_dim.rs b/src/modules/annotate/linear_dim.rs index ae0cd49c4..f326ecd1d 100644 --- a/src/modules/annotate/linear_dim.rs +++ b/src/modules/annotate/linear_dim.rs @@ -114,6 +114,7 @@ fn preview_wire(points: Vec) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/mleader_cmd.rs b/src/modules/annotate/mleader_cmd.rs index 808d43364..69455d26d 100644 --- a/src/modules/annotate/mleader_cmd.rs +++ b/src/modules/annotate/mleader_cmd.rs @@ -156,6 +156,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/mleader_edit.rs b/src/modules/annotate/mleader_edit.rs index 866c7446e..2583c2dd6 100644 --- a/src/modules/annotate/mleader_edit.rs +++ b/src/modules/annotate/mleader_edit.rs @@ -367,6 +367,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/qdim.rs b/src/modules/annotate/qdim.rs index 6c04f0570..672526560 100644 --- a/src/modules/annotate/qdim.rs +++ b/src/modules/annotate/qdim.rs @@ -100,6 +100,7 @@ impl CadCommand for QdimCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index 9470a5089..ab901a0bc 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -113,5 +113,6 @@ fn preview_wire(points: Vec) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/annotate/table_cmd.rs b/src/modules/annotate/table_cmd.rs index db7c3211a..dc9bc2db8 100644 --- a/src/modules/annotate/table_cmd.rs +++ b/src/modules/annotate/table_cmd.rs @@ -146,6 +146,7 @@ impl CadCommand for TableCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } else { None diff --git a/src/modules/annotate/tolerance_cmd.rs b/src/modules/annotate/tolerance_cmd.rs index 3f8c27530..d66d05d48 100644 --- a/src/modules/annotate/tolerance_cmd.rs +++ b/src/modules/annotate/tolerance_cmd.rs @@ -99,6 +99,7 @@ impl CadCommand for ToleranceCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/attdef.rs b/src/modules/home/draw/attdef.rs index 2df8f879c..91bb4eb08 100644 --- a/src/modules/home/draw/attdef.rs +++ b/src/modules/home/draw/attdef.rs @@ -135,6 +135,7 @@ impl CadCommand for AttdefCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/ellipse.rs b/src/modules/home/draw/ellipse.rs index c80a31e46..5911329df 100644 --- a/src/modules/home/draw/ellipse.rs +++ b/src/modules/home/draw/ellipse.rs @@ -528,5 +528,6 @@ fn line_wire(from: Vec3, to: Vec3) -> WireModel { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } diff --git a/src/modules/home/draw/line.rs b/src/modules/home/draw/line.rs index 07ec2cc2e..8f8d09b9f 100644 --- a/src/modules/home/draw/line.rs +++ b/src/modules/home/draw/line.rs @@ -89,6 +89,7 @@ impl CadCommand for LineCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/mline.rs b/src/modules/home/draw/mline.rs index e1ea3ebb8..b8352681a 100644 --- a/src/modules/home/draw/mline.rs +++ b/src/modules/home/draw/mline.rs @@ -114,6 +114,7 @@ impl CadCommand for MlineCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/raster_image.rs b/src/modules/home/draw/raster_image.rs index 3b1833455..921ec00b7 100644 --- a/src/modules/home/draw/raster_image.rs +++ b/src/modules/home/draw/raster_image.rs @@ -113,6 +113,7 @@ impl CadCommand for ImageCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/ray.rs b/src/modules/home/draw/ray.rs index c13cbc3d7..e76712751 100644 --- a/src/modules/home/draw/ray.rs +++ b/src/modules/home/draw/ray.rs @@ -87,6 +87,7 @@ impl CadCommand for RayCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } @@ -166,6 +167,7 @@ impl CadCommand for XLineCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/revcloud.rs b/src/modules/home/draw/revcloud.rs index ceb564668..34db9af82 100644 --- a/src/modules/home/draw/revcloud.rs +++ b/src/modules/home/draw/revcloud.rs @@ -84,6 +84,7 @@ impl CadCommand for RevCloudCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/draw/wipeout.rs b/src/modules/home/draw/wipeout.rs index 86b2108f0..7f2ccd937 100644 --- a/src/modules/home/draw/wipeout.rs +++ b/src/modules/home/draw/wipeout.rs @@ -120,6 +120,7 @@ impl CadCommand for WipeoutCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } WipeoutMode::Polygonal => { @@ -142,6 +143,7 @@ impl CadCommand for WipeoutCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/inquiry/area.rs b/src/modules/home/inquiry/area.rs index 2bb3c76b0..ccd578d69 100644 --- a/src/modules/home/inquiry/area.rs +++ b/src/modules/home/inquiry/area.rs @@ -77,6 +77,7 @@ impl CadCommand for AreaCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/inquiry/dist.rs b/src/modules/home/inquiry/dist.rs index 3dca411a5..4f527577c 100644 --- a/src/modules/home/inquiry/dist.rs +++ b/src/modules/home/inquiry/dist.rs @@ -72,6 +72,7 @@ impl CadCommand for DistCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/home/modify/stretch.rs b/src/modules/home/modify/stretch.rs index 14968c99e..6d27f3705 100644 --- a/src/modules/home/modify/stretch.rs +++ b/src/modules/home/modify/stretch.rs @@ -135,6 +135,7 @@ impl CadCommand for StretchCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } _ => None, diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index 5a76f0875..94b2c42d2 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -99,6 +99,7 @@ impl CadCommand for MviewCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index c5e1cbd13..34dfe5f98 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -64,6 +64,7 @@ impl CadCommand for PlotWindowCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index ef5732079..a68d3ebac 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -70,6 +70,7 @@ impl CadCommand for ZoomWindowCommand { key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) } } diff --git a/src/scene/complex_lt.rs b/src/scene/complex_lt.rs index 360974ac3..f8bdc216b 100644 --- a/src/scene/complex_lt.rs +++ b/src/scene/complex_lt.rs @@ -203,6 +203,7 @@ pub fn apply_along( key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }) .collect() } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 4067ce922..0e140e33d 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -976,6 +976,7 @@ impl Scene { out.points = clipped; out.color = [r * 0.80, g * 0.80, b * 0.80, a * 0.85]; out.line_weight_px = wire.line_weight_px; + out.vp_scissor = Some([vp_x0, vp_y0, vp_x1, vp_y1]); projected.push(out); } @@ -3212,6 +3213,7 @@ fn tessellate_entity( key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }); return wires; } diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 3533731ec..9c6a0420c 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -52,6 +52,8 @@ pub struct Pipeline { /// Cached texture format (needed to recreate MSAA / depth textures on resize). surface_format: wgpu::TextureFormat, gpu_wires: Vec, + /// Pixel scissor rects [x, y, w, h] for viewport-clipped wires. Recomputed each frame. + wire_pixel_scissors: Vec>, /// Ghost copies (25% alpha) of selected wires for the X-ray depth pass. gpu_selected_wires: Vec, gpu_hatches: Vec, @@ -584,6 +586,7 @@ impl Pipeline { blit_bind_group, surface_format: format, gpu_wires: vec![], + wire_pixel_scissors: vec![], gpu_selected_wires: vec![], gpu_hatches: vec![], gpu_wipeouts: vec![], @@ -607,6 +610,30 @@ impl Pipeline { .collect(); } + /// Recompute pixel scissor rects for viewport-clipped wires from the current view_proj. + /// Called every frame from prepare() because scissor pixels shift with pan/zoom. + pub fn compute_wire_scissors(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) { + let w = clip_w as f32; + let h = clip_h as f32; + self.wire_pixel_scissors = self.gpu_wires.iter().map(|wire| { + let [x0, y0, x1, y1] = wire.vp_scissor?; + let corners = [ + view_proj.project_point3(glam::Vec3::new(x0, y0, 0.0)), + view_proj.project_point3(glam::Vec3::new(x1, y0, 0.0)), + view_proj.project_point3(glam::Vec3::new(x0, y1, 0.0)), + view_proj.project_point3(glam::Vec3::new(x1, y1, 0.0)), + ]; + let px: Vec = corners.iter().map(|c| (c.x + 1.0) * 0.5 * w).collect(); + let py: Vec = corners.iter().map(|c| (1.0 - c.y) * 0.5 * h).collect(); + let sx0 = px.iter().cloned().fold(f32::INFINITY, f32::min).max(0.0) as u32; + let sy0 = py.iter().cloned().fold(f32::INFINITY, f32::min).max(0.0) as u32; + let sx1 = (px.iter().cloned().fold(f32::NEG_INFINITY, f32::max) as u32).min(clip_w); + let sy1 = (py.iter().cloned().fold(f32::NEG_INFINITY, f32::max) as u32).min(clip_h); + if sx1 <= sx0 || sy1 <= sy0 { return None; } + Some([sx0, sy0, sx1 - sx0, sy1 - sy0]) + }).collect(); + } + /// Upload all 3DFACE entities as two batched GPU objects: /// - `gpu_face3d_fill`: filled triangles (1 buffer, 1 draw call) /// - `gpu_face3d_edges`: merged edge wires (1 buffer, 1 draw call) @@ -875,11 +902,27 @@ impl Pipeline { pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.wire_pipeline); pass.set_bind_group(0, &self.uniform_bind_group, &[]); - for wire in &self.gpu_wires { - if wire.vertex_count >= 6 { - pass.set_vertex_buffer(0, wire.vertex_buffer.slice(..)); - pass.draw(0..wire.vertex_count, 0..1); + let mut scissor_active = false; + for (i, wire) in self.gpu_wires.iter().enumerate() { + if wire.vertex_count < 6 { + continue; } + match self.wire_pixel_scissors.get(i) { + Some(Some([x, y, w, h])) => { + pass.set_scissor_rect(*x, *y, *w, *h); + scissor_active = true; + } + _ if scissor_active => { + pass.set_scissor_rect(0, 0, vp.width, vp.height); + scissor_active = false; + } + _ => {} + } + pass.set_vertex_buffer(0, wire.vertex_buffer.slice(..)); + pass.draw(0..wire.vertex_count, 0..1); + } + if scissor_active { + pass.set_scissor_rect(0, 0, vp.width, vp.height); } } diff --git a/src/scene/pipeline/wire_gpu.rs b/src/scene/pipeline/wire_gpu.rs index 0ff064af9..8078e8bff 100644 --- a/src/scene/pipeline/wire_gpu.rs +++ b/src/scene/pipeline/wire_gpu.rs @@ -111,11 +111,16 @@ impl WireVertex { pub struct WireGpu { pub vertex_buffer: wgpu::Buffer, pub vertex_count: u32, + /// Paper-space bbox [x0, y0, x1, y1] for GPU scissor clipping. + /// Set only for viewport-projected wires; None for regular wires. + pub vp_scissor: Option<[f32; 4]>, } impl WireGpu { pub fn new(device: &wgpu::Device, wire: &WireModel) -> Self { - Self::build(device, wire, wire.color) + let mut g = Self::build(device, wire, wire.color); + g.vp_scissor = wire.vp_scissor; + g } /// Creates a ghost copy with `alpha` applied on top of the wire's own alpha. @@ -209,6 +214,7 @@ impl WireGpu { Self { vertex_buffer, vertex_count: chunk.len() as u32, + vp_scissor: None, } }) .collect() @@ -310,6 +316,7 @@ impl WireGpu { Self { vertex_buffer, vertex_count: vertices.len() as u32, + vp_scissor: None, } } } diff --git a/src/scene/render.rs b/src/scene/render.rs index bd09f066c..a038d7d97 100644 --- a/src/scene/render.rs +++ b/src/scene/render.rs @@ -131,6 +131,7 @@ impl shader::Primitive for Primitive { pipeline.upload_face3d(device, &self.face3d_wires[..]); pipeline.cached_epoch = self.geometry_epoch; } + pipeline.compute_wire_scissors(self.uniforms.view_proj, clip_size.width, clip_size.height); if self.show_viewcube { pipeline.viewcube.upload( queue, diff --git a/src/scene/tessellate.rs b/src/scene/tessellate.rs index 22dc7d667..5ce020178 100644 --- a/src/scene/tessellate.rs +++ b/src/scene/tessellate.rs @@ -105,6 +105,7 @@ pub fn tessellate( key_vertices: te.key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; } @@ -134,6 +135,7 @@ pub fn tessellate( key_vertices: te.key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; } _ => {} @@ -161,6 +163,7 @@ pub fn tessellate( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; } } @@ -186,6 +189,7 @@ pub fn tessellate( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; } } @@ -217,6 +221,7 @@ pub fn tessellate( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }; } @@ -243,6 +248,7 @@ pub fn tessellate( aci: 0, key_vertices, plinegen: false, + vp_scissor: None, aabb: WireModel::UNBOUNDED_AABB, }; } @@ -274,6 +280,7 @@ pub fn tessellate( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } @@ -330,6 +337,7 @@ pub fn tessellate_dimension( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }]; if let Some(text) = dimension_text_entity(dim) { @@ -379,6 +387,7 @@ fn tessellate_leader( key_vertices: vec![], aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }]; } @@ -448,6 +457,7 @@ fn tessellate_leader( key_vertices, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }] } @@ -558,6 +568,7 @@ fn tessellate_multileader( key_vertices: key_verts, aabb: WireModel::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, }]; // Render text content as MText wire diff --git a/src/scene/wire_model.rs b/src/scene/wire_model.rs index 6e7b57186..86ddba8ec 100644 --- a/src/scene/wire_model.rs +++ b/src/scene/wire_model.rs @@ -63,6 +63,9 @@ pub struct WireModel { /// When false the linetype pattern restarts at each NaN-separated segment /// (DXF PLINEGEN=0). When true the pattern runs continuously (PLINEGEN=1). pub plinegen: bool, + /// Paper-space bounding box [x0, y0, x1, y1] for GPU scissor clipping. + /// Set only for viewport-projected wires in paper-space layouts. + pub vp_scissor: Option<[f32; 4]>, } impl WireModel { @@ -89,6 +92,7 @@ impl WireModel { key_vertices: vec![], aabb: Self::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } @@ -189,6 +193,7 @@ impl Default for WireModel { key_vertices: Vec::new(), aabb: Self::UNBOUNDED_AABB, plinegen: true, + vp_scissor: None, } } }