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