View backgrounds: reference blend, image adjust, layouts, cleanup, style name

- Gradients: the middle colour sits (horizon - 0.5) viewport widths from the
  centre and blends over height viewport heights each side; two colours
  blend over a viewport height each side.
- Image backgrounds: offset in device pixels (right, down), negative scale
  mirrors, fitted with aspect ratio covers the viewport; the dialog keeps an
  image's offset and scale.
- Layouts: a model-space view restored on a sheet asks for the viewport,
  enters it and gives it the view's camera, background and visual style;
  inside a viewport the view goes there; a paper-space view's background is
  kept on the sheet viewport but not drawn; floating viewports draw their
  background.
- Background objects no view or viewport uses any more are erased; a view
  without a background restored where the viewport already is leaves the
  viewport's background.
- The viewport's named visual style (Realistic, ...) shows by name and the
  render-mode picker records its style on the viewport record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-12 17:31:01 +03:00
1 parent 1cf13971dc
commit 9ef8f4bedf
12 files changed
+349 -70

No files matched your search

+27 -27
View File
@@ -81,22 +81,25 @@ fn equirect_uv(ndc: vec2<f32>, rotation: f32) -> vec2<f32> {
fn image_uv(source_uv: vec2<f32>) -> vec2<f32> {
var uv = source_uv;
if u.background_image_params.x < 0.5 {
// Not fitted: the image at its own pixel size (times its scale),
// centred on the viewport and moved by its offset.
let pixel = (source_uv - vec2<f32>(0.5)) * max(u.viewport_size, vec2<f32>(1.0));
uv = pixel / max(abs(u.background_image_transform.zw), vec2<f32>(1e-6))
+ vec2<f32>(0.5) - u.background_image_transform.xy;
// Not fitted: the image at its own pixel size times its scale (a
// negative scale mirrors it), centred on the viewport and moved by
// its offset in pixels, right and down.
let pixel = (source_uv - vec2<f32>(0.5)) * max(u.viewport_size, vec2<f32>(1.0))
- u.background_image_transform.xy;
let size = u.background_image_transform.zw;
let safe = select(size, vec2<f32>(1e-6), abs(size) < vec2<f32>(1e-6));
uv = pixel / safe + vec2<f32>(0.5);
return select(uv, fract(uv), u.background_image_params.z > 0.5);
}
// Fitted: the image fills the viewport; keeping its aspect ratio it
// covers the viewport and the overflow is cut off evenly.
if u.background_image_params.y > 0.5 {
let image_aspect = max(u.background_image_params.w, 1e-6);
let viewport_aspect = max(u.viewport_size.x / max(u.viewport_size.y, 1.0), 1e-6);
if image_aspect > viewport_aspect {
let scale = viewport_aspect / image_aspect;
uv.y = (uv.y - 0.5) / scale + 0.5;
uv.x = (uv.x - 0.5) * (viewport_aspect / image_aspect) + 0.5;
} else {
let scale = image_aspect / viewport_aspect;
uv.x = (uv.x - 0.5) / scale + 0.5;
uv.y = (uv.y - 0.5) * (image_aspect / viewport_aspect) + 0.5;
}
}
if u.background_image_params.z > 0.5 {
@@ -112,28 +115,25 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
if mode == 1u {
color = u.viewport_background.rgb;
} else if mode == 2u {
// Bottom to top across the viewport, the "up" direction turned
// counter-clockwise by the rotation; in pixels, so the angle holds
// whatever the viewport's shape, and spanning corner to corner.
// Bottom to top along the "up" direction turned counter-clockwise by
// the rotation, in pixels from the viewport centre. As the reference
// draws it: the middle colour sits (horizon - 0.5) viewport widths
// from the centre and blends linearly into the top and bottom colours
// over height viewport heights on each side; two colours are one blend
// over a viewport height on each side of the centre.
let angle = u.background_params.w;
let up = vec2<f32>(-sin(angle), cos(angle));
let size = max(u.viewport_size, vec2<f32>(1.0));
let pixel = (vec2<f32>(in.uv.x, 1.0 - in.uv.y) - vec2<f32>(0.5)) * size;
let extent = max(abs(up.x) * size.x + abs(up.y) * size.y, 1.0);
let coordinate = clamp(dot(pixel, up) / extent + 0.5, 0.0, 1.0);
let horizon = clamp(u.background_params.y, 0.0, 1.0);
let half_height = abs(u.background_params.z) * 0.5;
if half_height <= 1e-6 {
// Two colours: one blend from the bottom to the top, over twice
// the viewport (the reference's two-colour gradient reaches the
// end colours a viewport beyond the edges).
color = mix(u.background_bottom.rgb, u.background_top.rgb, 0.25 + 0.5 * coordinate);
} else {
let lower = clamp((coordinate - (horizon - half_height)) / half_height, 0.0, 1.0);
let upper = clamp((coordinate - horizon) / half_height, 0.0, 1.0);
let low_color = mix(u.background_bottom.rgb, u.background_middle.rgb, lower);
color = mix(low_color, u.background_top.rgb, upper);
}
let along = dot(pixel, up);
let three = abs(u.background_params.z) > 1e-6;
let middle_at = select(0.0, (u.background_params.y - 0.5) * size.x, three);
let half_band = select(1.0, abs(u.background_params.z), three) * size.y;
let middle = select(mix(u.background_bottom.rgb, u.background_top.rgb, 0.5),
u.background_middle.rgb, three);
let lower = clamp((along - middle_at + half_band) / half_band, 0.0, 1.0);
let upper = clamp((along - middle_at) / half_band, 0.0, 1.0);
color = mix(mix(u.background_bottom.rgb, middle, lower), u.background_top.rgb, upper);
} else if mode == 3u {
let direction = world_direction(in.ndc);
if direction.z >= 0.0 {