fix(scene): render raster images at correct position and size (#98)
Match the image shader's uniform layout to the shared 112-byte Uniforms struct (was 144) and project quad corners via view_proj so texture-quad draws no longer fail uniform binding validation. Offset image quad corners by world_offset, matching all other Model-space geometry, so rasters land in the same local space instead of being shifted away. Downscale oversized images to the GPU's max texture dimension (8192) to keep texture creation from failing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a819a47c08
commit
a2cb833c83
3 changed files with 35 additions and 14 deletions
|
|
@ -36,12 +36,17 @@ pub struct ImageModel {
|
|||
impl ImageModel {
|
||||
/// Build an ImageModel from a DXF RasterImage entity.
|
||||
/// Returns `None` if the image file cannot be opened or decoded.
|
||||
pub fn from_raster_image(img: &acadrust::entities::RasterImage) -> Option<Self> {
|
||||
pub fn from_raster_image(
|
||||
img: &acadrust::entities::RasterImage,
|
||||
world_offset: [f64; 3],
|
||||
) -> Option<Self> {
|
||||
let w = img.size.x;
|
||||
let h = img.size.y;
|
||||
let ox = img.insertion_point.x as f32;
|
||||
let oy = img.insertion_point.y as f32;
|
||||
let oz = img.insertion_point.z as f32;
|
||||
// Model-space geometry is drawn in (WCS - world_offset) so large UTM-
|
||||
// scale coordinates stay within f32 precision; offset the image too.
|
||||
let ox = (img.insertion_point.x - world_offset[0]) as f32;
|
||||
let oy = (img.insertion_point.y - world_offset[1]) as f32;
|
||||
let oz = (img.insertion_point.z - world_offset[2]) as f32;
|
||||
let ux = (img.u_vector.x * w) as f32;
|
||||
let uy = (img.u_vector.y * w) as f32;
|
||||
let uz = (img.u_vector.z * w) as f32;
|
||||
|
|
@ -74,6 +79,19 @@ impl ImageModel {
|
|||
/// Returns `None` if the file does not exist or cannot be decoded.
|
||||
pub fn load_pixels(path_str: &str) -> Option<(Vec<u8>, u32, u32)> {
|
||||
let img = image::open(Path::new(path_str)).ok()?;
|
||||
// GPUs cap 2-D texture dimensions (8192 with wgpu's default limits).
|
||||
// Downscale oversized images to fit, preserving aspect ratio, so texture
|
||||
// creation can't fail — they're displayed scaled-down anyway.
|
||||
const MAX_DIM: u32 = 8192;
|
||||
let img = if img.width() > MAX_DIM || img.height() > MAX_DIM {
|
||||
let longest = img.width().max(img.height()) as f32;
|
||||
let scale = MAX_DIM as f32 / longest;
|
||||
let nw = ((img.width() as f32 * scale) as u32).clamp(1, MAX_DIM);
|
||||
let nh = ((img.height() as f32 * scale) as u32).clamp(1, MAX_DIM);
|
||||
img.resize(nw, nh, image::imageops::FilterType::Triangle)
|
||||
} else {
|
||||
img
|
||||
};
|
||||
let rgba = img.to_rgba8();
|
||||
let (w, h) = rgba.dimensions();
|
||||
Some((rgba.into_raw(), w, h))
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
|||
.par_iter()
|
||||
.filter_map(|&handle| {
|
||||
if let EntityType::RasterImage(img) = doc.get_entity(handle)? {
|
||||
ImageModel::from_raster_image(img).map(|m| (handle, m))
|
||||
ImageModel::from_raster_image(img, world_offset).map(|m| (handle, m))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -3441,7 +3441,7 @@ impl Scene {
|
|||
None
|
||||
};
|
||||
let image_seed = if let EntityType::RasterImage(img) = &entity {
|
||||
ImageModel::from_raster_image(img)
|
||||
ImageModel::from_raster_image(img, self.world_offset)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
@ -4252,7 +4252,7 @@ impl Scene {
|
|||
})
|
||||
.collect();
|
||||
for (handle, img) in entries {
|
||||
if let Some(model) = ImageModel::from_raster_image(&img) {
|
||||
if let Some(model) = ImageModel::from_raster_image(&img, self.world_offset) {
|
||||
self.images.insert(handle, model);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@
|
|||
// Renders a four-vertex quad (two triangles) with a sampled texture.
|
||||
|
||||
// ── Bind group 0: shared projection uniforms ─────────────────────────────────
|
||||
// Must match the shared `Uniforms` struct (scene::pipeline::uniforms, 112 B).
|
||||
struct Uniforms {
|
||||
mvp: mat4x4<f32>,
|
||||
view: mat4x4<f32>,
|
||||
width: f32,
|
||||
height: f32,
|
||||
cam_z: f32,
|
||||
_pad: f32,
|
||||
view_proj: mat4x4<f32>,
|
||||
camera_pos: vec4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
flat_shade: f32,
|
||||
transparency_enable: f32,
|
||||
_pad: vec2<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
||||
|
|
@ -43,7 +46,7 @@ struct VertOut {
|
|||
@vertex
|
||||
fn vs_main(in: VertIn) -> VertOut {
|
||||
var out: VertOut;
|
||||
out.clip_pos = u.mvp * vec4<f32>(in.pos, 1.0);
|
||||
out.clip_pos = u.view_proj * vec4<f32>(in.pos, 1.0);
|
||||
out.clip_pos.z = out.clip_pos.z - img_params.draw_depth * DRAW_ORDER_BIAS * out.clip_pos.w;
|
||||
out.uv = in.uv;
|
||||
return out;
|
||||
|
|
|
|||
Loading…
Reference in a new issue