1use core::f32::consts::FRAC_PI_2;
2use core::mem;
3
4use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment, VertexAttributeValues};
5use bevy_asset::RenderAssetUsages;
6
7use super::{Extrudable, MeshBuilder, Meshable};
8use bevy_math::prelude::Polyline2d;
9use bevy_math::{
10 ops,
11 primitives::{
12 Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse,
13 Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, Triangle3d,
14 WindingOrder,
15 },
16 FloatExt, Vec2, Vec3,
17};
18use bevy_reflect::prelude::*;
19use wgpu_types::PrimitiveTopology;
20
21#[derive(Clone, Copy, Debug, Reflect)]
23#[reflect(Default, Debug, Clone)]
24pub struct CircleMeshBuilder {
25 pub circle: Circle,
27 #[doc(alias = "vertices")]
30 pub resolution: u32,
31}
32
33impl Default for CircleMeshBuilder {
34 fn default() -> Self {
35 Self {
36 circle: Circle::default(),
37 resolution: 32,
38 }
39 }
40}
41
42impl CircleMeshBuilder {
43 #[inline]
45 pub const fn new(radius: f32, resolution: u32) -> Self {
46 Self {
47 circle: Circle { radius },
48 resolution,
49 }
50 }
51
52 #[inline]
54 #[doc(alias = "vertices")]
55 pub const fn resolution(mut self, resolution: u32) -> Self {
56 self.resolution = resolution;
57 self
58 }
59}
60
61impl MeshBuilder for CircleMeshBuilder {
62 fn build(&self) -> Mesh {
63 Ellipse::new(self.circle.radius, self.circle.radius)
64 .mesh()
65 .resolution(self.resolution)
66 .build()
67 }
68}
69
70impl Extrudable for CircleMeshBuilder {
71 fn perimeter(&self) -> Vec<PerimeterSegment> {
72 vec![PerimeterSegment::Smooth {
73 first_normal: Vec2::Y,
74 last_normal: Vec2::Y,
75 indices: (0..self.resolution).chain([0]).collect(),
76 }]
77 }
78}
79
80impl Meshable for Circle {
81 type Output = CircleMeshBuilder;
82
83 fn mesh(&self) -> Self::Output {
84 CircleMeshBuilder {
85 circle: *self,
86 ..Default::default()
87 }
88 }
89}
90
91impl From<Circle> for Mesh {
92 fn from(circle: Circle) -> Self {
93 circle.mesh().build()
94 }
95}
96
97#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
107#[reflect(Default, Debug, Clone)]
108#[non_exhaustive]
109pub enum CircularMeshUvMode {
110 Mask {
113 angle: f32,
115 },
116}
117
118impl Default for CircularMeshUvMode {
119 fn default() -> Self {
120 CircularMeshUvMode::Mask { angle: 0.0 }
121 }
122}
123
124#[derive(Clone, Debug, Reflect)]
129#[reflect(Default, Debug, Clone)]
130pub struct CircularSectorMeshBuilder {
131 pub sector: CircularSector,
133 #[doc(alias = "vertices")]
136 pub resolution: u32,
137 pub uv_mode: CircularMeshUvMode,
139}
140
141impl Default for CircularSectorMeshBuilder {
142 fn default() -> Self {
143 Self {
144 sector: CircularSector::default(),
145 resolution: 32,
146 uv_mode: CircularMeshUvMode::default(),
147 }
148 }
149}
150
151impl CircularSectorMeshBuilder {
152 #[inline]
154 pub fn new(sector: CircularSector) -> Self {
155 Self {
156 sector,
157 ..Self::default()
158 }
159 }
160
161 #[inline]
163 #[doc(alias = "vertices")]
164 pub const fn resolution(mut self, resolution: u32) -> Self {
165 self.resolution = resolution;
166 self
167 }
168
169 #[inline]
171 pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
172 self.uv_mode = uv_mode;
173 self
174 }
175}
176
177impl MeshBuilder for CircularSectorMeshBuilder {
178 fn build(&self) -> Mesh {
179 let resolution = self.resolution as usize;
180 let mut indices = Vec::with_capacity((resolution - 1) * 3);
181 let mut positions = Vec::with_capacity(resolution + 1);
182 let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
183 let mut uvs = Vec::with_capacity(resolution + 1);
184
185 let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
186
187 positions.push([0.0; 3]);
189 uvs.push([0.5; 2]);
190
191 let first_angle = FRAC_PI_2 - self.sector.half_angle();
192 let last_angle = FRAC_PI_2 + self.sector.half_angle();
193 let last_i = (self.resolution - 1) as f32;
194 for i in 0..self.resolution {
195 let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
196
197 let vertex = self.sector.radius() * Vec2::from_angle(angle);
199 let uv =
202 Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
203
204 positions.push([vertex.x, vertex.y, 0.0]);
205 uvs.push([uv.x, uv.y]);
206 }
207
208 for i in 1..self.resolution {
209 indices.extend_from_slice(&[0, i, i + 1]);
211 }
212
213 Mesh::new(
214 PrimitiveTopology::TriangleList,
215 RenderAssetUsages::default(),
216 )
217 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
218 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
219 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
220 .with_inserted_indices(Indices::U32(indices))
221 }
222}
223
224impl Extrudable for CircularSectorMeshBuilder {
225 fn perimeter(&self) -> Vec<PerimeterSegment> {
226 let (sin, cos) = ops::sin_cos(self.sector.arc.half_angle);
227 let first_normal = Vec2::new(sin, cos);
228 let last_normal = Vec2::new(-sin, cos);
229 vec![
230 PerimeterSegment::Flat {
231 indices: vec![self.resolution, 0, 1],
232 },
233 PerimeterSegment::Smooth {
234 first_normal,
235 last_normal,
236 indices: (1..=self.resolution).collect(),
237 },
238 ]
239 }
240}
241
242impl Meshable for CircularSector {
243 type Output = CircularSectorMeshBuilder;
244
245 fn mesh(&self) -> Self::Output {
246 CircularSectorMeshBuilder {
247 sector: *self,
248 ..Default::default()
249 }
250 }
251}
252
253impl From<CircularSector> for Mesh {
254 fn from(sector: CircularSector) -> Self {
258 sector.mesh().build()
259 }
260}
261
262#[derive(Clone, Copy, Debug, Reflect)]
267#[reflect(Default, Debug, Clone)]
268pub struct CircularSegmentMeshBuilder {
269 pub segment: CircularSegment,
271 #[doc(alias = "vertices")]
274 pub resolution: u32,
275 pub uv_mode: CircularMeshUvMode,
277}
278
279impl Default for CircularSegmentMeshBuilder {
280 fn default() -> Self {
281 Self {
282 segment: CircularSegment::default(),
283 resolution: 32,
284 uv_mode: CircularMeshUvMode::default(),
285 }
286 }
287}
288
289impl CircularSegmentMeshBuilder {
290 #[inline]
292 pub fn new(segment: CircularSegment) -> Self {
293 Self {
294 segment,
295 ..Self::default()
296 }
297 }
298
299 #[inline]
301 #[doc(alias = "vertices")]
302 pub const fn resolution(mut self, resolution: u32) -> Self {
303 self.resolution = resolution;
304 self
305 }
306
307 #[inline]
309 pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
310 self.uv_mode = uv_mode;
311 self
312 }
313}
314
315impl MeshBuilder for CircularSegmentMeshBuilder {
316 fn build(&self) -> Mesh {
317 let resolution = self.resolution as usize;
318 let mut indices = Vec::with_capacity((resolution - 1) * 3);
319 let mut positions = Vec::with_capacity(resolution + 1);
320 let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
321 let mut uvs = Vec::with_capacity(resolution + 1);
322
323 let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
324
325 let midpoint_vertex = self.segment.chord_midpoint();
327 positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
328 let midpoint_uv = Vec2::from_angle(-uv_angle - FRAC_PI_2).mul_add(
333 Vec2::splat(0.5 * (self.segment.apothem() / self.segment.radius())),
334 Vec2::splat(0.5),
335 );
336 uvs.push([midpoint_uv.x, midpoint_uv.y]);
337
338 let first_angle = FRAC_PI_2 - self.segment.half_angle();
339 let last_angle = FRAC_PI_2 + self.segment.half_angle();
340 let last_i = (self.resolution - 1) as f32;
341 for i in 0..self.resolution {
342 let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
343
344 let vertex = self.segment.radius() * Vec2::from_angle(angle);
346 let uv =
349 Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
350
351 positions.push([vertex.x, vertex.y, 0.0]);
352 uvs.push([uv.x, uv.y]);
353 }
354
355 for i in 1..self.resolution {
356 indices.extend_from_slice(&[0, i, i + 1]);
358 }
359
360 Mesh::new(
361 PrimitiveTopology::TriangleList,
362 RenderAssetUsages::default(),
363 )
364 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
365 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
366 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
367 .with_inserted_indices(Indices::U32(indices))
368 }
369}
370
371impl Extrudable for CircularSegmentMeshBuilder {
372 fn perimeter(&self) -> Vec<PerimeterSegment> {
373 let (sin, cos) = ops::sin_cos(self.segment.arc.half_angle);
374 let first_normal = Vec2::new(sin, cos);
375 let last_normal = Vec2::new(-sin, cos);
376 vec![
377 PerimeterSegment::Flat {
378 indices: vec![self.resolution, 0, 1],
379 },
380 PerimeterSegment::Smooth {
381 first_normal,
382 last_normal,
383 indices: (1..=self.resolution).collect(),
384 },
385 ]
386 }
387}
388
389impl Meshable for CircularSegment {
390 type Output = CircularSegmentMeshBuilder;
391
392 fn mesh(&self) -> Self::Output {
393 CircularSegmentMeshBuilder {
394 segment: *self,
395 ..Default::default()
396 }
397 }
398}
399
400impl From<CircularSegment> for Mesh {
401 fn from(segment: CircularSegment) -> Self {
405 segment.mesh().build()
406 }
407}
408
409#[derive(Clone, Debug, Reflect)]
414#[reflect(Debug, Clone)]
415pub struct ConvexPolygonMeshBuilder {
416 pub vertices: Vec<Vec2>,
417}
418
419impl Meshable for ConvexPolygon {
420 type Output = ConvexPolygonMeshBuilder;
421
422 fn mesh(&self) -> Self::Output {
423 Self::Output {
424 vertices: self.vertices().to_vec(),
425 }
426 }
427}
428
429impl MeshBuilder for ConvexPolygonMeshBuilder {
430 fn build(&self) -> Mesh {
431 let len = self.vertices.len();
432 let mut indices = Vec::with_capacity((len - 2) * 3);
433 let mut positions = Vec::with_capacity(len);
434
435 for vertex in &self.vertices {
436 positions.push([vertex.x, vertex.y, 0.0]);
437 }
438 for i in 2..len as u32 {
439 indices.extend_from_slice(&[0, i - 1, i]);
440 }
441 Mesh::new(
442 PrimitiveTopology::TriangleList,
443 RenderAssetUsages::default(),
444 )
445 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
446 .with_inserted_indices(Indices::U32(indices))
447 }
448}
449
450impl Extrudable for ConvexPolygonMeshBuilder {
451 fn perimeter(&self) -> Vec<PerimeterSegment> {
452 vec![PerimeterSegment::Flat {
453 indices: (0..self.vertices.len() as u32).chain([0]).collect(),
454 }]
455 }
456}
457
458impl From<ConvexPolygon> for Mesh {
459 fn from(polygon: ConvexPolygon) -> Self {
460 polygon.mesh().build()
461 }
462}
463
464#[derive(Clone, Copy, Debug, Reflect)]
466#[reflect(Default, Debug, Clone)]
467pub struct RegularPolygonMeshBuilder {
468 circumradius: f32,
469 sides: u32,
470}
471
472impl Default for RegularPolygonMeshBuilder {
473 fn default() -> Self {
475 Self {
476 circumradius: 0.5,
477 sides: 6,
478 }
479 }
480}
481
482impl RegularPolygonMeshBuilder {
483 pub const fn new(circumradius: f32, sides: u32) -> Self {
490 debug_assert!(
491 circumradius.is_sign_positive(),
492 "polygon has a negative radius"
493 );
494 debug_assert!(sides > 2, "polygon has less than 3 sides");
495
496 Self {
497 circumradius,
498 sides,
499 }
500 }
501}
502
503impl Meshable for RegularPolygon {
504 type Output = RegularPolygonMeshBuilder;
505
506 fn mesh(&self) -> Self::Output {
507 Self::Output {
508 circumradius: self.circumcircle.radius,
509 sides: self.sides,
510 }
511 }
512}
513
514impl MeshBuilder for RegularPolygonMeshBuilder {
515 fn build(&self) -> Mesh {
516 Ellipse::new(self.circumradius, self.circumradius)
518 .mesh()
519 .resolution(self.sides)
520 .build()
521 }
522}
523
524impl Extrudable for RegularPolygonMeshBuilder {
525 fn perimeter(&self) -> Vec<PerimeterSegment> {
526 vec![PerimeterSegment::Flat {
527 indices: (0..self.sides).chain([0]).collect(),
528 }]
529 }
530}
531
532impl From<RegularPolygon> for Mesh {
533 fn from(polygon: RegularPolygon) -> Self {
534 polygon.mesh().build()
535 }
536}
537
538#[derive(Clone, Copy, Debug, Reflect)]
540#[reflect(Default, Debug, Clone)]
541pub struct EllipseMeshBuilder {
542 pub ellipse: Ellipse,
544 #[doc(alias = "vertices")]
547 pub resolution: u32,
548}
549
550impl Default for EllipseMeshBuilder {
551 fn default() -> Self {
552 Self {
553 ellipse: Ellipse::default(),
554 resolution: 32,
555 }
556 }
557}
558
559impl EllipseMeshBuilder {
560 #[inline]
562 pub const fn new(half_width: f32, half_height: f32, resolution: u32) -> Self {
563 Self {
564 ellipse: Ellipse::new(half_width, half_height),
565 resolution,
566 }
567 }
568
569 #[inline]
571 #[doc(alias = "vertices")]
572 pub const fn resolution(mut self, resolution: u32) -> Self {
573 self.resolution = resolution;
574 self
575 }
576}
577
578impl MeshBuilder for EllipseMeshBuilder {
579 fn build(&self) -> Mesh {
580 let resolution = self.resolution as usize;
581 let mut indices = Vec::with_capacity((resolution - 2) * 3);
582 let mut positions = Vec::with_capacity(resolution);
583 let normals = vec![[0.0, 0.0, 1.0]; resolution];
584 let mut uvs = Vec::with_capacity(resolution);
585
586 let start_angle = FRAC_PI_2;
588 let step = core::f32::consts::TAU / self.resolution as f32;
589
590 for i in 0..self.resolution {
591 let theta = start_angle + i as f32 * step;
593 let (sin, cos) = ops::sin_cos(theta);
594 let x = cos * self.ellipse.half_size.x;
595 let y = sin * self.ellipse.half_size.y;
596
597 positions.push([x, y, 0.0]);
598 uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
599 }
600
601 for i in 1..(self.resolution - 1) {
602 indices.extend_from_slice(&[0, i, i + 1]);
603 }
604
605 Mesh::new(
606 PrimitiveTopology::TriangleList,
607 RenderAssetUsages::default(),
608 )
609 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
610 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
611 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
612 .with_inserted_indices(Indices::U32(indices))
613 }
614}
615
616impl Extrudable for EllipseMeshBuilder {
617 fn perimeter(&self) -> Vec<PerimeterSegment> {
618 vec![PerimeterSegment::Smooth {
619 first_normal: Vec2::Y,
620 last_normal: Vec2::Y,
621 indices: (0..self.resolution).chain([0]).collect(),
622 }]
623 }
624}
625
626impl Meshable for Ellipse {
627 type Output = EllipseMeshBuilder;
628
629 fn mesh(&self) -> Self::Output {
630 EllipseMeshBuilder {
631 ellipse: *self,
632 ..Default::default()
633 }
634 }
635}
636
637impl From<Ellipse> for Mesh {
638 fn from(ellipse: Ellipse) -> Self {
639 ellipse.mesh().build()
640 }
641}
642
643pub struct Segment2dMeshBuilder {
645 pub segment: Segment2d,
647}
648
649impl Segment2dMeshBuilder {
650 #[inline]
652 pub const fn new(line: Segment2d) -> Self {
653 Self { segment: line }
654 }
655}
656
657impl MeshBuilder for Segment2dMeshBuilder {
658 fn build(&self) -> Mesh {
659 let positions = self.segment.vertices.map(|v| v.extend(0.0)).to_vec();
660 let indices = Indices::U32(vec![0, 1]);
661
662 Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
663 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
664 .with_inserted_indices(indices)
665 }
666}
667
668impl Meshable for Segment2d {
669 type Output = Segment2dMeshBuilder;
670
671 fn mesh(&self) -> Self::Output {
672 Segment2dMeshBuilder::new(*self)
673 }
674}
675
676impl From<Segment2d> for Mesh {
677 fn from(segment: Segment2d) -> Self {
679 segment.mesh().build()
680 }
681}
682
683#[derive(Clone, Debug, Default, Reflect)]
685#[reflect(Default, Debug, Clone)]
686pub struct Polyline2dMeshBuilder {
687 polyline: Polyline2d,
688}
689
690impl MeshBuilder for Polyline2dMeshBuilder {
691 fn build(&self) -> Mesh {
692 let positions: Vec<_> = self
693 .polyline
694 .vertices
695 .iter()
696 .map(|v| v.extend(0.0))
697 .collect();
698
699 let indices = Indices::U32(
700 (0..self.polyline.vertices.len() as u32 - 1)
701 .flat_map(|i| [i, i + 1])
702 .collect(),
703 );
704
705 Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
706 .with_inserted_indices(indices)
707 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
708 }
709}
710
711impl Meshable for Polyline2d {
712 type Output = Polyline2dMeshBuilder;
713
714 fn mesh(&self) -> Self::Output {
715 Polyline2dMeshBuilder {
716 polyline: self.clone(),
717 }
718 }
719}
720
721impl From<Polyline2d> for Mesh {
722 fn from(polyline: Polyline2d) -> Self {
723 polyline.mesh().build()
724 }
725}
726
727#[derive(Clone, Copy, Debug, Reflect)]
729#[reflect(Default, Debug, Clone)]
730pub struct AnnulusMeshBuilder {
731 pub annulus: Annulus,
733
734 pub resolution: u32,
737}
738
739impl Default for AnnulusMeshBuilder {
740 fn default() -> Self {
741 Self {
742 annulus: Annulus::default(),
743 resolution: 32,
744 }
745 }
746}
747
748impl AnnulusMeshBuilder {
749 #[inline]
751 pub fn new(inner_radius: f32, outer_radius: f32, resolution: u32) -> Self {
752 Self {
753 annulus: Annulus::new(inner_radius, outer_radius),
754 resolution,
755 }
756 }
757
758 #[inline]
760 pub fn resolution(mut self, resolution: u32) -> Self {
761 self.resolution = resolution;
762 self
763 }
764}
765
766impl MeshBuilder for AnnulusMeshBuilder {
767 fn build(&self) -> Mesh {
768 let inner_radius = self.annulus.inner_circle.radius;
769 let outer_radius = self.annulus.outer_circle.radius;
770
771 let num_vertices = (self.resolution as usize + 1) * 2;
772 let mut indices = Vec::with_capacity(self.resolution as usize * 6);
773 let mut positions = Vec::with_capacity(num_vertices);
774 let mut uvs = Vec::with_capacity(num_vertices);
775 let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
776
777 let start_angle = FRAC_PI_2;
782 let step = core::f32::consts::TAU / self.resolution as f32;
783 for i in 0..=self.resolution {
784 let theta = start_angle + (i % self.resolution) as f32 * step;
785 let (sin, cos) = ops::sin_cos(theta);
786 let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
787 let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
788 positions.push(inner_pos);
789 positions.push(outer_pos);
790
791 let inner_uv = [0., i as f32 / self.resolution as f32];
796 let outer_uv = [1., i as f32 / self.resolution as f32];
797 uvs.push(inner_uv);
798 uvs.push(outer_uv);
799 }
800
801 for i in 0..self.resolution {
806 let inner_vertex = 2 * i;
807 let outer_vertex = 2 * i + 1;
808 let next_inner = inner_vertex + 2;
809 let next_outer = outer_vertex + 2;
810 indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
811 indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
812 }
813
814 Mesh::new(
815 PrimitiveTopology::TriangleList,
816 RenderAssetUsages::default(),
817 )
818 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
819 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
820 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
821 .with_inserted_indices(Indices::U32(indices))
822 }
823}
824
825impl Extrudable for AnnulusMeshBuilder {
826 fn perimeter(&self) -> Vec<PerimeterSegment> {
827 let vert_count = 2 * self.resolution;
828 vec![
829 PerimeterSegment::Smooth {
830 first_normal: Vec2::NEG_Y,
831 last_normal: Vec2::NEG_Y,
832 indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), },
834 PerimeterSegment::Smooth {
835 first_normal: Vec2::Y,
836 last_normal: Vec2::Y,
837 indices: (1..vert_count).step_by(2).chain([1]).collect(), },
839 ]
840 }
841}
842
843impl Meshable for Annulus {
844 type Output = AnnulusMeshBuilder;
845
846 fn mesh(&self) -> Self::Output {
847 AnnulusMeshBuilder {
848 annulus: *self,
849 ..Default::default()
850 }
851 }
852}
853
854impl From<Annulus> for Mesh {
855 fn from(annulus: Annulus) -> Self {
856 annulus.mesh().build()
857 }
858}
859
860#[derive(Clone, Copy, Debug, Reflect)]
862#[reflect(Default, Debug, Clone)]
863pub struct RhombusMeshBuilder {
864 half_diagonals: Vec2,
865}
866
867impl Default for RhombusMeshBuilder {
868 fn default() -> Self {
870 Self {
871 half_diagonals: Vec2::splat(0.5),
872 }
873 }
874}
875
876impl RhombusMeshBuilder {
877 pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
883 debug_assert!(
884 horizontal_diagonal >= 0.0,
885 "rhombus has a negative horizontal size",
886 );
887 debug_assert!(
888 vertical_diagonal >= 0.0,
889 "rhombus has a negative vertical size"
890 );
891
892 Self {
893 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
894 }
895 }
896}
897
898impl MeshBuilder for RhombusMeshBuilder {
899 fn build(&self) -> Mesh {
900 let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
901 let positions = vec![
902 [hhd, 0.0, 0.0],
903 [0.0, vhd, 0.0],
904 [-hhd, 0.0, 0.0],
905 [0.0, -vhd, 0.0],
906 ];
907 let normals = vec![[0.0, 0.0, 1.0]; 4];
908 let uvs = vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]];
909 let indices = Indices::U32(vec![2, 0, 1, 2, 3, 0]);
910
911 Mesh::new(
912 PrimitiveTopology::TriangleList,
913 RenderAssetUsages::default(),
914 )
915 .with_inserted_indices(indices)
916 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
917 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
918 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
919 }
920}
921
922impl Extrudable for RhombusMeshBuilder {
923 fn perimeter(&self) -> Vec<PerimeterSegment> {
924 vec![PerimeterSegment::Flat {
925 indices: vec![0, 1, 2, 3, 0],
926 }]
927 }
928}
929
930impl Meshable for Rhombus {
931 type Output = RhombusMeshBuilder;
932
933 fn mesh(&self) -> Self::Output {
934 Self::Output {
935 half_diagonals: self.half_diagonals,
936 }
937 }
938}
939
940impl From<Rhombus> for Mesh {
941 fn from(rhombus: Rhombus) -> Self {
942 rhombus.mesh().build()
943 }
944}
945
946#[derive(Clone, Copy, Debug, Default, Reflect)]
948#[reflect(Default, Debug, Clone)]
949pub struct Triangle2dMeshBuilder {
950 triangle: Triangle2d,
951}
952
953impl Triangle2dMeshBuilder {
954 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
956 Self {
957 triangle: Triangle2d::new(a, b, c),
958 }
959 }
960}
961
962impl Meshable for Triangle2d {
963 type Output = Triangle2dMeshBuilder;
964
965 fn mesh(&self) -> Self::Output {
966 Self::Output { triangle: *self }
967 }
968}
969
970impl MeshBuilder for Triangle2dMeshBuilder {
971 fn build(&self) -> Mesh {
972 let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
973
974 let positions: Vec<_> = vertices_3d.into();
975 let normals = vec![[0.0, 0.0, 1.0]; 3];
976
977 let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
978 vertices_3d[0],
979 vertices_3d[1],
980 vertices_3d[2],
981 ))
982 .into();
983
984 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
985 let indices = if is_ccw {
986 Indices::U32(vec![0, 1, 2])
987 } else {
988 Indices::U32(vec![2, 1, 0])
989 };
990
991 Mesh::new(
992 PrimitiveTopology::TriangleList,
993 RenderAssetUsages::default(),
994 )
995 .with_inserted_indices(indices)
996 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
997 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
998 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
999 }
1000}
1001
1002impl Extrudable for Triangle2dMeshBuilder {
1003 fn perimeter(&self) -> Vec<PerimeterSegment> {
1004 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
1005 if is_ccw {
1006 vec![PerimeterSegment::Flat {
1007 indices: vec![0, 1, 2, 0],
1008 }]
1009 } else {
1010 vec![PerimeterSegment::Flat {
1011 indices: vec![2, 1, 0, 2],
1012 }]
1013 }
1014 }
1015}
1016
1017impl From<Triangle2d> for Mesh {
1018 fn from(triangle: Triangle2d) -> Self {
1019 triangle.mesh().build()
1020 }
1021}
1022
1023#[derive(Clone, Copy, Debug, Reflect)]
1025#[reflect(Default, Debug, Clone)]
1026pub struct RectangleMeshBuilder {
1027 half_size: Vec2,
1028}
1029
1030impl Default for RectangleMeshBuilder {
1031 fn default() -> Self {
1033 Self {
1034 half_size: Vec2::splat(0.5),
1035 }
1036 }
1037}
1038
1039impl RectangleMeshBuilder {
1040 pub const fn new(width: f32, height: f32) -> Self {
1046 debug_assert!(width >= 0.0, "rectangle has a negative width");
1047 debug_assert!(height >= 0.0, "rectangle has a negative height");
1048
1049 Self {
1050 half_size: Vec2::new(width / 2.0, height / 2.0),
1051 }
1052 }
1053}
1054
1055impl MeshBuilder for RectangleMeshBuilder {
1056 fn build(&self) -> Mesh {
1057 let [hw, hh] = [self.half_size.x, self.half_size.y];
1058 let positions = vec![
1059 [hw, hh, 0.0],
1060 [-hw, hh, 0.0],
1061 [-hw, -hh, 0.0],
1062 [hw, -hh, 0.0],
1063 ];
1064 let normals = vec![[0.0, 0.0, 1.0]; 4];
1065 let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
1066 let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
1067
1068 Mesh::new(
1069 PrimitiveTopology::TriangleList,
1070 RenderAssetUsages::default(),
1071 )
1072 .with_inserted_indices(indices)
1073 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1074 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1075 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1076 }
1077}
1078
1079impl Extrudable for RectangleMeshBuilder {
1080 fn perimeter(&self) -> Vec<PerimeterSegment> {
1081 vec![PerimeterSegment::Flat {
1082 indices: vec![0, 1, 2, 3, 0],
1083 }]
1084 }
1085}
1086
1087impl Meshable for Rectangle {
1088 type Output = RectangleMeshBuilder;
1089
1090 fn mesh(&self) -> Self::Output {
1091 RectangleMeshBuilder {
1092 half_size: self.half_size,
1093 }
1094 }
1095}
1096
1097impl From<Rectangle> for Mesh {
1098 fn from(rectangle: Rectangle) -> Self {
1099 rectangle.mesh().build()
1100 }
1101}
1102
1103#[derive(Clone, Copy, Debug, Reflect)]
1105#[reflect(Default, Debug, Clone)]
1106pub struct Capsule2dMeshBuilder {
1107 pub capsule: Capsule2d,
1109 pub resolution: u32,
1114}
1115
1116impl Default for Capsule2dMeshBuilder {
1117 fn default() -> Self {
1118 Self {
1119 capsule: Capsule2d::default(),
1120 resolution: 16,
1121 }
1122 }
1123}
1124
1125impl Capsule2dMeshBuilder {
1126 #[inline]
1129 pub fn new(radius: f32, length: f32, resolution: u32) -> Self {
1130 Self {
1131 capsule: Capsule2d::new(radius, length),
1132 resolution,
1133 }
1134 }
1135
1136 #[inline]
1139 pub const fn resolution(mut self, resolution: u32) -> Self {
1140 self.resolution = resolution;
1141 self
1142 }
1143}
1144
1145impl MeshBuilder for Capsule2dMeshBuilder {
1146 fn build(&self) -> Mesh {
1147 let resolution = self.resolution;
1149 let vertex_count = 2 * resolution;
1150
1151 let mut indices = Vec::with_capacity((resolution as usize - 2) * 2 * 3 + 6);
1153 let mut positions = Vec::with_capacity(vertex_count as usize);
1154 let normals = vec![[0.0, 0.0, 1.0]; vertex_count as usize];
1155 let mut uvs = Vec::with_capacity(vertex_count as usize);
1156
1157 let radius = self.capsule.radius;
1158 let step = core::f32::consts::TAU / vertex_count as f32;
1159
1160 let start_angle = if vertex_count.is_multiple_of(2) {
1163 step / 2.0
1164 } else {
1165 0.0
1166 };
1167
1168 let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
1171
1172 for i in 0..resolution {
1174 let theta = start_angle + i as f32 * step;
1176 let (sin, cos) = ops::sin_cos(theta);
1177 let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
1178
1179 positions.push([x, y, 0.0]);
1180 uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
1181 }
1182
1183 for i in 1..resolution - 1 {
1185 indices.extend_from_slice(&[0, i, i + 1]);
1186 }
1187
1188 indices.extend_from_slice(&[0, resolution - 1, resolution]);
1190
1191 for i in resolution..vertex_count {
1193 let theta = start_angle + i as f32 * step;
1195 let (sin, cos) = ops::sin_cos(theta);
1196 let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
1197
1198 positions.push([x, y, 0.0]);
1199 uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
1200 }
1201
1202 for i in 1..resolution - 1 {
1204 indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
1205 }
1206
1207 indices.extend_from_slice(&[resolution, vertex_count - 1, 0]);
1209
1210 Mesh::new(
1211 PrimitiveTopology::TriangleList,
1212 RenderAssetUsages::default(),
1213 )
1214 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1215 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1216 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1217 .with_inserted_indices(Indices::U32(indices))
1218 }
1219}
1220
1221impl Extrudable for Capsule2dMeshBuilder {
1222 fn perimeter(&self) -> Vec<PerimeterSegment> {
1223 let resolution = self.resolution;
1224 let top_semi_indices = (0..resolution).collect();
1225 let bottom_semi_indices = (resolution..(2 * resolution)).collect();
1226 vec![
1227 PerimeterSegment::Smooth {
1228 first_normal: Vec2::X,
1229 last_normal: Vec2::NEG_X,
1230 indices: top_semi_indices,
1231 }, PerimeterSegment::Flat {
1233 indices: vec![resolution - 1, resolution],
1234 }, PerimeterSegment::Smooth {
1236 first_normal: Vec2::NEG_X,
1237 last_normal: Vec2::X,
1238 indices: bottom_semi_indices,
1239 }, PerimeterSegment::Flat {
1241 indices: vec![2 * resolution - 1, 0],
1242 }, ]
1244 }
1245}
1246
1247impl Meshable for Capsule2d {
1248 type Output = Capsule2dMeshBuilder;
1249
1250 fn mesh(&self) -> Self::Output {
1251 Capsule2dMeshBuilder {
1252 capsule: *self,
1253 ..Default::default()
1254 }
1255 }
1256}
1257
1258impl From<Capsule2d> for Mesh {
1259 fn from(capsule: Capsule2d) -> Self {
1260 capsule.mesh().build()
1261 }
1262}
1263
1264pub struct RingMeshBuilder<P>
1266where
1267 P: Primitive2d + Meshable,
1268{
1269 pub outer_shape_builder: P::Output,
1270 pub inner_shape_builder: P::Output,
1271}
1272
1273impl<P> RingMeshBuilder<P>
1274where
1275 P: Primitive2d + Meshable,
1276{
1277 pub fn new(ring: &Ring<P>) -> Self {
1279 Self {
1280 outer_shape_builder: ring.outer_shape.mesh(),
1281 inner_shape_builder: ring.inner_shape.mesh(),
1282 }
1283 }
1284
1285 pub fn with_inner(mut self, func: impl Fn(P::Output) -> P::Output) -> Self {
1287 self.outer_shape_builder = func(self.outer_shape_builder);
1288 self.inner_shape_builder = func(self.inner_shape_builder);
1289 self
1290 }
1291
1292 fn get_vertex_attributes(&self) -> Option<RingMeshBuilderVertexAttributes> {
1293 fn get_positions(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1294 if let VertexAttributeValues::Float32x3(data) =
1295 mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)?
1296 {
1297 Some(data)
1298 } else {
1299 None
1300 }
1301 }
1302
1303 fn get_uvs(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 2]>> {
1304 if let VertexAttributeValues::Float32x2(data) =
1305 mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0)?
1306 {
1307 Some(data)
1308 } else {
1309 None
1310 }
1311 }
1312
1313 fn get_normals(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1314 if let VertexAttributeValues::Float32x3(data) =
1315 mesh.attribute_mut(Mesh::ATTRIBUTE_NORMAL)?
1316 {
1317 Some(data)
1318 } else {
1319 None
1320 }
1321 }
1322
1323 let mut outer = self.outer_shape_builder.build();
1324 let mut inner = self.inner_shape_builder.build();
1325
1326 assert_eq!(
1327 outer.primitive_topology(),
1328 PrimitiveTopology::TriangleList,
1329 "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1330 );
1331 assert_eq!(
1332 inner.primitive_topology(),
1333 PrimitiveTopology::TriangleList,
1334 "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1335 );
1336
1337 Some(RingMeshBuilderVertexAttributes {
1338 outer_positions: mem::take(get_positions(&mut outer)?),
1339 inner_positions: mem::take(get_positions(&mut inner)?),
1340 outer_normals: mem::take(get_normals(&mut outer)?),
1341 inner_normals: mem::take(get_normals(&mut inner)?),
1342 outer_uvs: mem::take(get_uvs(&mut outer)?),
1343 inner_uvs: mem::take(get_uvs(&mut inner)?),
1344 })
1345 }
1346}
1347
1348struct RingMeshBuilderVertexAttributes {
1349 outer_positions: Vec<[f32; 3]>,
1350 inner_positions: Vec<[f32; 3]>,
1351 outer_normals: Vec<[f32; 3]>,
1352 inner_normals: Vec<[f32; 3]>,
1353 outer_uvs: Vec<[f32; 2]>,
1354 inner_uvs: Vec<[f32; 2]>,
1355}
1356
1357impl<P> MeshBuilder for RingMeshBuilder<P>
1358where
1359 P: Primitive2d + Meshable,
1360{
1361 fn build(&self) -> Mesh {
1375 if let Some(RingMeshBuilderVertexAttributes {
1376 outer_uvs,
1377 inner_uvs,
1378 outer_positions,
1379 inner_positions,
1380 outer_normals,
1381 inner_normals,
1382 }) = self.get_vertex_attributes()
1383 && outer_uvs.len() == inner_uvs.len()
1384 && outer_positions.len() == inner_positions.len()
1385 && outer_normals.len() == inner_normals.len()
1386 {
1387 let mut uvs = outer_uvs;
1388 let inner_uvs = inner_positions
1389 .iter()
1390 .zip(&outer_positions)
1391 .zip(&inner_uvs)
1392 .map(|((inner_position, outer_position), inner_uv)| -> [f32; 2] {
1393 const UV_CENTER: Vec2 = Vec2::splat(0.5);
1394 let ip = Vec3::from(*inner_position).truncate();
1395 let op = Vec3::from(*outer_position).truncate();
1396 let uv = Vec2::from(*inner_uv) - UV_CENTER;
1397 (uv * ip.length() / op.length() + UV_CENTER).into()
1398 });
1399 uvs.extend(inner_uvs);
1400
1401 let mut normals = outer_normals;
1402 normals.extend(inner_normals);
1403
1404 let points = outer_positions.len() as u32;
1405 let mut indices = Vec::with_capacity(outer_positions.len() * 6);
1406 for i in 0..points {
1407 indices.push(i); indices.push(i + 1); indices.push(points + i); indices.push(points + i); indices.push(i + 1); indices.push(points + i + 1); }
1415 let indices_length = indices.len();
1416 if let (_, [_, b, _, _, e, f]) = indices.split_at_mut(indices_length.saturating_sub(6))
1418 {
1419 *b = 0;
1420 *e = 0;
1421 *f = points;
1422 }
1423
1424 let mut positions = outer_positions;
1425 positions.extend_from_slice(&inner_positions);
1426
1427 Mesh::new(
1428 PrimitiveTopology::TriangleList,
1429 RenderAssetUsages::default(),
1430 )
1431 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1432 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1433 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1434 .with_inserted_indices(Indices::U32(indices))
1435 } else {
1436 panic!("The inner and outer meshes should have the same number of vertices, and have required attributes");
1437 }
1438 }
1439}
1440
1441impl<P> Extrudable for RingMeshBuilder<P>
1442where
1443 P: Primitive2d + Meshable,
1444 P::Output: Extrudable,
1445{
1446 fn perimeter(&self) -> Vec<PerimeterSegment> {
1460 let outer_vertex_count = self
1461 .get_vertex_attributes()
1462 .filter(|r| r.outer_positions.len() == r.inner_positions.len())
1463 .expect("The inner and outer meshes should have the same number of vertices, and have required attributes")
1464 .outer_positions
1465 .len();
1466
1467 let mut outer_perimeter = self.outer_shape_builder.perimeter();
1468 let inner_perimeter =
1469 self.inner_shape_builder
1470 .perimeter()
1471 .into_iter()
1472 .rev()
1473 .map(|segment| match segment {
1474 PerimeterSegment::Smooth {
1475 first_normal,
1476 last_normal,
1477 mut indices,
1478 } => PerimeterSegment::Smooth {
1479 first_normal: -last_normal,
1480 last_normal: -first_normal,
1481 indices: {
1482 let outer_perimeter_vertex_count = outer_vertex_count as u32;
1483 indices.reverse();
1484 for i in &mut indices {
1485 *i += outer_perimeter_vertex_count;
1486 }
1487 indices
1488 },
1489 },
1490 PerimeterSegment::Flat { mut indices } => PerimeterSegment::Flat {
1491 indices: {
1492 let outer_perimeter_vertex_count = outer_vertex_count as u32;
1493 indices.reverse();
1494 for i in &mut indices {
1495 *i += outer_perimeter_vertex_count;
1496 }
1497 indices
1498 },
1499 },
1500 });
1501
1502 outer_perimeter.extend(inner_perimeter);
1503 outer_perimeter
1504 }
1505}
1506
1507impl<P> Meshable for Ring<P>
1508where
1509 P: Primitive2d + Meshable,
1510{
1511 type Output = RingMeshBuilder<P>;
1512
1513 fn mesh(&self) -> Self::Output {
1514 RingMeshBuilder::new(self)
1515 }
1516}
1517
1518impl<P> From<Ring<P>> for Mesh
1519where
1520 P: Primitive2d + Meshable,
1521{
1522 fn from(ring: Ring<P>) -> Self {
1523 ring.mesh().build()
1524 }
1525}
1526
1527#[cfg(test)]
1528mod tests {
1529 use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd};
1530 use bevy_platform::collections::HashSet;
1531
1532 use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
1533
1534 fn count_distinct_positions(points: &[[f32; 3]]) -> usize {
1535 let mut map = <HashSet<_>>::default();
1536 for point in points {
1537 map.insert(point.map(FloatOrd));
1538 }
1539 map.len()
1540 }
1541
1542 #[test]
1543 fn test_annulus() {
1544 let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build();
1545
1546 assert_eq!(
1547 32,
1548 count_distinct_positions(
1549 mesh.attribute(Mesh::ATTRIBUTE_POSITION)
1550 .unwrap()
1551 .as_float3()
1552 .unwrap()
1553 )
1554 );
1555 }
1556
1557 fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1560 for point in points.iter_mut() {
1561 for coord in point.iter_mut() {
1562 let round = (*coord * 2.).round() / 2.;
1563 if (*coord - round).abs() < 0.00001 {
1564 *coord = round;
1565 }
1566 }
1567 }
1568 }
1569
1570 #[test]
1571 fn test_regular_polygon() {
1572 let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1573
1574 let Some(VertexAttributeValues::Float32x3(mut positions)) =
1575 mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1576 else {
1577 panic!("Expected positions f32x3");
1578 };
1579 let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1580 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1581 else {
1582 panic!("Expected uvs f32x2");
1583 };
1584 let Some(VertexAttributeValues::Float32x3(normals)) =
1585 mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1586 else {
1587 panic!("Expected normals f32x3");
1588 };
1589
1590 fix_floats(&mut positions);
1591 fix_floats(&mut uvs);
1592
1593 assert_eq!(
1594 [
1595 [0.0, 7.0, 0.0],
1596 [-7.0, 0.0, 0.0],
1597 [0.0, -7.0, 0.0],
1598 [7.0, 0.0, 0.0],
1599 ],
1600 &positions[..]
1601 );
1602
1603 assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1605
1606 assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1607 }
1608}