bevy_mesh/primitives/
dim2.rs

1use core::f32::consts::FRAC_PI_2;
2
3use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment};
4use bevy_asset::RenderAssetUsages;
5
6use super::{Extrudable, MeshBuilder, Meshable};
7use bevy_math::{
8    ops,
9    primitives::{
10        Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse,
11        Rectangle, RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
12    },
13    FloatExt, Vec2,
14};
15use wgpu::PrimitiveTopology;
16
17/// A builder used for creating a [`Mesh`] with a [`Circle`] shape.
18#[derive(Clone, Copy, Debug)]
19pub struct CircleMeshBuilder {
20    /// The [`Circle`] shape.
21    pub circle: Circle,
22    /// The number of vertices used for the circle mesh.
23    /// The default is `32`.
24    #[doc(alias = "vertices")]
25    pub resolution: u32,
26}
27
28impl Default for CircleMeshBuilder {
29    fn default() -> Self {
30        Self {
31            circle: Circle::default(),
32            resolution: 32,
33        }
34    }
35}
36
37impl CircleMeshBuilder {
38    /// Creates a new [`CircleMeshBuilder`] from a given radius and vertex count.
39    #[inline]
40    pub const fn new(radius: f32, resolution: u32) -> Self {
41        Self {
42            circle: Circle { radius },
43            resolution,
44        }
45    }
46
47    /// Sets the number of vertices used for the circle mesh.
48    #[inline]
49    #[doc(alias = "vertices")]
50    pub const fn resolution(mut self, resolution: u32) -> Self {
51        self.resolution = resolution;
52        self
53    }
54}
55
56impl MeshBuilder for CircleMeshBuilder {
57    fn build(&self) -> Mesh {
58        Ellipse::new(self.circle.radius, self.circle.radius)
59            .mesh()
60            .resolution(self.resolution)
61            .build()
62    }
63}
64
65impl Extrudable for CircleMeshBuilder {
66    fn perimeter(&self) -> Vec<PerimeterSegment> {
67        vec![PerimeterSegment::Smooth {
68            first_normal: Vec2::Y,
69            last_normal: Vec2::Y,
70            indices: (0..self.resolution).chain([0]).collect(),
71        }]
72    }
73}
74
75impl Meshable for Circle {
76    type Output = CircleMeshBuilder;
77
78    fn mesh(&self) -> Self::Output {
79        CircleMeshBuilder {
80            circle: *self,
81            ..Default::default()
82        }
83    }
84}
85
86impl From<Circle> for Mesh {
87    fn from(circle: Circle) -> Self {
88        circle.mesh().build()
89    }
90}
91
92/// Specifies how to generate UV-mappings for the [`CircularSector`] and [`CircularSegment`] shapes.
93///
94/// Currently the only variant is `Mask`, which is good for showing a portion of a texture that includes
95/// the entire circle, particularly the same texture will be displayed with different fractions of a
96/// complete circle.
97///
98/// It's expected that more will be added in the future, such as a variant that causes the texture to be
99/// scaled to fit the bounding box of the shape, which would be good for packed textures only including the
100/// portion of the circle that is needed to display.
101#[derive(Copy, Clone, Debug, PartialEq)]
102#[non_exhaustive]
103pub enum CircularMeshUvMode {
104    /// Treats the shape as a mask over a circle of equal size and radius,
105    /// with the center of the circle at the center of the texture.
106    Mask {
107        /// Angle by which to rotate the shape when generating the UV map.
108        angle: f32,
109    },
110}
111
112impl Default for CircularMeshUvMode {
113    fn default() -> Self {
114        CircularMeshUvMode::Mask { angle: 0.0 }
115    }
116}
117
118/// A builder used for creating a [`Mesh`] with a [`CircularSector`] shape.
119///
120/// The resulting mesh will have a UV-map such that the center of the circle is
121/// at the center of the texture.
122#[derive(Clone, Debug)]
123pub struct CircularSectorMeshBuilder {
124    /// The sector shape.
125    pub sector: CircularSector,
126    /// The number of vertices used for the arc portion of the sector mesh.
127    /// The default is `32`.
128    #[doc(alias = "vertices")]
129    pub resolution: u32,
130    /// The UV mapping mode
131    pub uv_mode: CircularMeshUvMode,
132}
133
134impl Default for CircularSectorMeshBuilder {
135    fn default() -> Self {
136        Self {
137            sector: CircularSector::default(),
138            resolution: 32,
139            uv_mode: CircularMeshUvMode::default(),
140        }
141    }
142}
143
144impl CircularSectorMeshBuilder {
145    /// Creates a new [`CircularSectorMeshBuilder`] from a given sector
146    #[inline]
147    pub fn new(sector: CircularSector) -> Self {
148        Self {
149            sector,
150            ..Self::default()
151        }
152    }
153
154    /// Sets the number of vertices used for the sector mesh.
155    #[inline]
156    #[doc(alias = "vertices")]
157    pub const fn resolution(mut self, resolution: u32) -> Self {
158        self.resolution = resolution;
159        self
160    }
161
162    /// Sets the uv mode used for the sector mesh
163    #[inline]
164    pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
165        self.uv_mode = uv_mode;
166        self
167    }
168}
169
170impl MeshBuilder for CircularSectorMeshBuilder {
171    fn build(&self) -> Mesh {
172        let resolution = self.resolution as usize;
173        let mut indices = Vec::with_capacity((resolution - 1) * 3);
174        let mut positions = Vec::with_capacity(resolution + 1);
175        let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
176        let mut uvs = Vec::with_capacity(resolution + 1);
177
178        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
179
180        // Push the center of the circle.
181        positions.push([0.0; 3]);
182        uvs.push([0.5; 2]);
183
184        let first_angle = FRAC_PI_2 - self.sector.half_angle();
185        let last_angle = FRAC_PI_2 + self.sector.half_angle();
186        let last_i = (self.resolution - 1) as f32;
187        for i in 0..self.resolution {
188            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
189
190            // Compute the vertex
191            let vertex = self.sector.radius() * Vec2::from_angle(angle);
192            // 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).
193            // We accomplish the Y axis flip by negating the angle.
194            let uv =
195                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
196
197            positions.push([vertex.x, vertex.y, 0.0]);
198            uvs.push([uv.x, uv.y]);
199        }
200
201        for i in 1..self.resolution {
202            // Index 0 is the center.
203            indices.extend_from_slice(&[0, i, i + 1]);
204        }
205
206        Mesh::new(
207            PrimitiveTopology::TriangleList,
208            RenderAssetUsages::default(),
209        )
210        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
211        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
212        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
213        .with_inserted_indices(Indices::U32(indices))
214    }
215}
216
217impl Extrudable for CircularSectorMeshBuilder {
218    fn perimeter(&self) -> Vec<PerimeterSegment> {
219        let (sin, cos) = ops::sin_cos(self.sector.arc.half_angle);
220        let first_normal = Vec2::new(sin, cos);
221        let last_normal = Vec2::new(-sin, cos);
222        vec![
223            PerimeterSegment::Flat {
224                indices: vec![self.resolution, 0, 1],
225            },
226            PerimeterSegment::Smooth {
227                first_normal,
228                last_normal,
229                indices: (1..=self.resolution).collect(),
230            },
231        ]
232    }
233}
234
235impl Meshable for CircularSector {
236    type Output = CircularSectorMeshBuilder;
237
238    fn mesh(&self) -> Self::Output {
239        CircularSectorMeshBuilder {
240            sector: *self,
241            ..Default::default()
242        }
243    }
244}
245
246impl From<CircularSector> for Mesh {
247    /// Converts this sector into a [`Mesh`] using a default [`CircularSectorMeshBuilder`].
248    ///
249    /// See the documentation of [`CircularSectorMeshBuilder`] for more details.
250    fn from(sector: CircularSector) -> Self {
251        sector.mesh().build()
252    }
253}
254
255/// A builder used for creating a [`Mesh`] with a [`CircularSegment`] shape.
256///
257/// The resulting mesh will have a UV-map such that the center of the circle is
258/// at the center of the texture.
259#[derive(Clone, Copy, Debug)]
260pub struct CircularSegmentMeshBuilder {
261    /// The segment shape.
262    pub segment: CircularSegment,
263    /// The number of vertices used for the arc portion of the segment mesh.
264    /// The default is `32`.
265    #[doc(alias = "vertices")]
266    pub resolution: u32,
267    /// The UV mapping mode
268    pub uv_mode: CircularMeshUvMode,
269}
270
271impl Default for CircularSegmentMeshBuilder {
272    fn default() -> Self {
273        Self {
274            segment: CircularSegment::default(),
275            resolution: 32,
276            uv_mode: CircularMeshUvMode::default(),
277        }
278    }
279}
280
281impl CircularSegmentMeshBuilder {
282    /// Creates a new [`CircularSegmentMeshBuilder`] from a given segment
283    #[inline]
284    pub fn new(segment: CircularSegment) -> Self {
285        Self {
286            segment,
287            ..Self::default()
288        }
289    }
290
291    /// Sets the number of vertices used for the segment mesh.
292    #[inline]
293    #[doc(alias = "vertices")]
294    pub const fn resolution(mut self, resolution: u32) -> Self {
295        self.resolution = resolution;
296        self
297    }
298
299    /// Sets the uv mode used for the segment mesh
300    #[inline]
301    pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
302        self.uv_mode = uv_mode;
303        self
304    }
305}
306
307impl MeshBuilder for CircularSegmentMeshBuilder {
308    fn build(&self) -> Mesh {
309        let resolution = self.resolution as usize;
310        let mut indices = Vec::with_capacity((resolution - 1) * 3);
311        let mut positions = Vec::with_capacity(resolution + 1);
312        let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
313        let mut uvs = Vec::with_capacity(resolution + 1);
314
315        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
316
317        // Push the center of the chord.
318        let midpoint_vertex = self.segment.chord_midpoint();
319        positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
320        // Compute the UV coordinate of the midpoint vertex.
321        // This is similar to the computation inside the loop for the arc vertices,
322        // but the vertex angle is PI/2, and we must scale by the ratio of the apothem to the radius
323        // to correctly position the vertex.
324        let midpoint_uv = Vec2::from_angle(-uv_angle - FRAC_PI_2).mul_add(
325            Vec2::splat(0.5 * (self.segment.apothem() / self.segment.radius())),
326            Vec2::splat(0.5),
327        );
328        uvs.push([midpoint_uv.x, midpoint_uv.y]);
329
330        let first_angle = FRAC_PI_2 - self.segment.half_angle();
331        let last_angle = FRAC_PI_2 + self.segment.half_angle();
332        let last_i = (self.resolution - 1) as f32;
333        for i in 0..self.resolution {
334            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
335
336            // Compute the vertex
337            let vertex = self.segment.radius() * Vec2::from_angle(angle);
338            // 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).
339            // We accomplish the Y axis flip by negating the angle.
340            let uv =
341                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
342
343            positions.push([vertex.x, vertex.y, 0.0]);
344            uvs.push([uv.x, uv.y]);
345        }
346
347        for i in 1..self.resolution {
348            // Index 0 is the midpoint of the chord.
349            indices.extend_from_slice(&[0, i, i + 1]);
350        }
351
352        Mesh::new(
353            PrimitiveTopology::TriangleList,
354            RenderAssetUsages::default(),
355        )
356        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
357        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
358        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
359        .with_inserted_indices(Indices::U32(indices))
360    }
361}
362
363impl Extrudable for CircularSegmentMeshBuilder {
364    fn perimeter(&self) -> Vec<PerimeterSegment> {
365        let (sin, cos) = ops::sin_cos(self.segment.arc.half_angle);
366        let first_normal = Vec2::new(sin, cos);
367        let last_normal = Vec2::new(-sin, cos);
368        vec![
369            PerimeterSegment::Flat {
370                indices: vec![self.resolution, 0, 1],
371            },
372            PerimeterSegment::Smooth {
373                first_normal,
374                last_normal,
375                indices: (1..=self.resolution).collect(),
376            },
377        ]
378    }
379}
380
381impl Meshable for CircularSegment {
382    type Output = CircularSegmentMeshBuilder;
383
384    fn mesh(&self) -> Self::Output {
385        CircularSegmentMeshBuilder {
386            segment: *self,
387            ..Default::default()
388        }
389    }
390}
391
392impl From<CircularSegment> for Mesh {
393    /// Converts this sector into a [`Mesh`] using a default [`CircularSegmentMeshBuilder`].
394    ///
395    /// See the documentation of [`CircularSegmentMeshBuilder`] for more details.
396    fn from(segment: CircularSegment) -> Self {
397        segment.mesh().build()
398    }
399}
400
401/// A builder used for creating a [`Mesh`] with a [`ConvexPolygon`] shape.
402pub struct ConvexPolygonMeshBuilder<const N: usize> {
403    pub vertices: [Vec2; N],
404}
405
406impl<const N: usize> Meshable for ConvexPolygon<N> {
407    type Output = ConvexPolygonMeshBuilder<N>;
408
409    fn mesh(&self) -> Self::Output {
410        Self::Output {
411            vertices: *self.vertices(),
412        }
413    }
414}
415
416impl<const N: usize> MeshBuilder for ConvexPolygonMeshBuilder<N> {
417    fn build(&self) -> Mesh {
418        let mut indices = Vec::with_capacity((N - 2) * 3);
419        let mut positions = Vec::with_capacity(N);
420
421        for vertex in self.vertices {
422            positions.push([vertex.x, vertex.y, 0.0]);
423        }
424        for i in 2..N as u32 {
425            indices.extend_from_slice(&[0, i - 1, i]);
426        }
427        Mesh::new(
428            PrimitiveTopology::TriangleList,
429            RenderAssetUsages::default(),
430        )
431        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
432        .with_inserted_indices(Indices::U32(indices))
433    }
434}
435
436impl<const N: usize> Extrudable for ConvexPolygonMeshBuilder<N> {
437    fn perimeter(&self) -> Vec<PerimeterSegment> {
438        vec![PerimeterSegment::Flat {
439            indices: (0..N as u32).chain([0]).collect(),
440        }]
441    }
442}
443
444impl<const N: usize> From<ConvexPolygon<N>> for Mesh {
445    fn from(polygon: ConvexPolygon<N>) -> Self {
446        polygon.mesh().build()
447    }
448}
449
450/// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape.
451pub struct RegularPolygonMeshBuilder {
452    circumradius: f32,
453    sides: u32,
454}
455impl Meshable for RegularPolygon {
456    type Output = RegularPolygonMeshBuilder;
457
458    fn mesh(&self) -> Self::Output {
459        Self::Output {
460            circumradius: self.circumcircle.radius,
461            sides: self.sides,
462        }
463    }
464}
465
466impl MeshBuilder for RegularPolygonMeshBuilder {
467    fn build(&self) -> Mesh {
468        // The ellipse mesh is just a regular polygon with two radii
469        Ellipse::new(self.circumradius, self.circumradius)
470            .mesh()
471            .resolution(self.sides)
472            .build()
473    }
474}
475
476impl Extrudable for RegularPolygonMeshBuilder {
477    fn perimeter(&self) -> Vec<PerimeterSegment> {
478        vec![PerimeterSegment::Flat {
479            indices: (0..self.sides).chain([0]).collect(),
480        }]
481    }
482}
483
484impl From<RegularPolygon> for Mesh {
485    fn from(polygon: RegularPolygon) -> Self {
486        polygon.mesh().build()
487    }
488}
489
490/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape.
491#[derive(Clone, Copy, Debug)]
492pub struct EllipseMeshBuilder {
493    /// The [`Ellipse`] shape.
494    pub ellipse: Ellipse,
495    /// The number of vertices used for the ellipse mesh.
496    /// The default is `32`.
497    #[doc(alias = "vertices")]
498    pub resolution: u32,
499}
500
501impl Default for EllipseMeshBuilder {
502    fn default() -> Self {
503        Self {
504            ellipse: Ellipse::default(),
505            resolution: 32,
506        }
507    }
508}
509
510impl EllipseMeshBuilder {
511    /// Creates a new [`EllipseMeshBuilder`] from a given half width and half height and a vertex count.
512    #[inline]
513    pub const fn new(half_width: f32, half_height: f32, resolution: u32) -> Self {
514        Self {
515            ellipse: Ellipse::new(half_width, half_height),
516            resolution,
517        }
518    }
519
520    /// Sets the number of vertices used for the ellipse mesh.
521    #[inline]
522    #[doc(alias = "vertices")]
523    pub const fn resolution(mut self, resolution: u32) -> Self {
524        self.resolution = resolution;
525        self
526    }
527}
528
529impl MeshBuilder for EllipseMeshBuilder {
530    fn build(&self) -> Mesh {
531        let resolution = self.resolution as usize;
532        let mut indices = Vec::with_capacity((resolution - 2) * 3);
533        let mut positions = Vec::with_capacity(resolution);
534        let normals = vec![[0.0, 0.0, 1.0]; resolution];
535        let mut uvs = Vec::with_capacity(resolution);
536
537        // Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0)
538        let start_angle = FRAC_PI_2;
539        let step = core::f32::consts::TAU / self.resolution as f32;
540
541        for i in 0..self.resolution {
542            // Compute vertex position at angle theta
543            let theta = start_angle + i as f32 * step;
544            let (sin, cos) = ops::sin_cos(theta);
545            let x = cos * self.ellipse.half_size.x;
546            let y = sin * self.ellipse.half_size.y;
547
548            positions.push([x, y, 0.0]);
549            uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
550        }
551
552        for i in 1..(self.resolution - 1) {
553            indices.extend_from_slice(&[0, i, i + 1]);
554        }
555
556        Mesh::new(
557            PrimitiveTopology::TriangleList,
558            RenderAssetUsages::default(),
559        )
560        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
561        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
562        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
563        .with_inserted_indices(Indices::U32(indices))
564    }
565}
566
567impl Extrudable for EllipseMeshBuilder {
568    fn perimeter(&self) -> Vec<PerimeterSegment> {
569        vec![PerimeterSegment::Smooth {
570            first_normal: Vec2::Y,
571            last_normal: Vec2::Y,
572            indices: (0..self.resolution).chain([0]).collect(),
573        }]
574    }
575}
576
577impl Meshable for Ellipse {
578    type Output = EllipseMeshBuilder;
579
580    fn mesh(&self) -> Self::Output {
581        EllipseMeshBuilder {
582            ellipse: *self,
583            ..Default::default()
584        }
585    }
586}
587
588impl From<Ellipse> for Mesh {
589    fn from(ellipse: Ellipse) -> Self {
590        ellipse.mesh().build()
591    }
592}
593
594/// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
595pub struct AnnulusMeshBuilder {
596    /// The [`Annulus`] shape.
597    pub annulus: Annulus,
598
599    /// The number of vertices used in constructing each concentric circle of the annulus mesh.
600    /// The default is `32`.
601    pub resolution: u32,
602}
603
604impl Default for AnnulusMeshBuilder {
605    fn default() -> Self {
606        Self {
607            annulus: Annulus::default(),
608            resolution: 32,
609        }
610    }
611}
612
613impl AnnulusMeshBuilder {
614    /// Create an [`AnnulusMeshBuilder`] with the given inner radius, outer radius, and angular vertex count.
615    #[inline]
616    pub fn new(inner_radius: f32, outer_radius: f32, resolution: u32) -> Self {
617        Self {
618            annulus: Annulus::new(inner_radius, outer_radius),
619            resolution,
620        }
621    }
622
623    /// Sets the number of vertices used in constructing the concentric circles of the annulus mesh.
624    #[inline]
625    pub fn resolution(mut self, resolution: u32) -> Self {
626        self.resolution = resolution;
627        self
628    }
629}
630
631impl MeshBuilder for AnnulusMeshBuilder {
632    fn build(&self) -> Mesh {
633        let inner_radius = self.annulus.inner_circle.radius;
634        let outer_radius = self.annulus.outer_circle.radius;
635
636        let num_vertices = (self.resolution as usize + 1) * 2;
637        let mut indices = Vec::with_capacity(self.resolution as usize * 6);
638        let mut positions = Vec::with_capacity(num_vertices);
639        let mut uvs = Vec::with_capacity(num_vertices);
640        let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
641
642        // We have one more set of vertices than might be naïvely expected;
643        // the vertices at `start_angle` are duplicated for the purposes of UV
644        // mapping. Here, each iteration places a pair of vertices at a fixed
645        // angle from the center of the annulus.
646        let start_angle = FRAC_PI_2;
647        let step = core::f32::consts::TAU / self.resolution as f32;
648        for i in 0..=self.resolution {
649            let theta = start_angle + (i % self.resolution) as f32 * step;
650            let (sin, cos) = ops::sin_cos(theta);
651            let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
652            let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
653            positions.push(inner_pos);
654            positions.push(outer_pos);
655
656            // The first UV direction is radial and the second is angular;
657            // i.e., a single UV rectangle is stretched around the annulus, with
658            // its top and bottom meeting as the circle closes. Lines of constant
659            // U map to circles, and lines of constant V map to radial line segments.
660            let inner_uv = [0., i as f32 / self.resolution as f32];
661            let outer_uv = [1., i as f32 / self.resolution as f32];
662            uvs.push(inner_uv);
663            uvs.push(outer_uv);
664        }
665
666        // Adjacent pairs of vertices form two triangles with each other; here,
667        // we are just making sure that they both have the right orientation,
668        // which is the CCW order of
669        // `inner_vertex` -> `outer_vertex` -> `next_outer` -> `next_inner`
670        for i in 0..self.resolution {
671            let inner_vertex = 2 * i;
672            let outer_vertex = 2 * i + 1;
673            let next_inner = inner_vertex + 2;
674            let next_outer = outer_vertex + 2;
675            indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
676            indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
677        }
678
679        Mesh::new(
680            PrimitiveTopology::TriangleList,
681            RenderAssetUsages::default(),
682        )
683        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
684        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
685        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
686        .with_inserted_indices(Indices::U32(indices))
687    }
688}
689
690impl Extrudable for AnnulusMeshBuilder {
691    fn perimeter(&self) -> Vec<PerimeterSegment> {
692        let vert_count = 2 * self.resolution;
693        vec![
694            PerimeterSegment::Smooth {
695                first_normal: Vec2::NEG_Y,
696                last_normal: Vec2::NEG_Y,
697                indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), // Inner hole
698            },
699            PerimeterSegment::Smooth {
700                first_normal: Vec2::Y,
701                last_normal: Vec2::Y,
702                indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter
703            },
704        ]
705    }
706}
707
708impl Meshable for Annulus {
709    type Output = AnnulusMeshBuilder;
710
711    fn mesh(&self) -> Self::Output {
712        AnnulusMeshBuilder {
713            annulus: *self,
714            ..Default::default()
715        }
716    }
717}
718
719impl From<Annulus> for Mesh {
720    fn from(annulus: Annulus) -> Self {
721        annulus.mesh().build()
722    }
723}
724
725pub struct RhombusMeshBuilder {
726    half_diagonals: Vec2,
727}
728
729impl MeshBuilder for RhombusMeshBuilder {
730    fn build(&self) -> Mesh {
731        let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
732        let positions = vec![
733            [hhd, 0.0, 0.0],
734            [-hhd, 0.0, 0.0],
735            [0.0, vhd, 0.0],
736            [0.0, -vhd, 0.0],
737        ];
738        let normals = vec![[0.0, 0.0, 1.0]; 4];
739        let uvs = vec![[1.0, 0.5], [0.0, 0.5], [0.5, 0.0], [0.5, 1.0]];
740        let indices = Indices::U32(vec![1, 0, 2, 1, 3, 0]);
741
742        Mesh::new(
743            PrimitiveTopology::TriangleList,
744            RenderAssetUsages::default(),
745        )
746        .with_inserted_indices(indices)
747        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
748        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
749        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
750    }
751}
752
753impl Extrudable for RhombusMeshBuilder {
754    fn perimeter(&self) -> Vec<PerimeterSegment> {
755        vec![PerimeterSegment::Flat {
756            indices: vec![0, 2, 1, 3, 0],
757        }]
758    }
759}
760
761impl Meshable for Rhombus {
762    type Output = RhombusMeshBuilder;
763
764    fn mesh(&self) -> Self::Output {
765        Self::Output {
766            half_diagonals: self.half_diagonals,
767        }
768    }
769}
770
771impl From<Rhombus> for Mesh {
772    fn from(rhombus: Rhombus) -> Self {
773        rhombus.mesh().build()
774    }
775}
776
777/// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape.
778pub struct Triangle2dMeshBuilder {
779    triangle: Triangle2d,
780}
781impl Meshable for Triangle2d {
782    type Output = Triangle2dMeshBuilder;
783
784    fn mesh(&self) -> Self::Output {
785        Self::Output { triangle: *self }
786    }
787}
788impl MeshBuilder for Triangle2dMeshBuilder {
789    fn build(&self) -> Mesh {
790        let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
791
792        let positions: Vec<_> = vertices_3d.into();
793        let normals = vec![[0.0, 0.0, 1.0]; 3];
794
795        let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
796            vertices_3d[0],
797            vertices_3d[1],
798            vertices_3d[2],
799        ))
800        .into();
801
802        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
803        let indices = if is_ccw {
804            Indices::U32(vec![0, 1, 2])
805        } else {
806            Indices::U32(vec![2, 1, 0])
807        };
808
809        Mesh::new(
810            PrimitiveTopology::TriangleList,
811            RenderAssetUsages::default(),
812        )
813        .with_inserted_indices(indices)
814        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
815        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
816        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
817    }
818}
819
820impl Extrudable for Triangle2dMeshBuilder {
821    fn perimeter(&self) -> Vec<PerimeterSegment> {
822        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
823        if is_ccw {
824            vec![PerimeterSegment::Flat {
825                indices: vec![0, 1, 2, 0],
826            }]
827        } else {
828            vec![PerimeterSegment::Flat {
829                indices: vec![2, 1, 0, 2],
830            }]
831        }
832    }
833}
834
835impl From<Triangle2d> for Mesh {
836    fn from(triangle: Triangle2d) -> Self {
837        triangle.mesh().build()
838    }
839}
840
841/// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape.
842pub struct RectangleMeshBuilder {
843    half_size: Vec2,
844}
845
846impl MeshBuilder for RectangleMeshBuilder {
847    fn build(&self) -> Mesh {
848        let [hw, hh] = [self.half_size.x, self.half_size.y];
849        let positions = vec![
850            [hw, hh, 0.0],
851            [-hw, hh, 0.0],
852            [-hw, -hh, 0.0],
853            [hw, -hh, 0.0],
854        ];
855        let normals = vec![[0.0, 0.0, 1.0]; 4];
856        let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
857        let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
858
859        Mesh::new(
860            PrimitiveTopology::TriangleList,
861            RenderAssetUsages::default(),
862        )
863        .with_inserted_indices(indices)
864        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
865        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
866        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
867    }
868}
869
870impl Extrudable for RectangleMeshBuilder {
871    fn perimeter(&self) -> Vec<PerimeterSegment> {
872        vec![PerimeterSegment::Flat {
873            indices: vec![0, 1, 2, 3, 0],
874        }]
875    }
876}
877
878impl Meshable for Rectangle {
879    type Output = RectangleMeshBuilder;
880
881    fn mesh(&self) -> Self::Output {
882        RectangleMeshBuilder {
883            half_size: self.half_size,
884        }
885    }
886}
887
888impl From<Rectangle> for Mesh {
889    fn from(rectangle: Rectangle) -> Self {
890        rectangle.mesh().build()
891    }
892}
893
894/// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape.
895#[derive(Clone, Copy, Debug)]
896pub struct Capsule2dMeshBuilder {
897    /// The [`Capsule2d`] shape.
898    pub capsule: Capsule2d,
899    /// The number of vertices used for one hemicircle.
900    /// The total number of vertices for the capsule mesh will be two times the resolution.
901    ///
902    /// The default is `16`.
903    pub resolution: u32,
904}
905
906impl Default for Capsule2dMeshBuilder {
907    fn default() -> Self {
908        Self {
909            capsule: Capsule2d::default(),
910            resolution: 16,
911        }
912    }
913}
914
915impl Capsule2dMeshBuilder {
916    /// Creates a new [`Capsule2dMeshBuilder`] from a given radius, length, and the number of vertices
917    /// used for one hemicircle. The total number of vertices for the capsule mesh will be two times the resolution.
918    #[inline]
919    pub fn new(radius: f32, length: f32, resolution: u32) -> Self {
920        Self {
921            capsule: Capsule2d::new(radius, length),
922            resolution,
923        }
924    }
925
926    /// Sets the number of vertices used for one hemicircle.
927    /// The total number of vertices for the capsule mesh will be two times the resolution.
928    #[inline]
929    pub const fn resolution(mut self, resolution: u32) -> Self {
930        self.resolution = resolution;
931        self
932    }
933}
934
935impl MeshBuilder for Capsule2dMeshBuilder {
936    fn build(&self) -> Mesh {
937        // The resolution is the number of vertices for one semicircle
938        let resolution = self.resolution;
939        let vertex_count = 2 * resolution;
940
941        // Six extra indices for the two triangles between the hemicircles
942        let mut indices = Vec::with_capacity((resolution as usize - 2) * 2 * 3 + 6);
943        let mut positions = Vec::with_capacity(vertex_count as usize);
944        let normals = vec![[0.0, 0.0, 1.0]; vertex_count as usize];
945        let mut uvs = Vec::with_capacity(vertex_count as usize);
946
947        let radius = self.capsule.radius;
948        let step = core::f32::consts::TAU / vertex_count as f32;
949
950        // If the vertex count is even, offset starting angle of top semicircle by half a step
951        // to position the vertices evenly.
952        let start_angle = if vertex_count % 2 == 0 {
953            step / 2.0
954        } else {
955            0.0
956        };
957
958        // How much the hemicircle radius is of the total half-height of the capsule.
959        // This is used to prevent the UVs from stretching between the hemicircles.
960        let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
961
962        // Create top semicircle
963        for i in 0..resolution {
964            // Compute vertex position at angle theta
965            let theta = start_angle + i as f32 * step;
966            let (sin, cos) = ops::sin_cos(theta);
967            let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
968
969            positions.push([x, y, 0.0]);
970            uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
971        }
972
973        // Add top semicircle indices
974        for i in 1..resolution - 1 {
975            indices.extend_from_slice(&[0, i, i + 1]);
976        }
977
978        // Add indices for top left triangle of the part between the hemicircles
979        indices.extend_from_slice(&[0, resolution - 1, resolution]);
980
981        // Create bottom semicircle
982        for i in resolution..vertex_count {
983            // Compute vertex position at angle theta
984            let theta = start_angle + i as f32 * step;
985            let (sin, cos) = ops::sin_cos(theta);
986            let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
987
988            positions.push([x, y, 0.0]);
989            uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
990        }
991
992        // Add bottom semicircle indices
993        for i in 1..resolution - 1 {
994            indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
995        }
996
997        // Add indices for bottom right triangle of the part between the hemicircles
998        indices.extend_from_slice(&[resolution, vertex_count - 1, 0]);
999
1000        Mesh::new(
1001            PrimitiveTopology::TriangleList,
1002            RenderAssetUsages::default(),
1003        )
1004        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1005        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1006        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1007        .with_inserted_indices(Indices::U32(indices))
1008    }
1009}
1010
1011impl Extrudable for Capsule2dMeshBuilder {
1012    fn perimeter(&self) -> Vec<PerimeterSegment> {
1013        let resolution = self.resolution;
1014        let top_semi_indices = (0..resolution).collect();
1015        let bottom_semi_indices = (resolution..(2 * resolution)).collect();
1016        vec![
1017            PerimeterSegment::Smooth {
1018                first_normal: Vec2::X,
1019                last_normal: Vec2::NEG_X,
1020                indices: top_semi_indices,
1021            }, // Top semi-circle
1022            PerimeterSegment::Flat {
1023                indices: vec![resolution - 1, resolution],
1024            }, // Left edge
1025            PerimeterSegment::Smooth {
1026                first_normal: Vec2::NEG_X,
1027                last_normal: Vec2::X,
1028                indices: bottom_semi_indices,
1029            }, // Bottom semi-circle
1030            PerimeterSegment::Flat {
1031                indices: vec![2 * resolution - 1, 0],
1032            }, // Right edge
1033        ]
1034    }
1035}
1036
1037impl Meshable for Capsule2d {
1038    type Output = Capsule2dMeshBuilder;
1039
1040    fn mesh(&self) -> Self::Output {
1041        Capsule2dMeshBuilder {
1042            capsule: *self,
1043            ..Default::default()
1044        }
1045    }
1046}
1047
1048impl From<Capsule2d> for Mesh {
1049    fn from(capsule: Capsule2d) -> Self {
1050        capsule.mesh().build()
1051    }
1052}
1053
1054#[cfg(test)]
1055mod tests {
1056    use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd};
1057    use bevy_utils::HashSet;
1058
1059    use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
1060
1061    fn count_distinct_positions(points: &[[f32; 3]]) -> usize {
1062        let mut map = HashSet::new();
1063        for point in points {
1064            map.insert(point.map(FloatOrd));
1065        }
1066        map.len()
1067    }
1068
1069    #[test]
1070    fn test_annulus() {
1071        let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build();
1072
1073        assert_eq!(
1074            32,
1075            count_distinct_positions(
1076                mesh.attribute(Mesh::ATTRIBUTE_POSITION)
1077                    .unwrap()
1078                    .as_float3()
1079                    .unwrap()
1080            )
1081        );
1082    }
1083
1084    /// Sin/cos and multiplication computations result in numbers like 0.4999999.
1085    /// Round these to numbers we expect like 0.5.
1086    fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1087        for point in points.iter_mut() {
1088            for coord in point.iter_mut() {
1089                let round = (*coord * 2.).round() / 2.;
1090                if (*coord - round).abs() < 0.00001 {
1091                    *coord = round;
1092                }
1093            }
1094        }
1095    }
1096
1097    #[test]
1098    fn test_regular_polygon() {
1099        let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1100
1101        let Some(VertexAttributeValues::Float32x3(mut positions)) =
1102            mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1103        else {
1104            panic!("Expected positions f32x3");
1105        };
1106        let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1107            mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1108        else {
1109            panic!("Expected uvs f32x2");
1110        };
1111        let Some(VertexAttributeValues::Float32x3(normals)) =
1112            mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1113        else {
1114            panic!("Expected normals f32x3");
1115        };
1116
1117        fix_floats(&mut positions);
1118        fix_floats(&mut uvs);
1119
1120        assert_eq!(
1121            [
1122                [0.0, 7.0, 0.0],
1123                [-7.0, 0.0, 0.0],
1124                [0.0, -7.0, 0.0],
1125                [7.0, 0.0, 0.0],
1126            ],
1127            &positions[..]
1128        );
1129
1130        // Note V coordinate increases in the opposite direction to the Y coordinate.
1131        assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1132
1133        assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1134    }
1135}