ncollide3d/procedural/
sphere.rs

1use super::utils;
2#[cfg(feature = "dim2")]
3use super::Polyline;
4#[cfg(feature = "dim3")]
5use super::{IndexBuffer, TriMesh};
6use na;
7#[cfg(feature = "dim3")]
8use na::{Point2, Point3, Vector3};
9use simba::scalar::RealField;
10
11/// Generates a UV sphere.
12#[cfg(feature = "dim3")]
13pub fn sphere<N>(
14    diameter: N,
15    ntheta_subdiv: u32,
16    nphi_subdiv: u32,
17    generate_uvs: bool,
18) -> TriMesh<N>
19where
20    N: RealField + Copy,
21{
22    let mut sphere = unit_sphere(ntheta_subdiv, nphi_subdiv, generate_uvs);
23
24    sphere.scale_by_scalar(diameter);
25
26    sphere
27}
28
29/// Generates a UV sphere centered at the origin and with a unit diameter.
30#[cfg(feature = "dim3")]
31pub fn unit_sphere<N>(ntheta_subdiv: u32, nphi_subdiv: u32, generate_uvs: bool) -> TriMesh<N>
32where
33    N: RealField + Copy,
34{
35    if generate_uvs {
36        unit_sphere_with_uvs(ntheta_subdiv, nphi_subdiv)
37    } else {
38        unit_sphere_without_uvs(ntheta_subdiv, nphi_subdiv)
39    }
40}
41
42// FIXME: n{theta,phi}_subdiv are not the right names.
43#[cfg(feature = "dim3")]
44fn unit_sphere_without_uvs<N>(ntheta_subdiv: u32, nphi_subdiv: u32) -> TriMesh<N>
45where
46    N: RealField + Copy,
47{
48    let pi = N::pi();
49    let two_pi = N::two_pi();
50    let pi_two = N::frac_pi_2();
51    let dtheta = two_pi / na::convert(ntheta_subdiv as f64);
52    let dphi = pi / na::convert(nphi_subdiv as f64);
53
54    let mut coords = Vec::new();
55    let mut curr_phi = -pi_two + dphi;
56
57    // coords.
58    coords.push(Point3::new(na::zero(), -na::one::<N>(), na::zero()));
59
60    for _ in 0..nphi_subdiv - 1 {
61        utils::push_circle(
62            curr_phi.cos(),
63            ntheta_subdiv,
64            dtheta,
65            curr_phi.sin(),
66            &mut coords,
67        );
68        curr_phi = curr_phi + dphi;
69    }
70
71    coords.push(Point3::new(na::zero(), na::one(), na::zero()));
72
73    // the normals are the same as the coords.
74    let normals: Vec<Vector3<N>> = coords.iter().map(|p| p.coords).collect();
75
76    // index buffer
77    let mut idx = Vec::new();
78
79    utils::push_degenerate_top_ring_indices(1, 0, ntheta_subdiv, &mut idx);
80
81    utils::reverse_clockwising(&mut idx[..]);
82
83    for i in 0..nphi_subdiv - 2 {
84        let bottom = 1 + i * ntheta_subdiv;
85        let up = bottom + ntheta_subdiv;
86        utils::push_ring_indices(bottom, up, ntheta_subdiv, &mut idx);
87    }
88
89    utils::push_degenerate_top_ring_indices(
90        1 + (nphi_subdiv - 2) * ntheta_subdiv,
91        coords.len() as u32 - 1,
92        ntheta_subdiv,
93        &mut idx,
94    );
95
96    let mut res = TriMesh::new(coords, Some(normals), None, Some(IndexBuffer::Unified(idx)));
97
98    let _0_5: N = na::convert(0.5);
99
100    res.scale_by_scalar(_0_5);
101
102    res
103}
104
105#[cfg(feature = "dim3")]
106fn unit_sphere_with_uvs<N: RealField + Copy>(ntheta_subdiv: u32, nphi_subdiv: u32) -> TriMesh<N> {
107    let pi = N::pi();
108    let two_pi = N::two_pi();
109    let pi_two = N::frac_pi_2();
110    let duvtheta = N::one() / na::convert(ntheta_subdiv as f64); // step of uv.x coordinates.
111    let duvphi = N::one() / na::convert(nphi_subdiv as f64); // step of uv.y coordinates.
112    let dtheta = two_pi * duvtheta;
113    let dphi = pi * duvphi;
114
115    let mut coords = Vec::new();
116    let mut curr_phi = -pi_two;
117
118    for _ in 0..nphi_subdiv + 1 {
119        utils::push_circle(
120            curr_phi.cos(),
121            ntheta_subdiv + 1,
122            dtheta,
123            curr_phi.sin(),
124            &mut coords,
125        );
126        curr_phi = curr_phi + dphi;
127    }
128
129    // the normals are the same as the coords
130    let normals: Vec<Vector3<N>> = coords.iter().map(|p| p.coords).collect();
131
132    // index buffer
133    let mut idx = Vec::new();
134
135    for i in 0..nphi_subdiv {
136        let bottom = i * (ntheta_subdiv + 1);
137        let up = bottom + (ntheta_subdiv + 1);
138        utils::push_open_ring_indices(bottom, up, ntheta_subdiv + 1, &mut idx);
139    }
140
141    let mut uvs = Vec::new();
142    let mut curr_uvphi = na::zero::<N>();
143
144    for _ in 0..nphi_subdiv + 1 {
145        let mut curr_uvtheta = na::zero::<N>();
146
147        for _ in 0..ntheta_subdiv + 1 {
148            uvs.push(Point2::new(curr_uvtheta, curr_uvphi));
149            curr_uvtheta = curr_uvtheta + duvtheta;
150        }
151
152        curr_uvphi = curr_uvphi + duvphi;
153    }
154
155    let mut res = TriMesh::new(
156        coords,
157        Some(normals),
158        Some(uvs),
159        Some(IndexBuffer::Unified(idx)),
160    );
161
162    let _0_5: N = na::convert(0.5);
163    res.scale_by_scalar(_0_5);
164
165    res
166}
167
168/// Creates an hemisphere with a diameter of 1.
169#[cfg(feature = "dim3")]
170pub fn unit_hemisphere<N: RealField + Copy>(ntheta_subdiv: u32, nphi_subdiv: u32) -> TriMesh<N> {
171    let two_pi = N::two_pi();
172    let pi_two = N::frac_pi_2();
173    let dtheta = two_pi / na::convert(ntheta_subdiv as f64);
174    let dphi = pi_two / na::convert(nphi_subdiv as f64);
175
176    let mut coords = Vec::new();
177    let mut curr_phi = na::zero::<N>();
178
179    for _ in 0..nphi_subdiv - 1 {
180        utils::push_circle(
181            curr_phi.cos(),
182            ntheta_subdiv,
183            dtheta,
184            curr_phi.sin(),
185            &mut coords,
186        );
187        curr_phi = curr_phi + dphi;
188    }
189
190    coords.push(Point3::new(na::zero(), na::one(), na::zero()));
191
192    let mut idx = Vec::new();
193
194    for i in 0..nphi_subdiv - 2 {
195        utils::push_ring_indices(
196            i * ntheta_subdiv,
197            (i + 1) * ntheta_subdiv,
198            ntheta_subdiv,
199            &mut idx,
200        );
201    }
202
203    utils::push_degenerate_top_ring_indices(
204        (nphi_subdiv - 2) * ntheta_subdiv,
205        coords.len() as u32 - 1,
206        ntheta_subdiv,
207        &mut idx,
208    );
209
210    // Result
211    let normals: Vec<Vector3<N>> = coords.iter().map(|p| p.coords).collect();
212    // FIXME: uvs
213    let mut out = TriMesh::new(coords, Some(normals), None, Some(IndexBuffer::Unified(idx)));
214
215    // set the radius to 0.5
216    let _0_5: N = na::convert(0.5);
217    out.scale_by_scalar(_0_5);
218
219    out
220}
221
222/// Creates a circle lying on the `(x,y)` plane.
223#[cfg(feature = "dim2")]
224pub fn circle<N: RealField + Copy>(diameter: &N, nsubdivs: u32) -> Polyline<N> {
225    let two_pi = N::two_pi();
226    let dtheta = two_pi / na::convert(nsubdivs as f64);
227
228    let mut pts = Vec::with_capacity(nsubdivs as usize);
229
230    utils::push_xy_arc(*diameter / na::convert(2.0), nsubdivs, dtheta, &mut pts);
231
232    // FIXME: normals
233
234    Polyline::new(pts, None)
235}
236
237/// Creates a circle lying on the `(x,y)` plane.
238#[cfg(feature = "dim2")]
239pub fn unit_circle<N: RealField + Copy>(nsubdivs: u32) -> Polyline<N> {
240    // FIXME: do this the other way round?
241    circle(&na::convert(1.0), nsubdivs)
242}