bevy_mesh/primitives/
dim2.rs

1use core::f32::consts::FRAC_PI_2;
2use core::mem;
3
4use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment, VertexAttributeValues};
5use bevy_asset::RenderAssetUsages;
6
7use super::{Extrudable, MeshBuilder, Meshable};
8use bevy_math::prelude::Polyline2d;
9use bevy_math::{
10    ops,
11    primitives::{
12        Annulus, Capsule2d, Circle, CircularSector, CircularSegment, ConvexPolygon, Ellipse,
13        Primitive2d, Rectangle, RegularPolygon, Rhombus, Ring, Segment2d, Triangle2d, Triangle3d,
14        WindingOrder,
15    },
16    FloatExt, Vec2, Vec3,
17};
18use bevy_reflect::prelude::*;
19use wgpu_types::PrimitiveTopology;
20
21/// A builder used for creating a [`Mesh`] with a [`Circle`] shape.
22#[derive(Clone, Copy, Debug, Reflect)]
23#[reflect(Default, Debug, Clone)]
24pub struct CircleMeshBuilder {
25    /// The [`Circle`] shape.
26    pub circle: Circle,
27    /// The number of vertices used for the circle mesh.
28    /// The default is `32`.
29    #[doc(alias = "vertices")]
30    pub resolution: u32,
31}
32
33impl Default for CircleMeshBuilder {
34    fn default() -> Self {
35        Self {
36            circle: Circle::default(),
37            resolution: 32,
38        }
39    }
40}
41
42impl CircleMeshBuilder {
43    /// Creates a new [`CircleMeshBuilder`] from a given radius and vertex count.
44    #[inline]
45    pub const fn new(radius: f32, resolution: u32) -> Self {
46        Self {
47            circle: Circle { radius },
48            resolution,
49        }
50    }
51
52    /// Sets the number of vertices used for the circle mesh.
53    #[inline]
54    #[doc(alias = "vertices")]
55    pub const fn resolution(mut self, resolution: u32) -> Self {
56        self.resolution = resolution;
57        self
58    }
59}
60
61impl MeshBuilder for CircleMeshBuilder {
62    fn build(&self) -> Mesh {
63        Ellipse::new(self.circle.radius, self.circle.radius)
64            .mesh()
65            .resolution(self.resolution)
66            .build()
67    }
68}
69
70impl Extrudable for CircleMeshBuilder {
71    fn perimeter(&self) -> Vec<PerimeterSegment> {
72        vec![PerimeterSegment::Smooth {
73            first_normal: Vec2::Y,
74            last_normal: Vec2::Y,
75            indices: (0..self.resolution).chain([0]).collect(),
76        }]
77    }
78}
79
80impl Meshable for Circle {
81    type Output = CircleMeshBuilder;
82
83    fn mesh(&self) -> Self::Output {
84        CircleMeshBuilder {
85            circle: *self,
86            ..Default::default()
87        }
88    }
89}
90
91impl From<Circle> for Mesh {
92    fn from(circle: Circle) -> Self {
93        circle.mesh().build()
94    }
95}
96
97/// Specifies how to generate UV-mappings for the [`CircularSector`] and [`CircularSegment`] shapes.
98///
99/// Currently the only variant is `Mask`, which is good for showing a portion of a texture that includes
100/// the entire circle, particularly the same texture will be displayed with different fractions of a
101/// complete circle.
102///
103/// It's expected that more will be added in the future, such as a variant that causes the texture to be
104/// scaled to fit the bounding box of the shape, which would be good for packed textures only including the
105/// portion of the circle that is needed to display.
106#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
107#[reflect(Default, Debug, Clone)]
108#[non_exhaustive]
109pub enum CircularMeshUvMode {
110    /// Treats the shape as a mask over a circle of equal size and radius,
111    /// with the center of the circle at the center of the texture.
112    Mask {
113        /// Angle by which to rotate the shape when generating the UV map.
114        angle: f32,
115    },
116}
117
118impl Default for CircularMeshUvMode {
119    fn default() -> Self {
120        CircularMeshUvMode::Mask { angle: 0.0 }
121    }
122}
123
124/// A builder used for creating a [`Mesh`] with a [`CircularSector`] shape.
125///
126/// The resulting mesh will have a UV-map such that the center of the circle is
127/// at the center of the texture.
128#[derive(Clone, Debug, Reflect)]
129#[reflect(Default, Debug, Clone)]
130pub struct CircularSectorMeshBuilder {
131    /// The sector shape.
132    pub sector: CircularSector,
133    /// The number of vertices used for the arc portion of the sector mesh.
134    /// The default is `32`.
135    #[doc(alias = "vertices")]
136    pub resolution: u32,
137    /// The UV mapping mode
138    pub uv_mode: CircularMeshUvMode,
139}
140
141impl Default for CircularSectorMeshBuilder {
142    fn default() -> Self {
143        Self {
144            sector: CircularSector::default(),
145            resolution: 32,
146            uv_mode: CircularMeshUvMode::default(),
147        }
148    }
149}
150
151impl CircularSectorMeshBuilder {
152    /// Creates a new [`CircularSectorMeshBuilder`] from a given sector
153    #[inline]
154    pub fn new(sector: CircularSector) -> Self {
155        Self {
156            sector,
157            ..Self::default()
158        }
159    }
160
161    /// Sets the number of vertices used for the sector mesh.
162    #[inline]
163    #[doc(alias = "vertices")]
164    pub const fn resolution(mut self, resolution: u32) -> Self {
165        self.resolution = resolution;
166        self
167    }
168
169    /// Sets the uv mode used for the sector mesh
170    #[inline]
171    pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
172        self.uv_mode = uv_mode;
173        self
174    }
175}
176
177impl MeshBuilder for CircularSectorMeshBuilder {
178    fn build(&self) -> Mesh {
179        let resolution = self.resolution as usize;
180        let mut indices = Vec::with_capacity((resolution - 1) * 3);
181        let mut positions = Vec::with_capacity(resolution + 1);
182        let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
183        let mut uvs = Vec::with_capacity(resolution + 1);
184
185        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
186
187        // Push the center of the circle.
188        positions.push([0.0; 3]);
189        uvs.push([0.5; 2]);
190
191        let first_angle = FRAC_PI_2 - self.sector.half_angle();
192        let last_angle = FRAC_PI_2 + self.sector.half_angle();
193        let last_i = (self.resolution - 1) as f32;
194        for i in 0..self.resolution {
195            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
196
197            // Compute the vertex
198            let vertex = self.sector.radius() * Vec2::from_angle(angle);
199            // Compute the UV coordinate by taking the modified angle's unit vector, negating the Y axis, and rescaling and centering it at (0.5, 0.5).
200            // We accomplish the Y axis flip by negating the angle.
201            let uv =
202                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
203
204            positions.push([vertex.x, vertex.y, 0.0]);
205            uvs.push([uv.x, uv.y]);
206        }
207
208        for i in 1..self.resolution {
209            // Index 0 is the center.
210            indices.extend_from_slice(&[0, i, i + 1]);
211        }
212
213        Mesh::new(
214            PrimitiveTopology::TriangleList,
215            RenderAssetUsages::default(),
216        )
217        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
218        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
219        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
220        .with_inserted_indices(Indices::U32(indices))
221    }
222}
223
224impl Extrudable for CircularSectorMeshBuilder {
225    fn perimeter(&self) -> Vec<PerimeterSegment> {
226        let (sin, cos) = ops::sin_cos(self.sector.arc.half_angle);
227        let first_normal = Vec2::new(sin, cos);
228        let last_normal = Vec2::new(-sin, cos);
229        vec![
230            PerimeterSegment::Flat {
231                indices: vec![self.resolution, 0, 1],
232            },
233            PerimeterSegment::Smooth {
234                first_normal,
235                last_normal,
236                indices: (1..=self.resolution).collect(),
237            },
238        ]
239    }
240}
241
242impl Meshable for CircularSector {
243    type Output = CircularSectorMeshBuilder;
244
245    fn mesh(&self) -> Self::Output {
246        CircularSectorMeshBuilder {
247            sector: *self,
248            ..Default::default()
249        }
250    }
251}
252
253impl From<CircularSector> for Mesh {
254    /// Converts this sector into a [`Mesh`] using a default [`CircularSectorMeshBuilder`].
255    ///
256    /// See the documentation of [`CircularSectorMeshBuilder`] for more details.
257    fn from(sector: CircularSector) -> Self {
258        sector.mesh().build()
259    }
260}
261
262/// A builder used for creating a [`Mesh`] with a [`CircularSegment`] shape.
263///
264/// The resulting mesh will have a UV-map such that the center of the circle is
265/// at the center of the texture.
266#[derive(Clone, Copy, Debug, Reflect)]
267#[reflect(Default, Debug, Clone)]
268pub struct CircularSegmentMeshBuilder {
269    /// The segment shape.
270    pub segment: CircularSegment,
271    /// The number of vertices used for the arc portion of the segment mesh.
272    /// The default is `32`.
273    #[doc(alias = "vertices")]
274    pub resolution: u32,
275    /// The UV mapping mode
276    pub uv_mode: CircularMeshUvMode,
277}
278
279impl Default for CircularSegmentMeshBuilder {
280    fn default() -> Self {
281        Self {
282            segment: CircularSegment::default(),
283            resolution: 32,
284            uv_mode: CircularMeshUvMode::default(),
285        }
286    }
287}
288
289impl CircularSegmentMeshBuilder {
290    /// Creates a new [`CircularSegmentMeshBuilder`] from a given segment
291    #[inline]
292    pub fn new(segment: CircularSegment) -> Self {
293        Self {
294            segment,
295            ..Self::default()
296        }
297    }
298
299    /// Sets the number of vertices used for the segment mesh.
300    #[inline]
301    #[doc(alias = "vertices")]
302    pub const fn resolution(mut self, resolution: u32) -> Self {
303        self.resolution = resolution;
304        self
305    }
306
307    /// Sets the uv mode used for the segment mesh
308    #[inline]
309    pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
310        self.uv_mode = uv_mode;
311        self
312    }
313}
314
315impl MeshBuilder for CircularSegmentMeshBuilder {
316    fn build(&self) -> Mesh {
317        let resolution = self.resolution as usize;
318        let mut indices = Vec::with_capacity((resolution - 1) * 3);
319        let mut positions = Vec::with_capacity(resolution + 1);
320        let normals = vec![[0.0, 0.0, 1.0]; resolution + 1];
321        let mut uvs = Vec::with_capacity(resolution + 1);
322
323        let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
324
325        // Push the center of the chord.
326        let midpoint_vertex = self.segment.chord_midpoint();
327        positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
328        // Compute the UV coordinate of the midpoint vertex.
329        // This is similar to the computation inside the loop for the arc vertices,
330        // but the vertex angle is PI/2, and we must scale by the ratio of the apothem to the radius
331        // to correctly position the vertex.
332        let midpoint_uv = Vec2::from_angle(-uv_angle - FRAC_PI_2).mul_add(
333            Vec2::splat(0.5 * (self.segment.apothem() / self.segment.radius())),
334            Vec2::splat(0.5),
335        );
336        uvs.push([midpoint_uv.x, midpoint_uv.y]);
337
338        let first_angle = FRAC_PI_2 - self.segment.half_angle();
339        let last_angle = FRAC_PI_2 + self.segment.half_angle();
340        let last_i = (self.resolution - 1) as f32;
341        for i in 0..self.resolution {
342            let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
343
344            // Compute the vertex
345            let vertex = self.segment.radius() * Vec2::from_angle(angle);
346            // Compute the UV coordinate by taking the modified angle's unit vector, negating the Y axis, and rescaling and centering it at (0.5, 0.5).
347            // We accomplish the Y axis flip by negating the angle.
348            let uv =
349                Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
350
351            positions.push([vertex.x, vertex.y, 0.0]);
352            uvs.push([uv.x, uv.y]);
353        }
354
355        for i in 1..self.resolution {
356            // Index 0 is the midpoint of the chord.
357            indices.extend_from_slice(&[0, i, i + 1]);
358        }
359
360        Mesh::new(
361            PrimitiveTopology::TriangleList,
362            RenderAssetUsages::default(),
363        )
364        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
365        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
366        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
367        .with_inserted_indices(Indices::U32(indices))
368    }
369}
370
371impl Extrudable for CircularSegmentMeshBuilder {
372    fn perimeter(&self) -> Vec<PerimeterSegment> {
373        let (sin, cos) = ops::sin_cos(self.segment.arc.half_angle);
374        let first_normal = Vec2::new(sin, cos);
375        let last_normal = Vec2::new(-sin, cos);
376        vec![
377            PerimeterSegment::Flat {
378                indices: vec![self.resolution, 0, 1],
379            },
380            PerimeterSegment::Smooth {
381                first_normal,
382                last_normal,
383                indices: (1..=self.resolution).collect(),
384            },
385        ]
386    }
387}
388
389impl Meshable for CircularSegment {
390    type Output = CircularSegmentMeshBuilder;
391
392    fn mesh(&self) -> Self::Output {
393        CircularSegmentMeshBuilder {
394            segment: *self,
395            ..Default::default()
396        }
397    }
398}
399
400impl From<CircularSegment> for Mesh {
401    /// Converts this sector into a [`Mesh`] using a default [`CircularSegmentMeshBuilder`].
402    ///
403    /// See the documentation of [`CircularSegmentMeshBuilder`] for more details.
404    fn from(segment: CircularSegment) -> Self {
405        segment.mesh().build()
406    }
407}
408
409/// A builder used for creating a [`Mesh`] with a [`ConvexPolygon`] shape.
410///
411/// You must verify that the `vertices` are not concave when constructing this type. You can
412/// guarantee this by creating a [`ConvexPolygon`] first, then calling [`ConvexPolygon::mesh()`].
413#[derive(Clone, Debug, Reflect)]
414#[reflect(Debug, Clone)]
415pub struct ConvexPolygonMeshBuilder {
416    pub vertices: Vec<Vec2>,
417}
418
419impl Meshable for ConvexPolygon {
420    type Output = ConvexPolygonMeshBuilder;
421
422    fn mesh(&self) -> Self::Output {
423        Self::Output {
424            vertices: self.vertices().to_vec(),
425        }
426    }
427}
428
429impl MeshBuilder for ConvexPolygonMeshBuilder {
430    fn build(&self) -> Mesh {
431        let len = self.vertices.len();
432        let mut indices = Vec::with_capacity((len - 2) * 3);
433        let mut positions = Vec::with_capacity(len);
434
435        for vertex in &self.vertices {
436            positions.push([vertex.x, vertex.y, 0.0]);
437        }
438        for i in 2..len as u32 {
439            indices.extend_from_slice(&[0, i - 1, i]);
440        }
441        Mesh::new(
442            PrimitiveTopology::TriangleList,
443            RenderAssetUsages::default(),
444        )
445        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
446        .with_inserted_indices(Indices::U32(indices))
447    }
448}
449
450impl Extrudable for ConvexPolygonMeshBuilder {
451    fn perimeter(&self) -> Vec<PerimeterSegment> {
452        vec![PerimeterSegment::Flat {
453            indices: (0..self.vertices.len() as u32).chain([0]).collect(),
454        }]
455    }
456}
457
458impl From<ConvexPolygon> for Mesh {
459    fn from(polygon: ConvexPolygon) -> Self {
460        polygon.mesh().build()
461    }
462}
463
464/// A builder used for creating a [`Mesh`] with a [`RegularPolygon`] shape.
465#[derive(Clone, Copy, Debug, Reflect)]
466#[reflect(Default, Debug, Clone)]
467pub struct RegularPolygonMeshBuilder {
468    circumradius: f32,
469    sides: u32,
470}
471
472impl Default for RegularPolygonMeshBuilder {
473    /// Returns the default [`RegularPolygonMeshBuilder`] with six sides (a hexagon) and a circumradius of `0.5`.
474    fn default() -> Self {
475        Self {
476            circumradius: 0.5,
477            sides: 6,
478        }
479    }
480}
481
482impl RegularPolygonMeshBuilder {
483    /// Creates a new [`RegularPolygonMeshBuilder`] from the radius of a circumcircle and a number
484    /// of sides.
485    ///
486    /// # Panics
487    ///
488    /// Panics in debug mode if `circumradius` is negative, or if `sides` is less than 3.
489    pub const fn new(circumradius: f32, sides: u32) -> Self {
490        debug_assert!(
491            circumradius.is_sign_positive(),
492            "polygon has a negative radius"
493        );
494        debug_assert!(sides > 2, "polygon has less than 3 sides");
495
496        Self {
497            circumradius,
498            sides,
499        }
500    }
501}
502
503impl Meshable for RegularPolygon {
504    type Output = RegularPolygonMeshBuilder;
505
506    fn mesh(&self) -> Self::Output {
507        Self::Output {
508            circumradius: self.circumcircle.radius,
509            sides: self.sides,
510        }
511    }
512}
513
514impl MeshBuilder for RegularPolygonMeshBuilder {
515    fn build(&self) -> Mesh {
516        // The ellipse mesh is just a regular polygon with two radii
517        Ellipse::new(self.circumradius, self.circumradius)
518            .mesh()
519            .resolution(self.sides)
520            .build()
521    }
522}
523
524impl Extrudable for RegularPolygonMeshBuilder {
525    fn perimeter(&self) -> Vec<PerimeterSegment> {
526        vec![PerimeterSegment::Flat {
527            indices: (0..self.sides).chain([0]).collect(),
528        }]
529    }
530}
531
532impl From<RegularPolygon> for Mesh {
533    fn from(polygon: RegularPolygon) -> Self {
534        polygon.mesh().build()
535    }
536}
537
538/// A builder used for creating a [`Mesh`] with an [`Ellipse`] shape.
539#[derive(Clone, Copy, Debug, Reflect)]
540#[reflect(Default, Debug, Clone)]
541pub struct EllipseMeshBuilder {
542    /// The [`Ellipse`] shape.
543    pub ellipse: Ellipse,
544    /// The number of vertices used for the ellipse mesh.
545    /// The default is `32`.
546    #[doc(alias = "vertices")]
547    pub resolution: u32,
548}
549
550impl Default for EllipseMeshBuilder {
551    fn default() -> Self {
552        Self {
553            ellipse: Ellipse::default(),
554            resolution: 32,
555        }
556    }
557}
558
559impl EllipseMeshBuilder {
560    /// Creates a new [`EllipseMeshBuilder`] from a given half width and half height and a vertex count.
561    #[inline]
562    pub const fn new(half_width: f32, half_height: f32, resolution: u32) -> Self {
563        Self {
564            ellipse: Ellipse::new(half_width, half_height),
565            resolution,
566        }
567    }
568
569    /// Sets the number of vertices used for the ellipse mesh.
570    #[inline]
571    #[doc(alias = "vertices")]
572    pub const fn resolution(mut self, resolution: u32) -> Self {
573        self.resolution = resolution;
574        self
575    }
576}
577
578impl MeshBuilder for EllipseMeshBuilder {
579    fn build(&self) -> Mesh {
580        let resolution = self.resolution as usize;
581        let mut indices = Vec::with_capacity((resolution - 2) * 3);
582        let mut positions = Vec::with_capacity(resolution);
583        let normals = vec![[0.0, 0.0, 1.0]; resolution];
584        let mut uvs = Vec::with_capacity(resolution);
585
586        // Add pi/2 so that there is a vertex at the top (sin is 1.0 and cos is 0.0)
587        let start_angle = FRAC_PI_2;
588        let step = core::f32::consts::TAU / self.resolution as f32;
589
590        for i in 0..self.resolution {
591            // Compute vertex position at angle theta
592            let theta = start_angle + i as f32 * step;
593            let (sin, cos) = ops::sin_cos(theta);
594            let x = cos * self.ellipse.half_size.x;
595            let y = sin * self.ellipse.half_size.y;
596
597            positions.push([x, y, 0.0]);
598            uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
599        }
600
601        for i in 1..(self.resolution - 1) {
602            indices.extend_from_slice(&[0, i, i + 1]);
603        }
604
605        Mesh::new(
606            PrimitiveTopology::TriangleList,
607            RenderAssetUsages::default(),
608        )
609        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
610        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
611        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
612        .with_inserted_indices(Indices::U32(indices))
613    }
614}
615
616impl Extrudable for EllipseMeshBuilder {
617    fn perimeter(&self) -> Vec<PerimeterSegment> {
618        vec![PerimeterSegment::Smooth {
619            first_normal: Vec2::Y,
620            last_normal: Vec2::Y,
621            indices: (0..self.resolution).chain([0]).collect(),
622        }]
623    }
624}
625
626impl Meshable for Ellipse {
627    type Output = EllipseMeshBuilder;
628
629    fn mesh(&self) -> Self::Output {
630        EllipseMeshBuilder {
631            ellipse: *self,
632            ..Default::default()
633        }
634    }
635}
636
637impl From<Ellipse> for Mesh {
638    fn from(ellipse: Ellipse) -> Self {
639        ellipse.mesh().build()
640    }
641}
642
643/// A builder used for creating a [`Mesh`] with a [`Segment2d`].
644pub struct Segment2dMeshBuilder {
645    /// The [`Segment2d`] shape.
646    pub segment: Segment2d,
647}
648
649impl Segment2dMeshBuilder {
650    /// Creates a new [`Segment2dMeshBuilder`] from a given segment.
651    #[inline]
652    pub const fn new(line: Segment2d) -> Self {
653        Self { segment: line }
654    }
655}
656
657impl MeshBuilder for Segment2dMeshBuilder {
658    fn build(&self) -> Mesh {
659        let positions = self.segment.vertices.map(|v| v.extend(0.0)).to_vec();
660        let indices = Indices::U32(vec![0, 1]);
661
662        Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
663            .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
664            .with_inserted_indices(indices)
665    }
666}
667
668impl Meshable for Segment2d {
669    type Output = Segment2dMeshBuilder;
670
671    fn mesh(&self) -> Self::Output {
672        Segment2dMeshBuilder::new(*self)
673    }
674}
675
676impl From<Segment2d> for Mesh {
677    /// Converts this segment into a [`Mesh`] using a default [`Segment2dMeshBuilder`].
678    fn from(segment: Segment2d) -> Self {
679        segment.mesh().build()
680    }
681}
682
683/// A builder used for creating a [`Mesh`] with a [`Polyline2d`] shape.
684#[derive(Clone, Debug, Default, Reflect)]
685#[reflect(Default, Debug, Clone)]
686pub struct Polyline2dMeshBuilder {
687    polyline: Polyline2d,
688}
689
690impl MeshBuilder for Polyline2dMeshBuilder {
691    fn build(&self) -> Mesh {
692        let positions: Vec<_> = self
693            .polyline
694            .vertices
695            .iter()
696            .map(|v| v.extend(0.0))
697            .collect();
698
699        let indices = Indices::U32(
700            (0..self.polyline.vertices.len() as u32 - 1)
701                .flat_map(|i| [i, i + 1])
702                .collect(),
703        );
704
705        Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
706            .with_inserted_indices(indices)
707            .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
708    }
709}
710
711impl Meshable for Polyline2d {
712    type Output = Polyline2dMeshBuilder;
713
714    fn mesh(&self) -> Self::Output {
715        Polyline2dMeshBuilder {
716            polyline: self.clone(),
717        }
718    }
719}
720
721impl From<Polyline2d> for Mesh {
722    fn from(polyline: Polyline2d) -> Self {
723        polyline.mesh().build()
724    }
725}
726
727/// A builder for creating a [`Mesh`] with an [`Annulus`] shape.
728#[derive(Clone, Copy, Debug, Reflect)]
729#[reflect(Default, Debug, Clone)]
730pub struct AnnulusMeshBuilder {
731    /// The [`Annulus`] shape.
732    pub annulus: Annulus,
733
734    /// The number of vertices used in constructing each concentric circle of the annulus mesh.
735    /// The default is `32`.
736    pub resolution: u32,
737}
738
739impl Default for AnnulusMeshBuilder {
740    fn default() -> Self {
741        Self {
742            annulus: Annulus::default(),
743            resolution: 32,
744        }
745    }
746}
747
748impl AnnulusMeshBuilder {
749    /// Create an [`AnnulusMeshBuilder`] with the given inner radius, outer radius, and angular vertex count.
750    #[inline]
751    pub fn new(inner_radius: f32, outer_radius: f32, resolution: u32) -> Self {
752        Self {
753            annulus: Annulus::new(inner_radius, outer_radius),
754            resolution,
755        }
756    }
757
758    /// Sets the number of vertices used in constructing the concentric circles of the annulus mesh.
759    #[inline]
760    pub fn resolution(mut self, resolution: u32) -> Self {
761        self.resolution = resolution;
762        self
763    }
764}
765
766impl MeshBuilder for AnnulusMeshBuilder {
767    fn build(&self) -> Mesh {
768        let inner_radius = self.annulus.inner_circle.radius;
769        let outer_radius = self.annulus.outer_circle.radius;
770
771        let num_vertices = (self.resolution as usize + 1) * 2;
772        let mut indices = Vec::with_capacity(self.resolution as usize * 6);
773        let mut positions = Vec::with_capacity(num_vertices);
774        let mut uvs = Vec::with_capacity(num_vertices);
775        let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
776
777        // We have one more set of vertices than might be naïvely expected;
778        // the vertices at `start_angle` are duplicated for the purposes of UV
779        // mapping. Here, each iteration places a pair of vertices at a fixed
780        // angle from the center of the annulus.
781        let start_angle = FRAC_PI_2;
782        let step = core::f32::consts::TAU / self.resolution as f32;
783        for i in 0..=self.resolution {
784            let theta = start_angle + (i % self.resolution) as f32 * step;
785            let (sin, cos) = ops::sin_cos(theta);
786            let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
787            let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
788            positions.push(inner_pos);
789            positions.push(outer_pos);
790
791            // The first UV direction is radial and the second is angular;
792            // i.e., a single UV rectangle is stretched around the annulus, with
793            // its top and bottom meeting as the circle closes. Lines of constant
794            // U map to circles, and lines of constant V map to radial line segments.
795            let inner_uv = [0., i as f32 / self.resolution as f32];
796            let outer_uv = [1., i as f32 / self.resolution as f32];
797            uvs.push(inner_uv);
798            uvs.push(outer_uv);
799        }
800
801        // Adjacent pairs of vertices form two triangles with each other; here,
802        // we are just making sure that they both have the right orientation,
803        // which is the CCW order of
804        // `inner_vertex` -> `outer_vertex` -> `next_outer` -> `next_inner`
805        for i in 0..self.resolution {
806            let inner_vertex = 2 * i;
807            let outer_vertex = 2 * i + 1;
808            let next_inner = inner_vertex + 2;
809            let next_outer = outer_vertex + 2;
810            indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
811            indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
812        }
813
814        Mesh::new(
815            PrimitiveTopology::TriangleList,
816            RenderAssetUsages::default(),
817        )
818        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
819        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
820        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
821        .with_inserted_indices(Indices::U32(indices))
822    }
823}
824
825impl Extrudable for AnnulusMeshBuilder {
826    fn perimeter(&self) -> Vec<PerimeterSegment> {
827        let vert_count = 2 * self.resolution;
828        vec![
829            PerimeterSegment::Smooth {
830                first_normal: Vec2::NEG_Y,
831                last_normal: Vec2::NEG_Y,
832                indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), // Inner hole
833            },
834            PerimeterSegment::Smooth {
835                first_normal: Vec2::Y,
836                last_normal: Vec2::Y,
837                indices: (1..vert_count).step_by(2).chain([1]).collect(), // Outer perimeter
838            },
839        ]
840    }
841}
842
843impl Meshable for Annulus {
844    type Output = AnnulusMeshBuilder;
845
846    fn mesh(&self) -> Self::Output {
847        AnnulusMeshBuilder {
848            annulus: *self,
849            ..Default::default()
850        }
851    }
852}
853
854impl From<Annulus> for Mesh {
855    fn from(annulus: Annulus) -> Self {
856        annulus.mesh().build()
857    }
858}
859
860/// A builder for creating a [`Mesh`] with an [`Rhombus`] shape.
861#[derive(Clone, Copy, Debug, Reflect)]
862#[reflect(Default, Debug, Clone)]
863pub struct RhombusMeshBuilder {
864    half_diagonals: Vec2,
865}
866
867impl Default for RhombusMeshBuilder {
868    /// Returns the default [`RhombusMeshBuilder`] with a half-horizontal and half-vertical diagonal of `0.5`.
869    fn default() -> Self {
870        Self {
871            half_diagonals: Vec2::splat(0.5),
872        }
873    }
874}
875
876impl RhombusMeshBuilder {
877    /// Creates a new [`RhombusMeshBuilder`] from a horizontal and vertical diagonal size.
878    ///
879    /// # Panics
880    ///
881    /// Panics in debug mode if `horizontal_diagonal` or `vertical_diagonal` is negative.
882    pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
883        debug_assert!(
884            horizontal_diagonal >= 0.0,
885            "rhombus has a negative horizontal size",
886        );
887        debug_assert!(
888            vertical_diagonal >= 0.0,
889            "rhombus has a negative vertical size"
890        );
891
892        Self {
893            half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
894        }
895    }
896}
897
898impl MeshBuilder for RhombusMeshBuilder {
899    fn build(&self) -> Mesh {
900        let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
901        let positions = vec![
902            [hhd, 0.0, 0.0],
903            [0.0, vhd, 0.0],
904            [-hhd, 0.0, 0.0],
905            [0.0, -vhd, 0.0],
906        ];
907        let normals = vec![[0.0, 0.0, 1.0]; 4];
908        let uvs = vec![[1.0, 0.5], [0.5, 0.0], [0.0, 0.5], [0.5, 1.0]];
909        let indices = Indices::U32(vec![2, 0, 1, 2, 3, 0]);
910
911        Mesh::new(
912            PrimitiveTopology::TriangleList,
913            RenderAssetUsages::default(),
914        )
915        .with_inserted_indices(indices)
916        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
917        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
918        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
919    }
920}
921
922impl Extrudable for RhombusMeshBuilder {
923    fn perimeter(&self) -> Vec<PerimeterSegment> {
924        vec![PerimeterSegment::Flat {
925            indices: vec![0, 1, 2, 3, 0],
926        }]
927    }
928}
929
930impl Meshable for Rhombus {
931    type Output = RhombusMeshBuilder;
932
933    fn mesh(&self) -> Self::Output {
934        Self::Output {
935            half_diagonals: self.half_diagonals,
936        }
937    }
938}
939
940impl From<Rhombus> for Mesh {
941    fn from(rhombus: Rhombus) -> Self {
942        rhombus.mesh().build()
943    }
944}
945
946/// A builder used for creating a [`Mesh`] with a [`Triangle2d`] shape.
947#[derive(Clone, Copy, Debug, Default, Reflect)]
948#[reflect(Default, Debug, Clone)]
949pub struct Triangle2dMeshBuilder {
950    triangle: Triangle2d,
951}
952
953impl Triangle2dMeshBuilder {
954    /// Creates a new [`Triangle2dMeshBuilder`] from the points `a`, `b`, and `c`.
955    pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
956        Self {
957            triangle: Triangle2d::new(a, b, c),
958        }
959    }
960}
961
962impl Meshable for Triangle2d {
963    type Output = Triangle2dMeshBuilder;
964
965    fn mesh(&self) -> Self::Output {
966        Self::Output { triangle: *self }
967    }
968}
969
970impl MeshBuilder for Triangle2dMeshBuilder {
971    fn build(&self) -> Mesh {
972        let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
973
974        let positions: Vec<_> = vertices_3d.into();
975        let normals = vec![[0.0, 0.0, 1.0]; 3];
976
977        let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
978            vertices_3d[0],
979            vertices_3d[1],
980            vertices_3d[2],
981        ))
982        .into();
983
984        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
985        let indices = if is_ccw {
986            Indices::U32(vec![0, 1, 2])
987        } else {
988            Indices::U32(vec![2, 1, 0])
989        };
990
991        Mesh::new(
992            PrimitiveTopology::TriangleList,
993            RenderAssetUsages::default(),
994        )
995        .with_inserted_indices(indices)
996        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
997        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
998        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
999    }
1000}
1001
1002impl Extrudable for Triangle2dMeshBuilder {
1003    fn perimeter(&self) -> Vec<PerimeterSegment> {
1004        let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
1005        if is_ccw {
1006            vec![PerimeterSegment::Flat {
1007                indices: vec![0, 1, 2, 0],
1008            }]
1009        } else {
1010            vec![PerimeterSegment::Flat {
1011                indices: vec![2, 1, 0, 2],
1012            }]
1013        }
1014    }
1015}
1016
1017impl From<Triangle2d> for Mesh {
1018    fn from(triangle: Triangle2d) -> Self {
1019        triangle.mesh().build()
1020    }
1021}
1022
1023/// A builder used for creating a [`Mesh`] with a [`Rectangle`] shape.
1024#[derive(Clone, Copy, Debug, Reflect)]
1025#[reflect(Default, Debug, Clone)]
1026pub struct RectangleMeshBuilder {
1027    half_size: Vec2,
1028}
1029
1030impl Default for RectangleMeshBuilder {
1031    /// Returns the default [`RectangleMeshBuilder`] with a half-width and half-height of `0.5`.
1032    fn default() -> Self {
1033        Self {
1034            half_size: Vec2::splat(0.5),
1035        }
1036    }
1037}
1038
1039impl RectangleMeshBuilder {
1040    /// Creates a new [`RectangleMeshBuilder`] from a full width and height.
1041    ///
1042    /// # Panics
1043    ///
1044    /// Panics in debug mode if `width` or `height` is negative.
1045    pub const fn new(width: f32, height: f32) -> Self {
1046        debug_assert!(width >= 0.0, "rectangle has a negative width");
1047        debug_assert!(height >= 0.0, "rectangle has a negative height");
1048
1049        Self {
1050            half_size: Vec2::new(width / 2.0, height / 2.0),
1051        }
1052    }
1053}
1054
1055impl MeshBuilder for RectangleMeshBuilder {
1056    fn build(&self) -> Mesh {
1057        let [hw, hh] = [self.half_size.x, self.half_size.y];
1058        let positions = vec![
1059            [hw, hh, 0.0],
1060            [-hw, hh, 0.0],
1061            [-hw, -hh, 0.0],
1062            [hw, -hh, 0.0],
1063        ];
1064        let normals = vec![[0.0, 0.0, 1.0]; 4];
1065        let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
1066        let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
1067
1068        Mesh::new(
1069            PrimitiveTopology::TriangleList,
1070            RenderAssetUsages::default(),
1071        )
1072        .with_inserted_indices(indices)
1073        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1074        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1075        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1076    }
1077}
1078
1079impl Extrudable for RectangleMeshBuilder {
1080    fn perimeter(&self) -> Vec<PerimeterSegment> {
1081        vec![PerimeterSegment::Flat {
1082            indices: vec![0, 1, 2, 3, 0],
1083        }]
1084    }
1085}
1086
1087impl Meshable for Rectangle {
1088    type Output = RectangleMeshBuilder;
1089
1090    fn mesh(&self) -> Self::Output {
1091        RectangleMeshBuilder {
1092            half_size: self.half_size,
1093        }
1094    }
1095}
1096
1097impl From<Rectangle> for Mesh {
1098    fn from(rectangle: Rectangle) -> Self {
1099        rectangle.mesh().build()
1100    }
1101}
1102
1103/// A builder used for creating a [`Mesh`] with a [`Capsule2d`] shape.
1104#[derive(Clone, Copy, Debug, Reflect)]
1105#[reflect(Default, Debug, Clone)]
1106pub struct Capsule2dMeshBuilder {
1107    /// The [`Capsule2d`] shape.
1108    pub capsule: Capsule2d,
1109    /// The number of vertices used for one hemicircle.
1110    /// The total number of vertices for the capsule mesh will be two times the resolution.
1111    ///
1112    /// The default is `16`.
1113    pub resolution: u32,
1114}
1115
1116impl Default for Capsule2dMeshBuilder {
1117    fn default() -> Self {
1118        Self {
1119            capsule: Capsule2d::default(),
1120            resolution: 16,
1121        }
1122    }
1123}
1124
1125impl Capsule2dMeshBuilder {
1126    /// Creates a new [`Capsule2dMeshBuilder`] from a given radius, length, and the number of vertices
1127    /// used for one hemicircle. The total number of vertices for the capsule mesh will be two times the resolution.
1128    #[inline]
1129    pub fn new(radius: f32, length: f32, resolution: u32) -> Self {
1130        Self {
1131            capsule: Capsule2d::new(radius, length),
1132            resolution,
1133        }
1134    }
1135
1136    /// Sets the number of vertices used for one hemicircle.
1137    /// The total number of vertices for the capsule mesh will be two times the resolution.
1138    #[inline]
1139    pub const fn resolution(mut self, resolution: u32) -> Self {
1140        self.resolution = resolution;
1141        self
1142    }
1143}
1144
1145impl MeshBuilder for Capsule2dMeshBuilder {
1146    fn build(&self) -> Mesh {
1147        // The resolution is the number of vertices for one semicircle
1148        let resolution = self.resolution;
1149        let vertex_count = 2 * resolution;
1150
1151        // Six extra indices for the two triangles between the semicircles
1152        let mut indices = Vec::with_capacity((resolution as usize - 2) * 2 * 3 + 6);
1153        let mut positions = Vec::with_capacity(vertex_count as usize);
1154        let normals = vec![[0.0, 0.0, 1.0]; vertex_count as usize];
1155        let mut uvs = Vec::with_capacity(vertex_count as usize);
1156
1157        let radius = self.capsule.radius;
1158        let step = core::f32::consts::TAU / vertex_count as f32;
1159
1160        // If the vertex count is even, offset starting angle of top semicircle by half a step
1161        // to position the vertices evenly.
1162        let start_angle = if vertex_count.is_multiple_of(2) {
1163            step / 2.0
1164        } else {
1165            0.0
1166        };
1167
1168        // How much the hemicircle radius is of the total half-height of the capsule.
1169        // This is used to prevent the UVs from stretching between the semicircles.
1170        let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
1171
1172        // Create top semicircle
1173        for i in 0..resolution {
1174            // Compute vertex position at angle theta
1175            let theta = start_angle + i as f32 * step;
1176            let (sin, cos) = ops::sin_cos(theta);
1177            let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
1178
1179            positions.push([x, y, 0.0]);
1180            uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
1181        }
1182
1183        // Add top semicircle indices
1184        for i in 1..resolution - 1 {
1185            indices.extend_from_slice(&[0, i, i + 1]);
1186        }
1187
1188        // Add indices for top left triangle of the part between the semicircles
1189        indices.extend_from_slice(&[0, resolution - 1, resolution]);
1190
1191        // Create bottom semicircle
1192        for i in resolution..vertex_count {
1193            // Compute vertex position at angle theta
1194            let theta = start_angle + i as f32 * step;
1195            let (sin, cos) = ops::sin_cos(theta);
1196            let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
1197
1198            positions.push([x, y, 0.0]);
1199            uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
1200        }
1201
1202        // Add bottom semicircle indices
1203        for i in 1..resolution - 1 {
1204            indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
1205        }
1206
1207        // Add indices for bottom right triangle of the part between the semicircles
1208        indices.extend_from_slice(&[resolution, vertex_count - 1, 0]);
1209
1210        Mesh::new(
1211            PrimitiveTopology::TriangleList,
1212            RenderAssetUsages::default(),
1213        )
1214        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1215        .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1216        .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1217        .with_inserted_indices(Indices::U32(indices))
1218    }
1219}
1220
1221impl Extrudable for Capsule2dMeshBuilder {
1222    fn perimeter(&self) -> Vec<PerimeterSegment> {
1223        let resolution = self.resolution;
1224        let top_semi_indices = (0..resolution).collect();
1225        let bottom_semi_indices = (resolution..(2 * resolution)).collect();
1226        vec![
1227            PerimeterSegment::Smooth {
1228                first_normal: Vec2::X,
1229                last_normal: Vec2::NEG_X,
1230                indices: top_semi_indices,
1231            }, // Top semi-circle
1232            PerimeterSegment::Flat {
1233                indices: vec![resolution - 1, resolution],
1234            }, // Left edge
1235            PerimeterSegment::Smooth {
1236                first_normal: Vec2::NEG_X,
1237                last_normal: Vec2::X,
1238                indices: bottom_semi_indices,
1239            }, // Bottom semi-circle
1240            PerimeterSegment::Flat {
1241                indices: vec![2 * resolution - 1, 0],
1242            }, // Right edge
1243        ]
1244    }
1245}
1246
1247impl Meshable for Capsule2d {
1248    type Output = Capsule2dMeshBuilder;
1249
1250    fn mesh(&self) -> Self::Output {
1251        Capsule2dMeshBuilder {
1252            capsule: *self,
1253            ..Default::default()
1254        }
1255    }
1256}
1257
1258impl From<Capsule2d> for Mesh {
1259    fn from(capsule: Capsule2d) -> Self {
1260        capsule.mesh().build()
1261    }
1262}
1263
1264/// A builder used for creating a [`Mesh`] with a [`Ring`] shape.
1265pub struct RingMeshBuilder<P>
1266where
1267    P: Primitive2d + Meshable,
1268{
1269    pub outer_shape_builder: P::Output,
1270    pub inner_shape_builder: P::Output,
1271}
1272
1273impl<P> RingMeshBuilder<P>
1274where
1275    P: Primitive2d + Meshable,
1276{
1277    /// Create a new `RingMeshBuilder<P>` from a given `Ring<P>` shape.
1278    pub fn new(ring: &Ring<P>) -> Self {
1279        Self {
1280            outer_shape_builder: ring.outer_shape.mesh(),
1281            inner_shape_builder: ring.inner_shape.mesh(),
1282        }
1283    }
1284
1285    /// Apply a function to the inner builders
1286    pub fn with_inner(mut self, func: impl Fn(P::Output) -> P::Output) -> Self {
1287        self.outer_shape_builder = func(self.outer_shape_builder);
1288        self.inner_shape_builder = func(self.inner_shape_builder);
1289        self
1290    }
1291
1292    fn get_vertex_attributes(&self) -> Option<RingMeshBuilderVertexAttributes> {
1293        fn get_positions(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1294            if let VertexAttributeValues::Float32x3(data) =
1295                mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION)?
1296            {
1297                Some(data)
1298            } else {
1299                None
1300            }
1301        }
1302
1303        fn get_uvs(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 2]>> {
1304            if let VertexAttributeValues::Float32x2(data) =
1305                mesh.attribute_mut(Mesh::ATTRIBUTE_UV_0)?
1306            {
1307                Some(data)
1308            } else {
1309                None
1310            }
1311        }
1312
1313        fn get_normals(mesh: &mut Mesh) -> Option<&mut Vec<[f32; 3]>> {
1314            if let VertexAttributeValues::Float32x3(data) =
1315                mesh.attribute_mut(Mesh::ATTRIBUTE_NORMAL)?
1316            {
1317                Some(data)
1318            } else {
1319                None
1320            }
1321        }
1322
1323        let mut outer = self.outer_shape_builder.build();
1324        let mut inner = self.inner_shape_builder.build();
1325
1326        assert_eq!(
1327            outer.primitive_topology(),
1328            PrimitiveTopology::TriangleList,
1329            "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1330        );
1331        assert_eq!(
1332            inner.primitive_topology(),
1333            PrimitiveTopology::TriangleList,
1334            "PrimitiveTopology must be a TriangleList, mesh builder not compatible"
1335        );
1336
1337        Some(RingMeshBuilderVertexAttributes {
1338            outer_positions: mem::take(get_positions(&mut outer)?),
1339            inner_positions: mem::take(get_positions(&mut inner)?),
1340            outer_normals: mem::take(get_normals(&mut outer)?),
1341            inner_normals: mem::take(get_normals(&mut inner)?),
1342            outer_uvs: mem::take(get_uvs(&mut outer)?),
1343            inner_uvs: mem::take(get_uvs(&mut inner)?),
1344        })
1345    }
1346}
1347
1348struct RingMeshBuilderVertexAttributes {
1349    outer_positions: Vec<[f32; 3]>,
1350    inner_positions: Vec<[f32; 3]>,
1351    outer_normals: Vec<[f32; 3]>,
1352    inner_normals: Vec<[f32; 3]>,
1353    outer_uvs: Vec<[f32; 2]>,
1354    inner_uvs: Vec<[f32; 2]>,
1355}
1356
1357impl<P> MeshBuilder for RingMeshBuilder<P>
1358where
1359    P: Primitive2d + Meshable,
1360{
1361    /// Builds a [`Mesh`] based on the configuration in `self`.
1362    ///
1363    /// # Panics
1364    ///
1365    /// Panics if the following assumptions are not met.
1366    ///
1367    /// It is assumed that the inner and outer meshes have the same number of vertices.
1368    /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated
1369    /// a different number of vertices for the inner and outer instances of the primitive.
1370    ///
1371    /// It is assumed that the `primitive_topology` of the mesh returned by
1372    /// the underlying builder is [`PrimitiveTopology::TriangleList`]
1373    /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes.
1374    fn build(&self) -> Mesh {
1375        if let Some(RingMeshBuilderVertexAttributes {
1376            outer_uvs,
1377            inner_uvs,
1378            outer_positions,
1379            inner_positions,
1380            outer_normals,
1381            inner_normals,
1382        }) = self.get_vertex_attributes()
1383            && outer_uvs.len() == inner_uvs.len()
1384            && outer_positions.len() == inner_positions.len()
1385            && outer_normals.len() == inner_normals.len()
1386        {
1387            let mut uvs = outer_uvs;
1388            let inner_uvs = inner_positions
1389                .iter()
1390                .zip(&outer_positions)
1391                .zip(&inner_uvs)
1392                .map(|((inner_position, outer_position), inner_uv)| -> [f32; 2] {
1393                    const UV_CENTER: Vec2 = Vec2::splat(0.5);
1394                    let ip = Vec3::from(*inner_position).truncate();
1395                    let op = Vec3::from(*outer_position).truncate();
1396                    let uv = Vec2::from(*inner_uv) - UV_CENTER;
1397                    (uv * ip.length() / op.length() + UV_CENTER).into()
1398                });
1399            uvs.extend(inner_uvs);
1400
1401            let mut normals = outer_normals;
1402            normals.extend(inner_normals);
1403
1404            let points = outer_positions.len() as u32;
1405            let mut indices = Vec::with_capacity(outer_positions.len() * 6);
1406            for i in 0..points {
1407                //                               for five points:
1408                indices.push(i); //              0  1  2  3  4
1409                indices.push(i + 1); //          1  2  3  4  0  <-
1410                indices.push(points + i); //     0' 1' 2' 3' 4'
1411                indices.push(points + i); //     0' 1' 2' 3' 4'
1412                indices.push(i + 1); //          1  2  3  4  0  <-
1413                indices.push(points + i + 1); // 1' 2' 3' 4' 0' <-
1414            }
1415            let indices_length = indices.len();
1416            // Fix up the last pair of triangles (return to start)
1417            if let (_, [_, b, _, _, e, f]) = indices.split_at_mut(indices_length.saturating_sub(6))
1418            {
1419                *b = 0;
1420                *e = 0;
1421                *f = points;
1422            }
1423
1424            let mut positions = outer_positions;
1425            positions.extend_from_slice(&inner_positions);
1426
1427            Mesh::new(
1428                PrimitiveTopology::TriangleList,
1429                RenderAssetUsages::default(),
1430            )
1431            .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
1432            .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
1433            .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
1434            .with_inserted_indices(Indices::U32(indices))
1435        } else {
1436            panic!("The inner and outer meshes should have the same number of vertices, and have required attributes");
1437        }
1438    }
1439}
1440
1441impl<P> Extrudable for RingMeshBuilder<P>
1442where
1443    P: Primitive2d + Meshable,
1444    P::Output: Extrudable,
1445{
1446    /// A list of the indices each representing a part of the perimeter of the mesh.
1447    ///
1448    /// # Panics
1449    ///
1450    /// Panics if the following assumptions are not met.
1451    ///
1452    /// It is assumed that the inner and outer meshes have the same number of vertices.
1453    /// If not, then the [`MeshBuilder`] of the underlying 2d primitive has generated
1454    /// a different number of vertices for the inner and outer instances of the primitive.
1455    ///
1456    /// It is assumed that the `primitive_topology` of the mesh returned by
1457    /// the underlying builder is [`PrimitiveTopology::TriangleList`]
1458    /// and that the mesh has [`Mesh::ATTRIBUTE_POSITION`], [`Mesh::ATTRIBUTE_NORMAL`] and [`Mesh::ATTRIBUTE_UV_0`] attributes.
1459    fn perimeter(&self) -> Vec<PerimeterSegment> {
1460        let outer_vertex_count = self
1461            .get_vertex_attributes()
1462            .filter(|r| r.outer_positions.len() == r.inner_positions.len())
1463            .expect("The inner and outer meshes should have the same number of vertices, and have required attributes")
1464            .outer_positions
1465            .len();
1466
1467        let mut outer_perimeter = self.outer_shape_builder.perimeter();
1468        let inner_perimeter =
1469            self.inner_shape_builder
1470                .perimeter()
1471                .into_iter()
1472                .rev()
1473                .map(|segment| match segment {
1474                    PerimeterSegment::Smooth {
1475                        first_normal,
1476                        last_normal,
1477                        mut indices,
1478                    } => PerimeterSegment::Smooth {
1479                        first_normal: -last_normal,
1480                        last_normal: -first_normal,
1481                        indices: {
1482                            let outer_perimeter_vertex_count = outer_vertex_count as u32;
1483                            indices.reverse();
1484                            for i in &mut indices {
1485                                *i += outer_perimeter_vertex_count;
1486                            }
1487                            indices
1488                        },
1489                    },
1490                    PerimeterSegment::Flat { mut indices } => PerimeterSegment::Flat {
1491                        indices: {
1492                            let outer_perimeter_vertex_count = outer_vertex_count as u32;
1493                            indices.reverse();
1494                            for i in &mut indices {
1495                                *i += outer_perimeter_vertex_count;
1496                            }
1497                            indices
1498                        },
1499                    },
1500                });
1501
1502        outer_perimeter.extend(inner_perimeter);
1503        outer_perimeter
1504    }
1505}
1506
1507impl<P> Meshable for Ring<P>
1508where
1509    P: Primitive2d + Meshable,
1510{
1511    type Output = RingMeshBuilder<P>;
1512
1513    fn mesh(&self) -> Self::Output {
1514        RingMeshBuilder::new(self)
1515    }
1516}
1517
1518impl<P> From<Ring<P>> for Mesh
1519where
1520    P: Primitive2d + Meshable,
1521{
1522    fn from(ring: Ring<P>) -> Self {
1523        ring.mesh().build()
1524    }
1525}
1526
1527#[cfg(test)]
1528mod tests {
1529    use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd};
1530    use bevy_platform::collections::HashSet;
1531
1532    use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
1533
1534    fn count_distinct_positions(points: &[[f32; 3]]) -> usize {
1535        let mut map = <HashSet<_>>::default();
1536        for point in points {
1537            map.insert(point.map(FloatOrd));
1538        }
1539        map.len()
1540    }
1541
1542    #[test]
1543    fn test_annulus() {
1544        let mesh = Annulus::new(1.0, 1.2).mesh().resolution(16).build();
1545
1546        assert_eq!(
1547            32,
1548            count_distinct_positions(
1549                mesh.attribute(Mesh::ATTRIBUTE_POSITION)
1550                    .unwrap()
1551                    .as_float3()
1552                    .unwrap()
1553            )
1554        );
1555    }
1556
1557    /// Sin/cos and multiplication computations result in numbers like 0.4999999.
1558    /// Round these to numbers we expect like 0.5.
1559    fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1560        for point in points.iter_mut() {
1561            for coord in point.iter_mut() {
1562                let round = (*coord * 2.).round() / 2.;
1563                if (*coord - round).abs() < 0.00001 {
1564                    *coord = round;
1565                }
1566            }
1567        }
1568    }
1569
1570    #[test]
1571    fn test_regular_polygon() {
1572        let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1573
1574        let Some(VertexAttributeValues::Float32x3(mut positions)) =
1575            mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1576        else {
1577            panic!("Expected positions f32x3");
1578        };
1579        let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1580            mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1581        else {
1582            panic!("Expected uvs f32x2");
1583        };
1584        let Some(VertexAttributeValues::Float32x3(normals)) =
1585            mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1586        else {
1587            panic!("Expected normals f32x3");
1588        };
1589
1590        fix_floats(&mut positions);
1591        fix_floats(&mut uvs);
1592
1593        assert_eq!(
1594            [
1595                [0.0, 7.0, 0.0],
1596                [-7.0, 0.0, 0.0],
1597                [0.0, -7.0, 0.0],
1598                [7.0, 0.0, 0.0],
1599            ],
1600            &positions[..]
1601        );
1602
1603        // Note V coordinate increases in the opposite direction to the Y coordinate.
1604        assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1605
1606        assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1607    }
1608}