diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index 20ee91047..363cfb635 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -34,11 +34,13 @@ use viewcube::{viewcube_nav_controls, viewcube_ucs_picker, UCS_PICKER_W}; pub(in crate::app) use overlay::{MTEXT_TEXT_ID, TEXT_INLINE_ID}; const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_REGION_PX; +/// The desk shown around the sheet, as the widget wants it. One definition, +/// shared with the renderer that clears the sheet viewport to the same thing. const PAPER_SPACE_BACKGROUND: Color = Color { - r: 138.0 / 255.0, - g: 138.0 / 255.0, - b: 138.0 / 255.0, - a: 1.0, + r: crate::scene::PAPER_DESK_COLOR[0], + g: crate::scene::PAPER_DESK_COLOR[1], + b: crate::scene::PAPER_DESK_COLOR[2], + a: crate::scene::PAPER_DESK_COLOR[3], }; /// Base surface directly under the crosshair. Paper content viewports render diff --git a/src/scene/mod.rs b/src/scene/mod.rs index dd84edd41..fc7c4e8fa 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -285,6 +285,12 @@ struct GeometryDelta { full: bool, } +/// The desk a paper layout lays its sheet on — the surface visible around the +/// page. Distinct from both the page (`paper_bg_color`) and the model canvas +/// (`bg_color`); if the desk took either of those colours the page edge would +/// vanish into its own surround. +pub const PAPER_DESK_COLOR: [f32; 4] = [138.0 / 255.0, 138.0 / 255.0, 138.0 / 255.0, 1.0]; + const CACHE_CATEGORY_ANNOTATIVE: u16 = 1 << 0; const CACHE_CATEGORY_HATCH: u16 = 1 << 1; const CACHE_CATEGORY_WIPEOUT: u16 = 1 << 2; diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index f09810e65..5b0c172a1 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -340,6 +340,9 @@ pub struct Pipeline { /// `render` skips the (per-pixel, GPU-dominating) hatch pass this frame. The /// scene-render cache holds the full-quality frame once the view settles. pub skip_hatch_frame: bool, + /// Skip the canvas entirely — no clear colour, no background pass — so + /// whatever was drawn underneath this viewport shows through it. + pub skip_background: bool, /// Stable identity of the viewport that last used this (index-addressed) /// pipeline slot. The renderer's viewport list drops off-canvas viewports, /// so a slot can be reused by a *different* viewport across frames; when the @@ -2015,6 +2018,7 @@ impl Pipeline { render_sig: u64::MAX, skip_geometry: false, skip_hatch_frame: false, + skip_background: false, slot_id: u64::MAX, } } @@ -3282,7 +3286,7 @@ impl Pipeline { }; let msaa = &self.msaa_view; let [r, g, b, a] = bg_color; - let clear_color = if self.clip_boundary.is_some() { + let clear_color = if self.clip_boundary.is_some() || self.skip_background { wgpu::Color::TRANSPARENT } else { wgpu::Color { @@ -3393,10 +3397,15 @@ impl Pipeline { pass.set_vertex_buffer(0, vbuf.slice(..)); pass.draw(0..*vcount, 0..1); } - pass.set_pipeline(&self.background_pipeline); - pass.set_bind_group(0, &self.uniform_bind_group, &[]); - pass.set_stencil_reference(stencil_ref); - pass.draw(0..3, 0..1); + // The background shader returns an opaque colour whatever alpha it + // is handed, so a see-through viewport cannot be asked for as a + // transparent background — the pass has to not run. + if !self.skip_background { + pass.set_pipeline(&self.background_pipeline); + pass.set_bind_group(0, &self.uniform_bind_group, &[]); + pass.set_stencil_reference(stencil_ref); + pass.draw(0..3, 0..1); + } // The capability-selected façade dispatches storage or texture // draws before wires so outlines remain on top in either backend. // Skipped while navigating because per-pixel hatch work dominates diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index 96cb7819c..c6ead7099 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -202,6 +202,10 @@ pub struct ViewportData { /// into the render signature so the settle frame re-renders hatches once and /// the scene-render cache holds it. See [`Scene::navigating_lod`]. pub(in crate::scene) skip_hatch: bool, + /// True when this viewport must not paint a canvas of its own. A floating + /// viewport on a paper layout sits ON the sheet: anything it painted first + /// would cover the page it is supposed to be a window in. + pub(in crate::scene) skip_background: bool, pub(in crate::scene) geometry_epoch: u64, /// Camera generation captured when this Primitive was assembled. Paired /// with `geometry_epoch` so the per-frame scissor / LOD recompute runs. @@ -434,6 +438,7 @@ impl shader::Primitive for Primitive { inner.skip_geometry = skip; // Interaction LOD: skip the hatch draw this frame while navigating. inner.skip_hatch_frame = vp.skip_hatch; + inner.skip_background = vp.skip_background; if skip { if vp.show_viewcube { inner.viewcube.upload( @@ -1250,6 +1255,7 @@ fn render_signature(vp: &ViewportData, clip_w: u32, clip_h: u32) -> u64 { // Interaction-LOD hatch suppression: differs the signature so the settle // frame (skip_hatch flips false) re-renders with hatches and re-caches. vp.skip_hatch.hash(&mut h); + vp.skip_background.hash(&mut h); clip_w.hash(&mut h); clip_h.hash(&mut h); // Live overlay (command preview / interim / grip drag). Small — a handful @@ -1912,7 +1918,19 @@ impl Scene { &self, viewport: &ViewportInstance, ) -> ViewportDisplaySettings { + // What lies behind a viewport depends on what the viewport is. The + // full-canvas paper sheet shows the desk, and the page is drawn on top + // of it by `paper_sheet_fill` — give the desk the page's own colour and + // the edge disappears into a same-coloured surround that no amount of + // zooming out reveals. A floating viewport sits ON that sheet, so it + // paints no canvas at all; anything else would erase the page beneath + // it. Only model space clears to the editor background. let canvas_background = if viewport.paper_sheet { + crate::scene::PAPER_DESK_COLOR + } else if self.current_layout != "Model" { + // This viewport paints no canvas of its own (`skip_background`), + // but shading and fog still blend toward whatever is behind it — + // and behind a floating viewport is the page. self.paper_bg_color } else { self.bg_color @@ -3594,6 +3612,7 @@ impl Scene { // (hatched) frame once it settles. Only applied to the on-screen // Model / paper content — the paper *sheet* keeps its fills. skip_hatch: self.hatch_lod_enabled() && !inst.paper_sheet && self.navigating_lod(), + skip_background: !inst.paper_sheet && self.current_layout != "Model", geometry_epoch: self.geometry_epoch, camera_generation: self.camera_generation, wire_content_id,