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 {
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 {
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();
192 if !has_emphasis && !any_face_hidden {
193 pass.set_bind_group(1, &gpu_solid.base_style.bind, &[]);
194 pass.draw_indexed(0..solid.mesh.indices.len() as u32, 0, 0..1);
195 continue;
196 }
197 let style_for = |state: EmphasisState| match state {
198 EmphasisState::Base => &gpu_solid.base_style.bind,
199 EmphasisState::Selected => &self.styles.face_selected.bind,
200 EmphasisState::Hovered => &self.styles.face_hovered.bind,
201 };
202 let mut run: Option<(EmphasisState, u32, u32)> = None; let flush = |pass: &mut wgpu::RenderPass, run: &mut Option<(EmphasisState, u32, u32)>| {
204 if let Some((state, first, count)) = run.take() {
205 if count > 0 {
206 pass.set_bind_group(1, style_for(state), &[]);
207 pass.draw_indexed(first..first + count, 0, 0..1);
208 }
209 }
210 };
211 for (index, face) in solid.faces.iter().enumerate() {
212 let range = &gpu_solid.faces[index];
213 if range.index_count == 0 {
214 continue;
215 }
216 if !solid.visibility.is_face_visible(index) {
219 flush(&mut pass, &mut run);
220 continue;
221 }
222 let state = if has_emphasis {
223 params.emphasis.face_state(&solid.name, &face.name)
224 } else {
225 EmphasisState::Base
226 };
227 match &mut run {
228 Some((run_state, first, count))
229 if *run_state == state && *first + *count == range.first_index =>
230 {
231 *count += range.index_count;
232 }
233 _ => {
234 flush(&mut pass, &mut run);
235 run = Some((state, range.first_index, range.index_count));
236 }
237 }
238 }
239 flush(&mut pass, &mut run);
240 }
241 }
242
243 if params.settings.hidden_edge_alpha > 0.0 {
246 pass.set_pipeline(&self.edge_hidden_pipeline);
247 pass.set_bind_group(1, &self.styles.edge_hidden.bind, &[]);
248 for (solid, gpu_solid) in &visible_solids {
249 let Some(edge_buf) = &gpu_solid.edge_buf else { continue };
250 if gpu_solid.edge_instances == 0 {
251 continue;
252 }
253 pass.set_vertex_buffer(0, edge_buf.slice(..));
254 if solid.visibility.any_edge_hidden() {
255 draw_visible_edge_ranges(&mut pass, solid, gpu_solid);
257 } else {
258 pass.draw(0..6, 0..gpu_solid.edge_instances);
259 }
260 }
261 }
262
263 pass.set_pipeline(&self.edge_visible_pipeline);
265 for (solid, gpu_solid) in &visible_solids {
266 let Some(edge_buf) = &gpu_solid.edge_buf else { continue };
267 if gpu_solid.edge_instances == 0 {
268 continue;
269 }
270 pass.set_vertex_buffer(0, edge_buf.slice(..));
271 let any_edge_hidden = solid.visibility.any_edge_hidden();
272 if !has_emphasis {
273 pass.set_bind_group(1, &self.styles.edge_base.bind, &[]);
274 if any_edge_hidden {
275 draw_visible_edge_ranges(&mut pass, solid, gpu_solid);
276 } else {
277 pass.draw(0..6, 0..gpu_solid.edge_instances);
278 }
279 continue;
280 }
281 let style_for = |state: EmphasisState| match state {
282 EmphasisState::Base => &self.styles.edge_base.bind,
283 EmphasisState::Selected => &self.styles.edge_selected.bind,
284 EmphasisState::Hovered => &self.styles.edge_hovered.bind,
285 };
286 let mut run: Option<(EmphasisState, u32, u32)> = None;
287 let flush = |pass: &mut wgpu::RenderPass, run: &mut Option<(EmphasisState, u32, u32)>| {
288 if let Some((state, first, count)) = run.take() {
289 if count > 0 {
290 pass.set_bind_group(1, style_for(state), &[]);
291 pass.draw(0..6, first..first + count);
292 }
293 }
294 };
295 for (index, edge) in solid.edges.iter().enumerate() {
296 let range = &gpu_solid.edges[index];
297 if range.instance_count == 0 {
298 continue;
299 }
300 if !solid.visibility.is_edge_visible(index) {
302 flush(&mut pass, &mut run);
303 continue;
304 }
305 let state = params.emphasis.edge_state(&solid.name, &edge.name);
306 match &mut run {
307 Some((run_state, first, count))
308 if *run_state == state
309 && *first + *count == range.first_instance =>
310 {
311 *count += range.instance_count;
312 }
313 _ => {
314 flush(&mut pass, &mut run);
315 run = Some((state, range.first_instance, range.instance_count));
316 }
317 }
318 }
319 flush(&mut pass, &mut run);
320 }
321
322 if has_emphasis {
324 pass.set_bind_group(1, &self.styles.boundary.bind, &[]);
325 for (_, gpu_solid) in &visible_solids {
326 let Some(boundary) = &gpu_solid.boundary else { continue };
327 let Some(buf) = &boundary.buf else { continue };
328 if boundary.count == 0 {
329 continue;
330 }
331 pass.set_vertex_buffer(0, buf.slice(..));
332 pass.draw(0..6, 0..boundary.count);
333 }
334 }
335
336 if params.settings.vertex_size_px > 0.0 {
338 pass.set_pipeline(&self.point_pipeline);
339 for (solid, gpu_solid) in &visible_solids {
340 let Some(point_buf) = &gpu_solid.point_buf else { continue };
341 if gpu_solid.point_count == 0 {
342 continue;
343 }
344 if solid.visibility.all_vertices_hidden(solid.vertices.len()) {
350 continue;
351 }
352 pass.set_vertex_buffer(0, point_buf.slice(..));
353 let any_vertex_hidden = solid.visibility.any_vertex_hidden();
354 if has_emphasis || any_vertex_hidden {
355 let tol = 1e-9_f64.max(params.world_per_pixel * 1e-3);
356 let mut base_run: Option<(u32, u32)> = None;
357 let mut emphasized: Vec<(EmphasisState, u32)> = Vec::new();
358 for (index, vertex) in solid.vertices.iter().enumerate() {
359 if !solid.visibility.is_vertex_visible(index) {
361 if let Some((first, count)) = base_run.take() {
362 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
363 pass.draw(0..6, first..first + count);
364 }
365 continue;
366 }
367 let state = if has_emphasis {
368 params.emphasis.vertex_state(&solid.name, vertex.position, tol)
369 } else {
370 EmphasisState::Base
371 };
372 if state == EmphasisState::Base {
373 match &mut base_run {
374 Some((first, count)) if *first + *count == index as u32 => {
375 *count += 1
376 }
377 _ => {
378 if let Some((first, count)) = base_run.take() {
379 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
380 pass.draw(0..6, first..first + count);
381 }
382 base_run = Some((index as u32, 1));
383 }
384 }
385 } else {
386 emphasized.push((state, index as u32));
387 }
388 }
389 if let Some((first, count)) = base_run {
390 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
391 pass.draw(0..6, first..first + count);
392 }
393 for (state, index) in emphasized {
394 let style = match state {
395 EmphasisState::Selected => &self.styles.point_selected.bind,
396 _ => &self.styles.point_hovered.bind,
397 };
398 pass.set_bind_group(1, style, &[]);
399 pass.draw(0..6, index..index + 1);
400 }
401 } else {
402 pass.set_bind_group(1, &self.styles.point_base.bind, &[]);
403 pass.draw(0..6, 0..gpu_solid.point_count);
404 }
405 }
406 }
407
408 if draw_axes {
410 if let Some(axis_buf) = &gpu_scene.axis_buf {
411 pass.set_pipeline(&self.edge_visible_pipeline);
412 pass.set_vertex_buffer(0, axis_buf.slice(..));
413 for (index, style) in [
414 &self.styles.axis_x,
415 &self.styles.axis_y,
416 &self.styles.axis_z,
417 ]
418 .iter()
419 .enumerate()
420 {
421 pass.set_bind_group(1, &style.bind, &[]);
422 let i = index as u32;
423 pass.draw(0..6, i..i + 1);
424 }
425 }
426 }
427 }
428
429 if let Some(overlay) = params.overlay {
434 let make_tris = |ov: &brep_gizmos::Overlay| -> Option<wgpu::Buffer> {
435 let verts = overlay_tri_verts(ov);
436 (!verts.is_empty()).then(|| {
437 self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
438 label: Some("overlay tris"),
439 contents: bytemuck::cast_slice(&verts),
440 usage: wgpu::BufferUsages::VERTEX,
441 })
442 })
443 };
444 let make_lines = |ov: &brep_gizmos::Overlay| -> Option<wgpu::Buffer> {
445 let insts = overlay_line_insts(ov);
446 (!insts.is_empty()).then(|| {
447 self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
448 label: Some("overlay lines"),
449 contents: bytemuck::cast_slice(&insts),
450 usage: wgpu::BufferUsages::VERTEX,
451 })
452 })
453 };
454
455 let main_tri_count = overlay.main.tris.len() as u32;
456 let main_line_count = (overlay.main.lines.len() / 2) as u32;
457 let main_tri_buf = make_tris(&overlay.main);
458 let main_line_buf = make_lines(&overlay.main);
459 if main_tri_buf.is_some() || main_line_buf.is_some() {
460 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
461 label: Some("overlay-main"),
462 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
463 view: &targets.msaa_view,
464 depth_slice: None,
465 resolve_target: Some(resolve_view),
466 ops: wgpu::Operations {
467 load: wgpu::LoadOp::Load,
468 store: wgpu::StoreOp::Store,
469 },
470 })],
471 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
472 view: &targets.depth_view,
473 depth_ops: Some(wgpu::Operations {
474 load: wgpu::LoadOp::Clear(1.0),
475 store: wgpu::StoreOp::Discard,
476 }),
477 stencil_ops: None,
478 }),
479 timestamp_writes: None,
480 occlusion_query_set: None,
481 multiview_mask: None,
482 });
483 pass.set_bind_group(0, &self.globals_bind, &[]);
484 pass.set_bind_group(1, &self.styles.overlay_line.bind, &[]);
485 if let Some(buf) = &main_tri_buf {
486 pass.set_vertex_buffer(0, buf.slice(..));
487 let plane_verts = (overlay.plane_tri_verts as u32).min(main_tri_count);
492 if plane_verts > 0 {
493 pass.set_pipeline(&self.overlay_tri_nodepth_pipeline);
494 pass.draw(0..plane_verts, 0..1);
495 }
496 if plane_verts < main_tri_count {
497 pass.set_pipeline(&self.overlay_tri_pipeline);
498 pass.draw(plane_verts..main_tri_count, 0..1);
499 }
500 }
501 if let Some(buf) = &main_line_buf {
502 pass.set_pipeline(&self.overlay_line_pipeline);
503 pass.set_vertex_buffer(0, buf.slice(..));
504 pass.draw(0..6, 0..main_line_count);
505 }
506 }
507
508 if let Some(vc) = &overlay.viewcube {
509 let dpr = params.dpr.max(1e-3);
510 let mut x = (vc.rect_css[0] * dpr).max(0.0);
511 let mut y = (vc.rect_css[1] * dpr).max(0.0);
512 let mut w = (vc.rect_css[2] * dpr).max(1.0);
513 let mut h = (vc.rect_css[3] * dpr).max(1.0);
514 w = w.min(width as f32 - x).max(1.0);
516 h = h.min(height as f32 - y).max(1.0);
517 x = x.min(width as f32 - w).max(0.0);
518 y = y.min(height as f32 - h).max(0.0);
519
520 let vc_globals = Globals {
521 view_proj: vc.view_proj,
522 viewport: [w, h, dpr, 0.0],
523 forward: [vc.forward[0], vc.forward[1], vc.forward[2], 0.0],
524 };
525 self.queue
526 .write_buffer(&self.vc_globals_buf, 0, bytemuck::bytes_of(&vc_globals));
527
528 let vc_tri_count = vc.overlay.tris.len() as u32;
529 let vc_line_count = (vc.overlay.lines.len() / 2) as u32;
530 let vc_tri_buf = make_tris(&vc.overlay);
531 let vc_line_buf = make_lines(&vc.overlay);
532 if vc_tri_buf.is_some() || vc_line_buf.is_some() {
533 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
534 label: Some("overlay-viewcube"),
535 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
536 view: &targets.msaa_view,
537 depth_slice: None,
538 resolve_target: Some(resolve_view),
539 ops: wgpu::Operations {
540 load: wgpu::LoadOp::Load,
541 store: wgpu::StoreOp::Store,
542 },
543 })],
544 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
545 view: &targets.depth_view,
546 depth_ops: Some(wgpu::Operations {
547 load: wgpu::LoadOp::Clear(1.0),
548 store: wgpu::StoreOp::Discard,
549 }),
550 stencil_ops: None,
551 }),
552 timestamp_writes: None,
553 occlusion_query_set: None,
554 multiview_mask: None,
555 });
556 pass.set_viewport(x, y, w, h, 0.0, 1.0);
557 pass.set_scissor_rect(x as u32, y as u32, w as u32, h as u32);
558 pass.set_bind_group(0, &self.vc_globals_bind, &[]);
559 pass.set_bind_group(1, &self.styles.overlay_line.bind, &[]);
560 if let Some(buf) = &vc_tri_buf {
561 pass.set_pipeline(&self.overlay_tri_pipeline);
562 pass.set_vertex_buffer(0, buf.slice(..));
563 pass.draw(0..vc_tri_count, 0..1);
564 }
565 if let Some(buf) = &vc_line_buf {
566 pass.set_pipeline(&self.overlay_line_pipeline);
567 pass.set_vertex_buffer(0, buf.slice(..));
568 pass.draw(0..6, 0..vc_line_count);
569 }
570 }
571 }
572 }
573
574 self.queue.submit([encoder.finish()]);
575 }
576
577 #[cfg(not(target_arch = "wasm32"))]
580 pub fn render_to_png(
581 &mut self,
582 scene: &RenderScene,
583 camera: &Camera,
584 width: u32,
585 height: u32,
586 ) -> Result<Vec<u8>, String> {
587 let settings = RenderSettings::artifact();
588 let emphasis = Emphasis::default();
589 let mut gpu_scene = self.upload_scene_with(scene, &settings);
590 let params = FrameParams {
591 camera,
592 width,
593 height,
594 dpr: 1.0,
595 settings: &settings,
596 emphasis: &emphasis,
597 world_per_pixel: 0.0,
598 overlay: None,
599 };
600 let resolve = self.device.create_texture(&wgpu::TextureDescriptor {
601 label: Some("resolve"),
602 size: wgpu::Extent3d {
603 width,
604 height,
605 depth_or_array_layers: 1,
606 },
607 mip_level_count: 1,
608 sample_count: 1,
609 dimension: wgpu::TextureDimension::D2,
610 format: self.format,
611 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
612 view_formats: &[],
613 });
614 let resolve_view = resolve.create_view(&Default::default());
615 self.render_to_view(&mut gpu_scene, scene, ¶ms, &resolve_view);
616
617 let bytes_per_row = (width * 4).div_ceil(256) * 256;
619 let readback = self.device.create_buffer(&wgpu::BufferDescriptor {
620 label: Some("readback"),
621 size: bytes_per_row as u64 * height as u64,
622 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
623 mapped_at_creation: false,
624 });
625 let mut encoder = self
626 .device
627 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
628 label: Some("readback"),
629 });
630 encoder.copy_texture_to_buffer(
631 wgpu::TexelCopyTextureInfo {
632 texture: &resolve,
633 mip_level: 0,
634 origin: wgpu::Origin3d::ZERO,
635 aspect: wgpu::TextureAspect::All,
636 },
637 wgpu::TexelCopyBufferInfo {
638 buffer: &readback,
639 layout: wgpu::TexelCopyBufferLayout {
640 offset: 0,
641 bytes_per_row: Some(bytes_per_row),
642 rows_per_image: None,
643 },
644 },
645 wgpu::Extent3d {
646 width,
647 height,
648 depth_or_array_layers: 1,
649 },
650 );
651 self.queue.submit([encoder.finish()]);
652
653 let slice = readback.slice(..);
654 let (sender, receiver) = std::sync::mpsc::channel();
655 slice.map_async(wgpu::MapMode::Read, move |result| {
656 let _ = sender.send(result);
657 });
658 self.device
659 .poll(wgpu::PollType::wait_indefinitely())
660 .map_err(|error| format!("wgpu poll: {error:?}"))?;
661 receiver
662 .recv()
663 .map_err(|_| "readback callback dropped".to_string())?
664 .map_err(|error| format!("readback map failed: {error:?}"))?;
665
666 let data = slice.get_mapped_range();
667 let mut rgb = Vec::with_capacity((width * height * 3) as usize);
668 for row in 0..height {
669 let start = (row * bytes_per_row) as usize;
670 for col in 0..width as usize {
671 let px = start + col * 4;
672 rgb.extend_from_slice(&data[px..px + 3]);
673 }
674 }
675 drop(data);
676 readback.unmap();
677
678 encode_png(&rgb, width, height)
679 }
680}
681
682fn draw_visible_edge_ranges(
687 pass: &mut wgpu::RenderPass<'_>,
688 solid: &SolidDisplay,
689 gpu_solid: &GpuSolid,
690) {
691 let mut run: Option<(u32, u32)> = None; for (index, _edge) in solid.edges.iter().enumerate() {
693 let range = &gpu_solid.edges[index];
694 if range.instance_count == 0 {
695 continue;
696 }
697 if !solid.visibility.is_edge_visible(index) {
698 if let Some((first, count)) = run.take() {
699 pass.draw(0..6, first..first + count);
700 }
701 continue;
702 }
703 match &mut run {
704 Some((first, count)) if *first + *count == range.first_instance => {
705 *count += range.instance_count;
706 }
707 _ => {
708 if let Some((first, count)) = run.take() {
709 pass.draw(0..6, first..first + count);
710 }
711 run = Some((range.first_instance, range.instance_count));
712 }
713 }
714 }
715 if let Some((first, count)) = run {
716 pass.draw(0..6, first..first + count);
717 }
718}
719
720fn overlay_tri_verts(ov: &brep_gizmos::Overlay) -> Vec<OverlayTriVertex> {
722 ov.tris
723 .iter()
724 .map(|v| OverlayTriVertex {
725 position: v.pos,
726 normal: v.normal,
727 color: v.color,
728 })
729 .collect()
730}
731
732fn overlay_line_insts(ov: &brep_gizmos::Overlay) -> Vec<OverlayLineInstance> {
735 ov.lines
736 .chunks_exact(2)
737 .map(|pair| OverlayLineInstance {
738 p0: pair[0].pos,
739 p1: pair[1].pos,
740 color: pair[0].color,
741 })
742 .collect()
743}