Skip to main content

bevy_mesh/primitives/
dim2.rs

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