ncollide3d/procedural/
utils.rs

1//! Utilities useful for various generations tasks.
2
3use crate::math::{Point, Vector};
4#[cfg(feature = "dim3")]
5use crate::utils::{DeterministicState, HashablePartialEq};
6#[cfg(feature = "dim3")]
7use na;
8use na::RealField;
9#[cfg(feature = "dim3")]
10use num::Zero;
11#[cfg(feature = "dim3")]
12use std::collections::hash_map::Entry;
13#[cfg(feature = "dim3")]
14use std::collections::HashMap;
15#[cfg(feature = "dim3")]
16use std::iter;
17
18// FIXME: remove that in favor of `push_xy_circle` ?
19/// Pushes a discretized counterclockwise circle to a buffer.
20#[cfg(feature = "dim3")]
21#[inline]
22pub fn push_circle<N: RealField + Copy>(
23    radius: N,
24    nsubdiv: u32,
25    dtheta: N,
26    y: N,
27    out: &mut Vec<Point<N>>,
28) {
29    let mut curr_theta = N::zero();
30
31    for _ in 0..nsubdiv {
32        out.push(Point::new(
33            curr_theta.cos() * radius,
34            y.clone(),
35            curr_theta.sin() * radius,
36        ));
37        curr_theta = curr_theta + dtheta;
38    }
39}
40
41/// Pushes a discretized counterclockwise circle to a buffer.
42/// The circle is contained on the plane spanned by the `x` and `y` axis.
43#[inline]
44pub fn push_xy_arc<N: RealField + Copy>(
45    radius: N,
46    nsubdiv: u32,
47    dtheta: N,
48    out: &mut Vec<Point<N>>,
49) {
50    let mut curr_theta = N::zero();
51
52    for _ in 0..nsubdiv {
53        let mut pt_coords = Vector::zeros();
54
55        pt_coords[0] = curr_theta.cos() * radius;
56        pt_coords[1] = curr_theta.sin() * radius;
57        out.push(Point::from(pt_coords));
58
59        curr_theta = curr_theta + dtheta;
60    }
61}
62
63/// Creates the faces from two circles with the same discretization.
64#[cfg(feature = "dim3")]
65#[inline]
66pub fn push_ring_indices(
67    base_lower_circle: u32,
68    base_upper_circle: u32,
69    nsubdiv: u32,
70    out: &mut Vec<Point<u32>>,
71) {
72    push_open_ring_indices(base_lower_circle, base_upper_circle, nsubdiv, out);
73
74    // adjust the last two triangles
75    push_rectangle_indices(
76        base_upper_circle,
77        base_upper_circle + nsubdiv - 1,
78        base_lower_circle,
79        base_lower_circle + nsubdiv - 1,
80        out,
81    );
82}
83
84/// Creates the faces from two circles with the same discretization.
85#[cfg(feature = "dim3")]
86#[inline]
87pub fn push_open_ring_indices(
88    base_lower_circle: u32,
89    base_upper_circle: u32,
90    nsubdiv: u32,
91    out: &mut Vec<Point<u32>>,
92) {
93    assert!(nsubdiv > 0);
94
95    for i in 0..nsubdiv - 1 {
96        let bli = base_lower_circle + i;
97        let bui = base_upper_circle + i;
98        push_rectangle_indices(bui + 1, bui, bli + 1, bli, out);
99    }
100}
101
102/// Creates the faces from a circle and a point that is shared by all triangle.
103#[cfg(feature = "dim3")]
104#[inline]
105pub fn push_degenerate_top_ring_indices(
106    base_circle: u32,
107    point: u32,
108    nsubdiv: u32,
109    out: &mut Vec<Point<u32>>,
110) {
111    push_degenerate_open_top_ring_indices(base_circle, point, nsubdiv, out);
112
113    out.push(Point::new(base_circle + nsubdiv - 1, point, base_circle));
114}
115
116/// Creates the faces from a circle and a point that is shared by all triangle.
117#[cfg(feature = "dim3")]
118#[inline]
119pub fn push_degenerate_open_top_ring_indices(
120    base_circle: u32,
121    point: u32,
122    nsubdiv: u32,
123    out: &mut Vec<Point<u32>>,
124) {
125    assert!(nsubdiv > 0);
126
127    for i in 0..nsubdiv - 1 {
128        out.push(Point::new(base_circle + i, point, base_circle + i + 1));
129    }
130}
131
132/// Pushes indices so that a circle is filled with triangles. Each triangle will have the
133/// `base_circle` point in common.
134/// Pushes `nsubdiv - 2` elements to `out`.
135#[cfg(feature = "dim3")]
136#[inline]
137pub fn push_filled_circle_indices(base_circle: u32, nsubdiv: u32, out: &mut Vec<Point<u32>>) {
138    for i in base_circle + 1..base_circle + nsubdiv - 1 {
139        out.push(Point::new(base_circle, i, i + 1));
140    }
141}
142
143/// Given four corner points, pushes to two counterclockwise triangles to `out`.
144///
145/// # Arguments:
146/// * `ul` - the up-left point.
147/// * `dl` - the down-left point.
148/// * `dr` - the down-left point.
149/// * `ur` - the up-left point.
150#[cfg(feature = "dim3")]
151#[inline]
152pub fn push_rectangle_indices(ul: u32, ur: u32, dl: u32, dr: u32, out: &mut Vec<Point<u32>>) {
153    out.push(Point::new(ul.clone(), dl, dr.clone()));
154    out.push(Point::new(dr, ur, ul));
155}
156
157/// Reverses the clockwising of a set of faces.
158#[cfg(feature = "dim3")]
159#[inline]
160pub fn reverse_clockwising(indices: &mut [Point<u32>]) {
161    for i in indices.iter_mut() {
162        i.coords.swap((0, 0), (1, 0));
163    }
164}
165
166/// Duplicates the indices of each triangle on the given index buffer.
167///
168/// For example: [ (0.0, 1.0, 2.0) ] becomes: [ (0.0, 0.0, 0.0), (1.0, 1.0, 1.0), (2.0, 2.0, 2.0)].
169#[cfg(feature = "dim3")]
170#[inline]
171pub fn split_index_buffer(indices: &[Point<u32>]) -> Vec<Point<Point<u32>>> {
172    let mut resi = Vec::new();
173
174    for vertex in indices.iter() {
175        resi.push(Point::new(
176            Point::new(vertex.x, vertex.x, vertex.x),
177            Point::new(vertex.y, vertex.y, vertex.y),
178            Point::new(vertex.z, vertex.z, vertex.z),
179        ));
180    }
181
182    resi
183}
184
185/// Duplicates the indices of each triangle on the given index buffer, giving the same id to each
186/// identical vertex.
187#[cfg(feature = "dim3")]
188#[inline]
189pub fn split_index_buffer_and_recover_topology<N: RealField + Copy>(
190    indices: &[Point<u32>],
191    coords: &[Point<N>],
192) -> (Vec<Point<Point<u32>>>, Vec<Point<N>>) {
193    let mut vtx_to_id = HashMap::with_hasher(DeterministicState::new());
194    let mut new_coords = Vec::with_capacity(coords.len());
195    let mut out = Vec::with_capacity(indices.len());
196
197    fn resolve_coord_id<N: RealField + Copy>(
198        coord: &Point<N>,
199        vtx_to_id: &mut HashMap<HashablePartialEq<Point<N>>, u32, DeterministicState>,
200        new_coords: &mut Vec<Point<N>>,
201    ) -> u32 {
202        let key = unsafe { HashablePartialEq::new(coord.clone()) };
203        let id = match vtx_to_id.entry(key) {
204            Entry::Occupied(entry) => entry.into_mut(),
205            Entry::Vacant(entry) => entry.insert(new_coords.len() as u32),
206        };
207
208        if *id == new_coords.len() as u32 {
209            new_coords.push(coord.clone());
210        }
211
212        *id
213    }
214
215    for t in indices.iter() {
216        let va = resolve_coord_id(&coords[t.x as usize], &mut vtx_to_id, &mut new_coords);
217        let oa = t.x;
218
219        let vb = resolve_coord_id(&coords[t.y as usize], &mut vtx_to_id, &mut new_coords);
220        let ob = t.y;
221
222        let vc = resolve_coord_id(&coords[t.z as usize], &mut vtx_to_id, &mut new_coords);
223        let oc = t.z;
224
225        out.push(Point::new(
226            Point::new(va, oa, oa),
227            Point::new(vb, ob, ob),
228            Point::new(vc, oc, oc),
229        ));
230    }
231
232    new_coords.shrink_to_fit();
233
234    (out, new_coords)
235}
236
237// FIXME: check at compile-time that we are in 3D?
238/// Computes the normals of a set of vertices.
239#[cfg(feature = "dim3")]
240#[inline]
241pub fn compute_normals<N: RealField + Copy>(
242    coordinates: &[Point<N>],
243    faces: &[Point<u32>],
244    normals: &mut Vec<Vector<N>>,
245) {
246    let mut divisor: Vec<N> = iter::repeat(na::zero()).take(coordinates.len()).collect();
247
248    // Shrink the output buffer if it is too big.
249    if normals.len() > coordinates.len() {
250        normals.truncate(coordinates.len())
251    }
252
253    // Reinit all normals to zero.
254    normals.clear();
255    normals.extend(iter::repeat(na::zero::<Vector<N>>()).take(coordinates.len()));
256
257    // Accumulate normals ...
258    for f in faces.iter() {
259        let edge1 = coordinates[f.y as usize] - coordinates[f.x as usize];
260        let edge2 = coordinates[f.z as usize] - coordinates[f.x as usize];
261        let cross = edge1.cross(&edge2);
262        let normal;
263
264        if !cross.is_zero() {
265            normal = cross.normalize()
266        } else {
267            normal = cross
268        }
269
270        normals[f.x as usize] = normals[f.x as usize] + normal;
271        normals[f.y as usize] = normals[f.y as usize] + normal;
272        normals[f.z as usize] = normals[f.z as usize] + normal;
273
274        divisor[f.x as usize] = divisor[f.x as usize] + na::one();
275        divisor[f.y as usize] = divisor[f.y as usize] + na::one();
276        divisor[f.z as usize] = divisor[f.z as usize] + na::one();
277    }
278
279    // ... and compute the mean
280    for (n, divisor) in normals.iter_mut().zip(divisor.iter()) {
281        *n = *n / *divisor
282    }
283}