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