ncollide3d/shape/
heightfield3.rs

1use na::{DMatrix, Point3, RealField};
2
3use crate::bounding_volume::AABB;
4use crate::math::Vector;
5use crate::query::{Contact, ContactKinematic, ContactPreprocessor};
6use crate::shape::{FeatureId, Triangle};
7
8bitflags! {
9    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10    #[derive(Default)]
11    /// The status of the cell of an heightfield.
12    pub struct HeightFieldCellStatus: u8 {
13        /// If this bit is set, the concerned heightfield cell is subdivided using a Z pattern.
14        const ZIGZAG_SUBDIVISION = 0b00000001;
15        /// If this bit is set, the leftmost triangle of the concerned heightfield cell is removed.
16        const LEFT_TRIANGLE_REMOVED = 0b00000010;
17        /// If this bit is set, the rightmost triangle of the concerned heightfield cell is removed.
18        const RIGHT_TRIANGLE_REMOVED = 0b00000100;
19        /// If this bit is set, both triangles of the concerned heightfield cell are removed.
20        const CELL_REMOVED = Self::LEFT_TRIANGLE_REMOVED.bits | Self::RIGHT_TRIANGLE_REMOVED.bits;
21    }
22}
23
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[derive(Clone, Debug)]
26/// An heightfield implicitly discretized with triangles.
27pub struct HeightField<N: RealField + Copy> {
28    heights: DMatrix<N>,
29    scale: Vector<N>,
30    aabb: AABB<N>,
31    num_triangles: usize,
32    status: DMatrix<HeightFieldCellStatus>,
33}
34
35impl<N: RealField + Copy> HeightField<N> {
36    /// Initializes a new heightfield with the given heights and a scaling factor.
37    pub fn new(heights: DMatrix<N>, scale: Vector<N>) -> Self {
38        assert!(
39            heights.nrows() > 1 && heights.ncols() > 1,
40            "A heightfield heights must have at least 2 rows and columns."
41        );
42        let max = heights.max();
43        let min = heights.min();
44        let hscale = scale * na::convert::<_, N>(0.5);
45        let aabb = AABB::new(
46            Point3::new(-hscale.x, min * scale.y, -hscale.z),
47            Point3::new(hscale.x, max * scale.y, hscale.z),
48        );
49        let num_triangles = (heights.nrows() - 1) * (heights.ncols() - 1) * 2;
50        let status = DMatrix::repeat(
51            heights.nrows() - 1,
52            heights.ncols() - 1,
53            HeightFieldCellStatus::default(),
54        );
55
56        HeightField {
57            heights,
58            scale,
59            aabb,
60            num_triangles,
61            status,
62        }
63    }
64
65    /// The number of rows of this heightfield.
66    pub fn nrows(&self) -> usize {
67        self.heights.nrows() - 1
68    }
69
70    /// The number of columns of this heightfield.
71    pub fn ncols(&self) -> usize {
72        self.heights.ncols() - 1
73    }
74
75    fn triangle_id(&self, i: usize, j: usize, left: bool) -> usize {
76        let tid = j * (self.heights.nrows() - 1) + i;
77        if left {
78            tid
79        } else {
80            tid + self.num_triangles / 2
81        }
82    }
83
84    fn face_id(&self, i: usize, j: usize, left: bool, front: bool) -> usize {
85        let tid = self.triangle_id(i, j, left);
86        if front {
87            tid
88        } else {
89            tid + self.num_triangles
90        }
91    }
92
93    fn quantize_floor(&self, val: N, cell_size: N, num_cells: usize) -> usize {
94        let _0_5: N = na::convert(0.5);
95        let i = na::clamp(
96            ((val + _0_5) / cell_size).floor(),
97            N::zero(),
98            na::convert((num_cells - 1) as f64),
99        );
100        na::convert_unchecked::<N, f64>(i) as usize
101    }
102
103    fn quantize_ceil(&self, val: N, cell_size: N, num_cells: usize) -> usize {
104        let _0_5: N = na::convert(0.5);
105        let i = na::clamp(
106            ((val + _0_5) / cell_size).ceil(),
107            N::zero(),
108            na::convert(num_cells as f64),
109        );
110        na::convert_unchecked::<N, f64>(i) as usize
111    }
112
113    /// The pair of index of the cell containing the vertical projection of the given point.
114    pub fn cell_at_point(&self, pt: &Point3<N>) -> Option<(usize, usize)> {
115        let _0_5: N = na::convert(0.5);
116        let scaled_pt = pt.coords.component_div(&self.scale);
117        let cell_width = self.unit_cell_width();
118        let cell_height = self.unit_cell_height();
119        let ncells_x = self.ncols();
120        let ncells_z = self.nrows();
121
122        if scaled_pt.x < -_0_5 || scaled_pt.x > _0_5 || scaled_pt.z < -_0_5 || scaled_pt.z > _0_5 {
123            // Outside of the heightfield bounds.
124            None
125        } else {
126            let j = self.quantize_floor(scaled_pt.x, cell_width, ncells_x);
127            let i = self.quantize_floor(scaled_pt.z, cell_height, ncells_z);
128            Some((i, j))
129        }
130    }
131
132    /// The smallest x coordinate of the `j`-th column of this heightfield.
133    pub fn x_at(&self, j: usize) -> N {
134        let _0_5: N = na::convert(0.5);
135        (-_0_5 + self.unit_cell_width() * na::convert(j as f64)) * self.scale.x
136    }
137
138    /// The smallest z coordinate of the start of the `i`-th row of this heightfield.
139    pub fn z_at(&self, i: usize) -> N {
140        let _0_5: N = na::convert(0.5);
141        (-_0_5 + self.unit_cell_height() * na::convert(i as f64)) * self.scale.z
142    }
143
144    /// An iterator through all the triangles of this heightfield.
145    pub fn triangles<'a>(&'a self) -> impl Iterator<Item = Triangle<N>> + 'a {
146        HeightfieldTriangles {
147            heightfield: self,
148            curr: (0, 0),
149            tris: self.triangles_at(0, 0),
150        }
151    }
152
153    /// The two triangles at the cell (i, j) of this heightfield.
154    ///
155    /// Returns `None` fore triangles that have been removed because of their user-defined status
156    /// flags (described by the `HeightFieldCellStatus` bitfield).
157    pub fn triangles_at(&self, i: usize, j: usize) -> (Option<Triangle<N>>, Option<Triangle<N>>) {
158        let status = self.status[(i, j)];
159
160        if status.contains(
161            HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED
162                | HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED,
163        ) {
164            return (None, None);
165        }
166
167        let cell_width = self.unit_cell_width();
168        let cell_height = self.unit_cell_height();
169
170        let _0_5: N = na::convert(0.5);
171        let z0 = -_0_5 + cell_height * na::convert(i as f64);
172        let z1 = z0 + cell_height;
173
174        let x0 = -_0_5 + cell_width * na::convert(j as f64);
175        let x1 = x0 + cell_width;
176
177        let y00 = self.heights[(i + 0, j + 0)];
178        let y10 = self.heights[(i + 1, j + 0)];
179        let y01 = self.heights[(i + 0, j + 1)];
180        let y11 = self.heights[(i + 1, j + 1)];
181
182        let mut p00 = Point3::new(x0, y00, z0);
183        let mut p10 = Point3::new(x0, y10, z1);
184        let mut p01 = Point3::new(x1, y01, z0);
185        let mut p11 = Point3::new(x1, y11, z1);
186
187        // Apply scales:
188        p00.coords.component_mul_assign(&self.scale);
189        p10.coords.component_mul_assign(&self.scale);
190        p01.coords.component_mul_assign(&self.scale);
191        p11.coords.component_mul_assign(&self.scale);
192
193        if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
194            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
195                None
196            } else {
197                Some(Triangle::new(p00, p10, p11))
198            };
199
200            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
201                None
202            } else {
203                Some(Triangle::new(p00, p11, p01))
204            };
205
206            (tri1, tri2)
207        } else {
208            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
209                None
210            } else {
211                Some(Triangle::new(p00, p10, p01))
212            };
213
214            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
215                None
216            } else {
217                Some(Triangle::new(p10, p11, p01))
218            };
219
220            (tri1, tri2)
221        }
222    }
223
224    /// The status of the `(i, j)`-th cell.
225    pub fn cell_status(&self, i: usize, j: usize) -> HeightFieldCellStatus {
226        self.status[(i, j)]
227    }
228
229    /// Set the status of the `(i, j)`-th cell.
230    pub fn set_cell_status(&mut self, i: usize, j: usize, status: HeightFieldCellStatus) {
231        self.status[(i, j)] = status
232    }
233
234    /// The statuses of all the cells of this heightfield.
235    pub fn cells_statuses(&self) -> &DMatrix<HeightFieldCellStatus> {
236        &self.status
237    }
238
239    /// The mutable statuses of all the cells of this heightfield.
240    pub fn cells_statuses_mut(&mut self) -> &mut DMatrix<HeightFieldCellStatus> {
241        &mut self.status
242    }
243
244    /// The heights of this heightfield.
245    pub fn heights(&self) -> &DMatrix<N> {
246        &self.heights
247    }
248
249    /// The scale factor applied to this heightfield.
250    pub fn scale(&self) -> &Vector<N> {
251        &self.scale
252    }
253
254    /// The width (extent along its local `x` axis) of each cell of this heightmap, including the scale factor.
255    pub fn cell_width(&self) -> N {
256        self.unit_cell_width() * self.scale.x
257    }
258
259    /// The height (extent along its local `z` axis) of each cell of this heightmap, including the scale factor.
260    pub fn cell_height(&self) -> N {
261        self.unit_cell_height() * self.scale.z
262    }
263
264    /// The width (extent along its local `x` axis) of each cell of this heightmap, excluding the scale factor.
265    pub fn unit_cell_width(&self) -> N {
266        N::one() / na::convert(self.heights.ncols() as f64 - 1.0)
267    }
268
269    /// The height (extent along its local `z` axis) of each cell of this heightmap, excluding the scale factor.
270    pub fn unit_cell_height(&self) -> N {
271        N::one() / na::convert(self.heights.nrows() as f64 - 1.0)
272    }
273
274    /// The AABB of this heightmap.
275    pub fn aabb(&self) -> &AABB<N> {
276        &self.aabb
277    }
278
279    /// Converts the FeatureID of the left or right triangle at the cell `(i, j)` into a FeatureId
280    /// of the whole heightfield.
281    pub fn convert_triangle_feature_id(
282        &self,
283        i: usize,
284        j: usize,
285        left: bool,
286        fid: FeatureId,
287    ) -> FeatureId {
288        match fid {
289            FeatureId::Vertex(ivertex) => {
290                let nrows = self.heights.nrows();
291                let ij = i + j * nrows;
292
293                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
294                    if left {
295                        FeatureId::Vertex([ij, ij + 1, ij + 1 + nrows][ivertex])
296                    } else {
297                        FeatureId::Vertex([ij, ij + 1 + nrows, ij + nrows][ivertex])
298                    }
299                } else {
300                    if left {
301                        FeatureId::Vertex([ij, ij + 1, ij + nrows][ivertex])
302                    } else {
303                        FeatureId::Vertex([ij + 1, ij + 1 + nrows, ij + nrows][ivertex])
304                    }
305                }
306            }
307            FeatureId::Edge(iedge) => {
308                let (nrows, ncols) = self.heights.shape();
309                let vshift = 0; // First vertical line index.
310                let hshift = (nrows - 1) * ncols; // First horizontal line index.
311                let dshift = hshift + nrows * (ncols - 1); // First diagonal line index.
312                let idiag = dshift + i + j * (nrows - 1);
313                let itop = hshift + i + j * nrows;
314                let ibottom = itop + 1;
315                let ileft = vshift + i + j * (nrows - 1);
316                let iright = ileft + nrows - 1;
317
318                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
319                    if left {
320                        // Triangle:
321                        //
322                        // |\
323                        // |_\
324                        //
325                        FeatureId::Edge([ileft, ibottom, idiag][iedge])
326                    } else {
327                        // Triangle:
328                        // ___
329                        // \ |
330                        //  \|
331                        //
332                        FeatureId::Edge([idiag, iright, itop][iedge])
333                    }
334                } else {
335                    if left {
336                        // Triangle:
337                        // ___
338                        // | /
339                        // |/
340                        //
341                        FeatureId::Edge([ileft, idiag, itop][iedge])
342                    } else {
343                        // Triangle:
344                        //
345                        //  /|
346                        // /_|
347                        //
348                        FeatureId::Edge([ibottom, iright, idiag][iedge])
349                    }
350                }
351            }
352            FeatureId::Face(iface) => {
353                if iface == 0 {
354                    FeatureId::Face(self.face_id(i, j, left, true))
355                } else {
356                    FeatureId::Face(self.face_id(i, j, left, false))
357                }
358            }
359            FeatureId::Unknown => FeatureId::Unknown,
360        }
361    }
362
363    /// Applies the function `f` to all the triangles of this heightfield intersecting the given AABB.
364    pub fn map_elements_in_local_aabb(
365        &self,
366        aabb: &AABB<N>,
367        f: &mut impl FnMut(usize, &Triangle<N>, &dyn ContactPreprocessor<N>),
368    ) {
369        let _0_5: N = na::convert(0.5);
370        let ncells_x = self.ncols();
371        let ncells_z = self.nrows();
372
373        let ref_mins = aabb.mins.coords.component_div(&self.scale);
374        let ref_maxs = aabb.maxs.coords.component_div(&self.scale);
375        let cell_width = self.unit_cell_width();
376        let cell_height = self.unit_cell_height();
377
378        if ref_maxs.x <= -_0_5 || ref_maxs.z <= -_0_5 || ref_mins.x >= _0_5 || ref_mins.z >= _0_5 {
379            // Outside of the heightfield bounds.
380            return;
381        }
382
383        let min_x = self.quantize_floor(ref_mins.x, cell_width, ncells_x);
384        let min_z = self.quantize_floor(ref_mins.z, cell_height, ncells_z);
385
386        let max_x = self.quantize_ceil(ref_maxs.x, cell_width, ncells_x);
387        let max_z = self.quantize_ceil(ref_maxs.z, cell_height, ncells_z);
388
389        // FIXME: find a way to avoid recomputing the same vertices
390        // multiple times.
391        for j in min_x..max_x {
392            for i in min_z..max_z {
393                let status = self.status[(i, j)];
394
395                if status.contains(HeightFieldCellStatus::CELL_REMOVED) {
396                    continue;
397                }
398
399                let z0 = -_0_5 + cell_height * na::convert(i as f64);
400                let z1 = z0 + cell_height;
401
402                let x0 = -_0_5 + cell_width * na::convert(j as f64);
403                let x1 = x0 + cell_width;
404
405                let y00 = self.heights[(i + 0, j + 0)];
406                let y10 = self.heights[(i + 1, j + 0)];
407                let y01 = self.heights[(i + 0, j + 1)];
408                let y11 = self.heights[(i + 1, j + 1)];
409
410                if (y00 > ref_maxs.y && y10 > ref_maxs.y && y01 > ref_maxs.y && y11 > ref_maxs.y)
411                    || (y00 < ref_mins.y
412                        && y10 < ref_mins.y
413                        && y01 < ref_mins.y
414                        && y11 < ref_mins.y)
415                {
416                    continue;
417                }
418
419                let mut p00 = Point3::new(x0, y00, z0);
420                let mut p10 = Point3::new(x0, y10, z1);
421                let mut p01 = Point3::new(x1, y01, z0);
422                let mut p11 = Point3::new(x1, y11, z1);
423
424                // Apply scales:
425                p00.coords.component_mul_assign(&self.scale);
426                p10.coords.component_mul_assign(&self.scale);
427                p01.coords.component_mul_assign(&self.scale);
428                p11.coords.component_mul_assign(&self.scale);
429
430                // Build the two triangles, contact processors and call f.
431                if !status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
432                    let tri1 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
433                        Triangle::new(p00, p10, p11)
434                    } else {
435                        Triangle::new(p00, p10, p01)
436                    };
437
438                    let tid = self.triangle_id(i, j, true);
439                    let proc1 = HeightFieldTriangleContactPreprocessor::new(self, tid);
440                    f(tid, &tri1, &proc1);
441                }
442
443                if !status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
444                    let tri2 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
445                        Triangle::new(p00, p11, p01)
446                    } else {
447                        Triangle::new(p10, p11, p01)
448                    };
449                    let tid = self.triangle_id(i, j, false);
450                    let proc2 = HeightFieldTriangleContactPreprocessor::new(self, tid);
451                    f(tid, &tri2, &proc2);
452                }
453            }
454        }
455    }
456}
457
458#[allow(dead_code)]
459pub struct HeightFieldTriangleContactPreprocessor<'a, N: RealField + Copy> {
460    heightfield: &'a HeightField<N>,
461    triangle: usize,
462}
463
464impl<'a, N: RealField + Copy> HeightFieldTriangleContactPreprocessor<'a, N> {
465    pub fn new(heightfield: &'a HeightField<N>, triangle: usize) -> Self {
466        HeightFieldTriangleContactPreprocessor {
467            heightfield,
468            triangle,
469        }
470    }
471}
472
473impl<'a, N: RealField + Copy> ContactPreprocessor<N>
474    for HeightFieldTriangleContactPreprocessor<'a, N>
475{
476    fn process_contact(
477        &self,
478        _c: &mut Contact<N>,
479        _kinematic: &mut ContactKinematic<N>,
480        _is_first: bool,
481    ) -> bool {
482        /*
483        // Fix the feature ID.
484        let feature = if is_first {
485            kinematic.feature1()
486        } else {
487            kinematic.feature2()
488        };
489
490        let face = &self.mesh.faces()[self.face_id];
491        let actual_feature = match feature {
492            FeatureId::Vertex(i) => FeatureId::Vertex(face.indices[i]),
493            FeatureId::Edge(i) => FeatureId::Edge(face.edges[i]),
494            FeatureId::Face(i) => {
495                if i == 0 {
496                    FeatureId::Face(self.face_id)
497                } else {
498                    FeatureId::Face(self.face_id + self.mesh.faces().len())
499                }
500            }
501            FeatureId::Unknown => FeatureId::Unknown,
502        };
503
504        if is_first {
505            kinematic.set_feature1(actual_feature);
506        } else {
507            kinematic.set_feature2(actual_feature);
508        }
509
510        // Test the validity of the LMD.
511        if c.depth > N::zero() {
512            true
513        } else {
514            // FIXME
515        }
516        */
517
518        true
519    }
520}
521
522struct HeightfieldTriangles<'a, N: RealField + Copy> {
523    heightfield: &'a HeightField<N>,
524    curr: (usize, usize),
525    tris: (Option<Triangle<N>>, Option<Triangle<N>>),
526}
527
528impl<'a, N: RealField + Copy> Iterator for HeightfieldTriangles<'a, N> {
529    type Item = Triangle<N>;
530
531    fn next(&mut self) -> Option<Triangle<N>> {
532        loop {
533            if let Some(tri1) = self.tris.0.take() {
534                return Some(tri1);
535            } else if let Some(tri2) = self.tris.1.take() {
536                return Some(tri2);
537            } else {
538                self.curr.0 += 1;
539
540                if self.curr.0 >= self.heightfield.nrows() {
541                    if self.curr.1 >= self.heightfield.ncols() - 1 {
542                        return None;
543                    }
544
545                    self.curr.0 = 0;
546                    self.curr.1 += 1;
547                }
548
549                // tri1 and tri2 are None
550                self.tris = self.heightfield.triangles_at(self.curr.0, self.curr.1);
551            }
552        }
553    }
554}