1use super::*;
2
3const AUX_EDGE_WIDTH_PX: f32 = 1.0;
5
6impl RenderCore {
7 fn face_base_color(solid: &SolidDisplay, settings: &RenderSettings) -> Rgba {
10 if let Some(rgb) = solid.color_override {
11 return Self::opaque_linear(rgb);
12 }
13 match settings.face_color_mode {
14 FaceColorMode::HashedBySolid => {
15 let srgb = solid_color_srgb(&solid.name);
16 [
17 srgb_to_linear(srgb[0]) as f32,
18 srgb_to_linear(srgb[1]) as f32,
19 srgb_to_linear(srgb[2]) as f32,
20 1.0,
21 ]
22 }
23 FaceColorMode::Uniform => {
24 let c = settings.face_color;
25 [
26 srgb_to_linear(c[0] as f64) as f32,
27 srgb_to_linear(c[1] as f64) as f32,
28 srgb_to_linear(c[2] as f64) as f32,
29 c[3],
30 ]
31 }
32 }
33 }
34
35 fn opaque_linear(rgb: [f32; 3]) -> Rgba {
38 [
39 srgb_to_linear(rgb[0] as f64) as f32,
40 srgb_to_linear(rgb[1] as f64) as f32,
41 srgb_to_linear(rgb[2] as f64) as f32,
42 1.0,
43 ]
44 }
45
46 fn style_params(settings: &RenderSettings) -> [f32; 4] {
48 [if settings.flat_shading { 1.0 } else { 0.0 }, 0.0, 0.0, 0.0]
49 }
50
51 fn face_style_palette(
60 &self,
61 solid: &SolidDisplay,
62 settings: &RenderSettings,
63 ) -> (Vec<StyleBuf>, Vec<Option<u32>>) {
64 let mut styles: Vec<StyleBuf> = Vec::new();
65 let mut colors: Vec<[f32; 3]> = Vec::new();
66 let mut per_face: Vec<Option<u32>> = Vec::with_capacity(solid.faces.len());
67 let params = Self::style_params(settings);
68 for face in &solid.faces {
69 let Some(rgb) = face.color_override else {
70 per_face.push(None);
71 continue;
72 };
73 let slot = match colors.iter().position(|c| *c == rgb) {
74 Some(slot) => slot,
75 None => {
76 let style = StyleBuf::new(
77 &self.device,
78 &self.style_layout,
79 solid.name.as_str(),
80 );
81 style.write(&self.queue, Self::opaque_linear(rgb), params);
82 styles.push(style);
83 colors.push(rgb);
84 colors.len() - 1
85 }
86 };
87 per_face.push(Some(slot as u32));
88 }
89 (styles, per_face)
90 }
91
92 fn upload_solid(&self, solid: &SolidDisplay, settings: &RenderSettings) -> GpuSolid {
93 let vertices: Vec<MeshVertex> = solid
94 .mesh
95 .positions
96 .iter()
97 .zip(&solid.mesh.normals)
98 .map(|(&position, &normal)| MeshVertex { position, normal })
99 .collect();
100 let vertex_buf = self
101 .device
102 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
103 label: Some(solid.name.as_str()),
104 contents: bytemuck::cast_slice(&vertices),
105 usage: wgpu::BufferUsages::VERTEX,
106 });
107 let index_buf = self
108 .device
109 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
110 label: Some(solid.name.as_str()),
111 contents: bytemuck::cast_slice(&solid.mesh.indices),
112 usage: wgpu::BufferUsages::INDEX,
113 });
114 let tri_total = solid.mesh.indices.len() / 3;
132 let mut wire_indices: Vec<u32> = Vec::with_capacity(solid.mesh.indices.len());
133 let mut wire_ranges: Vec<(u32, u32)> = Vec::with_capacity(solid.faces.len());
134 let mut covered = vec![false; tri_total];
135 let mut seen: HashSet<(u32, u32), BuildEdgeHasher> = HashSet::default();
136 let mut emit_triangle = |wire: &mut Vec<u32>,
137 seen: &mut HashSet<(u32, u32), BuildEdgeHasher>,
138 tri: usize| {
139 let idx = [
140 solid.mesh.indices[tri * 3],
141 solid.mesh.indices[tri * 3 + 1],
142 solid.mesh.indices[tri * 3 + 2],
143 ];
144 for k in 0..3 {
145 let (a, b) = (idx[k], idx[(k + 1) % 3]);
146 let key = if a < b { (a, b) } else { (b, a) };
147 if seen.insert(key) {
148 wire.push(a);
149 wire.push(b);
150 }
151 }
152 };
153 for face in &solid.faces {
154 let first = wire_indices.len() as u32;
155 seen.clear();
156 let start = (face.tri_start as usize).min(tri_total);
157 let end = (start + face.tri_count as usize).min(tri_total);
158 for tri in start..end {
159 covered[tri] = true;
160 emit_triangle(&mut wire_indices, &mut seen, tri);
161 }
162 wire_ranges.push((first, wire_indices.len() as u32 - first));
163 }
164 seen.clear();
168 for tri in 0..tri_total {
169 if !covered[tri] {
170 emit_triangle(&mut wire_indices, &mut seen, tri);
171 }
172 }
173 let wire_index_count = wire_indices.len() as u32;
174 let wire_index_buf = self
175 .device
176 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
177 label: Some(solid.name.as_str()),
178 contents: bytemuck::cast_slice(&wire_indices),
179 usage: wgpu::BufferUsages::INDEX,
180 });
181 let (face_styles, face_style_index) = self.face_style_palette(solid, settings);
182 let faces = solid
183 .faces
184 .iter()
185 .zip(&face_style_index)
186 .zip(&wire_ranges)
187 .map(|((face, style), &(wire_first, wire_count))| FaceRange {
188 first_index: face.tri_start * 3,
189 index_count: face.tri_count * 3,
190 wire_first,
191 wire_count,
192 style: *style,
193 })
194 .collect();
195
196 let mut segments: Vec<EdgeInstance> = Vec::new();
197 let mut edges = Vec::with_capacity(solid.edges.len());
198 for edge in &solid.edges {
199 let first_instance = segments.len() as u32;
200 for pair in edge.polyline.windows(2) {
201 segments.push(EdgeInstance {
202 p0: pair[0],
203 p1: pair[1],
204 });
205 }
206 edges.push(EdgeRange {
207 first_instance,
208 instance_count: segments.len() as u32 - first_instance,
209 });
210 }
211 let occludable_edge_instances = solid
212 .edges
213 .iter()
214 .zip(&edges)
215 .find(|(edge, _)| edge.aux)
216 .map_or(segments.len() as u32, |(_, range): (_, &EdgeRange)| range.first_instance);
217 let edge_buf = (!segments.is_empty()).then(|| {
218 self.device
219 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
220 label: Some(solid.name.as_str()),
221 contents: bytemuck::cast_slice(&segments),
222 usage: wgpu::BufferUsages::VERTEX,
223 })
224 });
225
226 let points: Vec<PointInstance> = solid
227 .vertices
228 .iter()
229 .map(|v| PointInstance {
230 center: [
231 v.position[0] as f32,
232 v.position[1] as f32,
233 v.position[2] as f32,
234 ],
235 })
236 .collect();
237 let point_buf = (!points.is_empty()).then(|| {
238 self.device
239 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
240 label: Some(solid.name.as_str()),
241 contents: bytemuck::cast_slice(&points),
242 usage: wgpu::BufferUsages::VERTEX,
243 })
244 });
245
246 let base_style = StyleBuf::new(&self.device, &self.style_layout, solid.name.as_str());
247 base_style.write(
248 &self.queue,
249 Self::face_base_color(solid, settings),
250 Self::style_params(settings),
251 );
252
253 GpuSolid {
254 revision: solid.revision,
255 face_styles,
256 vertex_buf,
257 index_buf,
258 wire_index_buf,
259 wire_index_count,
260 faces,
261 edge_buf,
262 edges,
263 edge_instances: segments.len() as u32,
264 occludable_edge_instances,
265 point_buf,
266 point_count: points.len() as u32,
267 base_style,
268 boundary: None,
269 }
270 }
271
272 pub fn sync_scene(
276 &self,
277 gpu: &mut GpuScene,
278 scene: &RenderScene,
279 settings: &RenderSettings,
280 settings_generation: u64,
281 ) {
282 gpu.order.clear();
283 let mut seen: Vec<&str> = Vec::with_capacity(scene.solids().len());
284 for solid in scene.solids() {
285 gpu.order.push(solid.name.clone());
286 seen.push(solid.name.as_str());
287 let refresh_style = gpu.settings_generation != settings_generation;
288 match gpu.solids.get_mut(&solid.name) {
289 Some(existing) if existing.revision == solid.revision => {
290 if refresh_style {
291 let params = Self::style_params(settings);
292 existing.base_style.write(
293 &self.queue,
294 Self::face_base_color(solid, settings),
295 params,
296 );
297 let mut seen: Vec<[f32; 3]> = Vec::new();
301 for face in &solid.faces {
302 let Some(rgb) = face.color_override else { continue };
303 if seen.contains(&rgb) {
304 continue;
305 }
306 if let Some(style) = existing.face_styles.get(seen.len()) {
307 style.write(&self.queue, Self::opaque_linear(rgb), params);
308 }
309 seen.push(rgb);
310 }
311 }
312 }
313 _ => {
314 let uploaded = self.upload_solid(solid, settings);
315 gpu.solids.insert(solid.name.clone(), uploaded);
316 gpu.uploads += 1;
317 }
318 }
319 }
320 gpu.solids.retain(|name, _| seen.contains(&name.as_str()));
321 gpu.settings_generation = settings_generation;
322 }
323
324 pub fn upload_scene(&self, scene: &RenderScene) -> GpuScene {
326 self.upload_scene_with(scene, &RenderSettings::artifact())
327 }
328
329 pub fn upload_scene_with(&self, scene: &RenderScene, settings: &RenderSettings) -> GpuScene {
330 let mut gpu = GpuScene::default();
331 self.sync_scene(&mut gpu, scene, settings, 0);
332 gpu
333 }
334
335 pub(super) fn ensure_targets(&mut self, width: u32, height: u32) {
336 if let Some(targets) = &self.targets {
337 if targets.width == width && targets.height == height {
338 return;
339 }
340 }
341 let size = wgpu::Extent3d {
342 width,
343 height,
344 depth_or_array_layers: 1,
345 };
346 let msaa = self.device.create_texture(&wgpu::TextureDescriptor {
347 label: Some("msaa color"),
348 size,
349 mip_level_count: 1,
350 sample_count: SAMPLES,
351 dimension: wgpu::TextureDimension::D2,
352 format: self.format,
353 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
354 view_formats: &[],
355 });
356 let depth = self.device.create_texture(&wgpu::TextureDescriptor {
357 label: Some("depth"),
358 size,
359 mip_level_count: 1,
360 sample_count: SAMPLES,
361 dimension: wgpu::TextureDimension::D2,
362 format: DEPTH_FORMAT,
363 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
364 view_formats: &[],
365 });
366 self.targets = Some(CachedTargets {
367 width,
368 height,
369 msaa_view: msaa.create_view(&Default::default()),
370 depth_view: depth.create_view(&Default::default()),
371 });
372 }
373
374 pub(super) fn write_global_styles(&self, settings: &RenderSettings) {
375 let flat = if settings.flat_shading { 1.0 } else { 0.0 };
376 let face = |c: Rgba| -> Rgba {
377 [
378 srgb_to_linear(c[0] as f64) as f32,
379 srgb_to_linear(c[1] as f64) as f32,
380 srgb_to_linear(c[2] as f64) as f32,
381 c[3],
382 ]
383 };
384 let s = &self.styles;
385 let q = &self.queue;
386 s.face_selected
387 .write(q, face(settings.face_selected_color), [flat, 0.0, 0.0, 0.0]);
388 s.face_hovered
389 .write(q, face(settings.hover_color), [flat, 0.0, 0.0, 0.0]);
390 let edge_params = [settings.edge_width_px, EDGE_NUDGE, 0.0, 0.0];
391 s.edge_base.write(q, settings.edge_color, edge_params);
392 s.edge_selected.write(q, settings.edge_selected_color, edge_params);
393 s.edge_hovered.write(q, settings.hover_color, edge_params);
394 let mut hidden = settings.edge_color;
395 hidden[3] = settings.hidden_edge_alpha;
396 s.edge_hidden.write(q, hidden, edge_params);
397 s.edge_aux.write(q, settings.edge_color, [AUX_EDGE_WIDTH_PX, EDGE_NUDGE, 0.0, 0.0]);
403 s.boundary.write(
406 q,
407 settings.edge_selected_color,
408 [settings.edge_width_px + 1.0, EDGE_NUDGE * 1.5, 0.0, 0.0],
409 );
410 let point_params = [settings.vertex_size_px, 0.0, 0.0, 0.0];
411 s.point_base.write(q, settings.vertex_color, point_params);
412 s.point_selected
413 .write(q, settings.vertex_selected_color, [settings.vertex_size_px + 1.0, 0.0, 0.0, 0.0]);
414 s.point_hovered
415 .write(q, settings.hover_color, [settings.vertex_size_px + 1.0, 0.0, 0.0, 0.0]);
416 let axis_params = [2.0, EDGE_NUDGE, 0.0, 0.0];
417 s.axis_x.write(q, [0.91, 0.30, 0.32, 1.0], axis_params);
418 s.axis_y.write(q, [0.27, 0.80, 0.42, 1.0], axis_params);
419 s.axis_z.write(q, [0.23, 0.51, 0.96, 1.0], axis_params);
420 s.overlay_line.write(q, [1.0, 1.0, 1.0, 1.0], [2.5, 0.0, 0.0, 0.0]);
422 }
423
424 pub(super) fn sync_boundary(&self, gpu_solid: &mut GpuSolid, solid: &SolidDisplay, emphasis: &Emphasis) {
428 if let Some(boundary) = &gpu_solid.boundary {
429 if boundary.emphasis_generation == emphasis.generation
430 && boundary.revision == solid.revision
431 {
432 return;
433 }
434 }
435 let mut segments: Vec<EdgeInstance> = Vec::new();
436 for face in &solid.faces {
437 if face.tri_count == 0 {
438 continue;
439 }
440 let state = emphasis.face_state(&solid.name, &face.name);
441 if state == EmphasisState::Base {
442 continue;
443 }
444 let mut edge_use: HashMap<(u32, u32), u32> = HashMap::new();
445 let start = face.tri_start as usize;
446 let end = (start + face.tri_count as usize).min(solid.mesh.indices.len() / 3);
447 for tri in start..end {
448 let idx = [
449 solid.mesh.indices[tri * 3],
450 solid.mesh.indices[tri * 3 + 1],
451 solid.mesh.indices[tri * 3 + 2],
452 ];
453 for k in 0..3 {
454 let a = idx[k];
455 let b = idx[(k + 1) % 3];
456 let key = if a < b { (a, b) } else { (b, a) };
457 *edge_use.entry(key).or_insert(0) += 1;
458 }
459 }
460 let mut boundary: Vec<(u32, u32)> = edge_use
461 .into_iter()
462 .filter_map(|(key, count)| (count == 1).then_some(key))
463 .collect();
464 boundary.sort_unstable();
465 for (a, b) in boundary {
466 segments.push(EdgeInstance {
467 p0: solid.mesh.positions[a as usize],
468 p1: solid.mesh.positions[b as usize],
469 });
470 }
471 }
472 let buf = (!segments.is_empty()).then(|| {
473 self.device
474 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
475 label: Some("emphasis boundary"),
476 contents: bytemuck::cast_slice(&segments),
477 usage: wgpu::BufferUsages::VERTEX,
478 })
479 });
480 gpu_solid.boundary = Some(BoundaryBuf {
481 emphasis_generation: emphasis.generation,
482 revision: solid.revision,
483 buf,
484 count: segments.len() as u32,
485 });
486 }
487}