Skip to main content

bevy_mesh/primitives/
dim2.rs

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/// A builder used for creating a [`Mesh`] with a [`Circle`] shape.
22#[derive(Clone, Copy, Debug, Reflect)]
23#[reflect(Default, Debug, Clone)]
24pub struct CircleMeshBuilder {
25    /// The [`Circle`] shape.
26    pub circle: Circle,
27    /// The number of vertices used for the circle mesh.
28    /// The default is `32`.
29    #[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    /// Creates a new [`CircleMeshBuilder`] from a given radius and vertex count.
44    #[inline]
45    pub const fn new(radius: f32, resolution: u32) -> Self {
46        Self {
47            circle: Circle { radius },
48            resolution,
49        }
50    }
51
52    /// Sets the number of vertices used for the circle mesh.
53    #[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/// Specifies how to generate UV-mappings for the [`CircularSector`] and [`CircularSegment`] shapes.
98///
99/// Currently the only variant is `Mask`, which is good for showing a portion of a texture that includes
100/// the entire circle, particularly the same texture will be displayed with different fractions of a
101/// complete circle.
102///
103/// It's expected that more will be added in the future, such as a variant that causes the texture to be
104/// scaled to fit the bounding box of the shape, which would be good for packed textures only including the
105/// portion of the circle that is needed to display.
106#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
107#[reflect(Default, Debug, Clone)]
108#[non_exhaustive]
109pub enum CircularMeshUvMode {
110    /// Treats the shape as a mask over a circle of equal size and radius,
111    /// with the center of the circle at the center of the texture.
112    Mask {
113        /// Angle by which to rotate the shape when generating the UV map.
114        angle: f32,
115    },
116}
117
118impl Default for CircularMeshUvMode {
119    fn default() -> Self {
120        CircularMeshUvMode::Mask { angle: 0.0 }
121    }
122}
123
124/// A builder used for creating a [`Mesh`] with a [`CircularSector`] shape.
125///
126/// The resulting mesh will have a UV-map such that the center of the circle is
127/// at the center of the texture.
128#[derive(Clone, Debug, Reflect)]
129#[reflect(Default, Debug, Clone)]
130pub struct CircularSectorMeshBuilder {
131    /// The sector shape.
132    pub sector: CircularSector,
133    /// The number of vertices used for the arc portion of the sector mesh.
134    /// The default is `32`.
135    #[doc(alias = "vertices")]
136    pub resolution: u32,
137    /// The UV mapping mode
138    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    /// Creates a new [`CircularSectorMeshBuilder`] from a given sector
153    #[inline]
154    pub fn new(sector: CircularSector) -> Self {
155        Self {
156            sector,
157            ..Self::default()
158        }
159    }
160
161    /// Sets the number of vertices used for the sector mesh.
162    #[inline]
163    #[doc(alias = "vertices")]
164    pub const fn resolution(mut self, resolution: u32) -> Self {
165        self.resolution = resolution;
166        self
167    }
168
169    /// Sets the uv mode used for the sector mesh
170    #[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        // Push the center of the circle.
188        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            // Compute the vertex
198            let vertex = self.sector.radius() * Vec2::from_angle(angle);
199            // Compute the UV coordinate by taking the modified angle's unit vector, negating the Y axis, and rescaling and centering it at (0.5, 0.5).
200            // We accomplish the Y axis flip by negating the angle.
201            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            // Index 0 is the center.
210            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    /// Converts this sector into a [`Mesh`] using a default [`CircularSectorMeshBuilder`].
255    ///
256    /// See the documentation of [`CircularSectorMeshBuilder`] for more details.
257    fn from(sector: CircularSector) -> Self {
258        sector.mesh().build()
259    }
260}
261
262/// A builder used for creating a [`Mesh`] with a [`CircularSegment`] shape.
263///
264/// The resulting mesh will have a UV-map such that the center of the circle is
265/// at the center of the texture.
266#[derive(Clone, Copy, Debug, Reflect)]
267#[reflect(Default, Debug, Clone)]
268pub struct CircularSegmentMeshBuilder {
269    /// The segment shape.
270    pub segment: CircularSegment,
271    /// The number of vertices used for the arc portion of the segment mesh.
272    /// The default is `32`.
273    #[doc(alias = "vertices")]
274    pub resolution: u32,
275    /// The UV mapping mode
276    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    /// Creates a new [`CircularSegmentMeshBuilder`] from a given segment
291    #[inline]
292    pub fn new(segment: CircularSegment) -> Self {
293        Self {
294            segment,
295            ..Self::default()
296        }
297    }
298
299    /// Sets the number of vertices used for the segment mesh.
300    #[inline]
301    #[doc(alias = "vertices")]
302    pub const fn resolution(mut self, resolution: u32) -> Self {
303        self.resolution = resolution;
304        self
305    }
306
307    /// Sets the uv mode used for the segment mesh
308    #[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        // Push the center of the chord.
326        let midpoint_vertex = self.segment.chord_midpoint();
327        positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
328        // Compute the UV coordinate of the midpoint vertex.
329        // This is similar to the computation inside the loop for the arc vertices,
330        // but the vertex angle is PI/2, and we must scale by the ratio of the apothem to the radius
331        // to correctly position the vertex.
332        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            // Compute the vertex
345            let vertex = self.segment.radius() * Vec2::from_angle(angle);
346            // Compute the UV coordinate by taking the modified angle's unit vector, negating the Y axis, and rescaling and centering it at (0.5, 0.5).
347            // We accomplish the Y axis flip by negating the angle.
348            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            // Index 0 is the midpoint of the chord.
357            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    /// Converts this sector into a [`Mesh`] using a default [`CircularSegmentMeshBuilder`].
402    ///
403    /// See the documentation of [`CircularSegmentMeshBuilder`] for more details.
404    fn from(segment: CircularSegment) -> Self {
405        segment.mesh().build()
406    }
407}
408
409/// A builder used for creating a [`Mesh`] with a [`ConvexPolygon`] shape.
410///
411/// You must verify that the `vertices` are not concave when constructing this type. You can
412/// guarantee this by creating a [`ConvexPolygon`] first, then calling [`ConvexPolygon::mesh()`].
413#[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            // Map each axis independently into [0, 1] over the polygon's AABB.
451            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/// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape.
482#[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    /// Returns the default [`RegularPolygonMeshBuilder`] with six sides (a hexagon) and a circumradius of `0.5`.
491    fn default() -> Self {
492        Self {
493            circumradius: 0.5,
494            sides: 6,
495        }
496    }
497}
498
499impl RegularPolygonMeshBuilder {
500    /// Creates a new [`RegularPolygonMeshBuilder`] from the radius of a circumcircle and a number
501    /// of sides.
502    ///
503    /// # Panics
504    ///
505    /// Panics in debug mode if `circumradius` is negative, or if `sides` is less than 3.
506    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        // The ellipse mesh is just a regular polygon with two radii
534        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/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape.
556#[derive(Clone, Copy, Debug, Reflect)]
557#[reflect(Default, Debug, Clone)]
558pub struct EllipseMeshBuilder {
559    /// The [`Ellipse`] shape.
560    pub ellipse: Ellipse,
561    /// The number of vertices used for the ellipse mesh.
562    /// The default is `32`.
563    #[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    /// Creates a new [`EllipseMeshBuilder`] from a given half width and half height and a vertex count.
578    #[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    /// Sets the number of vertices used for the ellipse mesh.
587    #[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        // Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0)
604        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            // Compute vertex position at angle theta
609            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
660/// A builder used for creating a [`Mesh`] with a [`Segment2d`].
661pub struct Segment2dMeshBuilder {
662    /// The [`Segment2d`] shape.
663    pub segment: Segment2d,
664}
665
666impl Segment2dMeshBuilder {
667    /// Creates a new [`Segment2dMeshBuilder`] from a given segment.
668    #[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    /// Converts this segment into a [`Mesh`] using a default [`Segment2dMeshBuilder`].
695    fn from(segment: Segment2d) -> Self {
696        segment.mesh().build()
697    }
698}
699
700/// A builder used for creating a [`Mesh`] with a [`Polyline2d`] shape.
701#[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/// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
745#[derive(Clone, Copy, Debug, Reflect)]
746#[reflect(Default, Debug, Clone)]
747pub struct AnnulusMeshBuilder {
748    /// The [`Annulus`] shape.
749    pub annulus: Annulus,
750
751    /// The number of vertices used in constructing each concentric circle of the annulus mesh.
752    /// The default is `32`.
753    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    /// Create an [`AnnulusMeshBuilder`] with the given inner radius, outer radius, and angular vertex count.
767    #[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    /// Sets the number of vertices used in constructing the concentric circles of the annulus mesh.
776    #[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        // We have one more set of vertices than might be naïvely expected;
795        // the vertices at `start_angle` are duplicated for the purposes of UV
796        // mapping. Here, each iteration places a pair of vertices at a fixed
797        // angle from the center of the annulus.
798        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            // The first UV direction is radial and the second is angular;
809            // i.e., a single UV rectangle is stretched around the annulus, with
810            // its top and bottom meeting as the circle closes. Lines of constant
811            // U map to circles, and lines of constant V map to radial line segments.
812            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        // Adjacent pairs of vertices form two triangles with each other; here,
819        // we are just making sure that they both have the right orientation,
820        // which is the CCW order of
821        // `inner_vertex` -> `outer_vertex` -> `next_outer` -> `next_inner`
822        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(), // Inner hole
850            },
851            PerimeterSegment::Smooth {
852                first_normal: Vec2::Y,
853                last_normal: Vec2::Y,
854                indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter
855            },
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/// A builder for creating a [`Mesh`] with an [`Rhombus`] shape.
878#[derive(Clone, Copy, Debug, Reflect)]
879#[reflect(Default, Debug, Clone)]
880pub struct RhombusMeshBuilder {
881    half_diagonals: Vec2,
882}
883
884impl Default for RhombusMeshBuilder {
885    /// Returns the default [`RhombusMeshBuilder`] with a half-horizontal and half-vertical diagonal of `0.5`.
886    fn default() -> Self {
887        Self {
888            half_diagonals: Vec2::splat(0.5),
889        }
890    }
891}
892
893impl RhombusMeshBuilder {
894    /// Creates a new [`RhombusMeshBuilder`] from a horizontal and vertical diagonal size.
895    ///
896    /// # Panics
897    ///
898    /// Panics in debug mode if `horizontal_diagonal` or `vertical_diagonal` is negative.
899    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/// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape.
964#[derive(Clone, Copy, Debug, Default, Reflect)]
965#[reflect(Default, Debug, Clone)]
966pub struct Triangle2dMeshBuilder {
967    triangle: Triangle2d,
968}
969
970impl Triangle2dMeshBuilder {
971    /// Creates a new [`Triangle2dMeshBuilder`] from the points `a`, `b`, and `c`.
972    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/// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape.
1041#[derive(Clone, Copy, Debug, Reflect)]
1042#[reflect(Default, Debug, Clone)]
1043pub struct RectangleMeshBuilder {
1044    half_size: Vec2,
1045}
1046
1047impl Default for RectangleMeshBuilder {
1048    /// Returns the default [`RectangleMeshBuilder`] with a half-width and half-height of `0.5`.
1049    fn default() -> Self {
1050        Self {
1051            half_size: Vec2::splat(0.5),
1052        }
1053    }
1054}
1055
1056impl RectangleMeshBuilder {
1057    /// Creates a new [`RectangleMeshBuilder`] from a full width and height.
1058    ///
1059    /// # Panics
1060    ///
1061    /// Panics in debug mode if `width` or `height` is negative.
1062    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/// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape.
1121#[derive(Clone, Copy, Debug, Reflect)]
1122#[reflect(Default, Debug, Clone)]
1123pub struct Capsule2dMeshBuilder {
1124    /// The [`Capsule2d`] shape.
1125    pub capsule: Capsule2d,
1126    /// The number of vertices used for one hemicircle.
1127    /// The total number of vertices for the capsule mesh will be two times the resolution.
1128    ///
1129    /// The default is `16`.
1130    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    /// Creates a new [`Capsule2dMeshBuilder`] from a given radius, length, and the number of vertices
1144    /// used for one hemicircle. The total number of vertices for the capsule mesh will be two times the resolution.
1145    #[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    /// Sets the number of vertices used for one hemicircle.
1154    /// The total number of vertices for the capsule mesh will be two times the resolution.
1155    #[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        // The resolution is the number of vertices for one semicircle
1165        let resolution = self.resolution;
1166        let vertex_count = 2 * resolution;
1167
1168        // Six extra indices for the two triangles between the semicircles
1169        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        // If the vertex count is even, offset starting angle of top semicircle by half a step
1178        // to position the vertices evenly.
1179        let start_angle = if vertex_count.is_multiple_of(2) {
1180            step / 2.0
1181        } else {
1182            0.0
1183        };
1184
1185        // How much the hemicircle radius is of the total half-height of the capsule.
1186        // This is used to prevent the UVs from stretching between the semicircles.
1187        let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
1188
1189        // Create top semicircle
1190        for i in 0..resolution {
1191            // Compute vertex position at angle theta
1192            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        // Add top semicircle indices
1201        for i in 1..resolution - 1 {
1202            indices.extend_from_slice(&[0, i, i + 1]);
1203        }
1204
1205        // Add indices for top left triangle of the part between the semicircles
1206        indices.extend_from_slice(&[0, resolution - 1, resolution]);
1207
1208        // Create bottom semicircle
1209        for i in resolution..vertex_count {
1210            // Compute vertex position at angle theta
1211            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        // Add bottom semicircle indices
1220        for i in 1..resolution - 1 {
1221            indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
1222        }
1223
1224        // Add indices for bottom right triangle of the part between the semicircles
1225        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            }, // Top semi-circle
1249            PerimeterSegment::Flat {
1250                indices: vec![resolution - 1, resolution],
1251            }, // Left edge
1252            PerimeterSegment::Smooth {
1253                first_normal: Vec2::NEG_X,
1254                last_normal: Vec2::X,
1255                indices: bottom_semi_indices,
1256            }, // Bottom semi-circle
1257            PerimeterSegment::Flat {
1258                indices: vec![2 * resolution - 1, 0],
1259            }, // Right edge
1260        ]
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
1281/// A builder used for creating a [`Mesh`] with a [`Ring`] shape.
1282pub 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    /// Create a new `RingMeshBuilder<P>` from a given `Ring<P>` shape.
1295    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    /// Apply a function to the inner builders
1303    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    /// Builds a [`Mesh`] based on the configuration in `self`.
1379    ///
1380    /// # Panics
1381    ///
1382    /// Panics if the following assumptions are not met.
1383    ///
1384    /// It is assumed that the inner and outer meshes have the same number of vertices.
1385    /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated
1386    /// a different number of vertices for the inner and outer instances of the primitive.
1387    ///
1388    /// It is assumed that the `primitive_topology` of the mesh returned by
1389    /// the underlying builder is [`PrimitiveTopology::TriangleList`]
1390    /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes.
1391    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                //                               for five points:
1425                indices.push(i); //              0  1  2  3  4
1426                indices.push(i + 1); //          1  2  3  4  0  <-
1427                indices.push(points + i); //     0' 1' 2' 3' 4'
1428                indices.push(points + i); //     0' 1' 2' 3' 4'
1429                indices.push(i + 1); //          1  2  3  4  0  <-
1430                indices.push(points + i + 1); // 1' 2' 3' 4' 0' <-
1431            }
1432            let indices_length = indices.len();
1433            // Fix up the last pair of triangles (return to start)
1434            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    /// A list of the indices each representing a part of the perimeter of the mesh.
1464    ///
1465    /// # Panics
1466    ///
1467    /// Panics if the following assumptions are not met.
1468    ///
1469    /// It is assumed that the inner and outer meshes have the same number of vertices.
1470    /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated
1471    /// a different number of vertices for the inner and outer instances of the primitive.
1472    ///
1473    /// It is assumed that the `primitive_topology` of the mesh returned by
1474    /// the underlying builder is [`PrimitiveTopology::TriangleList`]
1475    /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes.
1476    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    /// Sin/cos and multiplication computations result in numbers like 0.4999999.
1579    /// Round these to numbers we expect like 0.5.
1580    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        // Note V coordinate increases in the opposite direction to the Y coordinate.
1625        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}