Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/primitives/dim2.rs
9294 views
1
use core::f32::consts::FRAC_PI_2;
2
use core::mem;
3
4
use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment, VertexAttributeValues};
5
use bevy_asset::RenderAssetUsages;
6
7
use super::{Extrudable, MeshBuilder, Meshable};
8
use bevy_math::prelude::Polyline2d;
9
use 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
};
18
use bevy_reflect::prelude::*;
19
use 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)]
24
pub 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
33
impl Default for CircleMeshBuilder {
34
fn default() -> Self {
35
Self {
36
circle: Circle::default(),
37
resolution: 32,
38
}
39
}
40
}
41
42
impl 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
61
impl 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
70
impl 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
80
impl 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
91
impl 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]
109
pub 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
118
impl 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)]
130
pub 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
141
impl 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
151
impl 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
177
impl 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
224
impl 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
242
impl 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
253
impl 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)]
268
pub 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
279
impl 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
289
impl 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
315
impl 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
371
impl 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
389
impl 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
400
impl 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)]
415
pub struct ConvexPolygonMeshBuilder {
416
pub vertices: Vec<Vec2>,
417
}
418
419
impl 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
429
impl 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
450
impl 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
458
impl 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)]
467
pub struct RegularPolygonMeshBuilder {
468
circumradius: f32,
469
sides: u32,
470
}
471
472
impl 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
482
impl 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
503
impl 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
514
impl 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
524
impl 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
532
impl 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)]
541
pub 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
550
impl Default for EllipseMeshBuilder {
551
fn default() -> Self {
552
Self {
553
ellipse: Ellipse::default(),
554
resolution: 32,
555
}
556
}
557
}
558
559
impl 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
578
impl 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
616
impl 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
626
impl 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
637
impl 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`].
644
pub struct Segment2dMeshBuilder {
645
/// The [`Segment2d`] shape.
646
pub segment: Segment2d,
647
}
648
649
impl 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
657
impl 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
668
impl Meshable for Segment2d {
669
type Output = Segment2dMeshBuilder;
670
671
fn mesh(&self) -> Self::Output {
672
Segment2dMeshBuilder::new(*self)
673
}
674
}
675
676
impl 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)]
686
pub struct Polyline2dMeshBuilder {
687
polyline: Polyline2d,
688
}
689
690
impl 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
711
impl Meshable for Polyline2d {
712
type Output = Polyline2dMeshBuilder;
713
714
fn mesh(&self) -> Self::Output {
715
Polyline2dMeshBuilder {
716
polyline: self.clone(),
717
}
718
}
719
}
720
721
impl 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)]
730
pub 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
739
impl Default for AnnulusMeshBuilder {
740
fn default() -> Self {
741
Self {
742
annulus: Annulus::default(),
743
resolution: 32,
744
}
745
}
746
}
747
748
impl 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
766
impl 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
825
impl 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
843
impl 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
854
impl 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)]
863
pub struct RhombusMeshBuilder {
864
half_diagonals: Vec2,
865
}
866
867
impl 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
876
impl 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
898
impl 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
922
impl 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
930
impl 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
940
impl 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)]
949
pub struct Triangle2dMeshBuilder {
950
triangle: Triangle2d,
951
}
952
953
impl 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
962
impl Meshable for Triangle2d {
963
type Output = Triangle2dMeshBuilder;
964
965
fn mesh(&self) -> Self::Output {
966
Self::Output { triangle: *self }
967
}
968
}
969
970
impl 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
1002
impl 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
1017
impl 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)]
1026
pub struct RectangleMeshBuilder {
1027
half_size: Vec2,
1028
}
1029
1030
impl 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
1039
impl 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
1055
impl 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
1079
impl 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
1087
impl 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
1097
impl 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)]
1106
pub 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
1116
impl Default for Capsule2dMeshBuilder {
1117
fn default() -> Self {
1118
Self {
1119
capsule: Capsule2d::default(),
1120
resolution: 16,
1121
}
1122
}
1123
}
1124
1125
impl 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
1145
impl 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
1221
impl 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
1247
impl 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
1258
impl 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.
1265
pub struct RingMeshBuilder<P>
1266
where
1267
P: Primitive2d + Meshable,
1268
{
1269
pub outer_shape_builder: P::Output,
1270
pub inner_shape_builder: P::Output,
1271
}
1272
1273
impl<P> RingMeshBuilder<P>
1274
where
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
1348
struct 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
1357
impl<P> MeshBuilder for RingMeshBuilder<P>
1358
where
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
1441
impl<P> Extrudable for RingMeshBuilder<P>
1442
where
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
1507
impl<P> Meshable for Ring<P>
1508
where
1509
P: Primitive2d + Meshable,
1510
{
1511
type Output = RingMeshBuilder<P>;
1512
1513
fn mesh(&self) -> Self::Output {
1514
RingMeshBuilder::new(self)
1515
}
1516
}
1517
1518
impl<P> From<Ring<P>> for Mesh
1519
where
1520
P: Primitive2d + Meshable,
1521
{
1522
fn from(ring: Ring<P>) -> Self {
1523
ring.mesh().build()
1524
}
1525
}
1526
1527
#[cfg(test)]
1528
mod 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
}
1609
1610