1use super::*;
2
3impl RenderCore {
4 pub fn render_to_view(
8 &mut self,
9 gpu_scene: &mut GpuScene,
10 scene: &RenderScene,
11 params: &FrameParams,
12 resolve_view: &wgpu::TextureView,
13 ) {
14 let width = params.width.max(1);
15 let height = params.height.max(1);
16 self.ensure_targets(width, height);
17 self.write_global_styles(params.settings);
18
19 let globals = Globals {
20 view_proj: params.camera.view_proj,
21 viewport: [width as f32, height as f32, params.dpr.max(1e-3), 0.0],
22 forward: [
23 params.camera.forward[0],
24 params.camera.forward[1],
25 params.camera.forward[2],
26 0.0,
27 ],
28 };
29 self.queue
30 .write_buffer(&self.globals_buf, 0, bytemuck::bytes_of(&globals));
31
32 let axis_len = params.settings.axis_length_px as f64 * params.world_per_pixel;
34 let draw_axes = params.settings.axis_length_px > 0.0 && axis_len.is_finite() && axis_len > 0.0;
35 if draw_axes {
36 let l = axis_len as f32;
37 let segments = [
38 EdgeInstance { p0: [0.0; 3], p1: [l, 0.0, 0.0] },
39 EdgeInstance { p0: [0.0; 3], p1: [0.0, l, 0.0] },
40 EdgeInstance { p0: [0.0; 3], p1: [0.0, 0.0, l] },
41 ];
42 match &gpu_scene.axis_buf {
43 Some(buf) => self.queue.write_buffer(buf, 0, bytemuck::cast_slice(&segments)),
44 None => {
45 gpu_scene.axis_buf = Some(self.device.create_buffer_init(
46 &wgpu::util::BufferInitDescriptor {
47 label: Some("world axes"),
48 contents: bytemuck::cast_slice(&segments),
49 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
50 },
51 ));
52 }
53 }
54 }
55
56 let has_emphasis = !params.emphasis.is_empty();
58 for solid in scene.solids() {
59 if let Some(gpu_solid) = gpu_scene.solids.get_mut(&solid.name) {
60 self.sync_boundary(gpu_solid, solid, params.emphasis);
61 }
62 }
63
64 let bg = params.settings.background;
65 let targets = self.targets.as_ref().expect("targets ensured");
66 let mut encoder = self
67 .device
68 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
69 label: Some("brep-render frame"),
70 });
71 {
72 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
73 label: Some("scene"),
74 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
75 view: &targets.msaa_view,
76 depth_slice: None,
77 resolve_target: Some(resolve_view),
78 ops: wgpu::Operations {
79 load: wgpu::LoadOp::Clear(wgpu::Color {
80 r: bg[0] as f64,
81 g: bg[1] as f64,
82 b: bg[2] as f64,
83 a: 1.0,
84 }),
85 store: wgpu::StoreOp::Store,
86 },
87 })],
88 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
89 view: &targets.depth_view,
90 depth_ops: Some(wgpu::Operations {
91 load: wgpu::LoadOp::Clear(1.0),
92 store: wgpu::StoreOp::Discard,
93 }),
94 stencil_ops: None,
95 }),
96 timestamp_writes: None,
97 occlusion_query_set: None,
98 multiview_mask: None,
99 });
100 pass.set_bind_group(0, &self.globals_bind, &[]);
101
102 let visible_solids: Vec<(&SolidDisplay, &GpuSolid)> = gpu_scene
103 .order
104 .iter()
105 .filter_map(|name| {
106 let solid = scene.solid(name)?;
107 if !solid.visible {
108 return None;
109 }
110 Some((solid, gpu_scene.solids.get(name)?))
111 })
112 .collect();
113
114 if params.settings.wireframe && params.settings.show_faces {
121 pass.set_pipeline(&self.wire_pipeline);
122 for (solid, gpu_solid) in &visible_solids {
123 if gpu_solid.wire_index_count == 0 {
124 continue;
125 }
126 pass.set_vertex_buffer(0, gpu_solid.vertex_buf.slice(..));
127 pass.set_index_buffer(
128 gpu_solid.wire_index_buf.slice(..),
129 wgpu::IndexFormat::Uint32,
130 );
131 pass.set_bind_group(1, &gpu_solid.base_style.bind, &[]);
132 if !solid.visibility.any_face_hidden() {
134 pass.draw_indexed(0..gpu_solid.wire_index_count, 0, 0..1);
135 continue;
136 }
137 let mut run: Option<(u32, u32)> = None; let flush = |pass: &mut wgpu::RenderPass, run: &mut Option<(u32, u32)>| {
145 if let Some((first, count)) = run.take() {
146 if count > 0 {
147 pass.draw_indexed(first..first + count, 0, 0..1);
148 }
149 }
150 };
151 for (index, face) in solid.faces.iter().enumerate() {
152 if face.tri_count == 0 {
153 continue;
154 }
155 let first = face.tri_start * 6;
156 let count = face.tri_count * 6;
157 if !solid.visibility.is_face_visible(index) {
158 flush(&mut pass, &mut run);
159 continue;
160 }
161 match &mut run {
162 Some((run_first, run_count)) if *run_first + *run_count == first => {
163 *run_count += count;
164 }
165 _ => {
166 flush(&mut pass, &mut run);
167 run = Some((first, count));
168 }
169 }
170 }
171 flush(&mut pass, &mut run);
172 }
173 }
174
175 if !params.settings.wireframe && params.settings.show_faces {
180 pass.set_pipeline(&self.mesh_pipeline);
181 for (solid, gpu_solid) in &visible_solids {
182 if solid.mesh.indices.is_empty() {
183 continue;
184 }
185 pass.set_vertex_buffer(0, gpu_solid.vertex_buf.slice(..));
186 pass.set_index_buffer(gpu_solid.index_buf.slice(..), wgpu::IndexFormat::Uint32);
187 let any_face_hidden = solid.visibility.any_face_hidden();
195 let any_face_colored = !gpu_solid.face_styles.is_empty();
196 if !has_emphasis && !any_face_hidden && !any_face_colored {
197 pass.set_bind_group(1, &gpu_solid.base_style.bind, &[]);
198 pass.draw_indexed(0..solid.mesh.indices.len() as u32, 0, 0..1);
199 continue;
200 }
201 let style_for = |state: EmphasisState, slot: Option<u32>| match state {
207 EmphasisState::Base => match slot.and_then(|s| gpu_solid.face_styles.get(s as usize)) {
208 Some(style) => &style.bind,
209 None => &gpu_solid.base_style.bind,
210 },
211 EmphasisState::Selected => &self.styles.face_selected.bind,
212 EmphasisState::Hovered => &self.styles.face_hovered.bind,
213 };
214 type FaceRun = (EmphasisState, Option<u32>, u32, u32); let mut run: Option<FaceRun> = None;
216 let flush = |pass: &mut wgpu::RenderPass, run: &mut Option<FaceRun>| {
217 if let Some((state, slot, first, count)) = run.take() {
218 if count > 0 {
219 pass.set_bind_group(1, style_for(state, slot), &[]);
220 pass.draw_indexed(first..first + count, 0, 0..1);
221 }
222 }
223 };
224 for (index, face) in solid.faces.iter().enumerate() {
225 let range = &gpu_solid.faces[index];
226 if range.index_count == 0 {
227 continue;
228 }
229 if !solid.visibility.is_face_visible(index) {
232 flush(&mut pass, &mut run);
233 continue;
234 }
235 let state = if has_emphasis {
236 params.emphasis.face_state(&solid.name, &face.name)
237 } else {
238 EmphasisState::Base
239 };
240 let slot = if state == EmphasisState::Base {
241 range.style
242 } else {
243 None
244 };
245 match &mut run {
246 Some((run_state, run_slot, first, count))
247 if *run_state == state
248 && *run_slot == slot
249 && *first + *count == range.first_index =>
250 {
251 *count += range.index_count;
252 }
253 _ => {
254 flush(&mut pass, &mut run);
255 run = Some((state, slot, range.first_index, range.index_count));
256 }
257 }
258 }
259 flush(&mut pass, &mut run);
260 }
261 }
262
263 if params.settings.show_edges && params.settings.hidden_edge_alpha > 0.0 {
266 pass.set_pipeline(&self.edge_hidden_pipeline);
267 pass.set_bind_group(1, &self.styles.edge_hidden.bind, &[]);
268 for (solid, gpu_solid) in &visible_solids {
269 let Some(edge_buf) = &gpu_solid.edge_buf else { continue };
270 if gpu_solid.edge_instances == 0 {
271 continue;
272 }
273 pass.set_vertex_buffer(0, edge_buf.slice(..));
274 if solid.visibility.any_edge_hidden() {
275 draw_visible_edge_ranges(&mut pass, solid, gpu_solid);
277 } else {
278 pass.draw(0..6, 0..gpu_solid.edge_instances);
279 }
280 }
281 }
282
283 if params.settings.show_edges {
286 pass.set_pipeline(&self.edge_visible_pipeline);
287 for (solid, gpu_solid) in &visible_solids {
288 let Some(edge_buf) = &gpu_solid.edge_buf else { continue };
289 if gpu_solid.edge_instances == 0 {
290 continue;
291 }
292 pass.set_vertex_buffer(0, edge_buf.slice(..));
293 let any_edge_hidden = solid.visibility.any_edge_hidden();
294 if !has_emphasis {
295 pass.set_bind_group(1, &self.styles.edge_base.bind, &[]);
296 if any_edge_hidden {
297 draw_visible_edge_ranges(&mut pass, solid, gpu_solid);
298 } else {
299 pass.draw(0..6, 0..gpu_solid.edge_instances);
300 }
301 continue;
302 }
303 let style_for = |state: EmphasisState| match state {
304 EmphasisState::Base => &self.styles.edge_base.bind,
305 EmphasisState::Selected => &self.styles.edge_selected.bind,
306 EmphasisState::Hovered => &self.styles.edge_hovered.bind,
307 };
308 let mut run: Option<(EmphasisState, u32, u32)> = None;
309 let flush = |pass: &mut wgpu::RenderPass, run: &mut Option<(EmphasisState, u32, u32)>| {
310 if let Some((state, first, count)) = run.take() {
311 if count > 0 {
312 pass.set_bind_group(1, style_for(state), &[]);
313 pass.draw(0..6, first..first + count);
314 }
315 }
316 };
317 for (index, edge) in solid.edges.iter().enumerate() {
318 let range = &gpu_solid.edges[index];
319 if range.instance_count == 0 {
320 continue;
321 }
322 if !solid.visibility.is_edge_visible(index) {
324 flush(&mut pass, &mut run);
325 continue;
326 }
327 let state = params.emphasis.edge_state(&solid.name, &edge.name);
328 match &mut run {
329 Some((run_state, first, count))
330 if *run_state == state
331 && *first + *count == range.first_instance =>
332 {
333 *count += range.instance_count;
334 }
335 _ => {
336 flush(&mut pass, &mut run);
337 run = Some((state, range.first_instance, range.instance_count));
338 }
339 }
340 }
341 flush(&mut pass, &mut run);
342 }
343 }
344
345 if has_emphasis {
347 pass.set_bind_group(1, &self.styles.boundary.bind, &[]);
348 for (_, gpu_solid) in &visible_solids {
349 let Some(boundary) = &gpu_solid.boundary else { continue };
350 let Some(buf) = &boundary.buf else { continue };
351 if boundary.count == 0 {
352 continue;
353 }
354 pass.set_vertex_buffer(0, buf.slice(..));
355 pass.draw(0..6, 0..boundary.count);
356 }
357 }
358
359 if params.settings.show_vertices && params.settings.vertex_size_px > 0.0 {
361 pass.set_pipeline(&self.point_pipeline);
362 for (solid, gpu_solid) in &visible_solids {
363 let Some(point_buf) = &gpu_solid.point_buf else { continue };
364 if gpu_solid.point_count == 0 {
365 continue;
366 }
367 if solid.visibility.all_vertices_hidden(solid.vertices.len()) {
373 continue;
374 }
375 pass.set_vertex_buffer(0, point_buf.slice(..));
376 let any_vertex_hidden = solid.visibility.any_vertex_hidden();
377 if has_emphasis || any_vertex_hidden {
378 let tol = 1e-9_f64.max(params.world_per_pixel * 1e-3);
379 let mut base_run: Option<(u32, u32)> = None;
380 let mut emphasized: Vec<(EmphasisState, u32)> = Vec::new();
381 for (index, vertex) in solid.vertices.iter().enumerate() {
382 if !solid.visibility.is_vertex_visible(index) {
384 if let Some((first, count)) = base_run.take() {
385 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
386 pass.draw(0..6, first..first + count);
387 }
388 continue;
389 }
390 let state = if has_emphasis {
391 params.emphasis.vertex_state(&solid.name, vertex.position, tol)
392 } else {
393 EmphasisState::Base
394 };
395 if state == EmphasisState::Base {
396 match &mut base_run {
397 Some((first, count)) if *first + *count == index as u32 => {
398 *count += 1
399 }
400 _ => {
401 if let Some((first, count)) = base_run.take() {
402 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
403 pass.draw(0..6, first..first + count);
404 }
405 base_run = Some((index as u32, 1));
406 }
407 }
408 } else {
409 emphasized.push((state, index as u32));
410 }
411 }
412 if let Some((first, count)) = base_run {
413 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
414 pass.draw(0..6, first..first + count);
415 }
416 for (state, index) in emphasized {
417 let style = match state {
418 EmphasisState::Selected => &self.styles.point_selected.bind,
419 _ => &self.styles.point_hovered.bind,
420 };
421 pass.set_bind_group(1, style, &[]);
422 pass.draw(0..6, index..index + 1);
423 }
424 } else {
425 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
426 pass.draw(0..6, 0..gpu_solid.point_count);
427 }
428 }
429 }
430
431 if draw_axes {
433 if let Some(axis_buf) = &gpu_scene.axis_buf {
434 pass.set_pipeline(&self.edge_visible_pipeline);
435 pass.set_vertex_buffer(0, axis_buf.slice(..));
436 for (index, style) in [
437 &self.styles.axis_x,
438 &self.styles.axis_y,
439 &self.styles.axis_z,
440 ]
441 .iter()
442 .enumerate()
443 {
444 pass.set_bind_group(1, &style.bind, &[]);
445 let i = index as u32;
446 pass.draw(0..6, i..i + 1);
447 }
448 }
449 }
450 }
451
452 if let Some(overlay) = params.overlay {
457 let make_tris = |ov: &brep_gizmos::Overlay| -> Option<wgpu::Buffer> {
458 let verts = overlay_tri_verts(ov);
459 (!verts.is_empty()).then(|| {
460 self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
461 label: Some("overlay tris"),
462 contents: bytemuck::cast_slice(&verts),
463 usage: wgpu::BufferUsages::VERTEX,
464 })
465 })
466 };
467 let make_lines = |ov: &brep_gizmos::Overlay| -> Option<wgpu::Buffer> {
468 let insts = overlay_line_insts(ov);
469 (!insts.is_empty()).then(|| {
470 self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
471 label: Some("overlay lines"),
472 contents: bytemuck::cast_slice(&insts),
473 usage: wgpu::BufferUsages::VERTEX,
474 })
475 })
476 };
477
478 let main_tri_count = overlay.main.tris.len() as u32;
479 let main_line_count = (overlay.main.lines.len() / 2) as u32;
480 let main_tri_buf = make_tris(&overlay.main);
481 let main_line_buf = make_lines(&overlay.main);
482 if main_tri_buf.is_some() || main_line_buf.is_some() {
483 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
484 label: Some("overlay-main"),
485 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
486 view: &targets.msaa_view,
487 depth_slice: None,
488 resolve_target: Some(resolve_view),
489 ops: wgpu::Operations {
490 load: wgpu::LoadOp::Load,
491 store: wgpu::StoreOp::Store,
492 },
493 })],
494 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
495 view: &targets.depth_view,
496 depth_ops: Some(wgpu::Operations {
497 load: wgpu::LoadOp::Clear(1.0),
498 store: wgpu::StoreOp::Discard,
499 }),
500 stencil_ops: None,
501 }),
502 timestamp_writes: None,
503 occlusion_query_set: None,
504 multiview_mask: None,
505 });
506 pass.set_bind_group(0, &self.globals_bind, &[]);
507 pass.set_bind_group(1, &self.styles.overlay_line.bind, &[]);
508 if let Some(buf) = &main_tri_buf {
509 pass.set_vertex_buffer(0, buf.slice(..));
510 let plane_verts = (overlay.plane_tri_verts as u32).min(main_tri_count);
515 if plane_verts > 0 {
516 pass.set_pipeline(&self.overlay_tri_nodepth_pipeline);
517 pass.draw(0..plane_verts, 0..1);
518 }
519 if plane_verts < main_tri_count {
520 pass.set_pipeline(&self.overlay_tri_pipeline);
521 pass.draw(plane_verts..main_tri_count, 0..1);
522 }
523 }
524 if let Some(buf) = &main_line_buf {
525 pass.set_pipeline(&self.overlay_line_pipeline);
526 pass.set_vertex_buffer(0, buf.slice(..));
527 pass.draw(0..6, 0..main_line_count);
528 }
529 }
530
531 if let Some(vc) = &overlay.viewcube {
532 let dpr = params.dpr.max(1e-3);
533 let mut x = (vc.rect_css[0] * dpr).max(0.0);
534 let mut y = (vc.rect_css[1] * dpr).max(0.0);
535 let mut w = (vc.rect_css[2] * dpr).max(1.0);
536 let mut h = (vc.rect_css[3] * dpr).max(1.0);
537 w = w.min(width as f32 - x).max(1.0);
539 h = h.min(height as f32 - y).max(1.0);
540 x = x.min(width as f32 - w).max(0.0);
541 y = y.min(height as f32 - h).max(0.0);
542
543 let vc_globals = Globals {
544 view_proj: vc.view_proj,
545 viewport: [w, h, dpr, 0.0],
546 forward: [vc.forward[0], vc.forward[1], vc.forward[2], 0.0],
547 };
548 self.queue
549 .write_buffer(&self.vc_globals_buf, 0, bytemuck::bytes_of(&vc_globals));
550
551 let vc_tri_count = vc.overlay.tris.len() as u32;
552 let vc_line_count = (vc.overlay.lines.len() / 2) as u32;
553 let vc_tri_buf = make_tris(&vc.overlay);
554 let vc_line_buf = make_lines(&vc.overlay);
555 if vc_tri_buf.is_some() || vc_line_buf.is_some() {
556 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
557 label: Some("overlay-viewcube"),
558 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
559 view: &targets.msaa_view,
560 depth_slice: None,
561 resolve_target: Some(resolve_view),
562 ops: wgpu::Operations {
563 load: wgpu::LoadOp::Load,
564 store: wgpu::StoreOp::Store,
565 },
566 })],
567 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
568 view: &targets.depth_view,
569 depth_ops: Some(wgpu::Operations {
570 load: wgpu::LoadOp::Clear(1.0),
571 store: wgpu::StoreOp::Discard,
572 }),
573 stencil_ops: None,
574 }),
575 timestamp_writes: None,
576 occlusion_query_set: None,
577 multiview_mask: None,
578 });
579 pass.set_viewport(x, y, w, h, 0.0, 1.0);
580 pass.set_scissor_rect(x as u32, y as u32, w as u32, h as u32);
581 pass.set_bind_group(0, &self.vc_globals_bind, &[]);
582 pass.set_bind_group(1, &self.styles.overlay_line.bind, &[]);
583 if let Some(buf) = &vc_tri_buf {
584 pass.set_pipeline(&self.overlay_tri_pipeline);
585 pass.set_vertex_buffer(0, buf.slice(..));
586 pass.draw(0..vc_tri_count, 0..1);
587 }
588 if let Some(buf) = &vc_line_buf {
589 pass.set_pipeline(&self.overlay_line_pipeline);
590 pass.set_vertex_buffer(0, buf.slice(..));
591 pass.draw(0..6, 0..vc_line_count);
592 }
593 }
594 }
595 }
596
597 self.queue.submit([encoder.finish()]);
598 }
599
600 #[cfg(not(target_arch = "wasm32"))]
603 pub fn render_to_png(
604 &mut self,
605 scene: &RenderScene,
606 camera: &Camera,
607 width: u32,
608 height: u32,
609 ) -> Result<Vec<u8>, String> {
610 let settings = RenderSettings::artifact();
611 let emphasis = Emphasis::default();
612 let mut gpu_scene = self.upload_scene_with(scene, &settings);
613 let params = FrameParams {
614 camera,
615 width,
616 height,
617 dpr: 1.0,
618 settings: &settings,
619 emphasis: &emphasis,
620 world_per_pixel: 0.0,
621 overlay: None,
622 };
623 let resolve = self.device.create_texture(&wgpu::TextureDescriptor {
624 label: Some("resolve"),
625 size: wgpu::Extent3d {
626 width,
627 height,
628 depth_or_array_layers: 1,
629 },
630 mip_level_count: 1,
631 sample_count: 1,
632 dimension: wgpu::TextureDimension::D2,
633 format: self.format,
634 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
635 view_formats: &[],
636 });
637 let resolve_view = resolve.create_view(&Default::default());
638 self.render_to_view(&mut gpu_scene, scene, ¶ms, &resolve_view);
639
640 let bytes_per_row = (width * 4).div_ceil(256) * 256;
642 let readback = self.device.create_buffer(&wgpu::BufferDescriptor {
643 label: Some("readback"),
644 size: bytes_per_row as u64 * height as u64,
645 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
646 mapped_at_creation: false,
647 });
648 let mut encoder = self
649 .device
650 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
651 label: Some("readback"),
652 });
653 encoder.copy_texture_to_buffer(
654 wgpu::TexelCopyTextureInfo {
655 texture: &resolve,
656 mip_level: 0,
657 origin: wgpu::Origin3d::ZERO,
658 aspect: wgpu::TextureAspect::All,
659 },
660 wgpu::TexelCopyBufferInfo {
661 buffer: &readback,
662 layout: wgpu::TexelCopyBufferLayout {
663 offset: 0,
664 bytes_per_row: Some(bytes_per_row),
665 rows_per_image: None,
666 },
667 },
668 wgpu::Extent3d {
669 width,
670 height,
671 depth_or_array_layers: 1,
672 },
673 );
674 self.queue.submit([encoder.finish()]);
675
676 let slice = readback.slice(..);
677 let (sender, receiver) = std::sync::mpsc::channel();
678 slice.map_async(wgpu::MapMode::Read, move |result| {
679 let _ = sender.send(result);
680 });
681 self.device
682 .poll(wgpu::PollType::wait_indefinitely())
683 .map_err(|error| format!("wgpu poll: {error:?}"))?;
684 receiver
685 .recv()
686 .map_err(|_| "readback callback dropped".to_string())?
687 .map_err(|error| format!("readback map failed: {error:?}"))?;
688
689 let data = slice.get_mapped_range();
690 let mut rgb = Vec::with_capacity((width * height * 3) as usize);
691 for row in 0..height {
692 let start = (row * bytes_per_row) as usize;
693 for col in 0..width as usize {
694 let px = start + col * 4;
695 rgb.extend_from_slice(&data[px..px + 3]);
696 }
697 }
698 drop(data);
699 readback.unmap();
700
701 encode_png(&rgb, width, height)
702 }
703}
704
705fn draw_visible_edge_ranges(
710 pass: &mut wgpu::RenderPass<'_>,
711 solid: &SolidDisplay,
712 gpu_solid: &GpuSolid,
713) {
714 let mut run: Option<(u32, u32)> = None; for (index, _edge) in solid.edges.iter().enumerate() {
716 let range = &gpu_solid.edges[index];
717 if range.instance_count == 0 {
718 continue;
719 }
720 if !solid.visibility.is_edge_visible(index) {
721 if let Some((first, count)) = run.take() {
722 pass.draw(0..6, first..first + count);
723 }
724 continue;
725 }
726 match &mut run {
727 Some((first, count)) if *first + *count == range.first_instance => {
728 *count += range.instance_count;
729 }
730 _ => {
731 if let Some((first, count)) = run.take() {
732 pass.draw(0..6, first..first + count);
733 }
734 run = Some((range.first_instance, range.instance_count));
735 }
736 }
737 }
738 if let Some((first, count)) = run {
739 pass.draw(0..6, first..first + count);
740 }
741}
742
743fn overlay_tri_verts(ov: &brep_gizmos::Overlay) -> Vec<OverlayTriVertex> {
745 ov.tris
746 .iter()
747 .map(|v| OverlayTriVertex {
748 position: v.pos,
749 normal: v.normal,
750 color: v.color,
751 })
752 .collect()
753}
754
755fn overlay_line_insts(ov: &brep_gizmos::Overlay) -> Vec<OverlayLineInstance> {
758 ov.lines
759 .chunks_exact(2)
760 .map(|pair| OverlayLineInstance {
761 p0: pair[0].pos,
762 p1: pair[1].pos,
763 color: pair[0].color,
764 })
765 .collect()
766}