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