1use std::f32::consts::FRAC_PI_2;
2
3use crate::{
4 mesh::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment},
5 render_asset::RenderAssetUsages,
6};
7
8use super::{Extrudable, MeshBuilder, Meshable};
9use bevy_math::{
10 primitives::{
11 Annulus, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Rectangle,
12 RegularPolygon, Rhombus, Triangle2d, Triangle3d, WindingOrder,
13 },
14 FloatExt, Vec2,
15};
16use wgpu::PrimitiveTopology;
17
18#[derive(Clone, Copy, Debug)]
20pub struct CircleMeshBuilder {
21 pub circle: Circle,
23 #[doc(alias = "vertices")]
26 pub resolution: usize,
27}
28
29impl Default for CircleMeshBuilder {
30 fn default() -> Self {
31 Self {
32 circle: Circle::default(),
33 resolution: 32,
34 }
35 }
36}
37
38impl CircleMeshBuilder {
39 #[inline]
41 pub const fn new(radius: f32, resolution: usize) -> Self {
42 Self {
43 circle: Circle { radius },
44 resolution,
45 }
46 }
47
48 #[inline]
50 #[doc(alias = "vertices")]
51 pub const fn resolution(mut self, resolution: usize) -> Self {
52 self.resolution = resolution;
53 self
54 }
55}
56
57impl MeshBuilder for CircleMeshBuilder {
58 fn build(&self) -> Mesh {
59 RegularPolygon::new(self.circle.radius, self.resolution)
60 .mesh()
61 .build()
62 }
63}
64
65impl Extrudable for CircleMeshBuilder {
66 fn perimeter(&self) -> Vec<PerimeterSegment> {
67 vec![PerimeterSegment::Smooth {
68 first_normal: Vec2::Y,
69 last_normal: Vec2::Y,
70 indices: (0..self.resolution as u32).chain([0]).collect(),
71 }]
72 }
73}
74
75impl Meshable for Circle {
76 type Output = CircleMeshBuilder;
77
78 fn mesh(&self) -> Self::Output {
79 CircleMeshBuilder {
80 circle: *self,
81 ..Default::default()
82 }
83 }
84}
85
86impl From<Circle> for Mesh {
87 fn from(circle: Circle) -> Self {
88 circle.mesh().build()
89 }
90}
91
92#[derive(Copy, Clone, Debug, PartialEq)]
102#[non_exhaustive]
103pub enum CircularMeshUvMode {
104 Mask {
107 angle: f32,
109 },
110}
111
112impl Default for CircularMeshUvMode {
113 fn default() -> Self {
114 CircularMeshUvMode::Mask { angle: 0.0 }
115 }
116}
117
118#[derive(Clone, Debug)]
123pub struct CircularSectorMeshBuilder {
124 pub sector: CircularSector,
126 #[doc(alias = "vertices")]
129 pub resolution: usize,
130 pub uv_mode: CircularMeshUvMode,
132}
133
134impl Default for CircularSectorMeshBuilder {
135 fn default() -> Self {
136 Self {
137 sector: CircularSector::default(),
138 resolution: 32,
139 uv_mode: CircularMeshUvMode::default(),
140 }
141 }
142}
143
144impl CircularSectorMeshBuilder {
145 #[inline]
147 pub fn new(sector: CircularSector) -> Self {
148 Self {
149 sector,
150 ..Self::default()
151 }
152 }
153
154 #[inline]
156 #[doc(alias = "vertices")]
157 pub const fn resolution(mut self, resolution: usize) -> Self {
158 self.resolution = resolution;
159 self
160 }
161
162 #[inline]
164 pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
165 self.uv_mode = uv_mode;
166 self
167 }
168}
169
170impl MeshBuilder for CircularSectorMeshBuilder {
171 fn build(&self) -> Mesh {
172 let mut indices = Vec::with_capacity((self.resolution - 1) * 3);
173 let mut positions = Vec::with_capacity(self.resolution + 1);
174 let normals = vec![[0.0, 0.0, 1.0]; self.resolution + 1];
175 let mut uvs = Vec::with_capacity(self.resolution + 1);
176
177 let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
178
179 positions.push([0.0; 3]);
181 uvs.push([0.5; 2]);
182
183 let first_angle = FRAC_PI_2 - self.sector.half_angle();
184 let last_angle = FRAC_PI_2 + self.sector.half_angle();
185 let last_i = (self.resolution - 1) as f32;
186 for i in 0..self.resolution {
187 let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
188
189 let vertex = self.sector.radius() * Vec2::from_angle(angle);
191 let uv =
194 Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
195
196 positions.push([vertex.x, vertex.y, 0.0]);
197 uvs.push([uv.x, uv.y]);
198 }
199
200 for i in 1..(self.resolution as u32) {
201 indices.extend_from_slice(&[0, i, i + 1]);
203 }
204
205 Mesh::new(
206 PrimitiveTopology::TriangleList,
207 RenderAssetUsages::default(),
208 )
209 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
210 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
211 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
212 .with_inserted_indices(Indices::U32(indices))
213 }
214}
215
216impl Extrudable for CircularSectorMeshBuilder {
217 fn perimeter(&self) -> Vec<PerimeterSegment> {
218 let resolution = self.resolution as u32;
219 let (sin, cos) = self.sector.arc.half_angle.sin_cos();
220 let first_normal = Vec2::new(sin, cos);
221 let last_normal = Vec2::new(-sin, cos);
222 vec![
223 PerimeterSegment::Flat {
224 indices: vec![resolution, 0, 1],
225 },
226 PerimeterSegment::Smooth {
227 first_normal,
228 last_normal,
229 indices: (1..=resolution).collect(),
230 },
231 ]
232 }
233}
234
235impl Meshable for CircularSector {
236 type Output = CircularSectorMeshBuilder;
237
238 fn mesh(&self) -> Self::Output {
239 CircularSectorMeshBuilder {
240 sector: *self,
241 ..Default::default()
242 }
243 }
244}
245
246impl From<CircularSector> for Mesh {
247 fn from(sector: CircularSector) -> Self {
251 sector.mesh().build()
252 }
253}
254
255#[derive(Clone, Copy, Debug)]
260pub struct CircularSegmentMeshBuilder {
261 pub segment: CircularSegment,
263 #[doc(alias = "vertices")]
266 pub resolution: usize,
267 pub uv_mode: CircularMeshUvMode,
269}
270
271impl Default for CircularSegmentMeshBuilder {
272 fn default() -> Self {
273 Self {
274 segment: CircularSegment::default(),
275 resolution: 32,
276 uv_mode: CircularMeshUvMode::default(),
277 }
278 }
279}
280
281impl CircularSegmentMeshBuilder {
282 #[inline]
284 pub fn new(segment: CircularSegment) -> Self {
285 Self {
286 segment,
287 ..Self::default()
288 }
289 }
290
291 #[inline]
293 #[doc(alias = "vertices")]
294 pub const fn resolution(mut self, resolution: usize) -> Self {
295 self.resolution = resolution;
296 self
297 }
298
299 #[inline]
301 pub const fn uv_mode(mut self, uv_mode: CircularMeshUvMode) -> Self {
302 self.uv_mode = uv_mode;
303 self
304 }
305}
306
307impl MeshBuilder for CircularSegmentMeshBuilder {
308 fn build(&self) -> Mesh {
309 let mut indices = Vec::with_capacity((self.resolution - 1) * 3);
310 let mut positions = Vec::with_capacity(self.resolution + 1);
311 let normals = vec![[0.0, 0.0, 1.0]; self.resolution + 1];
312 let mut uvs = Vec::with_capacity(self.resolution + 1);
313
314 let CircularMeshUvMode::Mask { angle: uv_angle } = self.uv_mode;
315
316 let midpoint_vertex = self.segment.chord_midpoint();
318 positions.push([midpoint_vertex.x, midpoint_vertex.y, 0.0]);
319 let midpoint_uv = Vec2::from_angle(-uv_angle - FRAC_PI_2).mul_add(
324 Vec2::splat(0.5 * (self.segment.apothem() / self.segment.radius())),
325 Vec2::splat(0.5),
326 );
327 uvs.push([midpoint_uv.x, midpoint_uv.y]);
328
329 let first_angle = FRAC_PI_2 - self.segment.half_angle();
330 let last_angle = FRAC_PI_2 + self.segment.half_angle();
331 let last_i = (self.resolution - 1) as f32;
332 for i in 0..self.resolution {
333 let angle = f32::lerp(first_angle, last_angle, i as f32 / last_i);
334
335 let vertex = self.segment.radius() * Vec2::from_angle(angle);
337 let uv =
340 Vec2::from_angle(-(angle + uv_angle)).mul_add(Vec2::splat(0.5), Vec2::splat(0.5));
341
342 positions.push([vertex.x, vertex.y, 0.0]);
343 uvs.push([uv.x, uv.y]);
344 }
345
346 for i in 1..(self.resolution as u32) {
347 indices.extend_from_slice(&[0, i, i + 1]);
349 }
350
351 Mesh::new(
352 PrimitiveTopology::TriangleList,
353 RenderAssetUsages::default(),
354 )
355 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
356 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
357 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
358 .with_inserted_indices(Indices::U32(indices))
359 }
360}
361
362impl Extrudable for CircularSegmentMeshBuilder {
363 fn perimeter(&self) -> Vec<PerimeterSegment> {
364 let resolution = self.resolution as u32;
365 let (sin, cos) = self.segment.arc.half_angle.sin_cos();
366 let first_normal = Vec2::new(sin, cos);
367 let last_normal = Vec2::new(-sin, cos);
368 vec![
369 PerimeterSegment::Flat {
370 indices: vec![resolution, 0, 1],
371 },
372 PerimeterSegment::Smooth {
373 first_normal,
374 last_normal,
375 indices: (1..=resolution).collect(),
376 },
377 ]
378 }
379}
380
381impl Meshable for CircularSegment {
382 type Output = CircularSegmentMeshBuilder;
383
384 fn mesh(&self) -> Self::Output {
385 CircularSegmentMeshBuilder {
386 segment: *self,
387 ..Default::default()
388 }
389 }
390}
391
392impl From<CircularSegment> for Mesh {
393 fn from(segment: CircularSegment) -> Self {
397 segment.mesh().build()
398 }
399}
400
401pub struct RegularPolygonMeshBuilder {
403 circumradius: f32,
404 sides: usize,
405}
406impl Meshable for RegularPolygon {
407 type Output = RegularPolygonMeshBuilder;
408
409 fn mesh(&self) -> Self::Output {
410 Self::Output {
411 circumradius: self.circumcircle.radius,
412 sides: self.sides,
413 }
414 }
415}
416
417impl MeshBuilder for RegularPolygonMeshBuilder {
418 fn build(&self) -> Mesh {
419 Ellipse::new(self.circumradius, self.circumradius)
421 .mesh()
422 .resolution(self.sides)
423 .build()
424 }
425}
426
427impl Extrudable for RegularPolygonMeshBuilder {
428 fn perimeter(&self) -> Vec<PerimeterSegment> {
429 vec![PerimeterSegment::Flat {
430 indices: (0..self.sides as u32).chain([0]).collect(),
431 }]
432 }
433}
434
435impl From<RegularPolygon> for Mesh {
436 fn from(polygon: RegularPolygon) -> Self {
437 polygon.mesh().build()
438 }
439}
440
441#[derive(Clone, Copy, Debug)]
443pub struct EllipseMeshBuilder {
444 pub ellipse: Ellipse,
446 #[doc(alias = "vertices")]
449 pub resolution: usize,
450}
451
452impl Default for EllipseMeshBuilder {
453 fn default() -> Self {
454 Self {
455 ellipse: Ellipse::default(),
456 resolution: 32,
457 }
458 }
459}
460
461impl EllipseMeshBuilder {
462 #[inline]
464 pub const fn new(half_width: f32, half_height: f32, resolution: usize) -> Self {
465 Self {
466 ellipse: Ellipse::new(half_width, half_height),
467 resolution,
468 }
469 }
470
471 #[inline]
473 #[doc(alias = "vertices")]
474 pub const fn resolution(mut self, resolution: usize) -> Self {
475 self.resolution = resolution;
476 self
477 }
478}
479
480impl MeshBuilder for EllipseMeshBuilder {
481 fn build(&self) -> Mesh {
482 let mut indices = Vec::with_capacity((self.resolution - 2) * 3);
483 let mut positions = Vec::with_capacity(self.resolution);
484 let normals = vec![[0.0, 0.0, 1.0]; self.resolution];
485 let mut uvs = Vec::with_capacity(self.resolution);
486
487 let start_angle = std::f32::consts::FRAC_PI_2;
489 let step = std::f32::consts::TAU / self.resolution as f32;
490
491 for i in 0..self.resolution {
492 let theta = start_angle + i as f32 * step;
494 let (sin, cos) = theta.sin_cos();
495 let x = cos * self.ellipse.half_size.x;
496 let y = sin * self.ellipse.half_size.y;
497
498 positions.push([x, y, 0.0]);
499 uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
500 }
501
502 for i in 1..(self.resolution as u32 - 1) {
503 indices.extend_from_slice(&[0, i, i + 1]);
504 }
505
506 Mesh::new(
507 PrimitiveTopology::TriangleList,
508 RenderAssetUsages::default(),
509 )
510 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
511 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
512 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
513 .with_inserted_indices(Indices::U32(indices))
514 }
515}
516
517impl Extrudable for EllipseMeshBuilder {
518 fn perimeter(&self) -> Vec<PerimeterSegment> {
519 vec![PerimeterSegment::Smooth {
520 first_normal: Vec2::Y,
521 last_normal: Vec2::Y,
522 indices: (0..self.resolution as u32).chain([0]).collect(),
523 }]
524 }
525}
526
527impl Meshable for Ellipse {
528 type Output = EllipseMeshBuilder;
529
530 fn mesh(&self) -> Self::Output {
531 EllipseMeshBuilder {
532 ellipse: *self,
533 ..Default::default()
534 }
535 }
536}
537
538impl From<Ellipse> for Mesh {
539 fn from(ellipse: Ellipse) -> Self {
540 ellipse.mesh().build()
541 }
542}
543
544pub struct AnnulusMeshBuilder {
546 pub annulus: Annulus,
548
549 pub resolution: usize,
552}
553
554impl Default for AnnulusMeshBuilder {
555 fn default() -> Self {
556 Self {
557 annulus: Annulus::default(),
558 resolution: 32,
559 }
560 }
561}
562
563impl AnnulusMeshBuilder {
564 #[inline]
566 pub fn new(inner_radius: f32, outer_radius: f32, resolution: usize) -> Self {
567 Self {
568 annulus: Annulus::new(inner_radius, outer_radius),
569 resolution,
570 }
571 }
572
573 #[inline]
575 pub fn resolution(mut self, resolution: usize) -> Self {
576 self.resolution = resolution;
577 self
578 }
579}
580
581impl MeshBuilder for AnnulusMeshBuilder {
582 fn build(&self) -> Mesh {
583 let inner_radius = self.annulus.inner_circle.radius;
584 let outer_radius = self.annulus.outer_circle.radius;
585
586 let num_vertices = (self.resolution + 1) * 2;
587 let mut indices = Vec::with_capacity(self.resolution * 6);
588 let mut positions = Vec::with_capacity(num_vertices);
589 let mut uvs = Vec::with_capacity(num_vertices);
590 let normals = vec![[0.0, 0.0, 1.0]; num_vertices];
591
592 let start_angle = std::f32::consts::FRAC_PI_2;
597 let step = std::f32::consts::TAU / self.resolution as f32;
598 for i in 0..=self.resolution {
599 let theta = start_angle + i as f32 * step;
600 let (sin, cos) = theta.sin_cos();
601 let inner_pos = [cos * inner_radius, sin * inner_radius, 0.];
602 let outer_pos = [cos * outer_radius, sin * outer_radius, 0.];
603 positions.push(inner_pos);
604 positions.push(outer_pos);
605
606 let inner_uv = [0., i as f32 / self.resolution as f32];
611 let outer_uv = [1., i as f32 / self.resolution as f32];
612 uvs.push(inner_uv);
613 uvs.push(outer_uv);
614 }
615
616 for i in 0..(self.resolution as u32) {
621 let inner_vertex = 2 * i;
622 let outer_vertex = 2 * i + 1;
623 let next_inner = inner_vertex + 2;
624 let next_outer = outer_vertex + 2;
625 indices.extend_from_slice(&[inner_vertex, outer_vertex, next_outer]);
626 indices.extend_from_slice(&[next_outer, next_inner, inner_vertex]);
627 }
628
629 Mesh::new(
630 PrimitiveTopology::TriangleList,
631 RenderAssetUsages::default(),
632 )
633 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
634 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
635 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
636 .with_inserted_indices(Indices::U32(indices))
637 }
638}
639
640impl Extrudable for AnnulusMeshBuilder {
641 fn perimeter(&self) -> Vec<PerimeterSegment> {
642 let vert_count = 2 * self.resolution as u32;
643 vec![
644 PerimeterSegment::Smooth {
645 first_normal: Vec2::NEG_Y,
646 last_normal: Vec2::NEG_Y,
647 indices: (0..vert_count).step_by(2).chain([0]).rev().collect(), },
649 PerimeterSegment::Smooth {
650 first_normal: Vec2::Y,
651 last_normal: Vec2::Y,
652 indices: (1..vert_count).step_by(2).chain([1]).collect(), },
654 ]
655 }
656}
657
658impl Meshable for Annulus {
659 type Output = AnnulusMeshBuilder;
660
661 fn mesh(&self) -> Self::Output {
662 AnnulusMeshBuilder {
663 annulus: *self,
664 ..Default::default()
665 }
666 }
667}
668
669impl From<Annulus> for Mesh {
670 fn from(annulus: Annulus) -> Self {
671 annulus.mesh().build()
672 }
673}
674
675pub struct RhombusMeshBuilder {
676 half_diagonals: Vec2,
677}
678
679impl MeshBuilder for RhombusMeshBuilder {
680 fn build(&self) -> Mesh {
681 let [hhd, vhd] = [self.half_diagonals.x, self.half_diagonals.y];
682 let positions = vec![
683 [hhd, 0.0, 0.0],
684 [-hhd, 0.0, 0.0],
685 [0.0, vhd, 0.0],
686 [0.0, -vhd, 0.0],
687 ];
688 let normals = vec![[0.0, 0.0, 1.0]; 4];
689 let uvs = vec![[1.0, 0.5], [0.0, 0.5], [0.5, 0.0], [0.5, 1.0]];
690 let indices = Indices::U32(vec![1, 0, 2, 1, 3, 0]);
691
692 Mesh::new(
693 PrimitiveTopology::TriangleList,
694 RenderAssetUsages::default(),
695 )
696 .with_inserted_indices(indices)
697 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
698 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
699 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
700 }
701}
702
703impl Extrudable for RhombusMeshBuilder {
704 fn perimeter(&self) -> Vec<PerimeterSegment> {
705 vec![PerimeterSegment::Flat {
706 indices: vec![0, 2, 1, 3, 0],
707 }]
708 }
709}
710
711impl Meshable for Rhombus {
712 type Output = RhombusMeshBuilder;
713
714 fn mesh(&self) -> Self::Output {
715 Self::Output {
716 half_diagonals: self.half_diagonals,
717 }
718 }
719}
720
721impl From<Rhombus> for Mesh {
722 fn from(rhombus: Rhombus) -> Self {
723 rhombus.mesh().build()
724 }
725}
726
727pub struct Triangle2dMeshBuilder {
729 triangle: Triangle2d,
730}
731impl Meshable for Triangle2d {
732 type Output = Triangle2dMeshBuilder;
733
734 fn mesh(&self) -> Self::Output {
735 Self::Output { triangle: *self }
736 }
737}
738impl MeshBuilder for Triangle2dMeshBuilder {
739 fn build(&self) -> Mesh {
740 let vertices_3d = self.triangle.vertices.map(|v| v.extend(0.));
741
742 let positions: Vec<_> = vertices_3d.into();
743 let normals = vec![[0.0, 0.0, 1.0]; 3];
744
745 let uvs: Vec<_> = triangle3d::uv_coords(&Triangle3d::new(
746 vertices_3d[0],
747 vertices_3d[1],
748 vertices_3d[2],
749 ))
750 .into();
751
752 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
753 let indices = if is_ccw {
754 Indices::U32(vec![0, 1, 2])
755 } else {
756 Indices::U32(vec![2, 1, 0])
757 };
758
759 Mesh::new(
760 PrimitiveTopology::TriangleList,
761 RenderAssetUsages::default(),
762 )
763 .with_inserted_indices(indices)
764 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
765 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
766 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
767 }
768}
769
770impl Extrudable for Triangle2dMeshBuilder {
771 fn perimeter(&self) -> Vec<PerimeterSegment> {
772 let is_ccw = self.triangle.winding_order() == WindingOrder::CounterClockwise;
773 if is_ccw {
774 vec![PerimeterSegment::Flat {
775 indices: vec![0, 1, 2, 0],
776 }]
777 } else {
778 vec![PerimeterSegment::Flat {
779 indices: vec![2, 1, 0, 2],
780 }]
781 }
782 }
783}
784
785impl From<Triangle2d> for Mesh {
786 fn from(triangle: Triangle2d) -> Self {
787 triangle.mesh().build()
788 }
789}
790
791pub struct RectangleMeshBuilder {
793 half_size: Vec2,
794}
795
796impl MeshBuilder for RectangleMeshBuilder {
797 fn build(&self) -> Mesh {
798 let [hw, hh] = [self.half_size.x, self.half_size.y];
799 let positions = vec![
800 [hw, hh, 0.0],
801 [-hw, hh, 0.0],
802 [-hw, -hh, 0.0],
803 [hw, -hh, 0.0],
804 ];
805 let normals = vec![[0.0, 0.0, 1.0]; 4];
806 let uvs = vec![[1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [1.0, 1.0]];
807 let indices = Indices::U32(vec![0, 1, 2, 0, 2, 3]);
808
809 Mesh::new(
810 PrimitiveTopology::TriangleList,
811 RenderAssetUsages::default(),
812 )
813 .with_inserted_indices(indices)
814 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
815 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
816 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
817 }
818}
819
820impl Extrudable for RectangleMeshBuilder {
821 fn perimeter(&self) -> Vec<PerimeterSegment> {
822 vec![PerimeterSegment::Flat {
823 indices: vec![0, 1, 2, 3, 0],
824 }]
825 }
826}
827
828impl Meshable for Rectangle {
829 type Output = RectangleMeshBuilder;
830
831 fn mesh(&self) -> Self::Output {
832 RectangleMeshBuilder {
833 half_size: self.half_size,
834 }
835 }
836}
837
838impl From<Rectangle> for Mesh {
839 fn from(rectangle: Rectangle) -> Self {
840 rectangle.mesh().build()
841 }
842}
843
844#[derive(Clone, Copy, Debug)]
846pub struct Capsule2dMeshBuilder {
847 pub capsule: Capsule2d,
849 pub resolution: usize,
854}
855
856impl Default for Capsule2dMeshBuilder {
857 fn default() -> Self {
858 Self {
859 capsule: Capsule2d::default(),
860 resolution: 16,
861 }
862 }
863}
864
865impl Capsule2dMeshBuilder {
866 #[inline]
869 pub fn new(radius: f32, length: f32, resolution: usize) -> Self {
870 Self {
871 capsule: Capsule2d::new(radius, length),
872 resolution,
873 }
874 }
875
876 #[inline]
879 pub const fn resolution(mut self, resolution: usize) -> Self {
880 self.resolution = resolution;
881 self
882 }
883}
884
885impl MeshBuilder for Capsule2dMeshBuilder {
886 fn build(&self) -> Mesh {
887 let resolution = self.resolution as u32;
889 let vertex_count = 2 * self.resolution;
890
891 let mut indices = Vec::with_capacity((self.resolution - 2) * 2 * 3 + 6);
893 let mut positions = Vec::with_capacity(vertex_count);
894 let normals = vec![[0.0, 0.0, 1.0]; vertex_count];
895 let mut uvs = Vec::with_capacity(vertex_count);
896
897 let radius = self.capsule.radius;
898 let step = std::f32::consts::TAU / vertex_count as f32;
899
900 let start_angle = if vertex_count % 2 == 0 {
903 step / 2.0
904 } else {
905 0.0
906 };
907
908 let radius_frac = self.capsule.radius / (self.capsule.half_length + self.capsule.radius);
911
912 for i in 0..resolution {
914 let theta = start_angle + i as f32 * step;
916 let (sin, cos) = theta.sin_cos();
917 let (x, y) = (cos * radius, sin * radius + self.capsule.half_length);
918
919 positions.push([x, y, 0.0]);
920 uvs.push([0.5 * (cos + 1.0), radius_frac * (1.0 - 0.5 * (sin + 1.0))]);
921 }
922
923 for i in 1..resolution - 1 {
925 indices.extend_from_slice(&[0, i, i + 1]);
926 }
927
928 indices.extend_from_slice(&[0, resolution - 1, resolution]);
930
931 for i in resolution..vertex_count as u32 {
933 let theta = start_angle + i as f32 * step;
935 let (sin, cos) = theta.sin_cos();
936 let (x, y) = (cos * radius, sin * radius - self.capsule.half_length);
937
938 positions.push([x, y, 0.0]);
939 uvs.push([0.5 * (cos + 1.0), 1.0 - radius_frac * 0.5 * (sin + 1.0)]);
940 }
941
942 for i in 1..resolution - 1 {
944 indices.extend_from_slice(&[resolution, resolution + i, resolution + i + 1]);
945 }
946
947 indices.extend_from_slice(&[resolution, vertex_count as u32 - 1, 0]);
949
950 Mesh::new(
951 PrimitiveTopology::TriangleList,
952 RenderAssetUsages::default(),
953 )
954 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
955 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
956 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
957 .with_inserted_indices(Indices::U32(indices))
958 }
959}
960
961impl Extrudable for Capsule2dMeshBuilder {
962 fn perimeter(&self) -> Vec<PerimeterSegment> {
963 let resolution = self.resolution as u32;
964 let top_semi_indices = (0..resolution).collect();
965 let bottom_semi_indices = (resolution..(2 * resolution)).collect();
966 vec![
967 PerimeterSegment::Smooth {
968 first_normal: Vec2::X,
969 last_normal: Vec2::NEG_X,
970 indices: top_semi_indices,
971 }, PerimeterSegment::Flat {
973 indices: vec![resolution - 1, resolution],
974 }, PerimeterSegment::Smooth {
976 first_normal: Vec2::NEG_X,
977 last_normal: Vec2::X,
978 indices: bottom_semi_indices,
979 }, PerimeterSegment::Flat {
981 indices: vec![2 * resolution - 1, 0],
982 }, ]
984 }
985}
986
987impl Meshable for Capsule2d {
988 type Output = Capsule2dMeshBuilder;
989
990 fn mesh(&self) -> Self::Output {
991 Capsule2dMeshBuilder {
992 capsule: *self,
993 ..Default::default()
994 }
995 }
996}
997
998impl From<Capsule2d> for Mesh {
999 fn from(capsule: Capsule2d) -> Self {
1000 capsule.mesh().build()
1001 }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006 use bevy_math::primitives::RegularPolygon;
1007
1008 use crate::mesh::{Mesh, VertexAttributeValues};
1009
1010 fn fix_floats<const N: usize>(points: &mut [[f32; N]]) {
1013 for point in points.iter_mut() {
1014 for coord in point.iter_mut() {
1015 let round = (*coord * 2.).round() / 2.;
1016 if (*coord - round).abs() < 0.00001 {
1017 *coord = round;
1018 }
1019 }
1020 }
1021 }
1022
1023 #[test]
1024 fn test_regular_polygon() {
1025 let mut mesh = Mesh::from(RegularPolygon::new(7.0, 4));
1026
1027 let Some(VertexAttributeValues::Float32x3(mut positions)) =
1028 mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
1029 else {
1030 panic!("Expected positions f32x3");
1031 };
1032 let Some(VertexAttributeValues::Float32x2(mut uvs)) =
1033 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_0)
1034 else {
1035 panic!("Expected uvs f32x2");
1036 };
1037 let Some(VertexAttributeValues::Float32x3(normals)) =
1038 mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
1039 else {
1040 panic!("Expected normals f32x3");
1041 };
1042
1043 fix_floats(&mut positions);
1044 fix_floats(&mut uvs);
1045
1046 assert_eq!(
1047 [
1048 [0.0, 7.0, 0.0],
1049 [-7.0, 0.0, 0.0],
1050 [0.0, -7.0, 0.0],
1051 [7.0, 0.0, 0.0],
1052 ],
1053 &positions[..]
1054 );
1055
1056 assert_eq!([[0.5, 0.0], [0.0, 0.5], [0.5, 1.0], [1.0, 0.5],], &uvs[..]);
1058
1059 assert_eq!(&[[0.0, 0.0, 1.0]; 4], &normals[..]);
1060 }
1061}