forked from 1b-agent/opencadstudio-upstream
style: apply cargo fmt across workspace
This commit is contained in:
1 parent
85710e5454
commit
9cf9ff2c5c
531 files changed
+57740
-33906
No files matched your search
@@ -229,7 +229,14 @@ pub fn extract_circle_instance_from_geom_indexed(
|
||||
axis_x,
|
||||
axis_y,
|
||||
radius,
|
||||
} => (center, axis_x, axis_y, radius, 0.0f32, std::f32::consts::TAU),
|
||||
} => (
|
||||
center,
|
||||
axis_x,
|
||||
axis_y,
|
||||
radius,
|
||||
0.0f32,
|
||||
std::f32::consts::TAU,
|
||||
),
|
||||
crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center,
|
||||
axis_x,
|
||||
@@ -247,14 +254,29 @@ pub fn extract_circle_instance_from_geom_indexed(
|
||||
),
|
||||
_ => return None,
|
||||
};
|
||||
if radius <= 0.0 || !radius.is_finite() || !start_angle.is_finite() || !end_angle.is_finite() || radius > 1e6 {
|
||||
if radius <= 0.0
|
||||
|| !radius.is_finite()
|
||||
|| !start_angle.is_finite()
|
||||
|| !end_angle.is_finite()
|
||||
|| radius > 1e6
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let (ch, cl) = split_ds_xyz(center[0], center[1], center[2]);
|
||||
let hw = wire.line_weight_px * 0.5;
|
||||
let pat0 = [wire.pattern[0], wire.pattern[1], wire.pattern[2], wire.pattern[3]];
|
||||
let pat1 = [wire.pattern[4], wire.pattern[5], wire.pattern[6], wire.pattern[7]];
|
||||
let pat0 = [
|
||||
wire.pattern[0],
|
||||
wire.pattern[1],
|
||||
wire.pattern[2],
|
||||
wire.pattern[3],
|
||||
];
|
||||
let pat1 = [
|
||||
wire.pattern[4],
|
||||
wire.pattern[5],
|
||||
wire.pattern[6],
|
||||
wire.pattern[7],
|
||||
];
|
||||
|
||||
let (start_width, end_width) = if !wire.taper_widths.is_empty() {
|
||||
if wire.taper_widths.len() >= 2 && wire.tangent_geoms.len() == 1 {
|
||||
@@ -419,12 +441,13 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_planar_success() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [100.0, 200.0, 300.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 50.0,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [100.0, 200.0, 300.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 50.0,
|
||||
});
|
||||
wire.line_weight_px = 2.0;
|
||||
wire.color = [1.0, 0.0, 0.0, 1.0];
|
||||
|
||||
@@ -441,14 +464,15 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_arc_success() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [10.0, 20.0, 30.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 25.0,
|
||||
start_angle: 0.5,
|
||||
end_angle: 2.5,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [10.0, 20.0, 30.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 25.0,
|
||||
start_angle: 0.5,
|
||||
end_angle: 2.5,
|
||||
});
|
||||
wire.line_weight_px = 3.0;
|
||||
wire.color = [0.0, 1.0, 0.0, 1.0];
|
||||
|
||||
@@ -464,12 +488,13 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_rejects_non_planar_or_complex() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
// 1. With fill_tris (e.g. 3D solid or hatch)
|
||||
wire.fill_tris = vec![[0.0; 3]];
|
||||
assert!(extract_circle_instance(&wire, 0.0).is_none());
|
||||
@@ -496,14 +521,15 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_constant_wide_arc() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [100.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 40.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [100.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 40.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
wire.world_width = 12.0;
|
||||
wire.color = [1.0, 0.5, 0.0, 1.0];
|
||||
|
||||
@@ -516,14 +542,15 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_tapered_arc() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.2,
|
||||
end_angle: 1.8,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.2,
|
||||
end_angle: 1.8,
|
||||
});
|
||||
wire.world_width = 20.0;
|
||||
wire.taper_widths = vec![4.0, 20.0];
|
||||
wire.color = [0.2, 0.8, 1.0, 1.0];
|
||||
@@ -537,15 +564,17 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_circle_instance_accepts_pick_tris() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Circle {
|
||||
center: [5.0, 5.0, 0.0],
|
||||
radius: 15.0,
|
||||
});
|
||||
wire.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Circle {
|
||||
center: [5.0, 5.0, 0.0],
|
||||
radius: 15.0,
|
||||
});
|
||||
wire.world_width = 6.0;
|
||||
wire.pick_tris = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
|
||||
wire.pick_tris_low = vec![[0.0; 3]; 3];
|
||||
|
||||
let insts = extract_circle_instances(&wire, 0.0).expect("wide curve with pick_tris should extract");
|
||||
let insts =
|
||||
extract_circle_instances(&wire, 0.0).expect("wide curve with pick_tris should extract");
|
||||
assert_eq!(insts.len(), 1);
|
||||
assert_eq!(insts[0].start_width, 6.0);
|
||||
assert_eq!(insts[0].params[3], 6.0);
|
||||
@@ -572,11 +601,18 @@ mod tests {
|
||||
None,
|
||||
false,
|
||||
);
|
||||
assert_eq!(wires.len(), 2, "donut should split into 2 analytical arc wires");
|
||||
assert_eq!(
|
||||
wires.len(),
|
||||
2,
|
||||
"donut should split into 2 analytical arc wires"
|
||||
);
|
||||
for w in &wires {
|
||||
assert_eq!(w.world_width, 20.0);
|
||||
assert_eq!(w.tangent_geoms.len(), 1);
|
||||
assert!(matches!(w.tangent_geoms[0], crate::scene::model::wire_model::TangentGeom::Arc { .. }));
|
||||
assert!(matches!(
|
||||
w.tangent_geoms[0],
|
||||
crate::scene::model::wire_model::TangentGeom::Arc { .. }
|
||||
));
|
||||
}
|
||||
|
||||
let depth_map = rustc_hash::FxHashMap::default();
|
||||
|
||||
@@ -107,8 +107,8 @@ fn color_at(bytes: &[u8], ndc_x: f32) -> [u32; 3] {
|
||||
#[test]
|
||||
#[ignore = "requires a GPU adapter"]
|
||||
fn wipeout_respects_close_block_draw_order() {
|
||||
use codec::{entities::Wipeout, types::Vector2, EntityType};
|
||||
use crate::scene::{model::wire_model::TangentGeom, Scene};
|
||||
use codec::{entities::Wipeout, types::Vector2, EntityType};
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
|
||||
let adapter = block_on(instance.request_adapter(&Default::default())).expect("GPU adapter");
|
||||
@@ -151,40 +151,58 @@ fn wipeout_respects_close_block_draw_order() {
|
||||
let wires: Vec<_> = [(-0.3, 1), (0.3, 2)]
|
||||
.into_iter()
|
||||
.map(|(x, id)| {
|
||||
let mut wire = WireModel::solid(
|
||||
id.to_string(),
|
||||
Vec::new(),
|
||||
[1.; 4],
|
||||
false,
|
||||
);
|
||||
let mut wire = WireModel::solid(id.to_string(), Vec::new(), [1.; 4], false);
|
||||
let center = [x as f64 - 0.1, 0., 0.5];
|
||||
let axis_x = [1., 0., 0.];
|
||||
let axis_y = [0., 1., 0.];
|
||||
match kind {
|
||||
"circle" => wire.tangent_geoms.push(TangentGeom::PlanarCircle {
|
||||
center, axis_x, axis_y, radius: 0.1,
|
||||
center,
|
||||
axis_x,
|
||||
axis_y,
|
||||
radius: 0.1,
|
||||
}),
|
||||
"arc" => wire.tangent_geoms.push(TangentGeom::Arc {
|
||||
center, axis_x, axis_y, radius: 0.1,
|
||||
center,
|
||||
axis_x,
|
||||
axis_y,
|
||||
radius: 0.1,
|
||||
start_angle: -std::f64::consts::FRAC_PI_2,
|
||||
end_angle: std::f64::consts::FRAC_PI_2,
|
||||
}),
|
||||
"ellipse" => wire.tangent_geoms.push(TangentGeom::PlanarEllipse {
|
||||
center, major_axis: [0.1, 0., 0.], normal: [0., 0., 1.],
|
||||
minor_axis_ratio: 0.6, start_param: 0., end_param: std::f64::consts::TAU,
|
||||
center,
|
||||
major_axis: [0.1, 0., 0.],
|
||||
normal: [0., 0., 1.],
|
||||
minor_axis_ratio: 0.6,
|
||||
start_param: 0.,
|
||||
end_param: std::f64::consts::TAU,
|
||||
}),
|
||||
_ => wire.points = vec![[x, -0.65, 0.5], [x, 0.65, 0.5]],
|
||||
}
|
||||
wire
|
||||
})
|
||||
.collect();
|
||||
pipeline.gpu_circles = std::sync::Arc::new(pipeline.upload_circles(&device, &queue, &wires, &depths));
|
||||
pipeline.gpu_ellipses = std::sync::Arc::new(pipeline.upload_ellipses(&device, &queue, &wires, &depths));
|
||||
pipeline.gpu_circles =
|
||||
std::sync::Arc::new(pipeline.upload_circles(&device, &queue, &wires, &depths));
|
||||
pipeline.gpu_ellipses =
|
||||
std::sync::Arc::new(pipeline.upload_ellipses(&device, &queue, &wires, &depths));
|
||||
let bytes = pixels(&device, &queue, &mut pipeline, &uniforms, &wires, &depths);
|
||||
assert_eq!(color_at(&bytes, -0.3), [0; 3], "mask must cover earlier {kind}");
|
||||
assert_ne!(color_at(&bytes, 0.3), [0; 3], "mask erased later {kind}, packed={packed}");
|
||||
assert_eq!(
|
||||
color_at(&bytes, -0.3),
|
||||
[0; 3],
|
||||
"mask must cover earlier {kind}"
|
||||
);
|
||||
assert_ne!(
|
||||
color_at(&bytes, 0.3),
|
||||
[0; 3],
|
||||
"mask erased later {kind}, packed={packed}"
|
||||
);
|
||||
}
|
||||
assert!(block_on(validation.pop()).is_none(), "GPU validation failed");
|
||||
assert!(
|
||||
block_on(validation.pop()).is_none(),
|
||||
"GPU validation failed"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ impl DeviceCapabilities {
|
||||
pub fn supports_batched_hatch(self) -> bool {
|
||||
self.max_storage_buffers_per_shader_stage >= Self::HATCH_STORAGE_BINDINGS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -212,31 +212,53 @@ pub fn extract_ellipse_instance_from_geom(
|
||||
minor_axis_ratio,
|
||||
start_param,
|
||||
end_param,
|
||||
} = *geom else {
|
||||
} = *geom
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let major_len_sq = major_axis[0] * major_axis[0] + major_axis[1] * major_axis[1] + major_axis[2] * major_axis[2];
|
||||
let major_len_sq = major_axis[0] * major_axis[0]
|
||||
+ major_axis[1] * major_axis[1]
|
||||
+ major_axis[2] * major_axis[2];
|
||||
let norm_len_sq = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2];
|
||||
if major_len_sq <= 1e-12 || !major_len_sq.is_finite() || major_len_sq > 1e12
|
||||
|| norm_len_sq <= 1e-12 || !norm_len_sq.is_finite()
|
||||
|| minor_axis_ratio <= 1e-6 || !minor_axis_ratio.is_finite()
|
||||
|| !start_param.is_finite() || !end_param.is_finite()
|
||||
if major_len_sq <= 1e-12
|
||||
|| !major_len_sq.is_finite()
|
||||
|| major_len_sq > 1e12
|
||||
|| norm_len_sq <= 1e-12
|
||||
|| !norm_len_sq.is_finite()
|
||||
|| minor_axis_ratio <= 1e-6
|
||||
|| !minor_axis_ratio.is_finite()
|
||||
|| !start_param.is_finite()
|
||||
|| !end_param.is_finite()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let (ch, cl) = split_ds_xyz(center[0], center[1], center[2]);
|
||||
let hw = wire.line_weight_px * 0.5;
|
||||
let pat0 = [wire.pattern[0], wire.pattern[1], wire.pattern[2], wire.pattern[3]];
|
||||
let pat1 = [wire.pattern[4], wire.pattern[5], wire.pattern[6], wire.pattern[7]];
|
||||
let pat0 = [
|
||||
wire.pattern[0],
|
||||
wire.pattern[1],
|
||||
wire.pattern[2],
|
||||
wire.pattern[3],
|
||||
];
|
||||
let pat1 = [
|
||||
wire.pattern[4],
|
||||
wire.pattern[5],
|
||||
wire.pattern[6],
|
||||
wire.pattern[7],
|
||||
];
|
||||
|
||||
Some(EllipseInstance {
|
||||
center_high: ch,
|
||||
_pad0: 0.0,
|
||||
center_low: cl,
|
||||
minor_axis_ratio: minor_axis_ratio.min(1.0) as f32,
|
||||
major_axis: [major_axis[0] as f32, major_axis[1] as f32, major_axis[2] as f32],
|
||||
major_axis: [
|
||||
major_axis[0] as f32,
|
||||
major_axis[1] as f32,
|
||||
major_axis[2] as f32,
|
||||
],
|
||||
start_param: start_param as f32,
|
||||
normal: [normal[0] as f32, normal[1] as f32, normal[2] as f32],
|
||||
end_param: end_param as f32,
|
||||
@@ -279,12 +301,19 @@ pub fn can_extract_ellipse_instances(wire: &crate::scene::WireModel) -> bool {
|
||||
end_param,
|
||||
..
|
||||
} => {
|
||||
let major_len_sq = major_axis[0] * major_axis[0] + major_axis[1] * major_axis[1] + major_axis[2] * major_axis[2];
|
||||
let major_len_sq = major_axis[0] * major_axis[0]
|
||||
+ major_axis[1] * major_axis[1]
|
||||
+ major_axis[2] * major_axis[2];
|
||||
let norm_len_sq = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2];
|
||||
major_len_sq > 1e-12 && major_len_sq.is_finite() && major_len_sq <= 1e12
|
||||
&& norm_len_sq > 1e-12 && norm_len_sq.is_finite()
|
||||
&& minor_axis_ratio > 1e-6 && minor_axis_ratio.is_finite()
|
||||
&& start_param.is_finite() && end_param.is_finite()
|
||||
major_len_sq > 1e-12
|
||||
&& major_len_sq.is_finite()
|
||||
&& major_len_sq <= 1e12
|
||||
&& norm_len_sq > 1e-12
|
||||
&& norm_len_sq.is_finite()
|
||||
&& minor_axis_ratio > 1e-6
|
||||
&& minor_axis_ratio.is_finite()
|
||||
&& start_param.is_finite()
|
||||
&& end_param.is_finite()
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
@@ -366,14 +395,16 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_ellipse_instance_success() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [100.0, 200.0, 300.0],
|
||||
major_axis: [50.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.5,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
});
|
||||
wire.tangent_geoms.push(
|
||||
crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [100.0, 200.0, 300.0],
|
||||
major_axis: [50.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.5,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
},
|
||||
);
|
||||
wire.line_weight_px = 2.0;
|
||||
wire.color = [1.0, 0.5, 0.0, 1.0];
|
||||
|
||||
@@ -391,14 +422,16 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_elliptical_arc_instance_success() {
|
||||
let mut wire = crate::scene::WireModel::default();
|
||||
wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
major_axis: [10.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.8,
|
||||
start_param: 0.5,
|
||||
end_param: 2.5,
|
||||
});
|
||||
wire.tangent_geoms.push(
|
||||
crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
major_axis: [10.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.8,
|
||||
start_param: 0.5,
|
||||
end_param: 2.5,
|
||||
},
|
||||
);
|
||||
wire.line_weight_px = 1.0;
|
||||
wire.color = [0.0, 1.0, 0.0, 1.0];
|
||||
|
||||
|
||||
@@ -28,11 +28,7 @@ pub(crate) fn planar_solid_faces_view(wire: &WireModel, view_dir: glam::Vec3) ->
|
||||
let view = view_dir.as_dvec3().normalize_or_zero();
|
||||
let point = |index: usize| {
|
||||
let high = wire.fill_tris[index];
|
||||
let low = wire
|
||||
.fill_tris_low
|
||||
.get(index)
|
||||
.copied()
|
||||
.unwrap_or([0.0; 3]);
|
||||
let low = wire.fill_tris_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||
glam::DVec3::new(
|
||||
high[0] as f64 + low[0] as f64,
|
||||
high[1] as f64 + low[1] as f64,
|
||||
@@ -48,10 +44,7 @@ pub(crate) fn planar_solid_faces_view(wire: &WireModel, view_dir: glam::Vec3) ->
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn planar_solid_visibility_key(
|
||||
wires: &[WireModel],
|
||||
view_dir: glam::Vec3,
|
||||
) -> u64 {
|
||||
pub(crate) fn planar_solid_visibility_key(wires: &[WireModel], view_dir: glam::Vec3) -> u64 {
|
||||
let mut key = 0xcbf29ce484222325_u64;
|
||||
for wire in wires
|
||||
.iter()
|
||||
@@ -138,9 +131,21 @@ pub struct Face3DInstance {
|
||||
impl Face3DInstance {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(Face3DInstance, translation) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(Face3DInstance, translation_low) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(Face3DInstance, draw_depth) as u64, shader_location: 6, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(Face3DInstance, translation) as u64,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(Face3DInstance, translation_low) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(Face3DInstance, draw_depth) as u64,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
@@ -194,8 +199,7 @@ impl Face3DGpu {
|
||||
view_dir: glam::Vec3,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> Self {
|
||||
let depth_of =
|
||||
|w: &WireModel| -> f32 { super::wire_gpu::wire_draw_depth(w, depth_map) };
|
||||
let depth_of = |w: &WireModel| -> f32 { super::wire_gpu::wire_draw_depth(w, depth_map) };
|
||||
let mut verts_3d: Vec<Face3DVertex> = Vec::with_capacity(face3d_wires.len() * 6);
|
||||
let mut verts_2d: Vec<Face3DVertex> = Vec::new();
|
||||
|
||||
@@ -212,11 +216,7 @@ impl Face3DGpu {
|
||||
position,
|
||||
color: fill_color,
|
||||
draw_depth: 0.0,
|
||||
position_low: wire
|
||||
.fill_tris_low
|
||||
.get(index)
|
||||
.copied()
|
||||
.unwrap_or([0.0; 3]),
|
||||
position_low: wire.fill_tris_low.get(index).copied().unwrap_or([0.0; 3]),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,11 @@ impl Face3DGpu {
|
||||
zmin = zmin.min(p[2]);
|
||||
zmax = zmax.max(p[2]);
|
||||
}
|
||||
let depth = if zmax - zmin > 1e-4 { 0.0 } else { depth_of(wire) };
|
||||
let depth = if zmax - zmin > 1e-4 {
|
||||
0.0
|
||||
} else {
|
||||
depth_of(wire)
|
||||
};
|
||||
for (i, &position) in wire.fill_tris.iter().enumerate() {
|
||||
verts_2d.push(Face3DVertex {
|
||||
position,
|
||||
@@ -359,7 +363,11 @@ fn upload_block_chunks(
|
||||
continue;
|
||||
};
|
||||
let [r, g, b, a] = source.color;
|
||||
let color = if is_3d { [r * 0.45, g * 0.45, b * 0.45, a] } else { source.color };
|
||||
let color = if is_3d {
|
||||
[r * 0.45, g * 0.45, b * 0.45, a]
|
||||
} else {
|
||||
source.color
|
||||
};
|
||||
let vertices: Vec<Face3DVertex> = source
|
||||
.fill_tris
|
||||
.iter()
|
||||
@@ -368,11 +376,7 @@ fn upload_block_chunks(
|
||||
position,
|
||||
color,
|
||||
draw_depth: 0.0,
|
||||
position_low: source
|
||||
.fill_tris_low
|
||||
.get(index)
|
||||
.copied()
|
||||
.unwrap_or([0.0; 3]),
|
||||
position_low: source.fill_tris_low.get(index).copied().unwrap_or([0.0; 3]),
|
||||
})
|
||||
.collect();
|
||||
if vertices.is_empty() {
|
||||
|
||||
+144
-66
@@ -68,28 +68,44 @@ fn block_edits_preserve_cache_coordinates_and_arena_partition() {
|
||||
let first = block(1, 10.0);
|
||||
let second = block(2, 20.0);
|
||||
let depth = rustc_hash::FxHashMap::from_iter([(1, [0.1, 0.01]), (2, [0.2, 0.01])]);
|
||||
let original = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth, &mut block_geometry);
|
||||
let unchanged = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth, &mut block_geometry);
|
||||
let original = pipeline.upload_block_wires(
|
||||
&device,
|
||||
&queue,
|
||||
&[&first, &second],
|
||||
&depth,
|
||||
&mut block_geometry,
|
||||
);
|
||||
let unchanged = pipeline.upload_block_wires(
|
||||
&device,
|
||||
&queue,
|
||||
&[&first, &second],
|
||||
&depth,
|
||||
&mut block_geometry,
|
||||
);
|
||||
assert_eq!(original[0].geometry_id(), unchanged[0].geometry_id());
|
||||
assert_eq!(original[0].instance_count, 2);
|
||||
|
||||
let removed = pipeline.upload_block_wires(&device, &queue, &[&second], &depth, &mut block_geometry);
|
||||
let removed =
|
||||
pipeline.upload_block_wires(&device, &queue, &[&second], &depth, &mut block_geometry);
|
||||
assert_ne!(
|
||||
original[0].geometry_id(),
|
||||
removed[0].geometry_id(),
|
||||
"removing the base instance must replace vertices baked at its old position"
|
||||
);
|
||||
let moved = block(2, 30.0);
|
||||
let moved_gpu = pipeline.upload_block_wires(&device, &queue, &[&moved], &depth, &mut block_geometry);
|
||||
let moved_gpu =
|
||||
pipeline.upload_block_wires(&device, &queue, &[&moved], &depth, &mut block_geometry);
|
||||
assert_ne!(removed[0].geometry_id(), moved_gpu[0].geometry_id());
|
||||
let restored = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth, &mut block_geometry);
|
||||
let restored = pipeline.upload_block_wires(
|
||||
&device,
|
||||
&queue,
|
||||
&[&first, &second],
|
||||
&depth,
|
||||
&mut block_geometry,
|
||||
);
|
||||
assert_ne!(moved_gpu[0].geometry_id(), restored[0].geometry_id());
|
||||
assert_eq!(restored[0].instance_count, 2);
|
||||
assert_eq!(
|
||||
block_geometry.len(),
|
||||
1,
|
||||
"discard obsolete geometry"
|
||||
);
|
||||
assert_eq!(block_geometry.len(), 1, "discard obsolete geometry");
|
||||
|
||||
let line = WireModel {
|
||||
render_instance: None,
|
||||
@@ -462,12 +478,14 @@ fn test_selected_circle_arc_ellipse_highlight_overlay() {
|
||||
|
||||
let mut circle_wire = WireModel::default();
|
||||
circle_wire.name = "circle_wire".into();
|
||||
circle_wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
circle_wire
|
||||
.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
|
||||
let handle = codec::Handle::new(100);
|
||||
let mut selected_handles = rustc_hash::FxHashSet::default();
|
||||
@@ -548,8 +566,18 @@ fn test_selected_circle_arc_ellipse_highlight_overlay() {
|
||||
&mut encoder,
|
||||
&target,
|
||||
iced::Rectangle::with_size(iced::Size::new(512.0, 512.0)),
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
false,
|
||||
false,
|
||||
@@ -575,14 +603,16 @@ fn test_thick_and_tapered_arc_gpu_rendering() {
|
||||
// 1. Wide circular arc with pick triangles
|
||||
let mut wide_arc = WireModel::default();
|
||||
wide_arc.name = "wide_arc".into();
|
||||
wide_arc.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
wide_arc
|
||||
.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
wide_arc.world_width = 10.0;
|
||||
wide_arc.color = [1.0, 0.2, 0.2, 1.0];
|
||||
wide_arc.pick_tris = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
|
||||
@@ -591,14 +621,16 @@ fn test_thick_and_tapered_arc_gpu_rendering() {
|
||||
// 2. Tapered circular arc
|
||||
let mut tapered_arc = WireModel::default();
|
||||
tapered_arc.name = "tapered_arc".into();
|
||||
tapered_arc.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [150.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 40.0,
|
||||
start_angle: 0.2,
|
||||
end_angle: 2.8,
|
||||
});
|
||||
tapered_arc
|
||||
.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [150.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 40.0,
|
||||
start_angle: 0.2,
|
||||
end_angle: 2.8,
|
||||
});
|
||||
tapered_arc.world_width = 16.0;
|
||||
tapered_arc.taper_widths = vec![2.0, 16.0];
|
||||
tapered_arc.color = [0.2, 0.8, 1.0, 1.0];
|
||||
@@ -637,8 +669,18 @@ fn test_thick_and_tapered_arc_gpu_rendering() {
|
||||
&mut encoder,
|
||||
&target,
|
||||
iced::Rectangle::with_size(iced::Size::new(512.0, 512.0)),
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
false,
|
||||
false,
|
||||
@@ -674,22 +716,26 @@ fn test_tilted_3d_donut_and_thick_arc_gpu_rendering() {
|
||||
// Donut circular arc segments
|
||||
let mut donut = WireModel::default();
|
||||
donut.name = "donut".into();
|
||||
donut.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
donut.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: std::f64::consts::PI,
|
||||
end_angle: std::f64::consts::TAU,
|
||||
});
|
||||
donut
|
||||
.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: 0.0,
|
||||
end_angle: std::f64::consts::PI,
|
||||
});
|
||||
donut
|
||||
.tangent_geoms
|
||||
.push(crate::scene::model::wire_model::TangentGeom::Arc {
|
||||
center: [50.0, 50.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 30.0,
|
||||
start_angle: std::f64::consts::PI,
|
||||
end_angle: std::f64::consts::TAU,
|
||||
});
|
||||
donut.world_width = 12.0;
|
||||
donut.color = [0.2, 0.9, 0.3, 1.0];
|
||||
|
||||
@@ -743,8 +789,18 @@ fn test_tilted_3d_donut_and_thick_arc_gpu_rendering() {
|
||||
&mut encoder,
|
||||
&target,
|
||||
iced::Rectangle::with_size(iced::Size::new(512.0, 512.0)),
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
false,
|
||||
false,
|
||||
@@ -770,14 +826,16 @@ fn test_selected_ellipse_overlay() {
|
||||
|
||||
let mut ellipse_wire = WireModel::default();
|
||||
ellipse_wire.name = "ellipse_wire".into();
|
||||
ellipse_wire.tangent_geoms.push(crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
major_axis: [10.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.5,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
});
|
||||
ellipse_wire.tangent_geoms.push(
|
||||
crate::scene::model::wire_model::TangentGeom::PlanarEllipse {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
major_axis: [10.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.5,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
},
|
||||
);
|
||||
|
||||
let handle = codec::Handle::new(200);
|
||||
let mut selected_handles = rustc_hash::FxHashSet::default();
|
||||
@@ -859,8 +917,18 @@ fn test_selected_ellipse_overlay() {
|
||||
&mut encoder,
|
||||
&target,
|
||||
iced::Rectangle::with_size(iced::Size::new(512.0, 512.0)),
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
false,
|
||||
false,
|
||||
@@ -931,8 +999,18 @@ fn test_pline_arc_switch_preview_and_render() {
|
||||
&mut encoder,
|
||||
&target,
|
||||
iced::Rectangle::with_size(iced::Size::new(512.0, 512.0)),
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle { x: 0, y: 0, width: 512, height: 512 },
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
iced::Rectangle {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
false,
|
||||
false,
|
||||
|
||||
@@ -84,9 +84,15 @@ impl HatchGpu {
|
||||
})),
|
||||
});
|
||||
let vertex_layouts = if uses_storage {
|
||||
vec![storage::HatchVertex::layout(), storage::HatchPlacement::layout()]
|
||||
vec![
|
||||
storage::HatchVertex::layout(),
|
||||
storage::HatchPlacement::layout(),
|
||||
]
|
||||
} else {
|
||||
vec![texture::vertex_layout(), texture::TextureHatchPlacement::layout()]
|
||||
vec![
|
||||
texture::vertex_layout(),
|
||||
texture::TextureHatchPlacement::layout(),
|
||||
]
|
||||
};
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some(if uses_storage {
|
||||
@@ -180,7 +186,8 @@ impl HatchGpu {
|
||||
.filter(|hatch| hatch.boundary.len() >= 3)
|
||||
.cloned()
|
||||
.collect();
|
||||
*resident = StorageHatchBatch::build(device, queue, &self.bind_group_layout, &renderable);
|
||||
*resident =
|
||||
StorageHatchBatch::build(device, queue, &self.bind_group_layout, &renderable);
|
||||
}
|
||||
HatchBackend::Texture { resident, .. } => {
|
||||
let renderable: Vec<HatchModel> = hatches
|
||||
@@ -188,12 +195,8 @@ impl HatchGpu {
|
||||
.filter(|hatch| hatch.boundary.len() >= 3)
|
||||
.cloned()
|
||||
.collect();
|
||||
*resident = TextureHatch::from_models(
|
||||
device,
|
||||
queue,
|
||||
&renderable,
|
||||
&self.bind_group_layout,
|
||||
);
|
||||
*resident =
|
||||
TextureHatch::from_models(device, queue, &renderable, &self.bind_group_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,7 +214,8 @@ impl HatchGpu {
|
||||
.filter(|hatch| hatch.boundary.len() >= 3)
|
||||
.cloned()
|
||||
.collect();
|
||||
*preview = StorageHatchBatch::build(device, queue, &self.bind_group_layout, &renderable);
|
||||
*preview =
|
||||
StorageHatchBatch::build(device, queue, &self.bind_group_layout, &renderable);
|
||||
}
|
||||
HatchBackend::Texture { preview, .. } => {
|
||||
let renderable: Vec<HatchModel> = hatches
|
||||
@@ -219,12 +223,8 @@ impl HatchGpu {
|
||||
.filter(|hatch| hatch.boundary.len() >= 3)
|
||||
.cloned()
|
||||
.collect();
|
||||
*preview = TextureHatch::from_models(
|
||||
device,
|
||||
queue,
|
||||
&renderable,
|
||||
&self.bind_group_layout,
|
||||
);
|
||||
*preview =
|
||||
TextureHatch::from_models(device, queue, &renderable, &self.bind_group_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,8 +236,7 @@ impl HatchGpu {
|
||||
queue: &wgpu::Queue,
|
||||
mut is_visible: impl FnMut([f32; 4]) -> bool,
|
||||
) -> usize {
|
||||
let HatchBackend::Storage { resident, .. } = &mut self.backend
|
||||
else {
|
||||
let HatchBackend::Storage { resident, .. } = &mut self.backend else {
|
||||
return 0;
|
||||
};
|
||||
let mut updated = 0;
|
||||
@@ -287,10 +286,7 @@ impl HatchGpu {
|
||||
pass.set_bind_group(1, &hatch.bind_group, &[]);
|
||||
pass.set_vertex_buffer(0, hatch.vertex_buffer.slice(..));
|
||||
pass.set_vertex_buffer(1, hatch.placement_buffer.slice(..));
|
||||
pass.set_index_buffer(
|
||||
hatch.index_buffer.slice(..),
|
||||
wgpu::IndexFormat::Uint32,
|
||||
);
|
||||
pass.set_index_buffer(hatch.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
||||
pass.draw_indexed(0..hatch.index_count, 0, 0..hatch.instance_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ pub struct HatchInstance {
|
||||
pub family_count: u32, // 100
|
||||
/// Signed draw-order depth (-1,1); 0.0 = neutral. Applied as a clip-z
|
||||
/// bias in the vertex shader so this fill orders against other types.
|
||||
pub draw_depth: f32, // 104
|
||||
pub draw_depth: f32, // 104
|
||||
/// Gradient shape (`GradientKind::shader_kind`), bit 4 = inverted stops.
|
||||
pub grad_kind: u32, // 108
|
||||
pub grad_kind: u32, // 108
|
||||
}
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<HatchInstance>() == 112);
|
||||
@@ -43,7 +43,10 @@ const _: () = assert!(std::mem::size_of::<HatchInstance>() == 112);
|
||||
fn split_origin_ds(o: [f64; 2]) -> ([f32; 2], [f32; 2]) {
|
||||
let hx = o[0] as f32;
|
||||
let hy = o[1] as f32;
|
||||
([hx, hy], [(o[0] - hx as f64) as f32, (o[1] - hy as f64) as f32])
|
||||
(
|
||||
[hx, hy],
|
||||
[(o[0] - hx as f64) as f32, (o[1] - hy as f64) as f32],
|
||||
)
|
||||
}
|
||||
|
||||
/// Mirrors the per-family struct used by the existing per-hatch shader,
|
||||
@@ -53,18 +56,18 @@ fn split_origin_ds(o: [f64; 2]) -> ([f32; 2], [f32; 2]) {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct LineFamilyGpu {
|
||||
pub cos_a: f32, // 0
|
||||
pub sin_a: f32, // 4
|
||||
pub x0: f32, // 8
|
||||
pub y0: f32, // 12
|
||||
pub dx: f32, // 16
|
||||
pub dy: f32, // 20
|
||||
pub perp_step: f32, // 24
|
||||
pub along_step: f32, // 28
|
||||
pub line_width: f32, // 32
|
||||
pub period: f32, // 36
|
||||
pub n_dashes: u32, // 40
|
||||
pub dash_offset: u32, // 44
|
||||
pub cos_a: f32, // 0
|
||||
pub sin_a: f32, // 4
|
||||
pub x0: f32, // 8
|
||||
pub y0: f32, // 12
|
||||
pub dx: f32, // 16
|
||||
pub dy: f32, // 20
|
||||
pub perp_step: f32, // 24
|
||||
pub along_step: f32, // 28
|
||||
pub line_width: f32, // 32
|
||||
pub period: f32, // 36
|
||||
pub n_dashes: u32, // 40
|
||||
pub dash_offset: u32, // 44
|
||||
}
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<LineFamilyGpu>() == 48);
|
||||
@@ -113,10 +116,26 @@ impl HatchPlacement {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute { offset: 0, shader_location: 2, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: 8, shader_location: 3, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: 16, shader_location: 4, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute { offset: 20, shader_location: 5, format: wgpu::VertexFormat::Uint32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 8,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 16,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 20,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Uint32,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -137,18 +156,23 @@ pub(super) struct StorageHatchBatch {
|
||||
// dropping them would invalidate it, but the bind group is the
|
||||
// only direct consumer. Keep them as fields to keep ownership in
|
||||
// one place; `#[allow(dead_code)]` silences the read-never warning.
|
||||
#[allow(dead_code)] pub instance_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)] pub family_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)] pub dash_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)]
|
||||
pub instance_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)]
|
||||
pub family_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)]
|
||||
pub dash_buffer: wgpu::Buffer,
|
||||
/// Per-instance visibility flag (1=draw, 0=skip). Stored in its
|
||||
/// own small storage buffer so per-frame updates don't have to
|
||||
/// touch the large `instance_buffer`. Vertex shader reads
|
||||
/// `visibility[instance_index]` — when 0 it emits an out-of-NDC
|
||||
/// clip position so the GPU clips the primitive before the
|
||||
/// fragment stage runs.
|
||||
#[allow(dead_code)] pub visibility_buffer: wgpu::Buffer,
|
||||
#[allow(dead_code)]
|
||||
pub visibility_buffer: wgpu::Buffer,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
#[allow(dead_code)] pub instance_count: u32,
|
||||
#[allow(dead_code)]
|
||||
pub instance_count: u32,
|
||||
/// CPU mirror — `update_visibility` re-uploads this whole slice
|
||||
/// when any flag changes. ~4 B per hatch, so 40 KB / 10 k hatches
|
||||
/// per pan tick. Far cheaper than touching the instance data.
|
||||
@@ -168,7 +192,10 @@ fn hatch_buffer_cost(hatch: &HatchModel) -> [u64; 7] {
|
||||
let (families, dashes) = match &hatch.pattern {
|
||||
HatchPattern::Pattern(families) => (
|
||||
families.len() as u64,
|
||||
families.iter().map(|family| family.dashes.len() as u64).sum(),
|
||||
families
|
||||
.iter()
|
||||
.map(|family| family.dashes.len() as u64)
|
||||
.sum(),
|
||||
),
|
||||
_ => (0, 0),
|
||||
};
|
||||
@@ -234,7 +261,6 @@ impl StorageHatchBatch {
|
||||
bgl: &wgpu::BindGroupLayout,
|
||||
hatches: &[HatchModel],
|
||||
) -> Option<Self> {
|
||||
|
||||
let mut instances: Vec<HatchInstance> = Vec::with_capacity(hatches.len());
|
||||
let mut families: Vec<LineFamilyGpu> = Vec::new();
|
||||
let mut dashes: Vec<f32> = Vec::new();
|
||||
@@ -332,10 +358,18 @@ impl StorageHatchBatch {
|
||||
let mut max_y = f32::NEG_INFINITY;
|
||||
for &[x, y] in h.boundary.iter() {
|
||||
if x.is_finite() && y.is_finite() {
|
||||
if x < min_x { min_x = x; }
|
||||
if y < min_y { min_y = y; }
|
||||
if x > max_x { max_x = x; }
|
||||
if y > max_y { max_y = y; }
|
||||
if x < min_x {
|
||||
min_x = x;
|
||||
}
|
||||
if y < min_y {
|
||||
min_y = y;
|
||||
}
|
||||
if x > max_x {
|
||||
max_x = x;
|
||||
}
|
||||
if y > max_y {
|
||||
max_y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !min_x.is_finite() {
|
||||
@@ -435,7 +469,12 @@ impl StorageHatchBatch {
|
||||
if end > 0 {
|
||||
draws.push((0..end, 0..1));
|
||||
}
|
||||
let mut union = [f32::INFINITY, f32::INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY];
|
||||
let mut union = [
|
||||
f32::INFINITY,
|
||||
f32::INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
];
|
||||
for inst in instances.iter().take(unique_source_count) {
|
||||
union[0] = union[0].min(inst.aabb[0] + inst.world_origin[0]);
|
||||
union[1] = union[1].min(inst.aabb[1] + inst.world_origin[1]);
|
||||
@@ -576,12 +615,14 @@ impl StorageHatchBatch {
|
||||
});
|
||||
let source_aabbs = instances
|
||||
.iter()
|
||||
.map(|inst| [
|
||||
inst.aabb[0] + inst.world_origin[0],
|
||||
inst.aabb[1] + inst.world_origin[1],
|
||||
inst.aabb[2] + inst.world_origin[0],
|
||||
inst.aabb[3] + inst.world_origin[1],
|
||||
])
|
||||
.map(|inst| {
|
||||
[
|
||||
inst.aabb[0] + inst.world_origin[0],
|
||||
inst.aabb[1] + inst.world_origin[1],
|
||||
inst.aabb[2] + inst.world_origin[0],
|
||||
inst.aabb[3] + inst.world_origin[1],
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(Self {
|
||||
|
||||
@@ -44,9 +44,21 @@ impl TextureHatchPlacement {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute { offset: 0, shader_location: 1, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: 8, shader_location: 2, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: 16, shader_location: 3, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 8,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 16,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -164,7 +176,13 @@ impl TextureHatch {
|
||||
let (mode, color2, grad_cos, grad_sin) = match &model.pattern {
|
||||
HatchPattern::Solid => (1u32, [0.0f32; 4], 0.0f32, 0.0f32),
|
||||
HatchPattern::Pattern(_) => (0u32, [0.0f32; 4], 0.0f32, 0.0f32),
|
||||
HatchPattern::Gradient { angle_deg, color2, kind, invert, .. } => {
|
||||
HatchPattern::Gradient {
|
||||
angle_deg,
|
||||
color2,
|
||||
kind,
|
||||
invert,
|
||||
..
|
||||
} => {
|
||||
let gk = (kind.shader_kind() | if *invert { 16 } else { 0 }) << 8;
|
||||
if kind.radial() {
|
||||
// Radial: centre is the local origin; grad_cos/sin unused.
|
||||
@@ -177,8 +195,12 @@ impl TextureHatch {
|
||||
};
|
||||
|
||||
// ── Bounding box ─────────────────────────────────────────────────
|
||||
let (mut min_x, mut max_x, mut min_y, mut max_y) =
|
||||
(f32::INFINITY, f32::NEG_INFINITY, f32::INFINITY, f32::NEG_INFINITY);
|
||||
let (mut min_x, mut max_x, mut min_y, mut max_y) = (
|
||||
f32::INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
f32::INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
);
|
||||
for &[x, y] in model.boundary.iter() {
|
||||
if !x.is_finite() || !y.is_finite() {
|
||||
continue;
|
||||
@@ -196,7 +218,10 @@ impl TextureHatch {
|
||||
}
|
||||
let vertices: Vec<HatchVertex> = mesh_points
|
||||
.into_iter()
|
||||
.map(|[x, y]| HatchVertex { pos: [x, y, 0.0], _pad: 0.0 })
|
||||
.map(|[x, y]| HatchVertex {
|
||||
pos: [x, y, 0.0],
|
||||
_pad: 0.0,
|
||||
})
|
||||
.collect();
|
||||
let vertex_buffer = crate::scene::pipeline::gpu_upload::upload_buffer(
|
||||
device,
|
||||
@@ -362,8 +387,16 @@ impl TextureHatch {
|
||||
// Clamp like the desktop renderer so scale==0 can't make perp_step 0
|
||||
// → round(perp/0)=NaN → an invisible hatch.
|
||||
scale: model.scale.max(1e-6),
|
||||
grad_cos: if base_mode == 3 { radial_center[0] } else { grad_cos },
|
||||
grad_sin: if base_mode == 3 { radial_center[1] } else { grad_sin },
|
||||
grad_cos: if base_mode == 3 {
|
||||
radial_center[0]
|
||||
} else {
|
||||
grad_cos
|
||||
},
|
||||
grad_sin: if base_mode == 3 {
|
||||
radial_center[1]
|
||||
} else {
|
||||
grad_sin
|
||||
},
|
||||
grad_min,
|
||||
grad_range,
|
||||
origin: [origin[0] as f32, origin[1] as f32],
|
||||
|
||||
@@ -58,9 +58,21 @@ pub struct ImageInstance {
|
||||
impl ImageInstance {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(ImageInstance, translation) as u64, shader_location: 3, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(ImageInstance, translation_low) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(ImageInstance, draw_depth) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(ImageInstance, translation) as u64,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(ImageInstance, translation_low) as u64,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(ImageInstance, draw_depth) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
@@ -138,7 +150,11 @@ impl ImageGpu {
|
||||
groups
|
||||
.into_iter()
|
||||
.flat_map(|group| {
|
||||
let sampler = if group.first().is_some_and(|m| m.pixelated) { &pixelated } else { &smooth };
|
||||
let sampler = if group.first().is_some_and(|m| m.pixelated) {
|
||||
&pixelated
|
||||
} else {
|
||||
&smooth
|
||||
};
|
||||
Self::new(device, queue, &group, bgl1, sampler)
|
||||
})
|
||||
.collect()
|
||||
@@ -223,7 +239,14 @@ impl ImageGpu {
|
||||
"image.texture:{}:{}:{}",
|
||||
model.file_path, x.content_start, y.content_start
|
||||
);
|
||||
let levels = mip_chain(&model.pixels, model.width, x.data_start, y.data_start, width, height);
|
||||
let levels = mip_chain(
|
||||
&model.pixels,
|
||||
model.width,
|
||||
x.data_start,
|
||||
y.data_start,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(&tex_label),
|
||||
size: wgpu::Extent3d {
|
||||
@@ -235,8 +258,7 @@ impl ImageGpu {
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING
|
||||
| wgpu::TextureUsages::COPY_DST,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
for (level, (w, h, data)) in levels.iter().enumerate() {
|
||||
@@ -260,8 +282,7 @@ impl ImageGpu {
|
||||
},
|
||||
);
|
||||
}
|
||||
let tex_view =
|
||||
texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let tex_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("image.bind_group1"),
|
||||
layout: bgl1,
|
||||
@@ -317,7 +338,14 @@ struct TileRange {
|
||||
/// lines on a transparent page (PDF, DWF, DGN underlays) stay visible when
|
||||
/// zoomed out instead of falling between samples, and opaque images are
|
||||
/// averaged as usual.
|
||||
fn mip_chain(pixels: &[u8], stride: u32, x0: u32, y0: u32, w: u32, h: u32) -> Vec<(u32, u32, Vec<u8>)> {
|
||||
fn mip_chain(
|
||||
pixels: &[u8],
|
||||
stride: u32,
|
||||
x0: u32,
|
||||
y0: u32,
|
||||
w: u32,
|
||||
h: u32,
|
||||
) -> Vec<(u32, u32, Vec<u8>)> {
|
||||
let (stride, x0, y0) = (stride as usize, x0 as usize, y0 as usize);
|
||||
let mut base = Vec::with_capacity(w as usize * h as usize * 4);
|
||||
for row in 0..h as usize {
|
||||
|
||||
+139
-100
@@ -234,9 +234,7 @@ impl MeshInstanceGpu {
|
||||
translation[2] as f32,
|
||||
];
|
||||
let linear = glam::DMat3::from_cols_array(&[
|
||||
m[0][0], m[1][0], m[2][0],
|
||||
m[0][1], m[1][1], m[2][1],
|
||||
m[0][2], m[1][2], m[2][2],
|
||||
m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2],
|
||||
]);
|
||||
let normal = if linear.determinant().abs() > 1e-18 {
|
||||
linear.inverse().transpose()
|
||||
@@ -483,10 +481,8 @@ fn make_chunk(
|
||||
};
|
||||
let mut wire_vertices = Vec::with_capacity(wire_indices.len());
|
||||
for line in wire_indices.chunks_exact(2) {
|
||||
let (Some(start), Some(end)) = (
|
||||
verts.get(line[0] as usize),
|
||||
verts.get(line[1] as usize),
|
||||
) else {
|
||||
let (Some(start), Some(end)) = (verts.get(line[0] as usize), verts.get(line[1] as usize))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
for vertex in [start, end] {
|
||||
@@ -569,11 +565,20 @@ struct MeshSurfaceParams {
|
||||
}
|
||||
|
||||
fn valid_rgba_size(width: u32, height: u32, pixels: &[u8]) -> bool {
|
||||
width > 0 && height > 0
|
||||
&& (width as usize).checked_mul(height as usize).and_then(|size| size.checked_mul(4)) == Some(pixels.len())
|
||||
width > 0
|
||||
&& height > 0
|
||||
&& (width as usize)
|
||||
.checked_mul(height as usize)
|
||||
.and_then(|size| size.checked_mul(4))
|
||||
== Some(pixels.len())
|
||||
}
|
||||
|
||||
fn downscale_rgba_to_limit(width: u32, height: u32, rgba: &[u8], limit: u32) -> Option<(u32, u32, Vec<u8>)> {
|
||||
fn downscale_rgba_to_limit(
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: &[u8],
|
||||
limit: u32,
|
||||
) -> Option<(u32, u32, Vec<u8>)> {
|
||||
if limit == 0 || !valid_rgba_size(width, height, rgba) {
|
||||
return None;
|
||||
}
|
||||
@@ -581,7 +586,12 @@ fn downscale_rgba_to_limit(width: u32, height: u32, rgba: &[u8], limit: u32) ->
|
||||
let scale = (limit as f64 / width.max(height) as f64).min(1.0);
|
||||
let new_width = ((width as f64 * scale).round() as u32).clamp(1, limit);
|
||||
let new_height = ((height as f64 * scale).round() as u32).clamp(1, limit);
|
||||
let resized = image::imageops::resize(&buffer, new_width, new_height, image::imageops::FilterType::Triangle);
|
||||
let resized = image::imageops::resize(
|
||||
&buffer,
|
||||
new_width,
|
||||
new_height,
|
||||
image::imageops::FilterType::Triangle,
|
||||
);
|
||||
Some((new_width, new_height, resized.into_raw()))
|
||||
}
|
||||
|
||||
@@ -674,18 +684,54 @@ pub fn create_material_resources(
|
||||
let bump = material.and_then(|material| material.bump_map.image.as_deref());
|
||||
let refraction = material.and_then(|material| material.refraction_map.image.as_deref());
|
||||
let normal = material.and_then(|material| material.normal_map.image.as_deref());
|
||||
let diffuse_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.diffuse", diffuse, [255; 4], true);
|
||||
let specular_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.specular", specular, [255; 4], true);
|
||||
let reflection_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.reflection", reflection, [0, 0, 0, 255], true);
|
||||
let opacity_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.opacity", opacity, [255; 4], false);
|
||||
let bump_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.bump", bump, [128, 128, 128, 255], false);
|
||||
let refraction_view =
|
||||
upload_rgba_texture(device, queue, "mesh.material.refraction", refraction, [255; 4], true);
|
||||
let diffuse_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.diffuse",
|
||||
diffuse,
|
||||
[255; 4],
|
||||
true,
|
||||
);
|
||||
let specular_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.specular",
|
||||
specular,
|
||||
[255; 4],
|
||||
true,
|
||||
);
|
||||
let reflection_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.reflection",
|
||||
reflection,
|
||||
[0, 0, 0, 255],
|
||||
true,
|
||||
);
|
||||
let opacity_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.opacity",
|
||||
opacity,
|
||||
[255; 4],
|
||||
false,
|
||||
);
|
||||
let bump_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.bump",
|
||||
bump,
|
||||
[128, 128, 128, 255],
|
||||
false,
|
||||
);
|
||||
let refraction_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
"mesh.material.refraction",
|
||||
refraction,
|
||||
[255; 4],
|
||||
true,
|
||||
);
|
||||
let normal_view = upload_rgba_texture(
|
||||
device,
|
||||
queue,
|
||||
@@ -850,8 +896,7 @@ pub fn create_material_bind_group_from_resources(
|
||||
material: Option<&crate::scene::model::material_model::MeshMaterial>,
|
||||
face_color: [f32; 4],
|
||||
) -> wgpu::BindGroup {
|
||||
let (material_params, specular, ambient, advanced, flags) =
|
||||
material_vertex_params(material);
|
||||
let (material_params, specular, ambient, advanced, flags) = material_vertex_params(material);
|
||||
let surface = MeshSurfaceParams {
|
||||
face_color,
|
||||
material: material_params,
|
||||
@@ -947,14 +992,7 @@ pub fn create_material_bind_group(
|
||||
) -> wgpu::BindGroup {
|
||||
let resources = create_material_resources(device, queue, material);
|
||||
let color = material.map_or([0.8, 0.8, 0.8, 1.0], |material| material.diffuse);
|
||||
create_material_bind_group_from_resources(
|
||||
device,
|
||||
queue,
|
||||
layout,
|
||||
&resources,
|
||||
material,
|
||||
color,
|
||||
)
|
||||
create_material_bind_group_from_resources(device, queue, layout, &resources, material, color)
|
||||
}
|
||||
|
||||
pub fn upload_chunk_material_bind_groups(
|
||||
@@ -967,8 +1005,7 @@ pub fn upload_chunk_material_bind_groups(
|
||||
let mut materials = rustc_hash::FxHashMap::default();
|
||||
for chunk in chunks {
|
||||
let resources = match chunk.material.as_ref() {
|
||||
None => fallback
|
||||
.get_or_insert_with(|| create_material_resources(device, queue, None)),
|
||||
None => fallback.get_or_insert_with(|| create_material_resources(device, queue, None)),
|
||||
Some(material) => {
|
||||
let Some(handle) = material.handle else {
|
||||
let resources = create_material_resources(device, queue, Some(material));
|
||||
@@ -982,9 +1019,9 @@ pub fn upload_chunk_material_bind_groups(
|
||||
));
|
||||
continue;
|
||||
};
|
||||
materials.entry(handle.value()).or_insert_with(|| {
|
||||
create_material_resources(device, queue, Some(material))
|
||||
})
|
||||
materials
|
||||
.entry(handle.value())
|
||||
.or_insert_with(|| create_material_resources(device, queue, Some(material)))
|
||||
}
|
||||
};
|
||||
chunk.material_bind_group = Some(create_material_bind_group_from_resources(
|
||||
@@ -1044,9 +1081,7 @@ fn material_key(
|
||||
MaterialBatchKey(key)
|
||||
}
|
||||
|
||||
fn material_mapper_key(
|
||||
material: &crate::scene::model::material_model::MeshMaterial,
|
||||
) -> u64 {
|
||||
fn material_mapper_key(material: &crate::scene::model::material_model::MeshMaterial) -> u64 {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let Some(mapper) = material.mapper else {
|
||||
return 0;
|
||||
@@ -1260,8 +1295,7 @@ fn triangle_mapping_normal(mesh: &MeshModel, triangle: &[u32]) -> [f32; 3] {
|
||||
ab[2] * ac[0] - ab[0] * ac[2],
|
||||
ab[0] * ac[1] - ab[1] * ac[0],
|
||||
];
|
||||
let length = (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2])
|
||||
.sqrt();
|
||||
let length = (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
|
||||
if length > f32::EPSILON {
|
||||
[normal[0] / length, normal[1] / length, normal[2] / length]
|
||||
} else {
|
||||
@@ -1292,7 +1326,9 @@ fn build_instanced_chunks(
|
||||
material.map_or(0, |material| {
|
||||
let handle = material
|
||||
.handle
|
||||
.map_or(material as *const _ as usize as u64, |handle| handle.value());
|
||||
.map_or(material as *const _ as usize as u64, |handle| {
|
||||
handle.value()
|
||||
});
|
||||
handle ^ material_mapper_key(material).rotate_left(17)
|
||||
})
|
||||
} else {
|
||||
@@ -1307,10 +1343,10 @@ fn build_instanced_chunks(
|
||||
let needs_wire_vertices = first.include_edges && source.edge_verts.is_empty();
|
||||
let split_geometry = (first.include_faces && material_has_box_projection(material))
|
||||
|| mesh
|
||||
.verts
|
||||
.len()
|
||||
.saturating_mul(std::mem::size_of::<MeshVertex>())
|
||||
> budget
|
||||
.verts
|
||||
.len()
|
||||
.saturating_mul(std::mem::size_of::<MeshVertex>())
|
||||
> budget
|
||||
|| first
|
||||
.indices
|
||||
.len()
|
||||
@@ -1369,7 +1405,9 @@ fn build_instanced_chunks(
|
||||
};
|
||||
let verts: Vec<_> =
|
||||
if !split_geometry && (shared_vertex_buffer.is_none() || needs_wire_vertices) {
|
||||
(0..mesh.verts.len()).map(|index| vertex(index, None)).collect()
|
||||
(0..mesh.verts.len())
|
||||
.map(|index| vertex(index, None))
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
@@ -1744,10 +1782,7 @@ fn material_map_uv(
|
||||
position[0] * m[8] + position[1] * m[9] + position[2] * m[10] + m[11],
|
||||
];
|
||||
match map.projection {
|
||||
3 => [
|
||||
p[1].atan2(p[0]) / std::f32::consts::TAU + 0.5,
|
||||
p[2],
|
||||
],
|
||||
3 => [p[1].atan2(p[0]) / std::f32::consts::TAU + 0.5, p[2]],
|
||||
4 => {
|
||||
let radius = (p[0] * p[0] + p[1] * p[1] + p[2] * p[2]).sqrt();
|
||||
if radius <= f32::EPSILON {
|
||||
@@ -1892,8 +1927,7 @@ pub fn build_mesh_batch_filtered(
|
||||
let max_verts = super::gpu_budget::max_elements::<MeshVertex>(device).max(3);
|
||||
// A triangle contributes three line segments (six standalone edge
|
||||
// vertices). Bound chunks by the largest buffer produced for wire meshes.
|
||||
let max_tris =
|
||||
(budget / (6 * std::mem::size_of::<MeshEdgeVertex>())).max(1);
|
||||
let max_tris = (budget / (6 * std::mem::size_of::<MeshEdgeVertex>())).max(1);
|
||||
let stubs = MeshBatchStubs::new(device, queue);
|
||||
|
||||
let mut chunks = Vec::new();
|
||||
@@ -1903,14 +1937,11 @@ pub fn build_mesh_batch_filtered(
|
||||
let mut wire_indices: Vec<u32> = Vec::new();
|
||||
let mut edge_verts: Vec<MeshEdgeVertex> = Vec::new();
|
||||
let mut highlight_ranges: Vec<MeshBatchRange> = Vec::new();
|
||||
let mut chunk_handles: rustc_hash::FxHashSet<codec::Handle> =
|
||||
rustc_hash::FxHashSet::default();
|
||||
let mut chunk_handles: rustc_hash::FxHashSet<codec::Handle> = rustc_hash::FxHashSet::default();
|
||||
let mut total_tris = 0u64;
|
||||
let mut ordered: Vec<MeshBatchPart<'_>> = Vec::new();
|
||||
let mut source_indices: rustc_hash::FxHashMap<
|
||||
(bool, u64),
|
||||
(std::sync::Arc<[u32]>, u64),
|
||||
> = rustc_hash::FxHashMap::default();
|
||||
let mut source_indices: rustc_hash::FxHashMap<(bool, u64), (std::sync::Arc<[u32]>, u64)> =
|
||||
rustc_hash::FxHashMap::default();
|
||||
let mut face_partitions: rustc_hash::FxHashMap<FacePartitionKey, Vec<CachedFacePart<'_>>> =
|
||||
rustc_hash::FxHashMap::default();
|
||||
for set in sets {
|
||||
@@ -1922,9 +1953,9 @@ pub fn build_mesh_batch_filtered(
|
||||
continue;
|
||||
};
|
||||
let display_handle = set.entity_handle();
|
||||
if handles.is_some_and(|wanted| {
|
||||
display_handle.is_none_or(|handle| !wanted.contains(&handle))
|
||||
}) {
|
||||
if handles
|
||||
.is_some_and(|wanted| display_handle.is_none_or(|handle| !wanted.contains(&handle)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let display_color = set.display_color().unwrap_or(mesh.color);
|
||||
@@ -1933,8 +1964,7 @@ pub fn build_mesh_batch_filtered(
|
||||
|source| (true, source.handle.value()),
|
||||
);
|
||||
let triangle_count = mesh.indices.len() / 3;
|
||||
let has_face_materials =
|
||||
mesh.triangle_material_handles.len() == triangle_count
|
||||
let has_face_materials = mesh.triangle_material_handles.len() == triangle_count
|
||||
&& !set.face_materials.is_empty();
|
||||
let has_face_colors = mesh.triangle_colors.len() == triangle_count
|
||||
&& mesh.triangle_colors.iter().any(Option::is_some);
|
||||
@@ -1947,8 +1977,10 @@ pub fn build_mesh_batch_filtered(
|
||||
.as_ref()
|
||||
.map_or(true, |style| style.edges_visible());
|
||||
if !has_face_materials && !has_face_colors {
|
||||
let base_color =
|
||||
set.material.as_ref().map_or(display_color, |material| material.diffuse);
|
||||
let base_color = set
|
||||
.material
|
||||
.as_ref()
|
||||
.map_or(display_color, |material| material.diffuse);
|
||||
let color = set
|
||||
.visual_style
|
||||
.as_ref()
|
||||
@@ -2017,8 +2049,7 @@ pub fn build_mesh_batch_filtered(
|
||||
} else {
|
||||
set.material.as_ref()
|
||||
};
|
||||
let base_color =
|
||||
material.map_or(display_color, |material| material.diffuse);
|
||||
let base_color = material.map_or(display_color, |material| material.diffuse);
|
||||
let base_color = if has_face_colors {
|
||||
mesh.triangle_colors[triangle].unwrap_or(base_color)
|
||||
} else {
|
||||
@@ -2093,10 +2124,8 @@ pub fn build_mesh_batch_filtered(
|
||||
mesh_spatial_key(part.set, spatial_bounds),
|
||||
)
|
||||
});
|
||||
let mut instance_groups: std::collections::BTreeMap<
|
||||
InstanceGroupKey,
|
||||
Vec<MeshBatchPart<'_>>,
|
||||
> = std::collections::BTreeMap::new();
|
||||
let mut instance_groups: std::collections::BTreeMap<InstanceGroupKey, Vec<MeshBatchPart<'_>>> =
|
||||
std::collections::BTreeMap::new();
|
||||
let mut direct_parts = Vec::with_capacity(ordered.len());
|
||||
for part in ordered {
|
||||
let eligible = part.set.instance_transform.is_some()
|
||||
@@ -2189,7 +2218,9 @@ pub fn build_mesh_batch_filtered(
|
||||
let edge_color = set
|
||||
.visual_style
|
||||
.as_ref()
|
||||
.map_or(part.display_color, |style| style.edge_color(part.display_color));
|
||||
.map_or(part.display_color, |style| {
|
||||
style.edge_color(part.display_color)
|
||||
});
|
||||
let vtx = |vi: usize,
|
||||
local_mapping_normal: Option<[f32; 3]>,
|
||||
model_mapping_normal: Option<[f32; 3]>| {
|
||||
@@ -2198,21 +2229,13 @@ pub fn build_mesh_batch_filtered(
|
||||
} else {
|
||||
[0.0, 1.0, 0.0]
|
||||
};
|
||||
let local_position = uv_mesh
|
||||
.verts
|
||||
.get(vi)
|
||||
.copied()
|
||||
.unwrap_or(mesh.verts[vi]);
|
||||
let local_position = uv_mesh.verts.get(vi).copied().unwrap_or(mesh.verts[vi]);
|
||||
let local_position_low = uv_mesh
|
||||
.verts_low
|
||||
.get(vi)
|
||||
.copied()
|
||||
.unwrap_or_else(|| mesh.verts_low.get(vi).copied().unwrap_or([0.0; 3]));
|
||||
let local_normal = uv_mesh
|
||||
.normals
|
||||
.get(vi)
|
||||
.copied()
|
||||
.unwrap_or(normal);
|
||||
let local_normal = uv_mesh.normals.get(vi).copied().unwrap_or(normal);
|
||||
let uv = material_uvs(
|
||||
material,
|
||||
local_position,
|
||||
@@ -2260,9 +2283,7 @@ pub fn build_mesh_batch_filtered(
|
||||
let available = max_verts.saturating_sub(edge_verts.len());
|
||||
// LineList consumes pairs. Never split a segment between
|
||||
// chunks even when the vertex budget is odd.
|
||||
let take = available
|
||||
.min(edge_end - edge_start)
|
||||
& !1usize;
|
||||
let take = available.min(edge_end - edge_start) & !1usize;
|
||||
if take == 0 {
|
||||
chunks.push(make_chunk(
|
||||
device,
|
||||
@@ -2422,8 +2443,7 @@ pub fn build_mesh_batch_filtered(
|
||||
Vec::new()
|
||||
};
|
||||
if is_transp {
|
||||
let sub_handles: rustc_hash::FxHashSet<_> =
|
||||
entity_handle.into_iter().collect();
|
||||
let sub_handles: rustc_hash::FxHashSet<_> = entity_handle.into_iter().collect();
|
||||
chunks.push(make_chunk(
|
||||
device,
|
||||
queue,
|
||||
@@ -2443,8 +2463,7 @@ pub fn build_mesh_batch_filtered(
|
||||
None,
|
||||
));
|
||||
} else {
|
||||
let sub_handles: rustc_hash::FxHashSet<_> =
|
||||
entity_handle.into_iter().collect();
|
||||
let sub_handles: rustc_hash::FxHashSet<_> = entity_handle.into_iter().collect();
|
||||
chunks.push(make_chunk(
|
||||
device,
|
||||
queue,
|
||||
@@ -2510,7 +2529,11 @@ pub fn build_mesh_batch_filtered(
|
||||
verts.push(vtx(i, None, None));
|
||||
}
|
||||
if part.include_faces {
|
||||
let fill = if is_transp { &mut transp_indices } else { &mut indices };
|
||||
let fill = if is_transp {
|
||||
&mut transp_indices
|
||||
} else {
|
||||
&mut indices
|
||||
};
|
||||
let index_start = fill.len() as u32;
|
||||
for &idx in part.indices.iter() {
|
||||
fill.push(base + idx);
|
||||
@@ -2677,19 +2700,31 @@ mod texture_limit_tests {
|
||||
// both dimensions must come out <= limit and aspect ratio preserved.
|
||||
let (w, h) = (500u32, 10u32);
|
||||
let rgba = vec![0u8; (w * h * 4) as usize];
|
||||
let (new_w, new_h, buf) = downscale_rgba_to_limit(w, h, &rgba, 200).expect("well-formed input must resize");
|
||||
assert!(new_w <= 200 && new_h <= 200, "both dimensions must respect the limit, got {new_w}x{new_h}");
|
||||
assert_eq!(buf.len(), (new_w * new_h * 4) as usize, "the returned buffer must match its reported dimensions");
|
||||
let (new_w, new_h, buf) =
|
||||
downscale_rgba_to_limit(w, h, &rgba, 200).expect("well-formed input must resize");
|
||||
assert!(
|
||||
new_w <= 200 && new_h <= 200,
|
||||
"both dimensions must respect the limit, got {new_w}x{new_h}"
|
||||
);
|
||||
assert_eq!(
|
||||
buf.len(),
|
||||
(new_w * new_h * 4) as usize,
|
||||
"the returned buffer must match its reported dimensions"
|
||||
);
|
||||
// Aspect ratio 50:1 should be preserved within rounding.
|
||||
let ratio = new_w as f64 / new_h as f64;
|
||||
assert!((ratio - 50.0).abs() < 1.0, "expected aspect ratio near 50:1, got {ratio}");
|
||||
assert!(
|
||||
(ratio - 50.0).abs() < 1.0,
|
||||
"expected aspect ratio near 50:1, got {ratio}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_handles_a_square_image_over_the_limit() {
|
||||
let (w, h) = (300u32, 300u32);
|
||||
let rgba = vec![255u8; (w * h * 4) as usize];
|
||||
let (new_w, new_h, buf) = downscale_rgba_to_limit(w, h, &rgba, 128).expect("well-formed input must resize");
|
||||
let (new_w, new_h, buf) =
|
||||
downscale_rgba_to_limit(w, h, &rgba, 128).expect("well-formed input must resize");
|
||||
assert_eq!(new_w, 128);
|
||||
assert_eq!(new_h, 128);
|
||||
assert_eq!(buf.len(), (128 * 128 * 4) as usize);
|
||||
@@ -2704,11 +2739,15 @@ mod texture_limit_tests {
|
||||
}
|
||||
#[test]
|
||||
fn malformed_material_images_are_rejected_even_below_the_texture_limit() {
|
||||
for (width, height, pixels) in [(0, 1, vec![]), (1, 0, vec![]), (1, 1, vec![0; 3]), (1, 1, vec![0; 5])] {
|
||||
for (width, height, pixels) in [
|
||||
(0, 1, vec![]),
|
||||
(1, 0, vec![]),
|
||||
(1, 1, vec![0; 3]),
|
||||
(1, 1, vec![0; 5]),
|
||||
] {
|
||||
assert!(!super::valid_rgba_size(width, height, &pixels));
|
||||
assert!(downscale_rgba_to_limit(width, height, &pixels, 16).is_none());
|
||||
}
|
||||
assert!(downscale_rgba_to_limit(1, 1, &[0; 4], 0).is_none());
|
||||
}
|
||||
|
||||
}
|
||||
+1506
-1451
File diff suppressed because it is too large.
Load diff
@@ -84,7 +84,10 @@ impl PointCloudGpu {
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("point_cloud.bind_group1"),
|
||||
layout,
|
||||
entries: &[wgpu::BindGroupEntry { binding: 0, resource: params.as_entire_binding() }],
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: params.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
Self {
|
||||
clouds: Vec::new(),
|
||||
@@ -103,7 +106,11 @@ impl PointCloudGpu {
|
||||
/// Make `set` resident, keeping the buffers of clouds it already had.
|
||||
pub fn upload(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, set: &PointCloudSet) {
|
||||
let mut old = std::mem::take(&mut self.clouds);
|
||||
for cloud in set.clouds.iter().filter(|cloud| !cloud.instances.is_empty()) {
|
||||
for cloud in set
|
||||
.clouds
|
||||
.iter()
|
||||
.filter(|cloud| !cloud.instances.is_empty())
|
||||
{
|
||||
if let Some(at) = old.iter().position(|resident| resident.key == cloud.key) {
|
||||
self.clouds.push(old.swap_remove(at));
|
||||
continue;
|
||||
|
||||
@@ -66,9 +66,21 @@ pub struct BlockTextInstance {
|
||||
impl BlockTextInstance {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockTextInstance, translation) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockTextInstance, translation_low) as u64, shader_location: 6, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockTextInstance, draw_depth) as u64, shader_location: 7, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockTextInstance, translation) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockTextInstance, translation_low) as u64,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockTextInstance, draw_depth) as u64,
|
||||
shader_location: 7,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
@@ -537,8 +549,7 @@ pub fn upload_block_vertex_refs(
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let max_instances =
|
||||
super::gpu_budget::max_elements::<BlockTextInstance>(device);
|
||||
let max_instances = super::gpu_budget::max_elements::<BlockTextInstance>(device);
|
||||
for chunk in instances.chunks(max_instances) {
|
||||
let instance_buffer = super::gpu_upload::upload_buffer(
|
||||
device,
|
||||
@@ -606,7 +617,14 @@ mod tests {
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
// Large origin exercises the double-single precision path.
|
||||
push_glyph_vertices(&mut out, &[q], [1_000_000.0, 2_000_000.0, 0.0], 1.0, [1.0; 4], 0.0);
|
||||
push_glyph_vertices(
|
||||
&mut out,
|
||||
&[q],
|
||||
[1_000_000.0, 2_000_000.0, 0.0],
|
||||
1.0,
|
||||
[1.0; 4],
|
||||
0.0,
|
||||
);
|
||||
assert_eq!(out.len(), 6, "two triangles per glyph");
|
||||
|
||||
// Vertex 0 = BL corner (0,0) -> world origin, uv = (u_min, v_max).
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
// This eliminates gimbal lock at top/bottom views and keeps the cube in sync
|
||||
// with arcball orbit at all angles.
|
||||
|
||||
use crate::t;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use glam::camera::rh::proj::directx::orthographic;
|
||||
use glam::{Mat4, Vec3, Vec4};
|
||||
use iced::wgpu;
|
||||
use iced::{Rectangle, Size};
|
||||
use crate::t;
|
||||
|
||||
#[path = "viewcube_text_atlas.rs"]
|
||||
mod viewcube_text_atlas;
|
||||
@@ -1133,14 +1133,13 @@ impl ViewCubePipeline {
|
||||
&ring_idxs,
|
||||
wgpu::BufferUsages::INDEX,
|
||||
);
|
||||
let ring_line_vertex_buffer =
|
||||
super::gpu_upload::upload_buffer(
|
||||
device,
|
||||
queue,
|
||||
"vc.ring_line_vb",
|
||||
&ring_line_verts,
|
||||
wgpu::BufferUsages::VERTEX,
|
||||
);
|
||||
let ring_line_vertex_buffer = super::gpu_upload::upload_buffer(
|
||||
device,
|
||||
queue,
|
||||
"vc.ring_line_vb",
|
||||
&ring_line_verts,
|
||||
wgpu::BufferUsages::VERTEX,
|
||||
);
|
||||
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("vc.ub"),
|
||||
size: std::mem::size_of::<CubeUniforms>() as u64,
|
||||
@@ -1316,14 +1315,13 @@ impl ViewCubePipeline {
|
||||
},
|
||||
],
|
||||
});
|
||||
let composite_uniform_buffer =
|
||||
super::gpu_upload::upload_buffer(
|
||||
device,
|
||||
queue,
|
||||
"vc.composite_uniform",
|
||||
&[1.0f32, 1.0, 0.0, 0.0],
|
||||
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
);
|
||||
let composite_uniform_buffer = super::gpu_upload::upload_buffer(
|
||||
device,
|
||||
queue,
|
||||
"vc.composite_uniform",
|
||||
&[1.0f32, 1.0, 0.0, 0.0],
|
||||
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
);
|
||||
let composite_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("vc.composite_sampler"),
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
@@ -1460,16 +1458,15 @@ impl ViewCubePipeline {
|
||||
0.0,
|
||||
]),
|
||||
);
|
||||
self.text
|
||||
.update(
|
||||
queue,
|
||||
cam_rotation,
|
||||
compass_rotation,
|
||||
render_size,
|
||||
render_size,
|
||||
self.cube_px,
|
||||
text_color,
|
||||
);
|
||||
self.text.update(
|
||||
queue,
|
||||
cam_rotation,
|
||||
compass_rotation,
|
||||
render_size,
|
||||
render_size,
|
||||
self.cube_px,
|
||||
text_color,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn ensure_depth_texture(&mut self, device: &wgpu::Device, size: Size<u32>) {
|
||||
@@ -1522,7 +1519,11 @@ impl ViewCubePipeline {
|
||||
surface_clip: Rectangle<u32>,
|
||||
clip: &Rectangle<u32>,
|
||||
) -> bool {
|
||||
let min_size = self.depth_texture_size.width.min(self.depth_texture_size.height).max(1);
|
||||
let min_size = self
|
||||
.depth_texture_size
|
||||
.width
|
||||
.min(self.depth_texture_size.height)
|
||||
.max(1);
|
||||
viewcube_should_render(surface_dest, surface_clip, clip, min_size)
|
||||
}
|
||||
}
|
||||
@@ -1542,7 +1543,6 @@ pub fn viewcube_should_render(
|
||||
}
|
||||
|
||||
impl ViewCubePipeline {
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
@@ -1574,7 +1574,14 @@ impl ViewCubePipeline {
|
||||
occlusion_query_set: None,
|
||||
multiview_mask: None,
|
||||
});
|
||||
pass.set_viewport(0.0, 0.0, render_width as f32, render_height as f32, 0.0, 1.0);
|
||||
pass.set_viewport(
|
||||
0.0,
|
||||
0.0,
|
||||
render_width as f32,
|
||||
render_height as f32,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
@@ -1582,10 +1589,7 @@ impl ViewCubePipeline {
|
||||
pass.draw_indexed(0..self.index_count, 0, 0..1);
|
||||
pass.set_bind_group(0, &self.ring_uniform_bind_group, &[]);
|
||||
pass.set_vertex_buffer(0, self.ring_vertex_buffer.slice(..));
|
||||
pass.set_index_buffer(
|
||||
self.ring_index_buffer.slice(..),
|
||||
wgpu::IndexFormat::Uint32,
|
||||
);
|
||||
pass.set_index_buffer(self.ring_index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
||||
pass.draw_indexed(0..self.ring_index_count, 0, 0..1);
|
||||
pass.set_pipeline(&self.line_pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, SwashCache, Weight, Wrap};
|
||||
use cosmic_text::{
|
||||
Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, SwashCache, Weight, Wrap,
|
||||
};
|
||||
|
||||
pub(super) const ATLAS_WIDTH: u32 = 512;
|
||||
pub(super) const ATLAS_HEIGHT: u32 = 256;
|
||||
@@ -37,24 +39,14 @@ pub(super) fn empty_label_atlas() -> LabelAtlas {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn build_label_atlas(
|
||||
labels: &[String; FACE_TILE_COUNT],
|
||||
) -> Option<LabelAtlas> {
|
||||
pub(super) fn build_label_atlas(labels: &[String; FACE_TILE_COUNT]) -> Option<LabelAtlas> {
|
||||
let (mut font_system, family, weight) = font_system(labels)?;
|
||||
let mut swash_cache = SwashCache::new();
|
||||
let mut texts: Vec<&str> = labels.iter().map(String::as_str).collect();
|
||||
texts.extend(["N", "E", "S", "W"]);
|
||||
let bitmaps: Vec<LabelBitmap> = texts
|
||||
.into_iter()
|
||||
.map(|text| {
|
||||
rasterize_label(
|
||||
&mut font_system,
|
||||
&mut swash_cache,
|
||||
text,
|
||||
family,
|
||||
weight,
|
||||
)
|
||||
})
|
||||
.map(|text| rasterize_label(&mut font_system, &mut swash_cache, text, family, weight))
|
||||
.collect();
|
||||
|
||||
Some(pack_bitmaps(&bitmaps))
|
||||
|
||||
@@ -61,9 +61,21 @@ impl WipeoutPlacement {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute { offset: 0, shader_location: 1, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: 12, shader_location: 2, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: 24, shader_location: 3, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 12,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: 24,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -80,16 +92,16 @@ impl WipeoutPlacement {
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct HatchUniformData {
|
||||
pub color: [f32; 4], // 0: primary RGBA / gradient start
|
||||
pub color2: [f32; 4], // 16: gradient end color
|
||||
pub mode: u32, // 32: 0=pattern, 1=solid, 2=gradient
|
||||
pub vertex_count: u32, // 36: boundary vertex count
|
||||
pub angle_offset: f32, // 40: pattern rotation (radians)
|
||||
pub scale: f32, // 44: pattern scale multiplier
|
||||
pub grad_cos: f32, // 48: gradient direction cos
|
||||
pub grad_sin: f32, // 52: gradient direction sin
|
||||
pub grad_min: f32, // 56: gradient proj_min
|
||||
pub grad_range: f32, // 60: gradient proj_range
|
||||
pub color: [f32; 4], // 0: primary RGBA / gradient start
|
||||
pub color2: [f32; 4], // 16: gradient end color
|
||||
pub mode: u32, // 32: 0=pattern, 1=solid, 2=gradient
|
||||
pub vertex_count: u32, // 36: boundary vertex count
|
||||
pub angle_offset: f32, // 40: pattern rotation (radians)
|
||||
pub scale: f32, // 44: pattern scale multiplier
|
||||
pub grad_cos: f32, // 48: gradient direction cos
|
||||
pub grad_sin: f32, // 52: gradient direction sin
|
||||
pub grad_min: f32, // 56: gradient proj_min
|
||||
pub grad_range: f32, // 60: gradient proj_range
|
||||
pub origin: [f32; 2], // 64: reserved pattern origin
|
||||
pub origin_low: [f32; 2], // 72: reserved low residual
|
||||
pub draw_depth: f32, // 80: signed (-1,1) draw-order bias
|
||||
@@ -191,7 +203,9 @@ impl WipeoutGpu {
|
||||
let (mode, color2, grad_cos, grad_sin) = match &model.pattern {
|
||||
HatchPattern::Solid => (1u32, [0.0f32; 4], 0.0f32, 0.0f32),
|
||||
HatchPattern::Pattern(_) => (0u32, [0.0f32; 4], 0.0f32, 0.0f32),
|
||||
HatchPattern::Gradient { angle_deg, color2, .. } => {
|
||||
HatchPattern::Gradient {
|
||||
angle_deg, color2, ..
|
||||
} => {
|
||||
let r = angle_deg.to_radians();
|
||||
(2u32, *color2, r.cos(), r.sin())
|
||||
}
|
||||
@@ -236,12 +250,7 @@ impl WipeoutGpu {
|
||||
} else {
|
||||
(diag * 0.8 + max_spacing * 2.0 * model.scale).max(1.0)
|
||||
};
|
||||
let (x0, x1, y0, y1) = (
|
||||
min_x - pad,
|
||||
max_x + pad,
|
||||
min_y - pad,
|
||||
max_y + pad,
|
||||
);
|
||||
let (x0, x1, y0, y1) = (min_x - pad, max_x + pad, min_y - pad, max_y + pad);
|
||||
|
||||
let quad = [
|
||||
HatchVertex {
|
||||
@@ -279,7 +288,8 @@ impl WipeoutGpu {
|
||||
|
||||
// ── Gradient: projection range (in snapped-local space) ──────────
|
||||
let (grad_min, grad_range) = if mode == 2 {
|
||||
let projs: Vec<f32> = gpu_boundary.iter()
|
||||
let projs: Vec<f32> = gpu_boundary
|
||||
.iter()
|
||||
.filter(|v| v[0].is_finite() && v[1].is_finite())
|
||||
.map(|&[x, y]| x * grad_cos + y * grad_sin)
|
||||
.collect();
|
||||
@@ -345,7 +355,8 @@ impl WipeoutGpu {
|
||||
let mut boundary_data = BoundaryData {
|
||||
verts: [[0.0; 4]; MAX_HATCH_BOUNDARY_VERTS],
|
||||
};
|
||||
for (i, &[x, y]) in gpu_boundary.iter()
|
||||
for (i, &[x, y]) in gpu_boundary
|
||||
.iter()
|
||||
.take(MAX_HATCH_BOUNDARY_VERTS)
|
||||
.enumerate()
|
||||
{
|
||||
@@ -461,7 +472,12 @@ impl WipeoutGpu {
|
||||
} else if world_bounds[0].is_finite() && world_bounds[1].is_finite() {
|
||||
let ox = model.world_origin[0] as f32;
|
||||
let oy = model.world_origin[1] as f32;
|
||||
let mut aabb = [f32::INFINITY, f32::INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY];
|
||||
let mut aabb = [
|
||||
f32::INFINITY,
|
||||
f32::INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
f32::NEG_INFINITY,
|
||||
];
|
||||
for placement in &placements {
|
||||
aabb[0] = aabb[0].min(world_bounds[0] + ox + placement.translation[0]);
|
||||
aabb[1] = aabb[1].min(world_bounds[1] + oy + placement.translation[1]);
|
||||
|
||||
@@ -227,13 +227,18 @@ pub fn partition_wires<'a>(
|
||||
&& wire.text_verts.is_empty()
|
||||
{
|
||||
let depth = super::wire_gpu::wire_draw_depth(wire, depth_map);
|
||||
if super::circle_gpu::extract_circle_instances_into(wire, depth, &mut circle_instances) {
|
||||
if super::circle_gpu::extract_circle_instances_into(wire, depth, &mut circle_instances)
|
||||
{
|
||||
if let Some(handle) = handle_of(wire) {
|
||||
contributors.insert(handle);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if super::ellipse_gpu::extract_ellipse_instances_into(wire, depth, &mut ellipse_instances) {
|
||||
if super::ellipse_gpu::extract_ellipse_instances_into(
|
||||
wire,
|
||||
depth,
|
||||
&mut ellipse_instances,
|
||||
) {
|
||||
if let Some(handle) = handle_of(wire) {
|
||||
contributors.insert(handle);
|
||||
}
|
||||
@@ -284,8 +289,7 @@ pub fn split_wires(wires: &[WireModel]) -> (Vec<&WireModel>, Vec<&WireModel>) {
|
||||
/// resolve by submission order, which a tail relocation would flip.
|
||||
fn order_sensitive(wires: &[&WireModel], depth_map: &FxHashMap<u64, [f32; 2]>) -> bool {
|
||||
wires.iter().any(|w| {
|
||||
w.color[3] < 0.999
|
||||
|| handle_of(w).map_or(true, |h| !depth_map.contains_key(&h.value()))
|
||||
w.color[3] < 0.999 || handle_of(w).map_or(true, |h| !depth_map.contains_key(&h.value()))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -494,8 +498,7 @@ impl WireArena {
|
||||
const_bgl: &wgpu::BindGroupLayout,
|
||||
mesh_edge: bool,
|
||||
) -> Option<Self> {
|
||||
let instance_limit =
|
||||
super::gpu_budget::max_arena_elements::<WireInstance>(device) as u64;
|
||||
let instance_limit = super::gpu_budget::max_arena_elements::<WireInstance>(device) as u64;
|
||||
let constant_limit =
|
||||
super::gpu_budget::max_arena_storage_elements::<WireConst>(device) as u64;
|
||||
let ranges = handle_ranges(wires)?;
|
||||
@@ -557,10 +560,7 @@ impl WireArena {
|
||||
.par_iter()
|
||||
.map(|plan| {
|
||||
let run = &wires[plan.start..plan.end];
|
||||
let capacity: usize = run
|
||||
.iter()
|
||||
.map(|w| w.points.len().saturating_sub(1))
|
||||
.sum();
|
||||
let capacity: usize = run.iter().map(|w| w.points.len().saturating_sub(1)).sum();
|
||||
let mut instances: Vec<WireInstance> = Vec::with_capacity(capacity);
|
||||
let mut consts: Vec<WireConst> = Vec::with_capacity(run.len());
|
||||
for (local, &w) in run.iter().enumerate() {
|
||||
@@ -568,7 +568,11 @@ impl WireArena {
|
||||
// 3D mesh outline edges are occluded by true depth and must NOT
|
||||
// take the draw-order z-bias (or hidden back edges peek through
|
||||
// the shaded fill) — matching WireGpu::from_run.
|
||||
let dd = if mesh_edge { 0.0 } else { wire_draw_depth(w, depth_map) };
|
||||
let dd = if mesh_edge {
|
||||
0.0
|
||||
} else {
|
||||
wire_draw_depth(w, depth_map)
|
||||
};
|
||||
let (mut emitted, cst) = emit_wire_native(w, wire_id, w.color, dd);
|
||||
instances.append(&mut emitted);
|
||||
consts.push(cst);
|
||||
@@ -1040,10 +1044,7 @@ impl WireArena {
|
||||
}
|
||||
|
||||
if perf {
|
||||
let submitted: u64 = ranges
|
||||
.iter()
|
||||
.map(|(start, end)| (end - start) as u64)
|
||||
.sum();
|
||||
let submitted: u64 = ranges.iter().map(|(start, end)| (end - start) as u64).sum();
|
||||
let elapsed_ms = perf_started
|
||||
.map(|started| started.elapsed().as_secs_f64() * 1000.0)
|
||||
.unwrap_or_default();
|
||||
@@ -1139,8 +1140,7 @@ fn visible_ranges(
|
||||
let mut ranges: Vec<(u32, u32)> = slabs
|
||||
.values()
|
||||
.filter(|slab| {
|
||||
slab.inst_len > 0
|
||||
&& !super::aabb_offscreen(slab.aabb, view_rot, eye, clip_w, clip_h)
|
||||
slab.inst_len > 0 && !super::aabb_offscreen(slab.aabb, view_rot, eye, clip_w, clip_h)
|
||||
})
|
||||
.map(|slab| (slab.inst_off, slab.inst_off + slab.inst_len))
|
||||
.collect();
|
||||
@@ -1232,8 +1232,7 @@ impl PackedWireArena {
|
||||
return None;
|
||||
}
|
||||
let mut instances = Vec::with_capacity(inst_count);
|
||||
let mut slabs =
|
||||
FxHashMap::with_capacity_and_hasher(packed.len(), Default::default());
|
||||
let mut slabs = FxHashMap::with_capacity_and_hasher(packed.len(), Default::default());
|
||||
for mut packed_slab in packed {
|
||||
let inst_off = instances.len() as u32;
|
||||
let inst_len = packed_slab.instances.len() as u32;
|
||||
@@ -1290,8 +1289,7 @@ impl PackedWireArena {
|
||||
new_handles_are_suffix: bool,
|
||||
depth_map: &FxHashMap<u64, [f32; 2]>,
|
||||
) -> bool {
|
||||
let mut prepared: FxHashMap<Handle, PreparedPackedPatchRun> =
|
||||
FxHashMap::default();
|
||||
let mut prepared: FxHashMap<Handle, PreparedPackedPatchRun> = FxHashMap::default();
|
||||
for &(handle, kind) in changes {
|
||||
let run = runs.get(&handle).map(Vec::as_slice).unwrap_or(&[]);
|
||||
if matches!(kind, ChangeKind::Removed) || run.is_empty() {
|
||||
@@ -1308,12 +1306,8 @@ impl PackedWireArena {
|
||||
}
|
||||
let inst_len = insts.len() as u32;
|
||||
if matches!(kind, ChangeKind::Modified) {
|
||||
let known = self
|
||||
.slabs
|
||||
.get(&handle)
|
||||
.or_else(|| self.vacant.get(&handle));
|
||||
let shape_changed =
|
||||
known.is_some_and(|slab| slab.inst_len != inst_len);
|
||||
let known = self.slabs.get(&handle).or_else(|| self.vacant.get(&handle));
|
||||
let shape_changed = known.is_some_and(|slab| slab.inst_len != inst_len);
|
||||
let can_resize_tail = self.slabs.get(&handle).is_some_and(|slab| {
|
||||
can_resize_packed_terminal_slab(
|
||||
slab,
|
||||
@@ -1334,9 +1328,7 @@ impl PackedWireArena {
|
||||
base_depth: if self.mesh_edge {
|
||||
0.0
|
||||
} else {
|
||||
depth_map
|
||||
.get(&handle.value())
|
||||
.map_or(0.0, |depth| depth[0])
|
||||
depth_map.get(&handle.value()).map_or(0.0, |depth| depth[0])
|
||||
},
|
||||
aabb: run_aabb(run),
|
||||
order_sensitive: order_sensitive(run, depth_map),
|
||||
@@ -1348,8 +1340,7 @@ impl PackedWireArena {
|
||||
let run = runs.get(&handle).map(Vec::as_slice).unwrap_or(&[]);
|
||||
if matches!(kind, ChangeKind::Removed) || run.is_empty() {
|
||||
if let Some(slab) = self.slabs.remove(&handle) {
|
||||
let blanks =
|
||||
vec![blank_packed_instance(); slab.inst_len as usize];
|
||||
let blanks = vec![blank_packed_instance(); slab.inst_len as usize];
|
||||
self.write_insts(queue, slab.inst_off, &blanks);
|
||||
self.tombstoned += slab.inst_len;
|
||||
if matches!(kind, ChangeKind::Modified) {
|
||||
@@ -1379,8 +1370,7 @@ impl PackedWireArena {
|
||||
.is_some_and(|slab| slab.inst_len == inst_len)
|
||||
{
|
||||
let slab = self.vacant.remove(&handle).unwrap();
|
||||
self.tombstoned =
|
||||
self.tombstoned.saturating_sub(slab.inst_len);
|
||||
self.tombstoned = self.tombstoned.saturating_sub(slab.inst_len);
|
||||
self.slabs.insert(handle, slab);
|
||||
}
|
||||
|
||||
@@ -1430,8 +1420,7 @@ impl PackedWireArena {
|
||||
}
|
||||
self.vacant.remove(&handle);
|
||||
if let Some(slab) = self.slabs.remove(&handle) {
|
||||
let blanks =
|
||||
vec![blank_packed_instance(); slab.inst_len as usize];
|
||||
let blanks = vec![blank_packed_instance(); slab.inst_len as usize];
|
||||
self.write_insts(queue, slab.inst_off, &blanks);
|
||||
self.tombstoned += slab.inst_len;
|
||||
}
|
||||
@@ -1545,20 +1534,12 @@ impl PersistentWireArena {
|
||||
depth_map: &FxHashMap<u64, [f32; 2]>,
|
||||
) -> bool {
|
||||
match &mut self.inner {
|
||||
PersistentWireArenaKind::Indexed(arena) => arena.patch(
|
||||
queue,
|
||||
changes,
|
||||
runs,
|
||||
new_handles_are_suffix,
|
||||
depth_map,
|
||||
),
|
||||
PersistentWireArenaKind::Packed(arena) => arena.patch(
|
||||
queue,
|
||||
changes,
|
||||
runs,
|
||||
new_handles_are_suffix,
|
||||
depth_map,
|
||||
),
|
||||
PersistentWireArenaKind::Indexed(arena) => {
|
||||
arena.patch(queue, changes, runs, new_handles_are_suffix, depth_map)
|
||||
}
|
||||
PersistentWireArenaKind::Packed(arena) => {
|
||||
arena.patch(queue, changes, runs, new_handles_are_suffix, depth_map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1694,34 +1675,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn packed_terminal_resize_obeys_order_and_capacity() {
|
||||
assert!(can_resize_packed_terminal_slab(
|
||||
&slab(),
|
||||
110,
|
||||
1000,
|
||||
18,
|
||||
1,
|
||||
));
|
||||
assert!(!can_resize_packed_terminal_slab(
|
||||
&slab(),
|
||||
111,
|
||||
1000,
|
||||
18,
|
||||
1,
|
||||
));
|
||||
assert!(!can_resize_packed_terminal_slab(
|
||||
&slab(),
|
||||
110,
|
||||
117,
|
||||
18,
|
||||
1,
|
||||
));
|
||||
assert!(!can_resize_packed_terminal_slab(
|
||||
&slab(),
|
||||
110,
|
||||
1000,
|
||||
18,
|
||||
2,
|
||||
));
|
||||
assert!(can_resize_packed_terminal_slab(&slab(), 110, 1000, 18, 1,));
|
||||
assert!(!can_resize_packed_terminal_slab(&slab(), 111, 1000, 18, 1,));
|
||||
assert!(!can_resize_packed_terminal_slab(&slab(), 110, 117, 18, 1,));
|
||||
assert!(!can_resize_packed_terminal_slab(&slab(), 110, 1000, 18, 2,));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1734,12 +1691,14 @@ mod tests {
|
||||
fn partition_wires_partitions_preview_analytical_curves() {
|
||||
use crate::scene::model::wire_model::TangentGeom;
|
||||
let mut circle_preview = WireModel::default();
|
||||
circle_preview.tangent_geoms.push(TangentGeom::PlanarCircle {
|
||||
center: [1.0, 2.0, 3.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
circle_preview
|
||||
.tangent_geoms
|
||||
.push(TangentGeom::PlanarCircle {
|
||||
center: [1.0, 2.0, 3.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
axis_y: [0.0, 1.0, 0.0],
|
||||
radius: 10.0,
|
||||
});
|
||||
|
||||
let mut arc_preview = WireModel::default();
|
||||
arc_preview.tangent_geoms.push(TangentGeom::Arc {
|
||||
@@ -1752,14 +1711,16 @@ mod tests {
|
||||
});
|
||||
|
||||
let mut ellipse_preview = WireModel::default();
|
||||
ellipse_preview.tangent_geoms.push(TangentGeom::PlanarEllipse {
|
||||
center: [7.0, 8.0, 9.0],
|
||||
major_axis: [5.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.6,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
});
|
||||
ellipse_preview
|
||||
.tangent_geoms
|
||||
.push(TangentGeom::PlanarEllipse {
|
||||
center: [7.0, 8.0, 9.0],
|
||||
major_axis: [5.0, 0.0, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
minor_axis_ratio: 0.6,
|
||||
start_param: 0.0,
|
||||
end_param: std::f64::consts::TAU,
|
||||
});
|
||||
|
||||
let mut regular_line = WireModel::default();
|
||||
regular_line.points.push([0.0, 0.0, 0.0]);
|
||||
@@ -1779,11 +1740,7 @@ mod tests {
|
||||
use crate::scene::model::wire_model::TangentGeom;
|
||||
|
||||
let mut circle = WireModel::default();
|
||||
circle.points = vec![
|
||||
[10.0, 0.0, 0.0],
|
||||
[0.0, 10.0, 0.0],
|
||||
[-10.0, 0.0, 0.0],
|
||||
];
|
||||
circle.points = vec![[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [-10.0, 0.0, 0.0]];
|
||||
circle.tangent_geoms.push(TangentGeom::PlanarCircle {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
@@ -1794,11 +1751,7 @@ mod tests {
|
||||
circle.pattern = [0.5, -0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
||||
|
||||
let mut arc = WireModel::default();
|
||||
arc.points = vec![
|
||||
[10.0, 0.0, 0.0],
|
||||
[7.0, 7.0, 0.0],
|
||||
[0.0, 10.0, 0.0],
|
||||
];
|
||||
arc.points = vec![[10.0, 0.0, 0.0], [7.0, 7.0, 0.0], [0.0, 10.0, 0.0]];
|
||||
arc.tangent_geoms.push(TangentGeom::Arc {
|
||||
center: [0.0, 0.0, 0.0],
|
||||
axis_x: [1.0, 0.0, 0.0],
|
||||
|
||||
+233
-66
@@ -80,14 +80,46 @@ impl WireInstance {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
// Must match `InstanceIn` in wire_indexed.wgsl.
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, pos_a) as u64, shader_location: 0, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, pos_b) as u64, shader_location: 1, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, pos_a_low) as u64, shader_location: 2, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, pos_b_low) as u64, shader_location: 3, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_a) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_b) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, wire_id) as u64, shader_location: 6, format: wgpu::VertexFormat::Uint32 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, taper_ratio) as u64, shader_location: 7, format: wgpu::VertexFormat::Snorm16x2 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, pos_a) as u64,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, pos_b) as u64,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, pos_a_low) as u64,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, pos_b_low) as u64,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, distance_a) as u64,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, distance_b) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, wire_id) as u64,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Uint32,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(WireInstance, taper_ratio) as u64,
|
||||
shader_location: 7,
|
||||
format: wgpu::VertexFormat::Snorm16x2,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<WireInstance>() as u64,
|
||||
@@ -197,22 +229,74 @@ impl PackedWireInstance {
|
||||
// the web viewport drew no lines at all (#414). The struct fields are
|
||||
// laid out so each packed group is contiguous.
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pos_a) as u64, shader_location: 0, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pos_b) as u64, shader_location: 1, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, color) as u64, shader_location: 2, format: wgpu::VertexFormat::Unorm8x4 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pos_a) as u64,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pos_b) as u64,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, color) as u64,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Unorm8x4,
|
||||
},
|
||||
// dists = (distance_a, distance_b, half_width, pattern_length)
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, distance_a) as u64, shader_location: 3, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pat0) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pat1) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, distance_a) as u64,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pat0) as u64,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pat1) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
// misc = (draw_depth, align_end, align_total, world_half_width)
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, draw_depth) as u64, shader_location: 6, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pos_a_low) as u64, shader_location: 7, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, pos_b_low) as u64, shader_location: 8, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, draw_depth) as u64,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pos_a_low) as u64,
|
||||
shader_location: 7,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, pos_b_low) as u64,
|
||||
shader_location: 8,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
// taper = (world_hw_a, world_hw_b)
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, world_hw_a) as u64, shader_location: 9, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, marker_origin_high) as u64, shader_location: 10, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, marker_origin_low) as u64, shader_location: 11, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(PackedWireInstance, marker_normal_scale) as u64, shader_location: 12, format: wgpu::VertexFormat::Float32x4 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, world_hw_a) as u64,
|
||||
shader_location: 9,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, marker_origin_high) as u64,
|
||||
shader_location: 10,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, marker_origin_low) as u64,
|
||||
shader_location: 11,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(PackedWireInstance, marker_normal_scale) as u64,
|
||||
shader_location: 12,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<PackedWireInstance>() as u64,
|
||||
@@ -297,12 +381,36 @@ pub struct BlockWireVertex {
|
||||
impl BlockWireVertex {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, pos_a) as u64, shader_location: 0, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, pos_b) as u64, shader_location: 1, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, pos_a_low) as u64, shader_location: 2, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, pos_b_low) as u64, shader_location: 3, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, distances) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireVertex, taper_ratio) as u64, shader_location: 5, format: wgpu::VertexFormat::Snorm16x2 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, pos_a) as u64,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, pos_b) as u64,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, pos_a_low) as u64,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, pos_b_low) as u64,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, distances) as u64,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireVertex, taper_ratio) as u64,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Snorm16x2,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
@@ -323,9 +431,21 @@ pub struct BlockWireInstance {
|
||||
impl BlockWireInstance {
|
||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireInstance, translation) as u64, shader_location: 6, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireInstance, translation_low) as u64, shader_location: 7, format: wgpu::VertexFormat::Float32x3 },
|
||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(BlockWireInstance, depth) as u64, shader_location: 8, format: wgpu::VertexFormat::Float32x2 },
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireInstance, translation) as u64,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireInstance, translation_low) as u64,
|
||||
shader_location: 7,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(BlockWireInstance, depth) as u64,
|
||||
shader_location: 8,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
];
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as u64,
|
||||
@@ -541,15 +661,27 @@ fn marker_metadata(wire: &WireModel) -> ([f32; 4], [f32; 4], [f32; 4]) {
|
||||
for tg in &wire.tangent_geoms {
|
||||
match tg {
|
||||
crate::scene::model::wire_model::TangentGeom::Arc { axis_x, axis_y, .. }
|
||||
| crate::scene::model::wire_model::TangentGeom::PlanarCircle { axis_x, axis_y, .. } => {
|
||||
| crate::scene::model::wire_model::TangentGeom::PlanarCircle {
|
||||
axis_x,
|
||||
axis_y,
|
||||
..
|
||||
} => {
|
||||
let ax = glam::DVec3::from_array(*axis_x);
|
||||
let ay = glam::DVec3::from_array(*axis_y);
|
||||
let n = ax.cross(ay).normalize_or(glam::DVec3::Z);
|
||||
return ([0.0; 4], [0.0; 4], [n.x as f32, n.y as f32, n.z as f32, 0.0]);
|
||||
return (
|
||||
[0.0; 4],
|
||||
[0.0; 4],
|
||||
[n.x as f32, n.y as f32, n.z as f32, 0.0],
|
||||
);
|
||||
}
|
||||
crate::scene::model::wire_model::TangentGeom::PlanarEllipse { normal, .. } => {
|
||||
let n = glam::DVec3::from_array(*normal).normalize_or(glam::DVec3::Z);
|
||||
return ([0.0; 4], [0.0; 4], [n.x as f32, n.y as f32, n.z as f32, 0.0]);
|
||||
return (
|
||||
[0.0; 4],
|
||||
[0.0; 4],
|
||||
[n.x as f32, n.y as f32, n.z as f32, 0.0],
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -625,7 +757,11 @@ fn band_miters(wire: &WireModel, normal: [f32; 4]) -> Vec<[f32; 2]> {
|
||||
return None;
|
||||
}
|
||||
wrapped = true;
|
||||
i = if step > 0 { first?.0 as isize } else { last?.0 as isize };
|
||||
i = if step > 0 {
|
||||
first?.0 as isize
|
||||
} else {
|
||||
last?.0 as isize
|
||||
};
|
||||
continue;
|
||||
}
|
||||
let Some(q) = at(i as usize) else {
|
||||
@@ -644,7 +780,11 @@ fn band_miters(wire: &WireModel, normal: [f32; 4]) -> Vec<[f32; 2]> {
|
||||
}
|
||||
};
|
||||
let axis = glam::DVec3::new(normal[0] as f64, normal[1] as f64, normal[2] as f64);
|
||||
let axis = if axis.length() > 1e-4 { axis.normalize() } else { glam::DVec3::Z };
|
||||
let axis = if axis.length() > 1e-4 {
|
||||
axis.normalize()
|
||||
} else {
|
||||
glam::DVec3::Z
|
||||
};
|
||||
// tan of half the signed turn from `d1` to `d2` about the band normal.
|
||||
let half_turn = |d1: glam::DVec3, d2: glam::DVec3| -> f32 {
|
||||
let cos = d1.dot(d2);
|
||||
@@ -652,7 +792,11 @@ fn band_miters(wire: &WireModel, normal: [f32; 4]) -> Vec<[f32; 2]> {
|
||||
return 0.0;
|
||||
}
|
||||
let t = axis.dot(d1.cross(d2)) / (1.0 + cos);
|
||||
if t.abs() > MITER_LIMIT { 0.0 } else { (t / MITER_LIMIT) as f32 }
|
||||
if t.abs() > MITER_LIMIT {
|
||||
0.0
|
||||
} else {
|
||||
(t / MITER_LIMIT) as f32
|
||||
}
|
||||
};
|
||||
(0..n - 1)
|
||||
.map(|i| {
|
||||
@@ -677,8 +821,18 @@ pub(crate) fn emit_wire_packed(
|
||||
draw_depth: f32,
|
||||
) -> Vec<PackedWireInstance> {
|
||||
let color_u8 = pack_color(color);
|
||||
let pat0 = [wire.pattern[0], wire.pattern[1], wire.pattern[2], wire.pattern[3]];
|
||||
let pat1 = [wire.pattern[4], wire.pattern[5], wire.pattern[6], wire.pattern[7]];
|
||||
let pat0 = [
|
||||
wire.pattern[0],
|
||||
wire.pattern[1],
|
||||
wire.pattern[2],
|
||||
wire.pattern[3],
|
||||
];
|
||||
let pat1 = [
|
||||
wire.pattern[4],
|
||||
wire.pattern[5],
|
||||
wire.pattern[6],
|
||||
wire.pattern[7],
|
||||
];
|
||||
let half_width = wire.line_weight_px * 0.5;
|
||||
let n = wire.points.len();
|
||||
let seg_count = n.saturating_sub(1);
|
||||
@@ -769,8 +923,18 @@ pub(crate) fn emit_wire_native(
|
||||
let is_tapered = !wire.taper_widths.is_empty();
|
||||
let cst = WireConst {
|
||||
color,
|
||||
pat0: [wire.pattern[0], wire.pattern[1], wire.pattern[2], wire.pattern[3]],
|
||||
pat1: [wire.pattern[4], wire.pattern[5], wire.pattern[6], wire.pattern[7]],
|
||||
pat0: [
|
||||
wire.pattern[0],
|
||||
wire.pattern[1],
|
||||
wire.pattern[2],
|
||||
wire.pattern[3],
|
||||
],
|
||||
pat1: [
|
||||
wire.pattern[4],
|
||||
wire.pattern[5],
|
||||
wire.pattern[6],
|
||||
wire.pattern[7],
|
||||
],
|
||||
half_width: wire.line_weight_px * 0.5,
|
||||
pattern_length: wire.pattern_length,
|
||||
draw_depth,
|
||||
@@ -853,8 +1017,7 @@ pub(crate) fn wire_draw_depth(
|
||||
wire: &WireModel,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> f32 {
|
||||
let base = fast_parse_u64(&wire.name)
|
||||
.and_then(|h| depth_map.get(&h).copied());
|
||||
let base = fast_parse_u64(&wire.name).and_then(|h| depth_map.get(&h).copied());
|
||||
match (base, wire.depth_override) {
|
||||
(Some([d, half]), Some(local)) => d + local * half,
|
||||
(Some([d, _]), None) => d,
|
||||
@@ -865,10 +1028,8 @@ pub(crate) fn wire_draw_depth(
|
||||
/// Cached block geometry keyed by source, edge mode, colour and base translation.
|
||||
/// Vertices use the first instance's world coordinates, so changing that base
|
||||
/// must invalidate the geometry as well as rebuild the relative placements.
|
||||
pub type BlockGeometryCache = rustc_hash::FxHashMap<
|
||||
BlockGeometryKey,
|
||||
std::sync::Arc<Vec<BlockGeometryChunk>>,
|
||||
>;
|
||||
pub type BlockGeometryCache =
|
||||
rustc_hash::FxHashMap<BlockGeometryKey, std::sync::Arc<Vec<BlockGeometryChunk>>>;
|
||||
|
||||
/// How this device draws block wires. The layout and the mode are decided
|
||||
/// together when the pipeline is built and are never chosen independently, so
|
||||
@@ -936,8 +1097,7 @@ impl BlockWireGpu {
|
||||
mut cache: Option<&mut BlockGeometryCache>,
|
||||
) -> Vec<Self> {
|
||||
let BlockWireTarget { const_bgl, mode } = target;
|
||||
let mut slots: rustc_hash::FxHashMap<(u64, bool), usize> =
|
||||
rustc_hash::FxHashMap::default();
|
||||
let mut slots: rustc_hash::FxHashMap<(u64, bool), usize> = rustc_hash::FxHashMap::default();
|
||||
let mut groups: Vec<(bool, Vec<&WireModel>)> = Vec::new();
|
||||
for &wire in wires {
|
||||
if color_override.is_none() && !wire.display_visible {
|
||||
@@ -1073,16 +1233,15 @@ impl BlockWireGpu {
|
||||
}
|
||||
let max_verts =
|
||||
super::gpu_budget::max_elements_grouped::<BlockWireVertex>(device, 6);
|
||||
let shared = std::sync::Arc::new(device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
let shared =
|
||||
std::sync::Arc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("block_wire.const.bg"),
|
||||
layout: const_bgl,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: const_buffer.as_entire_binding(),
|
||||
}],
|
||||
},
|
||||
));
|
||||
}));
|
||||
vertices
|
||||
.chunks(max_verts)
|
||||
.map(|chunk| BlockGeometryChunk {
|
||||
@@ -1127,7 +1286,11 @@ impl BlockWireGpu {
|
||||
crate::perf_record!(
|
||||
"[perf] block-wire-geometry mode={} definitions={} segments={} bytes={} \
|
||||
bytes_if_packed={} poisoned={}",
|
||||
if mode.uses_storage() { "storage" } else { "packed" },
|
||||
if mode.uses_storage() {
|
||||
"storage"
|
||||
} else {
|
||||
"packed"
|
||||
},
|
||||
live_keys.len(),
|
||||
uploaded_segments,
|
||||
uploaded_bytes,
|
||||
@@ -1301,8 +1464,7 @@ impl WireGpu {
|
||||
emit_wire_native(wire, idx as u32, color, wire_draw_depth(wire, depth_map))
|
||||
})
|
||||
.collect();
|
||||
let mut instances =
|
||||
Vec::with_capacity(per.iter().map(|(items, _)| items.len()).sum());
|
||||
let mut instances = Vec::with_capacity(per.iter().map(|(items, _)| items.len()).sum());
|
||||
let mut consts = Vec::with_capacity(per.len());
|
||||
for (mut items, constant) in per {
|
||||
instances.append(&mut items);
|
||||
@@ -1327,9 +1489,7 @@ impl WireGpu {
|
||||
use crate::par::prelude::*;
|
||||
let per: Vec<Vec<PackedWireInstance>> = wires
|
||||
.par_iter()
|
||||
.map(|wire| {
|
||||
emit_wire_packed(wire, color, wire_draw_depth(wire, depth_map))
|
||||
})
|
||||
.map(|wire| emit_wire_packed(wire, color, wire_draw_depth(wire, depth_map)))
|
||||
.collect();
|
||||
let mut instances = Vec::with_capacity(per.iter().map(Vec::len).sum());
|
||||
for mut items in per {
|
||||
@@ -1375,8 +1535,7 @@ impl WireGpu {
|
||||
emit_wire_packed(wire, wire.color, depth)
|
||||
})
|
||||
.collect();
|
||||
let mut instances =
|
||||
Vec::with_capacity(per.iter().map(Vec::len).sum());
|
||||
let mut instances = Vec::with_capacity(per.iter().map(Vec::len).sum());
|
||||
for mut items in per {
|
||||
instances.append(&mut items);
|
||||
}
|
||||
@@ -1456,7 +1615,11 @@ impl WireGpu {
|
||||
// depth — they must NOT take the draw-order z-bias (which
|
||||
// pulls 2D wires toward the camera), or the hidden edges of a
|
||||
// small / distant mesh peek through its own shaded fill.
|
||||
let dd = if mesh_edge { 0.0 } else { wire_draw_depth(w, depth_map) };
|
||||
let dd = if mesh_edge {
|
||||
0.0
|
||||
} else {
|
||||
wire_draw_depth(w, depth_map)
|
||||
};
|
||||
emit_wire_native(w, idx as u32, w.color, dd)
|
||||
})
|
||||
.collect();
|
||||
@@ -1486,7 +1649,11 @@ impl WireGpu {
|
||||
let per: Vec<Vec<PackedWireInstance>> = wires
|
||||
.iter()
|
||||
.map(|w| {
|
||||
let dd = if mesh_edge { 0.0 } else { wire_draw_depth(w, depth_map) };
|
||||
let dd = if mesh_edge {
|
||||
0.0
|
||||
} else {
|
||||
wire_draw_depth(w, depth_map)
|
||||
};
|
||||
emit_wire_packed(w, w.color, dd)
|
||||
})
|
||||
.collect();
|
||||
@@ -1648,9 +1815,8 @@ mod block_wire_storage_tests {
|
||||
draw_order_shader!("block_wire_storage.wgsl"),
|
||||
] {
|
||||
assert!(source.contains("if world_hw < 0.0"));
|
||||
assert!(source.contains(
|
||||
"resolve_hw(0.0, wire_const.world_half_width, wire_const.half_width)"
|
||||
));
|
||||
assert!(source
|
||||
.contains("resolve_hw(0.0, wire_const.world_half_width, wire_const.half_width)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1670,7 +1836,8 @@ mod block_wire_storage_tests {
|
||||
#[test]
|
||||
fn wire_indexed_shader_validates_with_naga() {
|
||||
let source = draw_order_shader!("wire_indexed.wgsl");
|
||||
let module = naga::front::wgsl::parse_str(source).expect("wire_indexed.wgsl parses cleanly");
|
||||
let module =
|
||||
naga::front::wgsl::parse_str(source).expect("wire_indexed.wgsl parses cleanly");
|
||||
let mut validator = naga::valid::Validator::new(
|
||||
naga::valid::ValidationFlags::all(),
|
||||
naga::valid::Capabilities::all(),
|
||||
|
||||
Reference in new issue
Block a user