bevy_render/mesh/primitives/
dim2.rs

1use std::f32::consts::FRAC_PI_2;
2
3use crate::{
4    mesh::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment},
5    render_asset::RenderAssetUsages,
6};
7
8use super::{Extrudable, MeshBuilder, Meshable};
9use bevy_math::{
10    primitives::{
11        Annulus, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Rectangle,
12        RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
13    },
14    FloatExt, Vec2,
15};
16use wgpu::PrimitiveTopology;
17
18/// A builder used for creating a [`Mesh`] with a [`Circle`] shape.
19#[derive(Clone, Copy, Debug)]
20pub struct CircleMeshBuilder {
21    /// The [`Circle`] shape.
22    pub circle: Circle,
23    /// The number of vertices used for the circle mesh.
24    /// The default is `32`.
25    #[doc(alias = "vertices")]
26    pub resolution: usize,
27}
28
29impl Default for CircleMeshBuilder {
30    fn default() -> Self {
31        Self {
32            circle: Circle::default(),
33            resolution: 32,
34        }
35    }
36}
37
38impl CircleMeshBuilder {
39    /// Creates a new [`CircleMeshBuilder`] from a given radius and vertex count.
40    #[inline]
41    pub const fn new(radius: f32, resolution: usize) -> Self {
42        Self {
43            circle: Circle { radius },
44            resolution,
45        }
46    }
47
48    /// Sets the number of vertices used for the circle mesh.
49    #[inline]
50    #[doc(alias = "vertices")]
51    pub const fn resolution(mut self, resolution: usize) -> Self {
52        self.resolution = resolution;
53        self
54    }
55}
56
57impl MeshBuilder for CircleMeshBuilder {
58    fn build(&self) -> Mesh {
59        RegularPolygon::new(self.circle.radius, self.resolution)
60            .mesh()
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 as u32).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: usize,
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: usize) -> 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 mut indices = Vec::with_capacity((self.resolution - 1) * 3);
173        let mut positions = Vec::with_capacity(self.resolution + 1);
174        let normals = vec![[0.0, 0.0, 1.0]; self.resolution + 1];
175        let mut uvs = Vec::with_capacity(self.resolution + 1);
176
177        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
178
179        // Push the center of the circle.
180        positions.push([0.0; 3]);
181        uvs.push([0.5; 2]);
182
183        let first_angle = FRAC_PI_2 - self.sector.half_angle();
184        let last_angle = FRAC_PI_2 + self.sector.half_angle();
185        let last_i = (self.resolution - 1) as f32;
186        for i in 0..self.resolution {
187            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
188
189            // Compute the vertex
190            let vertex = self.sector.radius() * Vec2::from_angle(angle);
191            // 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).
192            // We accomplish the Y axis flip by negating the angle.
193            let uv =
194                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
195
196            positions.push([vertex.x, vertex.y, 0.0]);
197            uvs.push([uv.x, uv.y]);
198        }
199
200        for i in 1..(self.resolution as u32) {
201            // Index 0 is the center.
202            indices.extend_from_slice(&[0, i, i + 1]);
203        }
204
205        Mesh::new(
206            PrimitiveTopology::TriangleList,
207            RenderAssetUsages::default(),
208        )
209        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
210        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
211        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
212        .with_inserted_indices(Indices::U32(indices))
213    }
214}
215
216impl Extrudable for CircularSectorMeshBuilder {
217    fn perimeter(&self) -> Vec<PerimeterSegment> {
218        let resolution = self.resolution as u32;
219        let (sin, cos) = self.sector.arc.half_angle.sin_cos();
220        let first_normal = Vec2::new(sin, cos);
221        let last_normal = Vec2::new(-sin, cos);
222        vec![
223            PerimeterSegment::Flat {
224                indices: vec![resolution, 0, 1],
225            },
226            PerimeterSegment::Smooth {
227                first_normal,
228                last_normal,
229                indices: (1..=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: usize,
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: usize) -> 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 mut indices = Vec::with_capacity((self.resolution - 1) * 3);
310        let mut positions = Vec::with_capacity(self.resolution + 1);
311        let normals = vec![[0.0, 0.0, 1.0]; self.resolution + 1];
312        let mut uvs = Vec::with_capacity(self.resolution + 1);
313
314        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
315
316        // Push the center of the chord.
317        let midpoint_vertex = self.segment.chord_midpoint();
318        positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
319        // Compute the UV coordinate of the midpoint vertex.
320        // This is similar to the computation inside the loop for the arc vertices,
321        // but the vertex angle is PI/2, and we must scale by the ratio of the apothem to the radius
322        // to correctly position the vertex.
323        let midpoint_uv = Vec2::from_angle(-uv_angle - FRAC_PI_2).mul_add(
324            Vec2::splat(0.5 * (self.segment.apothem() / self.segment.radius())),
325            Vec2::splat(0.5),
326        );
327        uvs.push([midpoint_uv.x, midpoint_uv.y]);
328
329        let first_angle = FRAC_PI_2 - self.segment.half_angle();
330        let last_angle = FRAC_PI_2 + self.segment.half_angle();
331        let last_i = (self.resolution - 1) as f32;
332        for i in 0..self.resolution {
333            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
334
335            // Compute the vertex
336            let vertex = self.segment.radius() * Vec2::from_angle(angle);
337            // 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).
338            // We accomplish the Y axis flip by negating the angle.
339            let uv =
340                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
341
342            positions.push([vertex.x, vertex.y, 0.0]);
343            uvs.push([uv.x, uv.y]);
344        }
345
346        for i in 1..(self.resolution as u32) {
347            // Index 0 is the midpoint of the chord.
348            indices.extend_from_slice(&[0, i, i + 1]);
349        }
350
351        Mesh::new(
352            PrimitiveTopology::TriangleList,
353            RenderAssetUsages::default(),
354        )
355        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
356        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
357        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
358        .with_inserted_indices(Indices::U32(indices))
359    }
360}
361
362impl Extrudable for CircularSegmentMeshBuilder {
363    fn perimeter(&self) -> Vec<PerimeterSegment> {
364        let resolution = self.resolution as u32;
365        let (sin, cos) = self.segment.arc.half_angle.sin_cos();
366        let first_normal = Vec2::new(sin, cos);
367        let last_normal = Vec2::new(-sin, cos);
368        vec![
369            PerimeterSegment::Flat {
370                indices: vec![resolution, 0, 1],
371            },
372            PerimeterSegment::Smooth {
373                first_normal,
374                last_normal,
375                indices: (1..=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 [`RegularPolygon`] shape.
402pub struct RegularPolygonMeshBuilder {
403    circumradius: f32,
404    sides: usize,
405}
406impl Meshable for RegularPolygon {
407    type Output = RegularPolygonMeshBuilder;
408
409    fn mesh(&self) -> Self::Output {
410        Self::Output {
411            circumradius: self.circumcircle.radius,
412            sides: self.sides,
413        }
414    }
415}
416
417impl MeshBuilder for RegularPolygonMeshBuilder {
418    fn build(&self) -> Mesh {
419        // The ellipse mesh is just a regular polygon with two radii
420        Ellipse::new(self.circumradius, self.circumradius)
421            .mesh()
422            .resolution(self.sides)
423            .build()
424    }
425}
426
427impl Extrudable for RegularPolygonMeshBuilder {
428    fn perimeter(&self) -> Vec<PerimeterSegment> {
429        vec![PerimeterSegment::Flat {
430            indices: (0..self.sides as u32).chain([0]).collect(),
431        }]
432    }
433}
434
435impl From<RegularPolygon> for Mesh {
436    fn from(polygon: RegularPolygon) -> Self {
437        polygon.mesh().build()
438    }
439}
440
441/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape.
442#[derive(Clone, Copy, Debug)]
443pub struct EllipseMeshBuilder {
444    /// The [`Ellipse`] shape.
445    pub ellipse: Ellipse,
446    /// The number of vertices used for the ellipse mesh.
447    /// The default is `32`.
448    #[doc(alias = "vertices")]
449    pub resolution: usize,
450}
451
452impl Default for EllipseMeshBuilder {
453    fn default() -> Self {
454        Self {
455            ellipse: Ellipse::default(),
456            resolution: 32,
457        }
458    }
459}
460
461impl EllipseMeshBuilder {
462    /// Creates a new [`EllipseMeshBuilder`] from a given half width and half height and a vertex count.
463    #[inline]
464    pub const fn new(half_width: f32, half_height: f32, resolution: usize) -> Self {
465        Self {
466            ellipse: Ellipse::new(half_width, half_height),
467            resolution,
468        }
469    }
470
471    /// Sets the number of vertices used for the ellipse mesh.
472    #[inline]
473    #[doc(alias = "vertices")]
474    pub const fn resolution(mut self, resolution: usize) -> Self {
475        self.resolution = resolution;
476        self
477    }
478}
479
480impl MeshBuilder for EllipseMeshBuilder {
481    fn build(&self) -> Mesh {
482        let mut indices = Vec::with_capacity((self.resolution - 2) * 3);
483        let mut positions = Vec::with_capacity(self.resolution);
484        let normals = vec![[0.0, 0.0, 1.0]; self.resolution];
485        let mut uvs = Vec::with_capacity(self.resolution);
486
487        // Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0)
488        let start_angle = std::f32::consts::FRAC_PI_2;
489        let step = std::f32::consts::TAU / self.resolution as f32;
490
491        for i in 0..self.resolution {
492            // Compute vertex position at angle theta
493            let theta = start_angle + i as f32 * step;
494            let (sin, cos) = theta.sin_cos();
495            let x = cos * self.ellipse.half_size.x;
496            let y = sin * self.ellipse.half_size.y;
497
498            positions.push([x, y, 0.0]);
499            uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
500        }
501
502        for i in 1..(self.resolution as u32 - 1) {
503            indices.extend_from_slice(&[0, i, i + 1]);
504        }
505
506        Mesh::new(
507            PrimitiveTopology::TriangleList,
508            RenderAssetUsages::default(),
509        )
510        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
511        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
512        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
513        .with_inserted_indices(Indices::U32(indices))
514    }
515}
516
517impl Extrudable for EllipseMeshBuilder {
518    fn perimeter(&self) -> Vec<PerimeterSegment> {
519        vec![PerimeterSegment::Smooth {
520            first_normal: Vec2::Y,
521            last_normal: Vec2::Y,
522            indices: (0..self.resolution as u32).chain([0]).collect(),
523        }]
524    }
525}
526
527impl Meshable for Ellipse {
528    type Output = EllipseMeshBuilder;
529
530    fn mesh(&self) -> Self::Output {
531        EllipseMeshBuilder {
532            ellipse: *self,
533            ..Default::default()
534        }
535    }
536}
537
538impl From<Ellipse> for Mesh {
539    fn from(ellipse: Ellipse) -> Self {
540        ellipse.mesh().build()
541    }
542}
543
544/// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
545pub struct AnnulusMeshBuilder {
546    /// The [`Annulus`] shape.
547    pub annulus: Annulus,
548
549    /// The number of vertices used in constructing each concentric circle of the annulus mesh.
550    /// The default is `32`.
551    pub resolution: usize,
552}
553
554impl Default for AnnulusMeshBuilder {
555    fn default() -> Self {
556        Self {
557            annulus: Annulus::default(),
558            resolution: 32,
559        }
560    }
561}
562
563impl AnnulusMeshBuilder {
564    /// Create an [`AnnulusMeshBuilder`] with the given inner radius, outer radius, and angular vertex count.
565    #[inline]
566    pub fn new(inner_radius: f32, outer_radius: f32, resolution: usize) -> Self {
567        Self {
568            annulus: Annulus::new(inner_radius, outer_radius),
569            resolution,
570        }
571    }
572
573    /// Sets the number of vertices used in constructing the concentric circles of the annulus mesh.
574    #[inline]
575    pub fn resolution(mut self, resolution: usize) -> Self {
576        self.resolution = resolution;
577        self
578    }
579}
580
581impl MeshBuilder for AnnulusMeshBuilder {
582    fn build(&self) -> Mesh {
583        let inner_radius = self.annulus.inner_circle.radius;
584        let outer_radius = self.annulus.outer_circle.radius;
585
586        let num_vertices = (self.resolution + 1) * 2;
587        let mut indices = Vec::with_capacity(self.resolution * 6);
588        let mut positions = Vec::with_capacity(num_vertices);
589        let mut uvs = Vec::with_capacity(num_vertices);
590        let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
591
592        // We have one more set of vertices than might be naïvely expected;
593        // the vertices at `start_angle` are duplicated for the purposes of UV
594        // mapping. Here, each iteration places a pair of vertices at a fixed
595        // angle from the center of the annulus.
596        let start_angle = std::f32::consts::FRAC_PI_2;
597        let step = std::f32::consts::TAU / self.resolution as f32;
598        for i in 0..=self.resolution {
599            let theta = start_angle + i as f32 * step;
600            let (sin, cos) = theta.sin_cos();
601            let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
602            let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
603            positions.push(inner_pos);
604            positions.push(outer_pos);
605
606            // The first UV direction is radial and the second is angular;
607            // i.e., a single UV rectangle is stretched around the annulus, with
608            // its top and bottom meeting as the circle closes. Lines of constant
609            // U map to circles, and lines of constant V map to radial line segments.
610            let inner_uv = [0., i as f32 / self.resolution as f32];
611            let outer_uv = [1., i as f32 / self.resolution as f32];
612            uvs.push(inner_uv);
613            uvs.push(outer_uv);
614        }
615
616        // Adjacent pairs of vertices form two triangles with each other; here,
617        // we are just making sure that they both have the right orientation,
618        // which is the CCW order of
619        // `inner_vertex` -> `outer_vertex` -> `next_outer` -> `next_inner`
620        for i in 0..(self.resolution as u32) {
621            let inner_vertex = 2 * i;
622            let outer_vertex = 2 * i + 1;
623            let next_inner = inner_vertex + 2;
624            let next_outer = outer_vertex + 2;
625            indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
626            indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
627        }
628
629        Mesh::new(
630            PrimitiveTopology::TriangleList,
631            RenderAssetUsages::default(),
632        )
633        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
634        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
635        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
636        .with_inserted_indices(Indices::U32(indices))
637    }
638}
639
640impl Extrudable for AnnulusMeshBuilder {
641    fn perimeter(&self) -> Vec<PerimeterSegment> {
642        let vert_count = 2 * self.resolution as u32;
643        vec![
644            PerimeterSegment::Smooth {
645                first_normal: Vec2::NEG_Y,
646                last_normal: Vec2::NEG_Y,
647                indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), // Inner hole
648            },
649            PerimeterSegment::Smooth {
650                first_normal: Vec2::Y,
651                last_normal: Vec2::Y,
652                indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter
653            },
654        ]
655    }
656}
657
658impl Meshable for Annulus {
659    type Output = AnnulusMeshBuilder;
660
661    fn mesh(&self) -> Self::Output {
662        AnnulusMeshBuilder {
663            annulus: *self,
664            ..Default::default()
665        }
666    }
667}
668
669impl From<Annulus> for Mesh {
670    fn from(annulus: Annulus) -> Self {
671        annulus.mesh().build()
672    }
673}
674
675pub struct RhombusMeshBuilder {
676    half_diagonals: Vec2,
677}
678
679impl MeshBuilder for RhombusMeshBuilder {
680    fn build(&self) -> Mesh {
681        let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
682        let positions = vec![
683            [hhd, 0.0, 0.0],
684            [-hhd, 0.0, 0.0],
685            [0.0, vhd, 0.0],
686            [0.0, -vhd, 0.0],
687        ];
688        let normals = vec![[0.0, 0.0, 1.0]; 4];
689        let uvs = vec![[1.0, 0.5], [0.0, 0.5], [0.5, 0.0], [0.5, 1.0]];
690        let indices = Indices::U32(vec![1, 0, 2, 1, 3, 0]);
691
692        Mesh::new(
693            PrimitiveTopology::TriangleList,
694            RenderAssetUsages::default(),
695        )
696        .with_inserted_indices(indices)
697        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
698        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
699        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
700    }
701}
702
703impl Extrudable for RhombusMeshBuilder {
704    fn perimeter(&self) -> Vec<PerimeterSegment> {
705        vec![PerimeterSegment::Flat {
706            indices: vec![0, 2, 1, 3, 0],
707        }]
708    }
709}
710
711impl Meshable for Rhombus {
712    type Output = RhombusMeshBuilder;
713
714    fn mesh(&self) -> Self::Output {
715        Self::Output {
716            half_diagonals: self.half_diagonals,
717        }
718    }
719}
720
721impl From<Rhombus> for Mesh {
722    fn from(rhombus: Rhombus) -> Self {
723        rhombus.mesh().build()
724    }
725}
726
727/// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape.
728pub struct Triangle2dMeshBuilder {
729    triangle: Triangle2d,
730}
731impl Meshable for Triangle2d {
732    type Output = Triangle2dMeshBuilder;
733
734    fn mesh(&self) -> Self::Output {
735        Self::Output { triangle: *self }
736    }
737}
738impl MeshBuilder for Triangle2dMeshBuilder {
739    fn build(&self) -> Mesh {
740        let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
741
742        let positions: Vec<_> = vertices_3d.into();
743        let normals = vec![[0.0, 0.0, 1.0]; 3];
744
745        let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
746            vertices_3d[0],
747            vertices_3d[1],
748            vertices_3d[2],
749        ))
750        .into();
751
752        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
753        let indices = if is_ccw {
754            Indices::U32(vec![0, 1, 2])
755        } else {
756            Indices::U32(vec![2, 1, 0])
757        };
758
759        Mesh::new(
760            PrimitiveTopology::TriangleList,
761            RenderAssetUsages::default(),
762        )
763        .with_inserted_indices(indices)
764        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
765        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
766        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
767    }
768}
769
770impl Extrudable for Triangle2dMeshBuilder {
771    fn perimeter(&self) -> Vec<PerimeterSegment> {
772        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
773        if is_ccw {
774            vec![PerimeterSegment::Flat {
775                indices: vec![0, 1, 2, 0],
776            }]
777        } else {
778            vec![PerimeterSegment::Flat {
779                indices: vec![2, 1, 0, 2],
780            }]
781        }
782    }
783}
784
785impl From<Triangle2d> for Mesh {
786    fn from(triangle: Triangle2d) -> Self {
787        triangle.mesh().build()
788    }
789}
790
791/// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape.
792pub struct RectangleMeshBuilder {
793    half_size: Vec2,
794}
795
796impl MeshBuilder for RectangleMeshBuilder {
797    fn build(&self) -> Mesh {
798        let [hw, hh] = [self.half_size.x, self.half_size.y];
799        let positions = vec![
800            [hw, hh, 0.0],
801            [-hw, hh, 0.0],
802            [-hw, -hh, 0.0],
803            [hw, -hh, 0.0],
804        ];
805        let normals = vec![[0.0, 0.0, 1.0]; 4];
806        let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
807        let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
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 RectangleMeshBuilder {
821    fn perimeter(&self) -> Vec<PerimeterSegment> {
822        vec![PerimeterSegment::Flat {
823            indices: vec![0, 1, 2, 3, 0],
824        }]
825    }
826}
827
828impl Meshable for Rectangle {
829    type Output = RectangleMeshBuilder;
830
831    fn mesh(&self) -> Self::Output {
832        RectangleMeshBuilder {
833            half_size: self.half_size,
834        }
835    }
836}
837
838impl From<Rectangle> for Mesh {
839    fn from(rectangle: Rectangle) -> Self {
840        rectangle.mesh().build()
841    }
842}
843
844/// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape.
845#[derive(Clone, Copy, Debug)]
846pub struct Capsule2dMeshBuilder {
847    /// The [`Capsule2d`] shape.
848    pub capsule: Capsule2d,
849    /// The number of vertices used for one hemicircle.
850    /// The total number of vertices for the capsule mesh will be two times the resolution.
851    ///
852    /// The default is `16`.
853    pub resolution: usize,
854}
855
856impl Default for Capsule2dMeshBuilder {
857    fn default() -> Self {
858        Self {
859            capsule: Capsule2d::default(),
860            resolution: 16,
861        }
862    }
863}
864
865impl Capsule2dMeshBuilder {
866    /// Creates a new [`Capsule2dMeshBuilder`] from a given radius, length, and the number of vertices
867    /// used for one hemicircle. The total number of vertices for the capsule mesh will be two times the resolution.
868    #[inline]
869    pub fn new(radius: f32, length: f32, resolution: usize) -> Self {
870        Self {
871            capsule: Capsule2d::new(radius, length),
872            resolution,
873        }
874    }
875
876    /// Sets the number of vertices used for one hemicircle.
877    /// The total number of vertices for the capsule mesh will be two times the resolution.
878    #[inline]
879    pub const fn resolution(mut self, resolution: usize) -> Self {
880        self.resolution = resolution;
881        self
882    }
883}
884
885impl MeshBuilder for Capsule2dMeshBuilder {
886    fn build(&self) -> Mesh {
887        // The resolution is the number of vertices for one semicircle
888        let resolution = self.resolution as u32;
889        let vertex_count = 2 * self.resolution;
890
891        // Six extra indices for the two triangles between the hemicircles
892        let mut indices = Vec::with_capacity((self.resolution - 2) * 2 * 3 + 6);
893        let mut positions = Vec::with_capacity(vertex_count);
894        let normals = vec![[0.0, 0.0, 1.0]; vertex_count];
895        let mut uvs = Vec::with_capacity(vertex_count);
896
897        let radius = self.capsule.radius;
898        let step = std::f32::consts::TAU / vertex_count as f32;
899
900        // If the vertex count is even, offset starting angle of top semicircle by half a step
901        // to position the vertices evenly.
902        let start_angle = if vertex_count % 2 == 0 {
903            step / 2.0
904        } else {
905            0.0
906        };
907
908        // How much the hemicircle radius is of the total half-height of the capsule.
909        // This is used to prevent the UVs from stretching between the hemicircles.
910        let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
911
912        // Create top semicircle
913        for i in 0..resolution {
914            // Compute vertex position at angle theta
915            let theta = start_angle + i as f32 * step;
916            let (sin, cos) = theta.sin_cos();
917            let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
918
919            positions.push([x, y, 0.0]);
920            uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
921        }
922
923        // Add top semicircle indices
924        for i in 1..resolution - 1 {
925            indices.extend_from_slice(&[0, i, i + 1]);
926        }
927
928        // Add indices for top left triangle of the part between the hemicircles
929        indices.extend_from_slice(&[0, resolution - 1, resolution]);
930
931        // Create bottom semicircle
932        for i in resolution..vertex_count as u32 {
933            // Compute vertex position at angle theta
934            let theta = start_angle + i as f32 * step;
935            let (sin, cos) = theta.sin_cos();
936            let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
937
938            positions.push([x, y, 0.0]);
939            uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
940        }
941
942        // Add bottom semicircle indices
943        for i in 1..resolution - 1 {
944            indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
945        }
946
947        // Add indices for bottom right triangle of the part between the hemicircles
948        indices.extend_from_slice(&[resolution, vertex_count as u32 - 1, 0]);
949
950        Mesh::new(
951            PrimitiveTopology::TriangleList,
952            RenderAssetUsages::default(),
953        )
954        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
955        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
956        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
957        .with_inserted_indices(Indices::U32(indices))
958    }
959}
960
961impl Extrudable for Capsule2dMeshBuilder {
962    fn perimeter(&self) -> Vec<PerimeterSegment> {
963        let resolution = self.resolution as u32;
964        let top_semi_indices = (0..resolution).collect();
965        let bottom_semi_indices = (resolution..(2 * resolution)).collect();
966        vec![
967            PerimeterSegment::Smooth {
968                first_normal: Vec2::X,
969                last_normal: Vec2::NEG_X,
970                indices: top_semi_indices,
971            }, // Top semi-circle
972            PerimeterSegment::Flat {
973                indices: vec![resolution - 1, resolution],
974            }, // Left edge
975            PerimeterSegment::Smooth {
976                first_normal: Vec2::NEG_X,
977                last_normal: Vec2::X,
978                indices: bottom_semi_indices,
979            }, // Bottom semi-circle
980            PerimeterSegment::Flat {
981                indices: vec![2 * resolution - 1, 0],
982            }, // Right edge
983        ]
984    }
985}
986
987impl Meshable for Capsule2d {
988    type Output = Capsule2dMeshBuilder;
989
990    fn mesh(&self) -> Self::Output {
991        Capsule2dMeshBuilder {
992            capsule: *self,
993            ..Default::default()
994        }
995    }
996}
997
998impl From<Capsule2d> for Mesh {
999    fn from(capsule: Capsule2d) -> Self {
1000        capsule.mesh().build()
1001    }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use bevy_math::primitives::RegularPolygon;
1007
1008    use crate::mesh::{Mesh, VertexAttributeValues};
1009
1010    /// Sin/cos and multiplication computations result in numbers like 0.4999999.
1011    /// Round these to numbers we expect like 0.5.
1012    fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1013        for point in points.iter_mut() {
1014            for coord in point.iter_mut() {
1015                let round = (*coord * 2.).round() / 2.;
1016                if (*coord - round).abs() < 0.00001 {
1017                    *coord = round;
1018                }
1019            }
1020        }
1021    }
1022
1023    #[test]
1024    fn test_regular_polygon() {
1025        let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1026
1027        let Some(VertexAttributeValues::Float32x3(mut positions)) =
1028            mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1029        else {
1030            panic!("Expected positions f32x3");
1031        };
1032        let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1033            mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1034        else {
1035            panic!("Expected uvs f32x2");
1036        };
1037        let Some(VertexAttributeValues::Float32x3(normals)) =
1038            mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1039        else {
1040            panic!("Expected normals f32x3");
1041        };
1042
1043        fix_floats(&mut positions);
1044        fix_floats(&mut uvs);
1045
1046        assert_eq!(
1047            [
1048                [0.0, 7.0, 0.0],
1049                [-7.0, 0.0, 0.0],
1050                [0.0, -7.0, 0.0],
1051                [7.0, 0.0, 0.0],
1052            ],
1053            &positions[..]
1054        );
1055
1056        // Note V coordinate increases in the opposite direction to the Y coordinate.
1057        assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1058
1059        assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1060    }
1061}