Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/primitives/dim3/cylinder.rs
6598 views
1
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
2
use bevy_asset::RenderAssetUsages;
3
use bevy_math::{ops, primitives::Cylinder};
4
use bevy_reflect::prelude::*;
5
6
/// Anchoring options for [`CylinderMeshBuilder`]
7
#[derive(Debug, Copy, Clone, Default, Reflect)]
8
#[reflect(Default, Debug, Clone)]
9
pub enum CylinderAnchor {
10
#[default]
11
/// Midpoint between the top and bottom caps of the cylinder
12
MidPoint,
13
/// The center of the top circle cap
14
Top,
15
/// The center of the bottom circle cap
16
Bottom,
17
}
18
19
/// A builder used for creating a [`Mesh`] with a [`Cylinder`] shape.
20
#[derive(Clone, Copy, Debug, Reflect)]
21
#[reflect(Default, Debug, Clone)]
22
pub struct CylinderMeshBuilder {
23
/// The [`Cylinder`] shape.
24
pub cylinder: Cylinder,
25
/// The number of vertices used for the top and bottom of the cylinder.
26
///
27
/// The default is `32`.
28
pub resolution: u32,
29
/// The number of segments along the height of the cylinder.
30
/// Must be greater than `0` for geometry to be generated.
31
///
32
/// The default is `1`.
33
pub segments: u32,
34
/// If set to `true`, the cylinder caps (flat circle faces) are built,
35
/// otherwise the mesh will be a shallow tube
36
pub caps: bool,
37
/// The anchor point for the cylinder mesh, defaults to the midpoint between
38
/// the top and bottom caps
39
pub anchor: CylinderAnchor,
40
}
41
42
impl Default for CylinderMeshBuilder {
43
fn default() -> Self {
44
Self {
45
cylinder: Cylinder::default(),
46
resolution: 32,
47
segments: 1,
48
caps: true,
49
anchor: CylinderAnchor::default(),
50
}
51
}
52
}
53
54
impl CylinderMeshBuilder {
55
/// Creates a new [`CylinderMeshBuilder`] from the given radius, a height,
56
/// and a resolution used for the top and bottom.
57
#[inline]
58
pub fn new(radius: f32, height: f32, resolution: u32) -> Self {
59
Self {
60
cylinder: Cylinder::new(radius, height),
61
resolution,
62
..Default::default()
63
}
64
}
65
66
/// Sets the number of vertices used for the top and bottom of the cylinder.
67
#[inline]
68
pub const fn resolution(mut self, resolution: u32) -> Self {
69
self.resolution = resolution;
70
self
71
}
72
73
/// Sets the number of segments along the height of the cylinder.
74
/// Must be greater than `0` for geometry to be generated.
75
#[inline]
76
pub const fn segments(mut self, segments: u32) -> Self {
77
self.segments = segments;
78
self
79
}
80
81
/// Ignore the cylinder caps, making the mesh a shallow tube instead
82
#[inline]
83
pub const fn without_caps(mut self) -> Self {
84
self.caps = false;
85
self
86
}
87
88
/// Sets a custom anchor point for the mesh
89
#[inline]
90
pub const fn anchor(mut self, anchor: CylinderAnchor) -> Self {
91
self.anchor = anchor;
92
self
93
}
94
}
95
96
impl MeshBuilder for CylinderMeshBuilder {
97
fn build(&self) -> Mesh {
98
let resolution = self.resolution;
99
let segments = self.segments;
100
101
debug_assert!(resolution > 2);
102
debug_assert!(segments > 0);
103
104
let num_rings = segments + 1;
105
let num_vertices = resolution * 2 + num_rings * (resolution + 1);
106
let num_faces = resolution * (num_rings - 2);
107
let num_indices = (2 * num_faces + 2 * (resolution - 1) * 2) * 3;
108
109
let mut positions = Vec::with_capacity(num_vertices as usize);
110
let mut normals = Vec::with_capacity(num_vertices as usize);
111
let mut uvs = Vec::with_capacity(num_vertices as usize);
112
let mut indices = Vec::with_capacity(num_indices as usize);
113
114
let step_theta = core::f32::consts::TAU / resolution as f32;
115
let step_y = 2.0 * self.cylinder.half_height / segments as f32;
116
117
// rings
118
119
for ring in 0..num_rings {
120
let y = -self.cylinder.half_height + ring as f32 * step_y;
121
122
for segment in 0..=resolution {
123
let theta = segment as f32 * step_theta;
124
let (sin, cos) = ops::sin_cos(theta);
125
126
positions.push([self.cylinder.radius * cos, y, self.cylinder.radius * sin]);
127
normals.push([cos, 0., sin]);
128
uvs.push([
129
segment as f32 / resolution as f32,
130
ring as f32 / segments as f32,
131
]);
132
}
133
}
134
135
// barrel skin
136
137
for i in 0..segments {
138
let ring = i * (resolution + 1);
139
let next_ring = (i + 1) * (resolution + 1);
140
141
for j in 0..resolution {
142
indices.extend_from_slice(&[
143
ring + j,
144
next_ring + j,
145
ring + j + 1,
146
next_ring + j,
147
next_ring + j + 1,
148
ring + j + 1,
149
]);
150
}
151
}
152
153
// caps
154
if self.caps {
155
let mut build_cap = |top: bool| {
156
let offset = positions.len() as u32;
157
let (y, normal_y, winding) = if top {
158
(self.cylinder.half_height, 1., (1, 0))
159
} else {
160
(-self.cylinder.half_height, -1., (0, 1))
161
};
162
163
for i in 0..self.resolution {
164
let theta = i as f32 * step_theta;
165
let (sin, cos) = ops::sin_cos(theta);
166
167
positions.push([cos * self.cylinder.radius, y, sin * self.cylinder.radius]);
168
normals.push([0.0, normal_y, 0.0]);
169
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
170
}
171
172
for i in 1..(self.resolution - 1) {
173
indices.extend_from_slice(&[
174
offset,
175
offset + i + winding.0,
176
offset + i + winding.1,
177
]);
178
}
179
};
180
181
build_cap(true);
182
build_cap(false);
183
}
184
185
// Offset the vertex positions Y axis to match the anchor
186
match self.anchor {
187
CylinderAnchor::Top => positions
188
.iter_mut()
189
.for_each(|p| p[1] -= self.cylinder.half_height),
190
CylinderAnchor::Bottom => positions
191
.iter_mut()
192
.for_each(|p| p[1] += self.cylinder.half_height),
193
CylinderAnchor::MidPoint => (),
194
};
195
196
Mesh::new(
197
PrimitiveTopology::TriangleList,
198
RenderAssetUsages::default(),
199
)
200
.with_inserted_indices(Indices::U32(indices))
201
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
202
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
203
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
204
}
205
}
206
207
impl Meshable for Cylinder {
208
type Output = CylinderMeshBuilder;
209
210
fn mesh(&self) -> Self::Output {
211
CylinderMeshBuilder {
212
cylinder: *self,
213
..Default::default()
214
}
215
}
216
}
217
218
impl From<Cylinder> for Mesh {
219
fn from(cylinder: Cylinder) -> Self {
220
cylinder.mesh().build()
221
}
222
}
223
224