solstice_2d/d3/shapes/
sphere_geometry.rs

1use crate::{Geometry, Rad, Vertex3D};
2
3#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
4pub struct Sphere {
5    pub radius: f32,
6    pub width_segments: u32,
7    pub height_segments: u32,
8    pub phi_start: Rad,
9    pub phi_length: Rad,
10    pub theta_start: Rad,
11    pub theta_length: Rad,
12}
13
14impl Default for Sphere {
15    fn default() -> Self {
16        Self {
17            radius: 1.,
18            width_segments: 8,
19            height_segments: 6,
20            phi_start: Rad(0.0),
21            phi_length: Rad(std::f32::consts::PI * 2.),
22            theta_start: Rad(0.0),
23            theta_length: Rad(std::f32::consts::PI),
24        }
25    }
26}
27
28impl Sphere {
29    fn vertices(&self) -> Vec<Vertex3D> {
30        let mut vertices = vec![];
31        let theta_end = std::f32::consts::PI.min(self.theta_start.0 + self.theta_length.0);
32
33        for y in 0..=self.height_segments {
34            let v = y as f32 / self.height_segments as f32;
35
36            // special consideration for the poles
37            let u_offset = if y == 0 && self.theta_start.0 == 0. {
38                0.5 / self.width_segments as f32
39            } else if y == self.height_segments && theta_end == std::f32::consts::PI {
40                -0.5 / self.width_segments as f32
41            } else {
42                0.
43            };
44
45            for x in 0..=self.width_segments {
46                let u = x as f32 / self.width_segments as f32;
47
48                let position = nalgebra::Vector3::new(
49                    -self.radius
50                        * (self.phi_start.0 + u * self.phi_length.0).cos()
51                        * (self.theta_start.0 + v * self.theta_length.0).sin(),
52                    self.radius * (self.theta_start.0 + v * self.theta_length.0).cos(),
53                    self.radius
54                        * (self.phi_start.0 + u * self.phi_length.0).sin()
55                        * (self.theta_start.0 + v * self.theta_length.0).sin(),
56                );
57                vertices.push(Vertex3D {
58                    normal: position.normalize().into(),
59                    position: position.into(),
60                    uv: [u + u_offset, v],
61                    color: [1., 1., 1., 1.],
62                });
63            }
64        }
65
66        vertices
67    }
68
69    fn indices(&self) -> Vec<u32> {
70        let mut indices = vec![];
71        let theta_end = std::f32::consts::PI.min(self.theta_start.0 + self.theta_length.0);
72
73        let mut index = 0;
74        let mut grid = vec![];
75        for _y in 0..=self.height_segments {
76            let mut vertices_row = vec![];
77            for _x in 0..=self.width_segments {
78                vertices_row.push(index);
79                index += 1;
80            }
81            grid.push(vertices_row);
82        }
83
84        for iy in 0..self.height_segments as usize {
85            for ix in 0..self.width_segments as usize {
86                let a = grid[iy][ix + 1];
87                let b = grid[iy][ix];
88                let c = grid[iy + 1][ix];
89                let d = grid[iy + 1][ix + 1];
90
91                if iy as u32 != 0 || self.theta_start.0 > 0. {
92                    indices.extend_from_slice(&[a, b, d]);
93                }
94                if iy as u32 != self.height_segments - 1 || theta_end < std::f32::consts::PI {
95                    indices.extend_from_slice(&[b, c, d]);
96                }
97            }
98        }
99
100        indices
101    }
102}
103
104impl From<&Sphere> for Geometry<'_, Vertex3D> {
105    fn from(s: &Sphere) -> Self {
106        Self::new(s.vertices(), Some(s.indices()))
107    }
108}
109
110impl From<Sphere> for Geometry<'_, Vertex3D> {
111    fn from(s: Sphere) -> Self {
112        (&s).into()
113    }
114}