use super::*;
impl RenderCore {
fn face_base_color(solid: &SolidDisplay, settings: &RenderSettings) -> Rgba {
if let Some(rgb) = solid.color_override {
return [
srgb_to_linear(rgb[0] as f64) as f32,
srgb_to_linear(rgb[1] as f64) as f32,
srgb_to_linear(rgb[2] as f64) as f32,
1.0,
];
}
match settings.face_color_mode {
FaceColorMode::HashedBySolid => {
let srgb = solid_color_srgb(&solid.name);
[
srgb_to_linear(srgb[0]) as f32,
srgb_to_linear(srgb[1]) as f32,
srgb_to_linear(srgb[2]) as f32,
1.0,
]
}
FaceColorMode::Uniform => {
let c = settings.face_color;
[
srgb_to_linear(c[0] as f64) as f32,
srgb_to_linear(c[1] as f64) as f32,
srgb_to_linear(c[2] as f64) as f32,
c[3],
]
}
}
}
fn upload_solid(&self, solid: &SolidDisplay, settings: &RenderSettings) -> GpuSolid {
let vertices: Vec<MeshVertex> = solid
.mesh
.positions
.iter()
.zip(&solid.mesh.normals)
.map(|(&position, &normal)| MeshVertex { position, normal })
.collect();
let vertex_buf = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(solid.name.as_str()),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buf = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(solid.name.as_str()),
contents: bytemuck::cast_slice(&solid.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
let mut wire_indices: Vec<u32> = Vec::with_capacity(solid.mesh.indices.len() * 2);
for tri in solid.mesh.indices.chunks_exact(3) {
wire_indices.extend_from_slice(&[
tri[0], tri[1], tri[1], tri[2], tri[2], tri[0],
]);
}
let wire_index_count = wire_indices.len() as u32;
let wire_index_buf = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(solid.name.as_str()),
contents: bytemuck::cast_slice(&wire_indices),
usage: wgpu::BufferUsages::INDEX,
});
let faces = solid
.faces
.iter()
.map(|face| FaceRange {
first_index: face.tri_start * 3,
index_count: face.tri_count * 3,
})
.collect();
let mut segments: Vec<EdgeInstance> = Vec::new();
let mut edges = Vec::with_capacity(solid.edges.len());
for edge in &solid.edges {
let first_instance = segments.len() as u32;
for pair in edge.polyline.windows(2) {
segments.push(EdgeInstance {
p0: pair[0],
p1: pair[1],
});
}
edges.push(EdgeRange {
first_instance,
instance_count: segments.len() as u32 - first_instance,
});
}
let edge_buf = (!segments.is_empty()).then(|| {
self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(solid.name.as_str()),
contents: bytemuck::cast_slice(&segments),
usage: wgpu::BufferUsages::VERTEX,
})
});
let points: Vec<PointInstance> = solid
.vertices
.iter()
.map(|v| PointInstance {
center: [
v.position[0] as f32,
v.position[1] as f32,
v.position[2] as f32,
],
})
.collect();
let point_buf = (!points.is_empty()).then(|| {
self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(solid.name.as_str()),
contents: bytemuck::cast_slice(&points),
usage: wgpu::BufferUsages::VERTEX,
})
});
let base_style = StyleBuf::new(&self.device, &self.style_layout, solid.name.as_str());
base_style.write(
&self.queue,
Self::face_base_color(solid, settings),
[if settings.flat_shading { 1.0 } else { 0.0 }, 0.0, 0.0, 0.0],
);
GpuSolid {
revision: solid.revision,
vertex_buf,
index_buf,
wire_index_buf,
wire_index_count,
faces,
edge_buf,
edges,
edge_instances: segments.len() as u32,
point_buf,
point_count: points.len() as u32,
base_style,
boundary: None,
}
}
pub fn sync_scene(
&self,
gpu: &mut GpuScene,
scene: &RenderScene,
settings: &RenderSettings,
settings_generation: u64,
) {
gpu.order.clear();
let mut seen: Vec<&str> = Vec::with_capacity(scene.solids().len());
for solid in scene.solids() {
gpu.order.push(solid.name.clone());
seen.push(solid.name.as_str());
let refresh_style = gpu.settings_generation != settings_generation;
match gpu.solids.get_mut(&solid.name) {
Some(existing) if existing.revision == solid.revision => {
if refresh_style {
existing.base_style.write(
&self.queue,
Self::face_base_color(solid, settings),
[if settings.flat_shading { 1.0 } else { 0.0 }, 0.0, 0.0, 0.0],
);
}
}
_ => {
let uploaded = self.upload_solid(solid, settings);
gpu.solids.insert(solid.name.clone(), uploaded);
gpu.uploads += 1;
}
}
}
gpu.solids.retain(|name, _| seen.contains(&name.as_str()));
gpu.settings_generation = settings_generation;
}
pub fn upload_scene(&self, scene: &RenderScene) -> GpuScene {
self.upload_scene_with(scene, &RenderSettings::artifact())
}
pub fn upload_scene_with(&self, scene: &RenderScene, settings: &RenderSettings) -> GpuScene {
let mut gpu = GpuScene::default();
self.sync_scene(&mut gpu, scene, settings, 0);
gpu
}
pub(super) fn ensure_targets(&mut self, width: u32, height: u32) {
if let Some(targets) = &self.targets {
if targets.width == width && targets.height == height {
return;
}
}
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let msaa = self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("msaa color"),
size,
mip_level_count: 1,
sample_count: SAMPLES,
dimension: wgpu::TextureDimension::D2,
format: self.format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let depth = self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("depth"),
size,
mip_level_count: 1,
sample_count: SAMPLES,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
self.targets = Some(CachedTargets {
width,
height,
msaa_view: msaa.create_view(&Default::default()),
depth_view: depth.create_view(&Default::default()),
});
}
pub(super) fn write_global_styles(&self, settings: &RenderSettings) {
let flat = if settings.flat_shading { 1.0 } else { 0.0 };
let face = |c: Rgba| -> Rgba {
[
srgb_to_linear(c[0] as f64) as f32,
srgb_to_linear(c[1] as f64) as f32,
srgb_to_linear(c[2] as f64) as f32,
c[3],
]
};
let s = &self.styles;
let q = &self.queue;
s.face_selected
.write(q, face(settings.face_selected_color), [flat, 0.0, 0.0, 0.0]);
s.face_hovered
.write(q, face(settings.hover_color), [flat, 0.0, 0.0, 0.0]);
let edge_params = [settings.edge_width_px, EDGE_NUDGE, 0.0, 0.0];
s.edge_base.write(q, settings.edge_color, edge_params);
s.edge_selected.write(q, settings.edge_selected_color, edge_params);
s.edge_hovered.write(q, settings.hover_color, edge_params);
let mut hidden = settings.edge_color;
hidden[3] = settings.hidden_edge_alpha;
s.edge_hidden.write(q, hidden, edge_params);
s.boundary.write(
q,
settings.edge_selected_color,
[settings.edge_width_px + 1.0, EDGE_NUDGE * 1.5, 0.0, 0.0],
);
let point_params = [settings.vertex_size_px, 0.0, 0.0, 0.0];
s.point_base.write(q, settings.vertex_color, point_params);
s.point_selected
.write(q, settings.vertex_selected_color, [settings.vertex_size_px + 1.0, 0.0, 0.0, 0.0]);
s.point_hovered
.write(q, settings.hover_color, [settings.vertex_size_px + 1.0, 0.0, 0.0, 0.0]);
let axis_params = [2.0, EDGE_NUDGE, 0.0, 0.0];
s.axis_x.write(q, [0.91, 0.30, 0.32, 1.0], axis_params);
s.axis_y.write(q, [0.27, 0.80, 0.42, 1.0], axis_params);
s.axis_z.write(q, [0.23, 0.51, 0.96, 1.0], axis_params);
s.overlay_line.write(q, [1.0, 1.0, 1.0, 1.0], [2.5, 0.0, 0.0, 0.0]);
}
pub(super) fn sync_boundary(&self, gpu_solid: &mut GpuSolid, solid: &SolidDisplay, emphasis: &Emphasis) {
if let Some(boundary) = &gpu_solid.boundary {
if boundary.emphasis_generation == emphasis.generation
&& boundary.revision == solid.revision
{
return;
}
}
let mut segments: Vec<EdgeInstance> = Vec::new();
for face in &solid.faces {
if face.tri_count == 0 {
continue;
}
let state = emphasis.face_state(&solid.name, &face.name);
if state == EmphasisState::Base {
continue;
}
let mut edge_use: HashMap<(u32, u32), u32> = HashMap::new();
let start = face.tri_start as usize;
let end = (start + face.tri_count as usize).min(solid.mesh.indices.len() / 3);
for tri in start..end {
let idx = [
solid.mesh.indices[tri * 3],
solid.mesh.indices[tri * 3 + 1],
solid.mesh.indices[tri * 3 + 2],
];
for k in 0..3 {
let a = idx[k];
let b = idx[(k + 1) % 3];
let key = if a < b { (a, b) } else { (b, a) };
*edge_use.entry(key).or_insert(0) += 1;
}
}
let mut boundary: Vec<(u32, u32)> = edge_use
.into_iter()
.filter_map(|(key, count)| (count == 1).then_some(key))
.collect();
boundary.sort_unstable();
for (a, b) in boundary {
segments.push(EdgeInstance {
p0: solid.mesh.positions[a as usize],
p1: solid.mesh.positions[b as usize],
});
}
}
let buf = (!segments.is_empty()).then(|| {
self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("emphasis boundary"),
contents: bytemuck::cast_slice(&segments),
usage: wgpu::BufferUsages::VERTEX,
})
});
gpu_solid.boundary = Some(BoundaryBuf {
emphasis_generation: emphasis.generation,
revision: solid.revision,
buf,
count: segments.len() as u32,
});
}
}