myth_resources/primitives/
sphere.rs1use crate::geometry::{Attribute, Geometry};
2use std::f32::consts::PI;
3use wgpu::VertexFormat;
4
5pub struct SphereOptions {
6 pub radius: f32,
7 pub width_segments: u32,
8 pub height_segments: u32,
9}
10
11impl Default for SphereOptions {
12 fn default() -> Self {
13 Self {
14 radius: 1.0,
15 width_segments: 32,
16 height_segments: 16,
17 }
18 }
19}
20
21#[must_use]
22pub fn create_sphere(options: &SphereOptions) -> Geometry {
23 let radius = options.radius;
24 let width_segments = options.width_segments.max(3);
25 let height_segments = options.height_segments.max(2);
26
27 let mut positions = Vec::new();
28 let mut normals = Vec::new();
29 let mut uvs = Vec::new();
30 let mut indices = Vec::new();
31
32 for y in 0..=height_segments {
34 let v_ratio = y as f32 / height_segments as f32;
35 let theta = v_ratio * PI;
37
38 let py = -radius * (theta.cos());
40 let ring_radius = radius * (theta.sin());
42
43 for x in 0..=width_segments {
44 let u_ratio = x as f32 / width_segments as f32;
45 let phi = u_ratio * 2.0 * PI;
47
48 let px = -ring_radius * phi.cos();
49 let pz = ring_radius * phi.sin();
50
51 positions.push([px, py, pz]);
52
53 let nx = px / radius;
55 let ny = py / radius;
56 let nz = pz / radius;
57 normals.push([nx, ny, nz]);
58
59 uvs.push([u_ratio, 1.0 - v_ratio]);
61 }
62 }
63
64 let stride = width_segments + 1;
67 for y in 0..height_segments {
68 for x in 0..width_segments {
69 let v0 = y * stride + x;
70 let v1 = v0 + 1;
71 let v2 = (y + 1) * stride + x;
72 let v3 = v2 + 1;
73
74 if y != 0 {
76 indices.push(v0 as u16);
77 indices.push(v1 as u16);
78 indices.push(v2 as u16);
79 }
80
81 if y != height_segments - 1 {
83 indices.push(v1 as u16);
84 indices.push(v3 as u16);
85 indices.push(v2 as u16);
86 }
87 }
88 }
89
90 let mut geo = Geometry::new();
91 geo.set_attribute(
92 "position",
93 Attribute::new_planar(&positions, VertexFormat::Float32x3),
94 );
95 geo.set_attribute(
96 "normal",
97 Attribute::new_planar(&normals, VertexFormat::Float32x3),
98 );
99 geo.set_attribute("uv", Attribute::new_planar(&uvs, VertexFormat::Float32x2));
100 geo.set_indices(&indices);
101
102 geo.compute_bounding_volume();
103 geo
104}