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 let normals = vec![[0.0, 0.0, 1.0]; len];
435 let mut uvs = Vec::with_capacity(len);
436
437 let mut min = Vec2::splat(f32::INFINITY);
438 let mut max = Vec2::splat(f32::NEG_INFINITY);
439
440 for vertex in &self.vertices {
441 min = min.min(*vertex);
442 max = max.max(*vertex);
443 }
444
445 let size = (max - min).max(Vec2::splat(f32::EPSILON));
446
447 for vertex in &self.vertices {
448 positions.push([vertex.x, vertex.y, 0.0]);
449 let uv = (*vertex - min) / size;
450 uvs.push([uv.x, uv.y]);
452 }
453 for i in 2..len as u32 {
454 indices.extend_from_slice(&[0, i - 1, i]);
455 }
456 Mesh::new(
457 PrimitiveTopology::TriangleList,
458 RenderAssetUsages::default(),
459 )
460 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
461 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
462 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
463 .with_inserted_indices(Indices::U32(indices))
464 }
465}
466
467impl Extrudable for ConvexPolygonMeshBuilder {
468 fn perimeter(&self) -> Vec<PerimeterSegment> {
469 vec![PerimeterSegment::Flat {
470 indices: (0..self.vertices.len() as u32).chain([0]).collect(),
471 }]
472 }
473}
474
475impl From<ConvexPolygon> for Mesh {
476 fn from(polygon: ConvexPolygon) -> Self {
477 polygon.mesh().build()
478 }
479}
480
481#[derive(Clone, Copy, Debug, Reflect)]
483#[reflect(Default, Debug, Clone)]
484pub struct RegularPolygonMeshBuilder {
485 circumradius: f32,
486 sides: u32,
487}
488
489impl Default for RegularPolygonMeshBuilder {
490 fn default() -> Self {
492 Self {
493 circumradius: 0.5,
494 sides: 6,
495 }
496 }
497}
498
499impl RegularPolygonMeshBuilder {
500 pub const fn new(circumradius: f32, sides: u32) -> Self {
507 debug_assert!(
508 circumradius.is_sign_positive(),
509 "polygon has a negative radius"
510 );
511 debug_assert!(sides > 2, "polygon has less than 3 sides");
512
513 Self {
514 circumradius,
515 sides,
516 }
517 }
518}
519
520impl Meshable for RegularPolygon {
521 type Output = RegularPolygonMeshBuilder;
522
523 fn mesh(&self) -> Self::Output {
524 Self::Output {
525 circumradius: self.circumcircle.radius,
526 sides: self.sides,
527 }
528 }
529}
530
531impl MeshBuilder for RegularPolygonMeshBuilder {
532 fn build(&self) -> Mesh {
533 Ellipse::new(self.circumradius, self.circumradius)
535 .mesh()
536 .resolution(self.sides)
537 .build()
538 }
539}
540
541impl Extrudable for RegularPolygonMeshBuilder {
542 fn perimeter(&self) -> Vec<PerimeterSegment> {
543 vec![PerimeterSegment::Flat {
544 indices: (0..self.sides).chain([0]).collect(),
545 }]
546 }
547}
548
549impl From<RegularPolygon> for Mesh {
550 fn from(polygon: RegularPolygon) -> Self {
551 polygon.mesh().build()
552 }
553}
554
555#[derive(Clone, Copy, Debug, Reflect)]
557#[reflect(Default, Debug, Clone)]
558pub struct EllipseMeshBuilder {
559 pub ellipse: Ellipse,
561 #[doc(alias = "vertices")]
564 pub resolution: u32,
565}
566
567impl Default for EllipseMeshBuilder {
568 fn default() -> Self {
569 Self {
570 ellipse: Ellipse::default(),
571 resolution: 32,
572 }
573 }
574}
575
576impl EllipseMeshBuilder {
577 #[inline]
579 pub const fn new(half_width: f32, half_height: f32, resolution: u32) -> Self {
580 Self {
581 ellipse: Ellipse::new(half_width, half_height),
582 resolution,
583 }
584 }
585
586 #[inline]
588 #[doc(alias = "vertices")]
589 pub const fn resolution(mut self, resolution: u32) -> Self {
590 self.resolution = resolution;
591 self
592 }
593}
594
595impl MeshBuilder for EllipseMeshBuilder {
596 fn build(&self) -> Mesh {
597 let resolution = self.resolution as usize;
598 let mut indices = Vec::with_capacity((resolution - 2) * 3);
599 let mut positions = Vec::with_capacity(resolution);
600 let normals = vec![[0.0, 0.0, 1.0]; resolution];
601 let mut uvs = Vec::with_capacity(resolution);
602
603 let start_angle = FRAC_PI_2;
605 let step = core::f32::consts::TAU / self.resolution as f32;
606
607 for i in 0..self.resolution {
608 let theta = start_angle + i as f32 * step;
610 let (sin, cos) = ops::sin_cos(theta);
611 let x = cos * self.ellipse.half_size.x;
612 let y = sin * self.ellipse.half_size.y;
613
614 positions.push([x, y, 0.0]);
615 uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
616 }
617
618 for i in 1..(self.resolution - 1) {
619 indices.extend_from_slice(&[0, i, i + 1]);
620 }
621
622 Mesh::new(
623 PrimitiveTopology::TriangleList,
624 RenderAssetUsages::default(),
625 )
626 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
627 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
628 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
629 .with_inserted_indices(Indices::U32(indices))
630 }
631}
632
633impl Extrudable for EllipseMeshBuilder {
634 fn perimeter(&self) -> Vec<PerimeterSegment> {
635 vec![PerimeterSegment::Smooth {
636 first_normal: Vec2::Y,
637 last_normal: Vec2::Y,
638 indices: (0..self.resolution).chain([0]).collect(),
639 }]
640 }
641}
642
643impl Meshable for Ellipse {
644 type Output = EllipseMeshBuilder;
645
646 fn mesh(&self) -> Self::Output {
647 EllipseMeshBuilder {
648 ellipse: *self,
649 ..Default::default()
650 }
651 }
652}
653
654impl From<Ellipse> for Mesh {
655 fn from(ellipse: Ellipse) -> Self {
656 ellipse.mesh().build()
657 }
658}
659
660pub struct Segment2dMeshBuilder {
662 pub segment: Segment2d,
664}
665
666impl Segment2dMeshBuilder {
667 #[inline]
669 pub const fn new(line: Segment2d) -> Self {
670 Self { segment: line }
671 }
672}
673
674impl MeshBuilder for Segment2dMeshBuilder {
675 fn build(&self) -> Mesh {
676 let positions = self.segment.vertices.map(|v| v.extend(0.0)).to_vec();
677 let indices = Indices::U32(vec![0, 1]);
678
679 Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
680 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
681 .with_inserted_indices(indices)
682 }
683}
684
685impl Meshable for Segment2d {
686 type Output = Segment2dMeshBuilder;
687
688 fn mesh(&self) -> Self::Output {
689 Segment2dMeshBuilder::new(*self)
690 }
691}
692
693impl From<Segment2d> for Mesh {
694 fn from(segment: Segment2d) -> Self {
696 segment.mesh().build()
697 }
698}
699
700#[derive(Clone, Debug, Default, Reflect)]
702#[reflect(Default, Debug, Clone)]
703pub struct Polyline2dMeshBuilder {
704 polyline: Polyline2d,
705}
706
707impl MeshBuilder for Polyline2dMeshBuilder {
708 fn build(&self) -> Mesh {
709 let positions: Vec<_> = self
710 .polyline
711 .vertices
712 .iter()
713 .map(|v| v.extend(0.0))
714 .collect();
715
716 let indices = Indices::U32(
717 (0..self.polyline.vertices.len() as u32 - 1)
718 .flat_map(|i| [i, i + 1])
719 .collect(),
720 );
721
722 Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
723 .with_inserted_indices(indices)
724 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
725 }
726}
727
728impl Meshable for Polyline2d {
729 type Output = Polyline2dMeshBuilder;
730
731 fn mesh(&self) -> Self::Output {
732 Polyline2dMeshBuilder {
733 polyline: self.clone(),
734 }
735 }
736}
737
738impl From<Polyline2d> for Mesh {
739 fn from(polyline: Polyline2d) -> Self {
740 polyline.mesh().build()
741 }
742}
743
744#[derive(Clone, Copy, Debug, Reflect)]
746#[reflect(Default, Debug, Clone)]
747pub struct AnnulusMeshBuilder {
748 pub annulus: Annulus,
750
751 pub resolution: u32,
754}
755
756impl Default for AnnulusMeshBuilder {
757 fn default() -> Self {
758 Self {
759 annulus: Annulus::default(),
760 resolution: 32,
761 }
762 }
763}
764
765impl AnnulusMeshBuilder {
766 #[inline]
768 pub fn new(inner_radius: f32, outer_radius: f32, resolution: u32) -> Self {
769 Self {
770 annulus: Annulus::new(inner_radius, outer_radius),
771 resolution,
772 }
773 }
774
775 #[inline]
777 pub fn resolution(mut self, resolution: u32) -> Self {
778 self.resolution = resolution;
779 self
780 }
781}
782
783impl MeshBuilder for AnnulusMeshBuilder {
784 fn build(&self) -> Mesh {
785 let inner_radius = self.annulus.inner_circle.radius;
786 let outer_radius = self.annulus.outer_circle.radius;
787
788 let num_vertices = (self.resolution as usize + 1) * 2;
789 let mut indices = Vec::with_capacity(self.resolution as usize * 6);
790 let mut positions = Vec::with_capacity(num_vertices);
791 let mut uvs = Vec::with_capacity(num_vertices);
792 let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
793
794 let start_angle = FRAC_PI_2;
799 let step = core::f32::consts::TAU / self.resolution as f32;
800 for i in 0..=self.resolution {
801 let theta = start_angle + (i % self.resolution) as f32 * step;
802 let (sin, cos) = ops::sin_cos(theta);
803 let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
804 let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
805 positions.push(inner_pos);
806 positions.push(outer_pos);
807
808 let inner_uv = [0., i as f32 / self.resolution as f32];
813 let outer_uv = [1., i as f32 / self.resolution as f32];
814 uvs.push(inner_uv);
815 uvs.push(outer_uv);
816 }
817
818 for i in 0..self.resolution {
823 let inner_vertex = 2 * i;
824 let outer_vertex = 2 * i + 1;
825 let next_inner = inner_vertex + 2;
826 let next_outer = outer_vertex + 2;
827 indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
828 indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
829 }
830
831 Mesh::new(
832 PrimitiveTopology::TriangleList,
833 RenderAssetUsages::default(),
834 )
835 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
836 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
837 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
838 .with_inserted_indices(Indices::U32(indices))
839 }
840}
841
842impl Extrudable for AnnulusMeshBuilder {
843 fn perimeter(&self) -> Vec<PerimeterSegment> {
844 let vert_count = 2 * self.resolution;
845 vec![
846 PerimeterSegment::Smooth {
847 first_normal: Vec2::NEG_Y,
848 last_normal: Vec2::NEG_Y,
849 indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), },
851 PerimeterSegment::Smooth {
852 first_normal: Vec2::Y,
853 last_normal: Vec2::Y,
854 indices: (1..vert_count).step_by(2).chain([1]).collect(), },
856 ]
857 }
858}
859
860impl Meshable for Annulus {
861 type Output = AnnulusMeshBuilder;
862
863 fn mesh(&self) -> Self::Output {
864 AnnulusMeshBuilder {
865 annulus: *self,
866 ..Default::default()
867 }
868 }
869}
870
871impl From<Annulus> for Mesh {
872 fn from(annulus: Annulus) -> Self {
873 annulus.mesh().build()
874 }
875}
876
877#[derive(Clone, Copy, Debug, Reflect)]
879#[reflect(Default, Debug, Clone)]
880pub struct RhombusMeshBuilder {
881 half_diagonals: Vec2,
882}
883
884impl Default for RhombusMeshBuilder {
885 fn default() -> Self {
887 Self {
888 half_diagonals: Vec2::splat(0.5),
889 }
890 }
891}
892
893impl RhombusMeshBuilder {
894 pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
900 debug_assert!(
901 horizontal_diagonal >= 0.0,
902 "rhombus has a negative horizontal size",
903 );
904 debug_assert!(
905 vertical_diagonal >= 0.0,
906 "rhombus has a negative vertical size"
907 );
908
909 Self {
910 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
911 }
912 }
913}
914
915impl MeshBuilder for RhombusMeshBuilder {
916 fn build(&self) -> Mesh {
917 let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
918 let positions = vec![
919 [hhd, 0.0, 0.0],
920 [0.0, vhd, 0.0],
921 [-hhd, 0.0, 0.0],
922 [0.0, -vhd, 0.0],
923 ];
924 let normals = vec![[0.0, 0.0, 1.0]; 4];
925 let uvs = vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]];
926 let indices = Indices::U32(vec![2, 0, 1, 2, 3, 0]);
927
928 Mesh::new(
929 PrimitiveTopology::TriangleList,
930 RenderAssetUsages::default(),
931 )
932 .with_inserted_indices(indices)
933 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
934 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
935 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
936 }
937}
938
939impl Extrudable for RhombusMeshBuilder {
940 fn perimeter(&self) -> Vec<PerimeterSegment> {
941 vec![PerimeterSegment::Flat {
942 indices: vec![0, 1, 2, 3, 0],
943 }]
944 }
945}
946
947impl Meshable for Rhombus {
948 type Output = RhombusMeshBuilder;
949
950 fn mesh(&self) -> Self::Output {
951 Self::Output {
952 half_diagonals: self.half_diagonals,
953 }
954 }
955}
956
957impl From<Rhombus> for Mesh {
958 fn from(rhombus: Rhombus) -> Self {
959 rhombus.mesh().build()
960 }
961}
962
963#[derive(Clone, Copy, Debug, Default, Reflect)]
965#[reflect(Default, Debug, Clone)]
966pub struct Triangle2dMeshBuilder {
967 triangle: Triangle2d,
968}
969
970impl Triangle2dMeshBuilder {
971 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
973 Self {
974 triangle: Triangle2d::new(a, b, c),
975 }
976 }
977}
978
979impl Meshable for Triangle2d {
980 type Output = Triangle2dMeshBuilder;
981
982 fn mesh(&self) -> Self::Output {
983 Self::Output { triangle: *self }
984 }
985}
986
987impl MeshBuilder for Triangle2dMeshBuilder {
988 fn build(&self) -> Mesh {
989 let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
990
991 let positions: Vec<_> = vertices_3d.into();
992 let normals = vec![[0.0, 0.0, 1.0]; 3];
993
994 let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
995 vertices_3d[0],
996 vertices_3d[1],
997 vertices_3d[2],
998 ))
999 .into();
1000
1001 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
1002 let indices = if is_ccw {
1003 Indices::U32(vec![0, 1, 2])
1004 } else {
1005 Indices::U32(vec![2, 1, 0])
1006 };
1007
1008 Mesh::new(
1009 PrimitiveTopology::TriangleList,
1010 RenderAssetUsages::default(),
1011 )
1012 .with_inserted_indices(indices)
1013 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1014 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1015 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1016 }
1017}
1018
1019impl Extrudable for Triangle2dMeshBuilder {
1020 fn perimeter(&self) -> Vec<PerimeterSegment> {
1021 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
1022 if is_ccw {
1023 vec![PerimeterSegment::Flat {
1024 indices: vec![0, 1, 2, 0],
1025 }]
1026 } else {
1027 vec![PerimeterSegment::Flat {
1028 indices: vec![2, 1, 0, 2],
1029 }]
1030 }
1031 }
1032}
1033
1034impl From<Triangle2d> for Mesh {
1035 fn from(triangle: Triangle2d) -> Self {
1036 triangle.mesh().build()
1037 }
1038}
1039
1040#[derive(Clone, Copy, Debug, Reflect)]
1042#[reflect(Default, Debug, Clone)]
1043pub struct RectangleMeshBuilder {
1044 half_size: Vec2,
1045}
1046
1047impl Default for RectangleMeshBuilder {
1048 fn default() -> Self {
1050 Self {
1051 half_size: Vec2::splat(0.5),
1052 }
1053 }
1054}
1055
1056impl RectangleMeshBuilder {
1057 pub const fn new(width: f32, height: f32) -> Self {
1063 debug_assert!(width >= 0.0, "rectangle has a negative width");
1064 debug_assert!(height >= 0.0, "rectangle has a negative height");
1065
1066 Self {
1067 half_size: Vec2::new(width / 2.0, height / 2.0),
1068 }
1069 }
1070}
1071
1072impl MeshBuilder for RectangleMeshBuilder {
1073 fn build(&self) -> Mesh {
1074 let [hw, hh] = [self.half_size.x, self.half_size.y];
1075 let positions = vec![
1076 [hw, hh, 0.0],
1077 [-hw, hh, 0.0],
1078 [-hw, -hh, 0.0],
1079 [hw, -hh, 0.0],
1080 ];
1081 let normals = vec![[0.0, 0.0, 1.0]; 4];
1082 let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
1083 let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
1084
1085 Mesh::new(
1086 PrimitiveTopology::TriangleList,
1087 RenderAssetUsages::default(),
1088 )
1089 .with_inserted_indices(indices)
1090 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1091 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1092 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1093 }
1094}
1095
1096impl Extrudable for RectangleMeshBuilder {
1097 fn perimeter(&self) -> Vec<PerimeterSegment> {
1098 vec![PerimeterSegment::Flat {
1099 indices: vec![0, 1, 2, 3, 0],
1100 }]
1101 }
1102}
1103
1104impl Meshable for Rectangle {
1105 type Output = RectangleMeshBuilder;
1106
1107 fn mesh(&self) -> Self::Output {
1108 RectangleMeshBuilder {
1109 half_size: self.half_size,
1110 }
1111 }
1112}
1113
1114impl From<Rectangle> for Mesh {
1115 fn from(rectangle: Rectangle) -> Self {
1116 rectangle.mesh().build()
1117 }
1118}
1119
1120#[derive(Clone, Copy, Debug, Reflect)]
1122#[reflect(Default, Debug, Clone)]
1123pub struct Capsule2dMeshBuilder {
1124 pub capsule: Capsule2d,
1126 pub resolution: u32,
1131}
1132
1133impl Default for Capsule2dMeshBuilder {
1134 fn default() -> Self {
1135 Self {
1136 capsule: Capsule2d::default(),
1137 resolution: 16,
1138 }
1139 }
1140}
1141
1142impl Capsule2dMeshBuilder {
1143 #[inline]
1146 pub fn new(radius: f32, length: f32, resolution: u32) -> Self {
1147 Self {
1148 capsule: Capsule2d::new(radius, length),
1149 resolution,
1150 }
1151 }
1152
1153 #[inline]
1156 pub const fn resolution(mut self, resolution: u32) -> Self {
1157 self.resolution = resolution;
1158 self
1159 }
1160}
1161
1162impl MeshBuilder for Capsule2dMeshBuilder {
1163 fn build(&self) -> Mesh {
1164 let resolution = self.resolution;
1166 let vertex_count = 2 * resolution;
1167
1168 let mut indices = Vec::with_capacity((resolution as usize - 2) * 2 * 3 + 6);
1170 let mut positions = Vec::with_capacity(vertex_count as usize);
1171 let normals = vec![[0.0, 0.0, 1.0]; vertex_count as usize];
1172 let mut uvs = Vec::with_capacity(vertex_count as usize);
1173
1174 let radius = self.capsule.radius;
1175 let step = core::f32::consts::TAU / vertex_count as f32;
1176
1177 let start_angle = if vertex_count.is_multiple_of(2) {
1180 step / 2.0
1181 } else {
1182 0.0
1183 };
1184
1185 let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
1188
1189 for i in 0..resolution {
1191 let theta = start_angle + i as f32 * step;
1193 let (sin, cos) = ops::sin_cos(theta);
1194 let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
1195
1196 positions.push([x, y, 0.0]);
1197 uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
1198 }
1199
1200 for i in 1..resolution - 1 {
1202 indices.extend_from_slice(&[0, i, i + 1]);
1203 }
1204
1205 indices.extend_from_slice(&[0, resolution - 1, resolution]);
1207
1208 for i in resolution..vertex_count {
1210 let theta = start_angle + i as f32 * step;
1212 let (sin, cos) = ops::sin_cos(theta);
1213 let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
1214
1215 positions.push([x, y, 0.0]);
1216 uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
1217 }
1218
1219 for i in 1..resolution - 1 {
1221 indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
1222 }
1223
1224 indices.extend_from_slice(&[resolution, vertex_count - 1, 0]);
1226
1227 Mesh::new(
1228 PrimitiveTopology::TriangleList,
1229 RenderAssetUsages::default(),
1230 )
1231 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1232 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1233 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1234 .with_inserted_indices(Indices::U32(indices))
1235 }
1236}
1237
1238impl Extrudable for Capsule2dMeshBuilder {
1239 fn perimeter(&self) -> Vec<PerimeterSegment> {
1240 let resolution = self.resolution;
1241 let top_semi_indices = (0..resolution).collect();
1242 let bottom_semi_indices = (resolution..(2 * resolution)).collect();
1243 vec![
1244 PerimeterSegment::Smooth {
1245 first_normal: Vec2::X,
1246 last_normal: Vec2::NEG_X,
1247 indices: top_semi_indices,
1248 }, PerimeterSegment::Flat {
1250 indices: vec![resolution - 1, resolution],
1251 }, PerimeterSegment::Smooth {
1253 first_normal: Vec2::NEG_X,
1254 last_normal: Vec2::X,
1255 indices: bottom_semi_indices,
1256 }, PerimeterSegment::Flat {
1258 indices: vec![2 * resolution - 1, 0],
1259 }, ]
1261 }
1262}
1263
1264impl Meshable for Capsule2d {
1265 type Output = Capsule2dMeshBuilder;
1266
1267 fn mesh(&self) -> Self::Output {
1268 Capsule2dMeshBuilder {
1269 capsule: *self,
1270 ..Default::default()
1271 }
1272 }
1273}
1274
1275impl From<Capsule2d> for Mesh {
1276 fn from(capsule: Capsule2d) -> Self {
1277 capsule.mesh().build()
1278 }
1279}
1280
1281pub struct RingMeshBuilder<P>
1283where
1284 P: Primitive2d + Meshable,
1285{
1286 pub outer_shape_builder: P::Output,
1287 pub inner_shape_builder: P::Output,
1288}
1289
1290impl<P> RingMeshBuilder<P>
1291where
1292 P: Primitive2d + Meshable,
1293{
1294 pub fn new(ring: &Ring<P>) -> Self {
1296 Self {
1297 outer_shape_builder: ring.outer_shape.mesh(),
1298 inner_shape_builder: ring.inner_shape.mesh(),
1299 }
1300 }
1301
1302 pub fn with_inner(mut self, func: impl Fn(P::Output) -> P::Output) -> Self {
1304 self.outer_shape_builder = func(self.outer_shape_builder);
1305 self.inner_shape_builder = func(self.inner_shape_builder);
1306 self
1307 }
1308
1309 fn get_vertex_attributes(&self) -> Option<RingMeshBuilderVertexAttributes> {
1310 fn get_positions(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1311 if let VertexAttributeValues::Float32x3(data) =
1312 mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)?
1313 {
1314 Some(data)
1315 } else {
1316 None
1317 }
1318 }
1319
1320 fn get_uvs(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 2]>> {
1321 if let VertexAttributeValues::Float32x2(data) =
1322 mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0)?
1323 {
1324 Some(data)
1325 } else {
1326 None
1327 }
1328 }
1329
1330 fn get_normals(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1331 if let VertexAttributeValues::Float32x3(data) =
1332 mesh.attribute_mut(Mesh::ATTRIBUTE_NORMAL)?
1333 {
1334 Some(data)
1335 } else {
1336 None
1337 }
1338 }
1339
1340 let mut outer = self.outer_shape_builder.build();
1341 let mut inner = self.inner_shape_builder.build();
1342
1343 assert_eq!(
1344 outer.primitive_topology(),
1345 PrimitiveTopology::TriangleList,
1346 "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1347 );
1348 assert_eq!(
1349 inner.primitive_topology(),
1350 PrimitiveTopology::TriangleList,
1351 "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1352 );
1353
1354 Some(RingMeshBuilderVertexAttributes {
1355 outer_positions: mem::take(get_positions(&mut outer)?),
1356 inner_positions: mem::take(get_positions(&mut inner)?),
1357 outer_normals: mem::take(get_normals(&mut outer)?),
1358 inner_normals: mem::take(get_normals(&mut inner)?),
1359 outer_uvs: mem::take(get_uvs(&mut outer)?),
1360 inner_uvs: mem::take(get_uvs(&mut inner)?),
1361 })
1362 }
1363}
1364
1365struct RingMeshBuilderVertexAttributes {
1366 outer_positions: Vec<[f32; 3]>,
1367 inner_positions: Vec<[f32; 3]>,
1368 outer_normals: Vec<[f32; 3]>,
1369 inner_normals: Vec<[f32; 3]>,
1370 outer_uvs: Vec<[f32; 2]>,
1371 inner_uvs: Vec<[f32; 2]>,
1372}
1373
1374impl<P> MeshBuilder for RingMeshBuilder<P>
1375where
1376 P: Primitive2d + Meshable,
1377{
1378 fn build(&self) -> Mesh {
1392 if let Some(RingMeshBuilderVertexAttributes {
1393 outer_uvs,
1394 inner_uvs,
1395 outer_positions,
1396 inner_positions,
1397 outer_normals,
1398 inner_normals,
1399 }) = self.get_vertex_attributes()
1400 && outer_uvs.len() == inner_uvs.len()
1401 && outer_positions.len() == inner_positions.len()
1402 && outer_normals.len() == inner_normals.len()
1403 {
1404 let mut uvs = outer_uvs;
1405 let inner_uvs = inner_positions
1406 .iter()
1407 .zip(&outer_positions)
1408 .zip(&inner_uvs)
1409 .map(|((inner_position, outer_position), inner_uv)| -> [f32; 2] {
1410 const UV_CENTER: Vec2 = Vec2::splat(0.5);
1411 let ip = Vec3::from(*inner_position).truncate();
1412 let op = Vec3::from(*outer_position).truncate();
1413 let uv = Vec2::from(*inner_uv) - UV_CENTER;
1414 (uv * ip.length() / op.length() + UV_CENTER).into()
1415 });
1416 uvs.extend(inner_uvs);
1417
1418 let mut normals = outer_normals;
1419 normals.extend(inner_normals);
1420
1421 let points = outer_positions.len() as u32;
1422 let mut indices = Vec::with_capacity(outer_positions.len() * 6);
1423 for i in 0..points {
1424 indices.push(i); indices.push(i + 1); indices.push(points + i); indices.push(points + i); indices.push(i + 1); indices.push(points + i + 1); }
1432 let indices_length = indices.len();
1433 if let (_, [_, b, _, _, e, f]) = indices.split_at_mut(indices_length.saturating_sub(6))
1435 {
1436 *b = 0;
1437 *e = 0;
1438 *f = points;
1439 }
1440
1441 let mut positions = outer_positions;
1442 positions.extend_from_slice(&inner_positions);
1443
1444 Mesh::new(
1445 PrimitiveTopology::TriangleList,
1446 RenderAssetUsages::default(),
1447 )
1448 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1449 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1450 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1451 .with_inserted_indices(Indices::U32(indices))
1452 } else {
1453 panic!("The inner and outer meshes should have the same number of vertices, and have required attributes");
1454 }
1455 }
1456}
1457
1458impl<P> Extrudable for RingMeshBuilder<P>
1459where
1460 P: Primitive2d + Meshable,
1461 P::Output: Extrudable,
1462{
1463 fn perimeter(&self) -> Vec<PerimeterSegment> {
1477 let outer_vertex_count = self
1478 .get_vertex_attributes()
1479 .filter(|r| r.outer_positions.len() == r.inner_positions.len())
1480 .expect("The inner and outer meshes should have the same number of vertices, and have required attributes")
1481 .outer_positions
1482 .len();
1483
1484 let mut outer_perimeter = self.outer_shape_builder.perimeter();
1485 let inner_perimeter =
1486 self.inner_shape_builder
1487 .perimeter()
1488 .into_iter()
1489 .rev()
1490 .map(|segment| match segment {
1491 PerimeterSegment::Smooth {
1492 first_normal,
1493 last_normal,
1494 mut indices,
1495 } => PerimeterSegment::Smooth {
1496 first_normal: -last_normal,
1497 last_normal: -first_normal,
1498 indices: {
1499 let outer_perimeter_vertex_count = outer_vertex_count as u32;
1500 indices.reverse();
1501 for i in &mut indices {
1502 *i += outer_perimeter_vertex_count;
1503 }
1504 indices
1505 },
1506 },
1507 PerimeterSegment::Flat { mut indices } => PerimeterSegment::Flat {
1508 indices: {
1509 let outer_perimeter_vertex_count = outer_vertex_count as u32;
1510 indices.reverse();
1511 for i in &mut indices {
1512 *i += outer_perimeter_vertex_count;
1513 }
1514 indices
1515 },
1516 },
1517 });
1518
1519 outer_perimeter.extend(inner_perimeter);
1520 outer_perimeter
1521 }
1522}
1523
1524impl<P> Meshable for Ring<P>
1525where
1526 P: Primitive2d + Meshable,
1527{
1528 type Output = RingMeshBuilder<P>;
1529
1530 fn mesh(&self) -> Self::Output {
1531 RingMeshBuilder::new(self)
1532 }
1533}
1534
1535impl<P> From<Ring<P>> for Mesh
1536where
1537 P: Primitive2d + Meshable,
1538{
1539 fn from(ring: Ring<P>) -> Self {
1540 ring.mesh().build()
1541 }
1542}
1543
1544#[cfg(test)]
1545mod tests {
1546 use bevy_math::{
1547 prelude::Annulus,
1548 primitives::{ConvexPolygon, RegularPolygon},
1549 FloatOrd, Vec2,
1550 };
1551 use bevy_platform::collections::HashSet;
1552
1553 use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
1554
1555 fn count_distinct_positions(points: &[[f32; 3]]) -> usize {
1556 let mut map = <HashSet<_>>::default();
1557 for point in points {
1558 map.insert(point.map(FloatOrd));
1559 }
1560 map.len()
1561 }
1562
1563 #[test]
1564 fn test_annulus() {
1565 let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build();
1566
1567 assert_eq!(
1568 32,
1569 count_distinct_positions(
1570 mesh.attribute(Mesh::ATTRIBUTE_POSITION)
1571 .unwrap()
1572 .as_float3()
1573 .unwrap()
1574 )
1575 );
1576 }
1577
1578 fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1581 for point in points.iter_mut() {
1582 for coord in point.iter_mut() {
1583 let round = (*coord * 2.).round() / 2.;
1584 if (*coord - round).abs() < 0.00001 {
1585 *coord = round;
1586 }
1587 }
1588 }
1589 }
1590
1591 #[test]
1592 fn test_regular_polygon() {
1593 let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1594
1595 let Some(VertexAttributeValues::Float32x3(mut positions)) =
1596 mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1597 else {
1598 panic!("Expected positions f32x3");
1599 };
1600 let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1601 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1602 else {
1603 panic!("Expected uvs f32x2");
1604 };
1605 let Some(VertexAttributeValues::Float32x3(normals)) =
1606 mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1607 else {
1608 panic!("Expected normals f32x3");
1609 };
1610
1611 fix_floats(&mut positions);
1612 fix_floats(&mut uvs);
1613
1614 assert_eq!(
1615 [
1616 [0.0, 7.0, 0.0],
1617 [-7.0, 0.0, 0.0],
1618 [0.0, -7.0, 0.0],
1619 [7.0, 0.0, 0.0],
1620 ],
1621 &positions[..]
1622 );
1623
1624 assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1626
1627 assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1628 }
1629
1630 #[test]
1631 fn test_convex_polygon() {
1632 let polygon = ConvexPolygon::new(vec![
1633 Vec2::new(-2.0, -1.0),
1634 Vec2::new(2.0, -1.0),
1635 Vec2::new(1.0, 3.0),
1636 Vec2::new(-1.0, 2.0),
1637 ])
1638 .unwrap();
1639
1640 let mut mesh = Mesh::from(polygon);
1641
1642 let Some(VertexAttributeValues::Float32x3(mut positions)) =
1643 mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1644 else {
1645 panic!("Expected positions f32x3");
1646 };
1647 let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1648 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1649 else {
1650 panic!("Expected uvs f32x2");
1651 };
1652 let Some(VertexAttributeValues::Float32x3(normals)) =
1653 mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1654 else {
1655 panic!("Expected normals f32x3");
1656 };
1657
1658 fix_floats(&mut positions);
1659 fix_floats(&mut uvs);
1660
1661 assert_eq!(
1662 [
1663 [-2.0, -1.0, 0.0],
1664 [2.0, -1.0, 0.0],
1665 [1.0, 3.0, 0.0],
1666 [-1.0, 2.0, 0.0],
1667 ],
1668 &positions[..]
1669 );
1670
1671 assert_eq!(
1672 [[0.0, 0.0], [1.0, 0.0], [0.75, 1.0], [0.25, 0.75]],
1673 &uvs[..]
1674 );
1675
1676 assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1677 }
1678}