bevy_mesh/primitives/dim3/
sphere.rs1use crate::{Indices, Mesh, MeshBuilder, Meshable};
2use bevy_asset::RenderAssetUsages;
3use bevy_math::{ops, primitives::Sphere};
4use core::f32::consts::PI;
5use derive_more::derive::{Display, Error};
6use hexasphere::shapes::IcoSphere;
7use wgpu::PrimitiveTopology;
8
9#[derive(Clone, Copy, Debug, Error, Display)]
11pub enum IcosphereError {
12 #[display("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")]
14 TooManyVertices {
15 subdivisions: u32,
17 number_of_resulting_points: u32,
19 },
20}
21
22#[derive(Clone, Copy, Debug)]
24pub enum SphereKind {
25 Ico {
27 subdivisions: u32,
30 },
31 Uv {
34 #[doc(alias = "horizontal_resolution")]
36 sectors: u32,
37 #[doc(alias = "vertical_resolution")]
39 stacks: u32,
40 },
41}
42
43impl Default for SphereKind {
44 fn default() -> Self {
45 Self::Ico { subdivisions: 5 }
46 }
47}
48
49#[derive(Clone, Copy, Debug, Default)]
51pub struct SphereMeshBuilder {
52 pub sphere: Sphere,
54 pub kind: SphereKind,
56}
57
58impl SphereMeshBuilder {
59 #[inline]
61 pub const fn new(radius: f32, kind: SphereKind) -> Self {
62 Self {
63 sphere: Sphere { radius },
64 kind,
65 }
66 }
67
68 #[inline]
70 pub const fn kind(mut self, kind: SphereKind) -> Self {
71 self.kind = kind;
72 self
73 }
74
75 pub fn ico(&self, subdivisions: u32) -> Result<Mesh, IcosphereError> {
83 if subdivisions >= 80 {
84 let temp = subdivisions + 1;
113 let number_of_resulting_points = temp * temp * 10 + 2;
114 return Err(IcosphereError::TooManyVertices {
115 subdivisions,
116 number_of_resulting_points,
117 });
118 }
119 let generated = IcoSphere::new(subdivisions as usize, |point| {
120 let inclination = ops::acos(point.y);
121 let azimuth = ops::atan2(point.z, point.x);
122
123 let norm_inclination = inclination / PI;
124 let norm_azimuth = 0.5 - (azimuth / core::f32::consts::TAU);
125
126 [norm_azimuth, norm_inclination]
127 });
128
129 let raw_points = generated.raw_points();
130
131 let points = raw_points
132 .iter()
133 .map(|&p| (p * self.sphere.radius).into())
134 .collect::<Vec<[f32; 3]>>();
135
136 let normals = raw_points
137 .iter()
138 .copied()
139 .map(Into::into)
140 .collect::<Vec<[f32; 3]>>();
141
142 let uvs = generated.raw_data().to_owned();
143
144 let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20);
145
146 for i in 0..20 {
147 generated.get_indices(i, &mut indices);
148 }
149
150 let indices = Indices::U32(indices);
151
152 Ok(Mesh::new(
153 PrimitiveTopology::TriangleList,
154 RenderAssetUsages::default(),
155 )
156 .with_inserted_indices(indices)
157 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)
158 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
159 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs))
160 }
161
162 pub fn uv(&self, sectors: u32, stacks: u32) -> Mesh {
167 let sectors_f32 = sectors as f32;
170 let stacks_f32 = stacks as f32;
171 let length_inv = 1. / self.sphere.radius;
172 let sector_step = 2. * PI / sectors_f32;
173 let stack_step = PI / stacks_f32;
174
175 let n_vertices = (stacks * sectors) as usize;
176 let mut vertices: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
177 let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
178 let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);
179 let mut indices: Vec<u32> = Vec::with_capacity(n_vertices * 2 * 3);
180
181 for i in 0..stacks + 1 {
182 let stack_angle = PI / 2. - (i as f32) * stack_step;
183 let xy = self.sphere.radius * ops::cos(stack_angle);
184 let z = self.sphere.radius * ops::sin(stack_angle);
185
186 for j in 0..sectors + 1 {
187 let sector_angle = (j as f32) * sector_step;
188 let x = xy * ops::cos(sector_angle);
189 let y = xy * ops::sin(sector_angle);
190
191 vertices.push([x, y, z]);
192 normals.push([x * length_inv, y * length_inv, z * length_inv]);
193 uvs.push([(j as f32) / sectors_f32, (i as f32) / stacks_f32]);
194 }
195 }
196
197 for i in 0..stacks {
203 let mut k1 = i * (sectors + 1);
204 let mut k2 = k1 + sectors + 1;
205 for _j in 0..sectors {
206 if i != 0 {
207 indices.push(k1);
208 indices.push(k2);
209 indices.push(k1 + 1);
210 }
211 if i != stacks - 1 {
212 indices.push(k1 + 1);
213 indices.push(k2);
214 indices.push(k2 + 1);
215 }
216 k1 += 1;
217 k2 += 1;
218 }
219 }
220
221 Mesh::new(
222 PrimitiveTopology::TriangleList,
223 RenderAssetUsages::default(),
224 )
225 .with_inserted_indices(Indices::U32(indices))
226 .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
227 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
228 .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
229 }
230}
231
232impl MeshBuilder for SphereMeshBuilder {
233 fn build(&self) -> Mesh {
240 match self.kind {
241 SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(),
242 SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks),
243 }
244 }
245}
246
247impl Meshable for Sphere {
248 type Output = SphereMeshBuilder;
249
250 fn mesh(&self) -> Self::Output {
251 SphereMeshBuilder {
252 sphere: *self,
253 ..Default::default()
254 }
255 }
256}
257
258impl From<Sphere> for Mesh {
259 fn from(sphere: Sphere) -> Self {
260 sphere.mesh().build()
261 }
262}