Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/primitives/dim3/torus.rs
6598 views
1
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
2
use bevy_asset::RenderAssetUsages;
3
use bevy_math::{ops, primitives::Torus, Vec3};
4
use bevy_reflect::prelude::*;
5
use core::ops::RangeInclusive;
6
7
/// A builder used for creating a [`Mesh`] with a [`Torus`] shape.
8
#[derive(Clone, Debug, Reflect)]
9
#[reflect(Default, Debug, Clone)]
10
pub struct TorusMeshBuilder {
11
/// The [`Torus`] shape.
12
pub torus: Torus,
13
/// The number of vertices used for each circular segment
14
/// in the ring or tube of the torus.
15
///
16
/// The default is `24`.
17
pub minor_resolution: usize,
18
/// The number of segments used for the main ring of the torus.
19
///
20
/// A resolution of `4` would make the torus appear rectangular,
21
/// while a resolution of `32` resembles a circular ring.
22
///
23
/// The default is `32`.
24
pub major_resolution: usize,
25
/// Optional angle range in radians, defaults to a full circle (0.0..=2 * PI)
26
pub angle_range: RangeInclusive<f32>,
27
}
28
29
impl Default for TorusMeshBuilder {
30
fn default() -> Self {
31
Self {
32
torus: Torus::default(),
33
minor_resolution: 24,
34
major_resolution: 32,
35
angle_range: (0.0..=2.0 * core::f32::consts::PI),
36
}
37
}
38
}
39
40
impl TorusMeshBuilder {
41
/// Creates a new [`TorusMeshBuilder`] from an inner and outer radius.
42
///
43
/// The inner radius is the radius of the hole, and the outer radius
44
/// is the radius of the entire object.
45
#[inline]
46
pub fn new(inner_radius: f32, outer_radius: f32) -> Self {
47
Self {
48
torus: Torus::new(inner_radius, outer_radius),
49
..Default::default()
50
}
51
}
52
53
/// Sets the number of vertices used for each circular segment
54
/// in the ring or tube of the torus.
55
#[inline]
56
pub const fn minor_resolution(mut self, resolution: usize) -> Self {
57
self.minor_resolution = resolution;
58
self
59
}
60
61
/// Sets the number of segments used for the main ring of the torus.
62
///
63
/// A resolution of `4` would make the torus appear rectangular,
64
/// while a resolution of `32` resembles a circular ring.
65
#[inline]
66
pub const fn major_resolution(mut self, resolution: usize) -> Self {
67
self.major_resolution = resolution;
68
self
69
}
70
71
/// Sets a custom angle range in radians instead of a full circle
72
#[inline]
73
pub const fn angle_range(mut self, range: RangeInclusive<f32>) -> Self {
74
self.angle_range = range;
75
self
76
}
77
}
78
79
impl MeshBuilder for TorusMeshBuilder {
80
fn build(&self) -> Mesh {
81
// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
82
83
let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1);
84
let mut positions: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
85
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
86
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);
87
88
let start_angle = self.angle_range.start();
89
let end_angle = self.angle_range.end();
90
91
let segment_stride = (end_angle - start_angle) / self.major_resolution as f32;
92
let side_stride = 2.0 * core::f32::consts::PI / self.minor_resolution as f32;
93
94
for segment in 0..=self.major_resolution {
95
let theta = start_angle + segment_stride * segment as f32;
96
97
for side in 0..=self.minor_resolution {
98
let phi = side_stride * side as f32;
99
let (sin_theta, cos_theta) = ops::sin_cos(theta);
100
let (sin_phi, cos_phi) = ops::sin_cos(phi);
101
let radius = self.torus.major_radius + self.torus.minor_radius * cos_phi;
102
103
let position = Vec3::new(
104
cos_theta * radius,
105
self.torus.minor_radius * sin_phi,
106
sin_theta * radius,
107
);
108
109
let center = Vec3::new(
110
self.torus.major_radius * cos_theta,
111
0.,
112
self.torus.major_radius * sin_theta,
113
);
114
let normal = (position - center).normalize();
115
116
positions.push(position.into());
117
normals.push(normal.into());
118
uvs.push([
119
segment as f32 / self.major_resolution as f32,
120
side as f32 / self.minor_resolution as f32,
121
]);
122
}
123
}
124
125
let n_faces = (self.major_resolution) * (self.minor_resolution);
126
let n_triangles = n_faces * 2;
127
let n_indices = n_triangles * 3;
128
129
let mut indices: Vec<u32> = Vec::with_capacity(n_indices);
130
131
let n_vertices_per_row = self.minor_resolution + 1;
132
for segment in 0..self.major_resolution {
133
for side in 0..self.minor_resolution {
134
let lt = side + segment * n_vertices_per_row;
135
let rt = (side + 1) + segment * n_vertices_per_row;
136
137
let lb = side + (segment + 1) * n_vertices_per_row;
138
let rb = (side + 1) + (segment + 1) * n_vertices_per_row;
139
140
indices.push(lt as u32);
141
indices.push(rt as u32);
142
indices.push(lb as u32);
143
144
indices.push(rt as u32);
145
indices.push(rb as u32);
146
indices.push(lb as u32);
147
}
148
}
149
150
Mesh::new(
151
PrimitiveTopology::TriangleList,
152
RenderAssetUsages::default(),
153
)
154
.with_inserted_indices(Indices::U32(indices))
155
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
156
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
157
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
158
}
159
}
160
161
impl Meshable for Torus {
162
type Output = TorusMeshBuilder;
163
164
fn mesh(&self) -> Self::Output {
165
TorusMeshBuilder {
166
torus: *self,
167
..Default::default()
168
}
169
}
170
}
171
172
impl From<Torus> for Mesh {
173
fn from(torus: Torus) -> Self {
174
torus.mesh().build()
175
}
176
}
177
178