feat(render): double-single corners for the image pipeline
Raster-image quad corners derive from a large f64 insertion point plus small u/v spans; the single-f32 cast quantized them at UTM scale. The ImageModel now keeps a corners_low residual, ImageVertex carries pos_low (location 2), and image.wgsl reconstructs the eye-relative position from the high/low pair. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0f7920dc2c
commit
bd12cc2f5a
3 changed files with 50 additions and 42 deletions
|
|
@ -23,6 +23,9 @@ pub struct ImageModel {
|
||||||
/// [2] origin + U*W + V*H (top-right)
|
/// [2] origin + U*W + V*H (top-right)
|
||||||
/// [3] origin + V*H (top-left)
|
/// [3] origin + V*H (top-left)
|
||||||
pub corners: [[f32; 3]; 4],
|
pub corners: [[f32; 3]; 4],
|
||||||
|
/// Low residual paired with `corners` (double-single) so the GPU keeps
|
||||||
|
/// sub-unit precision at UTM-scale insertion points.
|
||||||
|
pub corners_low: [[f32; 3]; 4],
|
||||||
/// Optional world-space XY rect [x0, y0, x1, y1] for paper-space
|
/// Optional world-space XY rect [x0, y0, x1, y1] for paper-space
|
||||||
/// viewport clipping. Mirrors `WireModel.vp_scissor` /
|
/// viewport clipping. Mirrors `WireModel.vp_scissor` /
|
||||||
/// `HatchModel.vp_scissor`.
|
/// `HatchModel.vp_scissor`.
|
||||||
|
|
@ -44,21 +47,33 @@ impl ImageModel {
|
||||||
let h = img.size.y;
|
let h = img.size.y;
|
||||||
// Model-space geometry is drawn in (WCS - world_offset) so large UTM-
|
// Model-space geometry is drawn in (WCS - world_offset) so large UTM-
|
||||||
// scale coordinates stay within f32 precision; offset the image too.
|
// scale coordinates stay within f32 precision; offset the image too.
|
||||||
let ox = (img.insertion_point.x - world_offset[0]) as f32;
|
// Corners come from a large insertion point plus small u/v spans.
|
||||||
let oy = (img.insertion_point.y - world_offset[1]) as f32;
|
// Split each into double-single (high, low) f32 so the GPU keeps
|
||||||
let oz = (img.insertion_point.z - world_offset[2]) as f32;
|
// sub-unit precision at UTM scale and after a cross-drawing paste.
|
||||||
|
let oxv = img.insertion_point.x - world_offset[0];
|
||||||
|
let oyv = img.insertion_point.y - world_offset[1];
|
||||||
|
let ozv = img.insertion_point.z - world_offset[2];
|
||||||
let ux = (img.u_vector.x * w) as f32;
|
let ux = (img.u_vector.x * w) as f32;
|
||||||
let uy = (img.u_vector.y * w) as f32;
|
let uy = (img.u_vector.y * w) as f32;
|
||||||
let uz = (img.u_vector.z * w) as f32;
|
let uz = (img.u_vector.z * w) as f32;
|
||||||
let vx = (img.v_vector.x * h) as f32;
|
let vx = (img.v_vector.x * h) as f32;
|
||||||
let vy = (img.v_vector.y * h) as f32;
|
let vy = (img.v_vector.y * h) as f32;
|
||||||
let vz = (img.v_vector.z * h) as f32;
|
let vz = (img.v_vector.z * h) as f32;
|
||||||
|
// High/low split of the anchor; the u/v spans are small and added to
|
||||||
|
// the high half (their own residual is below f32 noise at this scale).
|
||||||
|
let ox = oxv as f32;
|
||||||
|
let oy = oyv as f32;
|
||||||
|
let oz = ozv as f32;
|
||||||
|
let oxl = (oxv - ox as f64) as f32;
|
||||||
|
let oyl = (oyv - oy as f64) as f32;
|
||||||
|
let ozl = (ozv - oz as f64) as f32;
|
||||||
let corners = [
|
let corners = [
|
||||||
[ox, oy, oz],
|
[ox, oy, oz],
|
||||||
[ox + ux, oy + uy, oz + uz],
|
[ox + ux, oy + uy, oz + uz],
|
||||||
[ox + ux + vx, oy + uy + vy, oz + uz + vz],
|
[ox + ux + vx, oy + uy + vy, oz + uz + vz],
|
||||||
[ox + vx, oy + vy, oz + vz],
|
[ox + vx, oy + vy, oz + vz],
|
||||||
];
|
];
|
||||||
|
let corners_low = [[oxl, oyl, ozl]; 4];
|
||||||
let opacity = 1.0 - img.fade as f32 / 100.0;
|
let opacity = 1.0 - img.fade as f32 / 100.0;
|
||||||
|
|
||||||
let (pixels, width, height) = load_pixels(&img.file_path)?;
|
let (pixels, width, height) = load_pixels(&img.file_path)?;
|
||||||
|
|
@ -69,6 +84,7 @@ impl ImageModel {
|
||||||
height,
|
height,
|
||||||
opacity,
|
opacity,
|
||||||
corners,
|
corners,
|
||||||
|
corners_low,
|
||||||
vp_scissor: None,
|
vp_scissor: None,
|
||||||
draw_depth: 0.0,
|
draw_depth: 0.0,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,32 @@ use iced::wgpu::util::DeviceExt;
|
||||||
pub struct ImageVertex {
|
pub struct ImageVertex {
|
||||||
pub pos: [f32; 3],
|
pub pos: [f32; 3],
|
||||||
pub uv: [f32; 2],
|
pub uv: [f32; 2],
|
||||||
|
pub pos_low: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageVertex {
|
impl ImageVertex {
|
||||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
|
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(ImageVertex, pos) as u64,
|
||||||
|
shader_location: 0,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(ImageVertex, uv) as u64,
|
||||||
|
shader_location: 1,
|
||||||
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(ImageVertex, pos_low) as u64,
|
||||||
|
shader_location: 2,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
];
|
||||||
wgpu::VertexBufferLayout {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<ImageVertex>() as u64,
|
array_stride: std::mem::size_of::<ImageVertex>() as u64,
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
attributes: &[
|
attributes: ATTRS,
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 0,
|
|
||||||
shader_location: 0,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 12,
|
|
||||||
shader_location: 1,
|
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,31 +161,14 @@ impl ImageGpu {
|
||||||
// corners: [p0=BL, p1=BR, p2=TR, p3=TL]
|
// corners: [p0=BL, p1=BR, p2=TR, p3=TL]
|
||||||
// UV: BL=(0,1), BR=(1,1), TR=(1,0), TL=(0,0)
|
// UV: BL=(0,1), BR=(1,1), TR=(1,0), TL=(0,0)
|
||||||
let [p0, p1, p2, p3] = model.corners;
|
let [p0, p1, p2, p3] = model.corners;
|
||||||
|
let [l0, l1, l2, l3] = model.corners_low;
|
||||||
let verts = [
|
let verts = [
|
||||||
ImageVertex {
|
ImageVertex { pos: p0, uv: [0.0, 1.0], pos_low: l0 },
|
||||||
pos: p0,
|
ImageVertex { pos: p1, uv: [1.0, 1.0], pos_low: l1 },
|
||||||
uv: [0.0, 1.0],
|
ImageVertex { pos: p2, uv: [1.0, 0.0], pos_low: l2 },
|
||||||
},
|
ImageVertex { pos: p0, uv: [0.0, 1.0], pos_low: l0 },
|
||||||
ImageVertex {
|
ImageVertex { pos: p2, uv: [1.0, 0.0], pos_low: l2 },
|
||||||
pos: p1,
|
ImageVertex { pos: p3, uv: [0.0, 0.0], pos_low: l3 },
|
||||||
uv: [1.0, 1.0],
|
|
||||||
},
|
|
||||||
ImageVertex {
|
|
||||||
pos: p2,
|
|
||||||
uv: [1.0, 0.0],
|
|
||||||
},
|
|
||||||
ImageVertex {
|
|
||||||
pos: p0,
|
|
||||||
uv: [0.0, 1.0],
|
|
||||||
},
|
|
||||||
ImageVertex {
|
|
||||||
pos: p2,
|
|
||||||
uv: [1.0, 0.0],
|
|
||||||
},
|
|
||||||
ImageVertex {
|
|
||||||
pos: p3,
|
|
||||||
uv: [0.0, 0.0],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("image.vbuf"),
|
label: Some("image.vbuf"),
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,9 @@ const DRAW_ORDER_BIAS: f32 = 0.001;
|
||||||
|
|
||||||
// ── Vertex stage ──────────────────────────────────────────────────────────────
|
// ── Vertex stage ──────────────────────────────────────────────────────────────
|
||||||
struct VertIn {
|
struct VertIn {
|
||||||
@location(0) pos: vec3<f32>,
|
@location(0) pos: vec3<f32>,
|
||||||
@location(1) uv: vec2<f32>,
|
@location(1) uv: vec2<f32>,
|
||||||
|
@location(2) pos_low: vec3<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertOut {
|
struct VertOut {
|
||||||
|
|
@ -52,7 +53,8 @@ struct VertOut {
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(in: VertIn) -> VertOut {
|
fn vs_main(in: VertIn) -> VertOut {
|
||||||
var out: VertOut;
|
var out: VertOut;
|
||||||
out.clip_pos = u.view_rot * vec4<f32>((in.pos - u.eye_high) - u.eye_low, 1.0);
|
let rel = (in.pos - u.eye_high) + (in.pos_low - u.eye_low);
|
||||||
|
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
|
||||||
out.clip_pos.z = out.clip_pos.z - img_params.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w;
|
out.clip_pos.z = out.clip_pos.z - img_params.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w;
|
||||||
out.uv = in.uv;
|
out.uv = in.uv;
|
||||||
return out;
|
return out;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue