parry3d/shape/voxels/
voxels.rs

1use super::{
2    VoxelIndex, EMPTY_FACE_MASK, FACES_TO_FEATURE_MASKS, FACES_TO_OCTANT_MASKS,
3    FACES_TO_VOXEL_TYPES, INTERIOR_FACE_MASK,
4};
5use crate::bounding_volume::{Aabb, BoundingVolume};
6#[cfg(not(feature = "std"))]
7use crate::math::ComplexField;
8use crate::math::{ivect_to_vect, vect_to_ivect, IVector, Int, Vector};
9use crate::partitioning::{Bvh, BvhBuildStrategy, BvhNode};
10use crate::shape::voxels::voxels_chunk::{VoxelsChunk, VoxelsChunkHeader};
11use crate::shape::VoxelsChunkRef;
12use crate::utils::hashmap::HashMap;
13use alloc::{vec, vec::Vec};
14
15/// Categorization of a voxel based on its neighbors.
16///
17/// This enum describes the local topology of a filled voxel by examining which of its
18/// immediate neighbors (along coordinate axes) are also filled. This information is crucial
19/// for collision detection to avoid the "internal edges problem" where objects can get
20/// caught on edges between adjacent voxels.
21///
22/// # Type Classification
23///
24/// - **Empty**: The voxel is not filled.
25/// - **Interior**: All axis-aligned neighbors are filled (safest for collision).
26/// - **Face**: Missing neighbors in one coordinate direction (e.g., top face exposed).
27/// - **Edge** (3D only): Missing neighbors in two coordinate directions (e.g., corner edge exposed).
28/// - **Vertex**: Missing neighbors in all coordinate directions (isolated corner).
29///
30/// # Examples
31///
32/// ```
33/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
34/// use parry3d::shape::{Voxels, VoxelType};
35/// use parry3d::math::{Vector, IVector};
36///
37/// // Create a small 2x2x2 cube of voxels
38/// let voxels = Voxels::new(
39///     Vector::new(1.0, 1.0, 1.0),
40///     &[
41///         IVector::new(0, 0, 0),
42///         IVector::new(1, 0, 0),
43///         IVector::new(0, 1, 0),
44///         IVector::new(1, 1, 0),
45///         IVector::new(0, 0, 1),
46///         IVector::new(1, 0, 1),
47///         IVector::new(0, 1, 1),
48///         IVector::new(1, 1, 1),
49///     ],
50/// );
51///
52/// // Check voxel type - interior voxels are fully surrounded
53/// let state = voxels.voxel_state(IVector::new(0, 0, 0)).unwrap();
54/// let voxel_type = state.voxel_type();
55/// println!("Voxel type: {:?}", voxel_type);
56/// # }
57/// ```
58#[derive(Copy, Clone, Debug, PartialEq, Eq)]
59pub enum VoxelType {
60    /// The voxel is empty.
61    Empty,
62    /// The voxel is a vertex if all three coordinate axis directions have at
63    /// least one empty neighbor.
64    Vertex,
65    /// The voxel is on an edge if it has non-empty neighbors in both directions of
66    /// a single coordinate axis.
67    #[cfg(feature = "dim3")]
68    Edge,
69    /// The voxel is on an edge if it has non-empty neighbors in both directions of
70    /// two coordinate axes.
71    Face,
72    /// The voxel is on an edge if it has non-empty neighbors in both directions of
73    /// all coordinate axes.
74    Interior,
75}
76
77#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
78/// The status of the cell of an heightfield.
79pub struct AxisMask(u8);
80
81bitflags::bitflags! {
82    /// Flags for identifying signed directions along coordinate axes, or faces of a voxel.
83    impl AxisMask: u8 {
84        /// The direction or face along the `+x` coordinate axis.
85        const X_POS = 1 << 0;
86        /// The direction or face along the `-x` coordinate axis.
87        const X_NEG = 1 << 1;
88        /// The direction or face along the `+y` coordinate axis.
89        const Y_POS = 1 << 2;
90        /// The direction or face along the `-y` coordinate axis.
91        const Y_NEG = 1 << 3;
92        /// The direction or face along the `+z` coordinate axis.
93        #[cfg(feature= "dim3")]
94        const Z_POS = 1 << 4;
95        /// The direction or face along the `-z` coordinate axis.
96        #[cfg(feature= "dim3")]
97        const Z_NEG = 1 << 5;
98    }
99}
100
101/// Indicates the local shape of a voxel on each octant.
102///
103/// This provides geometric information of the shape’s exposed features on each octant.
104// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
105// collision-detection algorithms.
106#[derive(Copy, Clone, Debug, PartialEq, Eq)]
107pub struct OctantPattern;
108
109// NOTE: it is important that the max value of any OctantPattern variant
110//       is 7 because we don’t allocate more than 3 bits to store it in
111//      `FACES_TO_OCTANT_MASKS`.
112/// Indicates the local shape of a voxel on each octant.
113///
114/// This provides geometric information of the shape’s exposed features on each octant.
115// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
116// collision-detection algorithms.
117#[cfg(feature = "dim3")]
118impl OctantPattern {
119    /// The voxel doesn't have any exposed feature on the octant with this mask.
120    pub const INTERIOR: u32 = 0;
121    /// The voxel has an exposed vertex on the octant with this mask.
122    pub const VERTEX: u32 = 1;
123    /// The voxel has an exposed edges with direction X on the octant with this mask.
124    pub const EDGE_X: u32 = 2;
125    /// The voxel has an exposed edges with direction Y on the octant with this mask.
126    pub const EDGE_Y: u32 = 3;
127    /// The voxel has an exposed edges with direction Z on the octant with this mask.
128    pub const EDGE_Z: u32 = 4;
129    /// The voxel has an exposed face with normal X on the octant with this mask.
130    pub const FACE_X: u32 = 5;
131    /// The voxel has an exposed face with normal Y on the octant with this mask.
132    pub const FACE_Y: u32 = 6;
133    /// The voxel has an exposed face with normal Z on the octant with this mask.
134    pub const FACE_Z: u32 = 7;
135}
136
137// NOTE: it is important that the max value of any OctantPattern variant
138//       is 7 because we don’t allocate more than 3 bits to store it in
139//      `FACES_TO_OCTANT_MASKS`.
140/// Indicates the local shape of a voxel on each octant.
141///
142/// This provides geometric information of the shape’s exposed features on each octant.
143/// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
144/// collision-detection algorithms.
145#[cfg(feature = "dim2")]
146impl OctantPattern {
147    /// The voxel doesn't have any exposed feature on the octant with this mask.
148    pub const INTERIOR: u32 = 0;
149    /// The voxel has an exposed vertex on the octant with this mask.
150    pub const VERTEX: u32 = 1;
151    /// The voxel has an exposed face with normal X on the octant with this mask.
152    pub const FACE_X: u32 = 2;
153    /// The voxel has an exposed face with normal Y on the octant with this mask.
154    pub const FACE_Y: u32 = 3;
155}
156
157// The local neighborhood information is encoded in a 8-bits number in groups of two bits
158// per coordinate axis: `0bwwzzyyxx`. In each group of two bits, e.g. `xx`, the rightmost (resp.
159// leftmost) bit set to 1 means that the neighbor voxel with coordinate `+1` (resp `-1`) relative
160// to the current voxel along the `x` axis is filled. If the bit is 0, then the corresponding
161// neighbor is empty. See the `AxisMask` bitflags.
162// For example, in 2D, the mask `0b00_00_10_01` matches the following configuration (assuming +y goes
163// up, and +x goes right):
164//
165// ```txt
166//  0 0 0
167//  0 x 1
168//  0 1 0
169// ```
170//
171// The special value `0b01000000` indicates that the voxel is empty.
172// And the value `0b00111111` (`0b00001111` in 2D) indicates that the voxel is an interior voxel (its whole neighborhood
173// is filled).
174/// A description of the local neighborhood of a voxel.
175///
176/// This compact representation stores which immediate neighbors (along coordinate axes) of a
177/// voxel are filled. This information is essential for proper collision detection between voxels
178/// and other shapes, as it helps avoid the "internal edges problem."
179///
180/// The state is encoded as a single byte where each pair of bits represents one coordinate axis,
181/// indicating whether neighbors in the positive and negative directions are filled.
182///
183/// # Special States
184///
185/// - [`VoxelState::EMPTY`]: The voxel itself is empty (not part of the shape).
186/// - [`VoxelState::INTERIOR`]: All neighbors are filled (completely surrounded voxel).
187///
188/// # Examples
189///
190/// ```
191/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
192/// use parry3d::shape::{Voxels, VoxelState, AxisMask};
193/// use parry3d::math::{Vector, IVector};
194///
195/// // Create a simple voxel shape
196/// let voxels = Voxels::new(
197///     Vector::new(1.0, 1.0, 1.0),
198///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0)],
199/// );
200///
201/// // Query the state of a voxel
202/// let state = voxels.voxel_state(IVector::new(0, 0, 0)).unwrap();
203///
204/// // Check if empty
205/// assert!(!state.is_empty());
206///
207/// // Get which faces are exposed (not adjacent to other voxels)
208/// let free_faces = state.free_faces();
209/// if free_faces.contains(AxisMask::X_NEG) {
210///     println!("The -X face is exposed");
211/// }
212///
213/// // Get the voxel type based on neighborhood
214/// println!("Voxel type: {:?}", state.voxel_type());
215/// # }
216/// ```
217#[derive(Copy, Clone, Debug, PartialEq, Eq)]
218#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
219pub struct VoxelState(pub(super) u8);
220
221impl VoxelState {
222    /// The value of empty voxels.
223    pub const EMPTY: VoxelState = VoxelState(EMPTY_FACE_MASK);
224    /// The value of a voxel with non-empty neighbors in all directions.
225    pub const INTERIOR: VoxelState = VoxelState(INTERIOR_FACE_MASK);
226
227    pub(crate) const fn new(state: u8) -> Self {
228        Self(state)
229    }
230
231    /// Is this voxel empty?
232    pub const fn is_empty(self) -> bool {
233        self.0 == EMPTY_FACE_MASK
234    }
235
236    /// A bit mask indicating which faces of the voxel don’t have any
237    /// adjacent non-empty voxel.
238    pub const fn free_faces(self) -> AxisMask {
239        if self.0 == INTERIOR_FACE_MASK || self.0 == EMPTY_FACE_MASK {
240            AxisMask::empty()
241        } else {
242            AxisMask::from_bits_truncate((!self.0) & INTERIOR_FACE_MASK)
243        }
244    }
245
246    /// The [`VoxelType`] of this voxel.
247    pub const fn voxel_type(self) -> VoxelType {
248        FACES_TO_VOXEL_TYPES[self.0 as usize]
249    }
250
251    // Bitmask indicating what vertices, edges, or faces of the voxel are "free".
252    pub(crate) const fn feature_mask(self) -> u16 {
253        FACES_TO_FEATURE_MASKS[self.0 as usize]
254    }
255
256    pub(crate) const fn octant_mask(self) -> u32 {
257        FACES_TO_OCTANT_MASKS[self.0 as usize]
258    }
259}
260
261/// Information associated to a voxel.
262///
263/// This structure provides complete information about a single voxel including its position
264/// in both grid coordinates and world space, as well as its state (empty/filled and neighborhood).
265///
266/// # Note
267///
268/// The `linear_id` field is an internal implementation detail that can become invalidated when
269/// the voxel structure is modified (e.g., via [`Voxels::set_voxel`] or [`Voxels::crop`]).
270/// For stable references to voxels, always use `grid_coords`.
271///
272/// # Examples
273///
274/// ```
275/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
276/// use parry3d::shape::Voxels;
277/// use parry3d::math::{Vector, IVector};
278///
279/// let voxels = Voxels::new(
280///     Vector::new(0.5, 0.5, 0.5),
281///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0)],
282/// );
283///
284/// // Iterate through all voxels
285/// for voxel in voxels.voxels() {
286///     if !voxel.state.is_empty() {
287///         println!("Voxel at grid position {:?}", voxel.grid_coords);
288///         println!("  World center: {:?}", voxel.center);
289///         println!("  Type: {:?}", voxel.state.voxel_type());
290///     }
291/// }
292/// # }
293/// ```
294#[derive(Copy, Clone, Debug, PartialEq)]
295pub struct VoxelData {
296    /// The temporary index in the internal voxels' storage.
297    ///
298    /// This index can be invalidated after a call to [`Voxels::set_voxel`], or
299    /// [`Voxels::crop`].
300    pub linear_id: VoxelIndex,
301    /// The voxel's integer grid coordinates.
302    pub grid_coords: IVector,
303    /// The voxel's center position in the local-space of the [`Voxels`] shape it is part of.
304    pub center: Vector,
305    /// The voxel's state, indicating if it's empty or full.
306    pub state: VoxelState,
307}
308
309/// A shape made of axis-aligned, uniformly sized cubes (aka. voxels).
310///
311/// # What are Voxels?
312///
313/// Voxels (volumetric pixels) are 3D cubes (or 2D squares) arranged on a regular grid. Think of
314/// them as 3D building blocks, like LEGO bricks or Minecraft blocks. Each voxel has:
315/// - A position on an integer grid (e.g., `(0, 0, 0)`, `(1, 2, 3)`)
316/// - A uniform size (e.g., 1.0 × 1.0 × 1.0 meters)
317/// - A state: filled (solid) or empty (air)
318///
319/// # When to Use Voxels?
320///
321/// Voxels are ideal for:
322/// - **Minecraft-style worlds**: Block-based terrain and structures
323/// - **Destructible environments**: Easy to add/remove individual blocks
324/// - **Procedural generation**: Grid-based algorithms for caves, terrain, dungeons
325/// - **Volumetric data**: Medical imaging, scientific simulations
326/// - **Retro aesthetics**: Pixel art style in 3D
327///
328/// Voxels may NOT be ideal for:
329/// - Smooth organic shapes (use meshes instead)
330/// - Very large sparse worlds (consider octrees or chunk-based systems)
331/// - Scenes requiring fine geometric detail at all scales
332///
333/// # The Internal Edges Problem
334///
335/// When an object slides across a flat surface made of voxels, it can snag on the edges between
336/// adjacent voxels, causing jerky motion. Parry's `Voxels` shape solves this by tracking neighbor
337/// relationships: it knows which voxel faces are internal (adjacent to another voxel) vs external
338/// (exposed to air), allowing smooth collision response.
339///
340/// # Memory Efficiency
341///
342/// The internal storage uses sparse chunks, storing only one byte per voxel for neighborhood
343/// information. Empty regions consume minimal memory. This is much more efficient than storing
344/// a triangle mesh representation of all voxel surfaces.
345///
346/// # Examples
347///
348/// ## Basic Usage: Creating a Voxel Shape
349///
350/// ```
351/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
352/// use parry3d::shape::Voxels;
353/// use parry3d::math::{Vector, IVector};
354///
355/// // Create a simple 3×3×3 cube of voxels
356/// let voxel_size = Vector::new(1.0, 1.0, 1.0);
357/// let mut coords = Vec::new();
358/// for x in 0..3 {
359///     for y in 0..3 {
360///         for z in 0..3 {
361///             coords.push(IVector::new(x, y, z));
362///         }
363///     }
364/// }
365///
366/// let voxels = Voxels::new(voxel_size, &coords);
367/// println!("Created voxel shape with {} voxels", coords.len());
368/// # }
369/// ```
370///
371/// ## Creating Voxels from World-Space Vectors
372///
373/// ```
374/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
375/// use parry3d::shape::Voxels;
376/// use parry3d::math::Vector;
377///
378/// // Sample points in world space (e.g., from a point cloud)
379/// let points = vec![
380///     Vector::new(0.1, 0.2, 0.3),
381///     Vector::new(1.5, 2.1, 3.7),
382///     Vector::new(0.8, 0.9, 1.2),
383/// ];
384///
385/// // Create voxels with 0.5 unit size - nearby points merge into same voxel
386/// let voxels = Voxels::from_points(Vector::new(0.5, 0.5, 0.5), &points);
387/// println!("Created voxel shape from {} points", points.len());
388/// # }
389/// ```
390///
391/// ## Querying Voxel State
392///
393/// ```
394/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
395/// use parry3d::shape::Voxels;
396/// use parry3d::math::{Vector, IVector};
397///
398/// let voxels = Voxels::new(
399///     Vector::new(1.0, 1.0, 1.0),
400///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0)],
401/// );
402///
403/// // Check if a specific grid position is filled
404/// if let Some(state) = voxels.voxel_state(IVector::new(0, 0, 0)) {
405///     println!("Voxel is filled!");
406///     println!("Type: {:?}", state.voxel_type());
407///     println!("Free faces: {:?}", state.free_faces());
408/// } else {
409///     println!("Voxel is empty or outside the domain");
410/// }
411///
412/// // Convert world-space point to grid coordinates
413/// let world_point = Vector::new(1.3, 0.7, 0.2);
414/// let grid_coord = voxels.voxel_at_point(world_point);
415/// println!("Vector at {:?} is in voxel {:?}", world_point, grid_coord);
416/// # }
417/// ```
418///
419/// ## Iterating Through Voxels
420///
421/// ```
422/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
423/// use parry3d::shape::Voxels;
424/// use parry3d::math::{Vector, IVector};
425///
426/// let voxels = Voxels::new(
427///     Vector::new(0.5, 0.5, 0.5),
428///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0), IVector::new(0, 1, 0)],
429/// );
430///
431/// // Iterate through all non-empty voxels
432/// for voxel in voxels.voxels() {
433///     if !voxel.state.is_empty() {
434///         println!("Voxel at grid {:?}, world center {:?}",
435///                  voxel.grid_coords, voxel.center);
436///     }
437/// }
438/// # }
439/// ```
440///
441/// ## Modifying Voxels Dynamically
442///
443/// ```
444/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
445/// use parry3d::shape::Voxels;
446/// use parry3d::math::{Vector, IVector};
447///
448/// let mut voxels = Voxels::new(
449///     Vector::new(1.0, 1.0, 1.0),
450///     &[IVector::new(0, 0, 0)],
451/// );
452///
453/// // Add a new voxel
454/// voxels.set_voxel(IVector::new(1, 0, 0), true);
455///
456/// // Remove a voxel
457/// voxels.set_voxel(IVector::new(0, 0, 0), false);
458///
459/// // Check the result
460/// assert!(voxels.voxel_state(IVector::new(0, 0, 0)).unwrap().is_empty());
461/// assert!(!voxels.voxel_state(IVector::new(1, 0, 0)).unwrap().is_empty());
462/// # }
463/// ```
464///
465/// ## Spatial Queries
466///
467/// ```
468/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
469/// use parry3d::shape::Voxels;
470/// use parry3d::bounding_volume::Aabb;
471/// use parry3d::math::{Vector, IVector};
472///
473/// let voxels = Voxels::new(
474///     Vector::new(1.0, 1.0, 1.0),
475///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0), IVector::new(2, 0, 0)],
476/// );
477///
478/// // Find voxels intersecting an AABB
479/// let query_aabb = Aabb::new(Vector::new(-0.5, -0.5, -0.5), Vector::new(1.5, 1.5, 1.5));
480/// let count = voxels.voxels_intersecting_local_aabb(&query_aabb)
481///     .filter(|v| !v.state.is_empty())
482///     .count();
483/// println!("Found {} voxels in AABB", count);
484///
485/// // Get the overall domain bounds
486/// let [mins, maxs] = voxels.domain();
487/// println!("Voxel grid spans from {:?} to {:?}", mins, maxs);
488/// # }
489/// ```
490///
491/// # See Also
492///
493/// - [`VoxelState`]: Information about a voxel's neighbors
494/// - [`VoxelType`]: Classification of voxels by their topology
495/// - [`VoxelData`]: Complete information about a single voxel
496/// - [`crate::transformation::voxelization`]: Convert meshes to voxels
497#[derive(Clone, Debug)]
498#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
499pub struct Voxels {
500    /// A BVH of chunk keys.
501    ///
502    /// The bounding boxes are the ones of the chunk’s voxels **keys**. This is equivalent to a bvh
503    /// of the chunks with a uniform voxel size of 1.
504    pub(super) chunk_bvh: Bvh,
505    pub(super) chunk_headers: HashMap<IVector, VoxelsChunkHeader>,
506    pub(super) chunk_keys: Vec<IVector>,
507    pub(super) chunks: Vec<VoxelsChunk>,
508    pub(super) free_chunks: Vec<usize>,
509    pub(super) voxel_size: Vector,
510}
511
512impl Voxels {
513    /// Initializes a voxel shape from grid coordinates.
514    ///
515    /// This is the primary constructor for creating a `Voxels` shape. You provide:
516    /// - `voxel_size`: The physical dimensions of each voxel (e.g., `1.0 × 1.0 × 1.0` meters)
517    /// - `grid_coordinates`: Integer grid positions for each filled voxel
518    ///
519    /// # Coordinate System
520    ///
521    /// Each voxel with grid coordinates `(x, y, z)` will be positioned such that:
522    /// - Its minimum corner (bottom-left-back) is at `(x, y, z) * voxel_size`
523    /// - Its center is at `((x, y, z) + 0.5) * voxel_size`
524    /// - Its maximum corner is at `((x, y, z) + 1) * voxel_size`
525    ///
526    /// For example, with `voxel_size = 2.0` and grid coord `(1, 0, 0)`:
527    /// - Minimum corner: `(2.0, 0.0, 0.0)`
528    /// - Center: `(3.0, 1.0, 1.0)`
529    /// - Maximum corner: `(4.0, 2.0, 2.0)`
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
535    /// use parry3d::shape::Voxels;
536    /// use parry3d::math::{Vector, IVector};
537    ///
538    /// // Create a 2×2×2 cube of voxels with 1.0 unit size
539    /// let voxels = Voxels::new(
540    ///     Vector::new(1.0, 1.0, 1.0),
541    ///     &[
542    ///         IVector::new(0, 0, 0), IVector::new(1, 0, 0),
543    ///         IVector::new(0, 1, 0), IVector::new(1, 1, 0),
544    ///         IVector::new(0, 0, 1), IVector::new(1, 0, 1),
545    ///         IVector::new(0, 1, 1), IVector::new(1, 1, 1),
546    ///     ],
547    /// );
548    ///
549    /// // Verify the first voxel's center position
550    /// let center = voxels.voxel_center(IVector::new(0, 0, 0));
551    /// assert_eq!(center, Vector::new(0.5, 0.5, 0.5));
552    /// # }
553    /// ```
554    ///
555    /// ```
556    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
557    /// use parry3d::shape::Voxels;
558    /// use parry3d::math::{Vector, IVector};
559    ///
560    /// // Create a line of voxels along the X axis
561    /// let voxels = Voxels::new(
562    ///     Vector::new(0.5, 0.5, 0.5),
563    ///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0), IVector::new(2, 0, 0)],
564    /// );
565    ///
566    /// // Query the domain (bounding grid coordinates)
567    /// // Note: domain is aligned to internal chunk boundaries for efficiency
568    /// let [mins, maxs] = voxels.domain();
569    /// assert_eq!(mins, IVector::new(0, 0, 0));
570    /// // maxs will be chunk-aligned (chunks are 8x8x8), so it includes more space
571    /// assert!(maxs.x >= 3 && maxs.y >= 1 && maxs.z >= 1);
572    /// # }
573    /// ```
574    pub fn new(voxel_size: Vector, grid_coordinates: &[IVector]) -> Self {
575        let mut result = Self {
576            chunk_bvh: Bvh::new(),
577            chunk_headers: HashMap::default(),
578            chunk_keys: vec![],
579            chunks: vec![],
580            free_chunks: vec![],
581            voxel_size,
582        };
583
584        for vox in grid_coordinates {
585            let (chunk_key, id_in_chunk) = Self::chunk_key_and_id_in_chunk(*vox);
586            let chunk_header = result.chunk_headers.entry(chunk_key).or_insert_with(|| {
587                let id = result.chunks.len();
588                result.chunks.push(VoxelsChunk::default());
589                result.chunk_keys.push(chunk_key);
590                VoxelsChunkHeader { id, len: 0 }
591            });
592            chunk_header.len += 1;
593            result.chunks[chunk_header.id].states[id_in_chunk] = VoxelState::INTERIOR;
594        }
595
596        result.chunk_bvh = Bvh::from_iter(
597            BvhBuildStrategy::Ploc,
598            result.chunk_headers.iter().map(|(chunk_key, chunk_id)| {
599                (chunk_id.id, VoxelsChunk::aabb(chunk_key, result.voxel_size))
600            }),
601        );
602
603        result.recompute_all_voxels_states();
604        result
605    }
606
607    /// Computes a voxel shape from a set of world-space points.
608    ///
609    /// This constructor converts continuous world-space coordinates into discrete grid coordinates
610    /// by snapping each point to the voxel grid. Multiple points can map to the same voxel.
611    ///
612    /// # How it Works
613    ///
614    /// Each point is converted to grid coordinates by:
615    /// 1. Dividing the point's coordinates by `voxel_size`
616    /// 2. Taking the floor to get integer grid coordinates
617    /// 3. Removing duplicates (multiple points in the same voxel become one voxel)
618    ///
619    /// For example, with `voxel_size = 1.0`:
620    /// - Vector `(0.3, 0.7, 0.9)` → Grid `(0, 0, 0)`
621    /// - Vector `(1.1, 0.2, 0.5)` → Grid `(1, 0, 0)`
622    /// - Vector `(0.9, 0.1, 0.8)` → Grid `(0, 0, 0)` (merges with first)
623    ///
624    /// # Use Cases
625    ///
626    /// - Converting point clouds into voxel representations
627    /// - Creating voxel shapes from scattered data
628    /// - Simplifying complex point sets into uniform grids
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
634    /// use parry3d::shape::Voxels;
635    /// use parry3d::math::Vector;
636    ///
637    /// // Sample points in world space
638    /// let points = vec![
639    ///     Vector::new(0.1, 0.2, 0.3),   // → Grid (0, 0, 0)
640    ///     Vector::new(0.7, 0.8, 0.9),   // → Grid (0, 0, 0) - same voxel!
641    ///     Vector::new(1.2, 0.3, 0.1),   // → Grid (1, 0, 0)
642    ///     Vector::new(0.5, 1.5, 0.2),   // → Grid (0, 1, 0)
643    /// ];
644    ///
645    /// // Create voxels with 1.0 unit size
646    /// let voxels = Voxels::from_points(Vector::new(1.0, 1.0, 1.0), &points);
647    ///
648    /// // Only 3 unique voxels created (first two points merged)
649    /// let filled_count = voxels.voxels()
650    ///     .filter(|v| !v.state.is_empty())
651    ///     .count();
652    /// assert_eq!(filled_count, 3);
653    /// # }
654    /// ```
655    ///
656    /// ```
657    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
658    /// use parry3d::shape::Voxels;
659    /// use parry3d::math::{Vector, IVector};
660    ///
661    /// // Higher resolution voxelization
662    /// let points = vec![
663    ///     Vector::ZERO,
664    ///     Vector::new(1.0, 1.0, 1.0),
665    /// ];
666    ///
667    /// // Smaller voxels = finer detail
668    /// let voxels = Voxels::from_points(Vector::new(0.5, 0.5, 0.5), &points);
669    ///
670    /// // First point at grid (0,0,0), second at grid (2,2,2) due to smaller voxel size
671    /// assert!(voxels.voxel_state(IVector::new(0, 0, 0)).is_some());
672    /// assert!(voxels.voxel_state(IVector::new(2, 2, 2)).is_some());
673    /// # }
674    /// ```
675    pub fn from_points(voxel_size: Vector, points: &[Vector]) -> Self {
676        let voxels: Vec<_> = points
677            .iter()
678            .map(|pt| vect_to_ivect((*pt / voxel_size).floor()))
679            .collect();
680        Self::new(voxel_size, &voxels)
681    }
682
683    pub(crate) fn chunk_bvh(&self) -> &Bvh {
684        &self.chunk_bvh
685    }
686
687    /// The semi-open range of grid coordinates covered by this voxel shape.
688    ///
689    /// Returns `[mins, maxs]` where the domain is the semi-open interval `[mins, maxs)`,
690    /// meaning `mins` is included but `maxs` is excluded. This provides conservative bounds
691    /// on the range of voxel grid coordinates that might be filled.
692    ///
693    /// This is useful for:
694    /// - Determining the spatial extent of the voxel shape
695    /// - Pre-allocating storage for processing voxels
696    /// - Clipping operations to valid regions
697    ///
698    /// # Examples
699    ///
700    /// ```
701    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
702    /// use parry3d::shape::Voxels;
703    /// use parry3d::math::{Vector, IVector};
704    ///
705    /// let voxels = Voxels::new(
706    ///     Vector::new(1.0, 1.0, 1.0),
707    ///     &[IVector::new(0, 0, 0), IVector::new(2, 3, 1)],
708    /// );
709    ///
710    /// let [mins, maxs] = voxels.domain();
711    /// assert_eq!(mins, IVector::new(0, 0, 0));
712    /// // Domain is conservative and chunk-aligned
713    /// assert!(maxs.x > 2 && maxs.y > 3 && maxs.z > 1);
714    ///
715    /// // Iterate through filled voxels (more efficient than iterating domain)
716    /// for voxel in voxels.voxels() {
717    ///     if !voxel.state.is_empty() {
718    ///         println!("Filled voxel at {:?}", voxel.grid_coords);
719    ///     }
720    /// }
721    /// # }
722    /// ```
723    pub fn domain(&self) -> [IVector; 2] {
724        let aabb = self.chunk_bvh.root_aabb();
725
726        // NOTE that we shift the AABB's bounds so the endpoint matches a voxel center
727        //      to avoid rounding errors.
728        let half_sz = self.voxel_size() / 2.0;
729        let mins = self.voxel_at_point(aabb.mins + half_sz);
730        // + 1 because the range is semi-open.
731        let maxs = self.voxel_at_point(aabb.maxs - half_sz);
732        let one = IVector::splat(1);
733        [mins, maxs + one]
734    }
735
736    // /// The number of voxels along each coordinate axis.
737    // pub fn dimensions(&self) -> Vector<u32> {
738    //     (self.domain_maxs - self.domain_mins).map(|e| e as u32)
739    // }
740
741    /// The size of each voxel part this [`Voxels`] shape.
742    pub fn voxel_size(&self) -> Vector {
743        self.voxel_size
744    }
745
746    /// Scale this shape.
747    pub fn scaled(mut self, scale: Vector) -> Self {
748        self.voxel_size *= scale;
749        self
750    }
751
752    /// A reference to the chunk with id `chunk_id`.
753    ///
754    /// Panics if the chunk doesn’t exist.
755    pub fn chunk_ref(&self, chunk_id: u32) -> VoxelsChunkRef<'_> {
756        VoxelsChunkRef {
757            my_id: chunk_id as usize,
758            parent: self,
759            states: &self.chunks[chunk_id as usize].states,
760            key: &self.chunk_keys[chunk_id as usize],
761        }
762    }
763
764    /// The AABB of the voxel with the given quantized `key`.
765    pub fn voxel_aabb(&self, key: IVector) -> Aabb {
766        let center = self.voxel_center(key);
767        let hext = self.voxel_size / 2.0;
768        Aabb::from_half_extents(center, hext)
769    }
770
771    /// Returns the state of the voxel at the given grid coordinates.
772    ///
773    /// Returns `None` if the voxel doesn't exist in this shape's internal storage,
774    /// or `Some(VoxelState)` containing information about whether the voxel is filled
775    /// and which of its neighbors are also filled.
776    ///
777    /// # Examples
778    ///
779    /// ```
780    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
781    /// use parry3d::shape::Voxels;
782    /// use parry3d::math::{Vector, IVector};
783    ///
784    /// let voxels = Voxels::new(
785    ///     Vector::new(1.0, 1.0, 1.0),
786    ///     &[IVector::new(0, 0, 0), IVector::new(1, 0, 0)],
787    /// );
788    ///
789    /// // Query an existing voxel
790    /// if let Some(state) = voxels.voxel_state(IVector::new(0, 0, 0)) {
791    ///     assert!(!state.is_empty());
792    ///     println!("Voxel type: {:?}", state.voxel_type());
793    /// }
794    ///
795    /// // Query a non-existent voxel
796    /// assert!(voxels.voxel_state(IVector::new(10, 10, 10)).is_none());
797    /// # }
798    /// ```
799    pub fn voxel_state(&self, key: IVector) -> Option<VoxelState> {
800        let vid = self.linear_index(key)?;
801        Some(self.chunks[vid.chunk_id].states[vid.id_in_chunk])
802    }
803
804    /// Calculates the grid coordinates of the voxel containing the given world-space point.
805    ///
806    /// This conversion is independent of whether the voxel is actually filled or empty - it
807    /// simply determines which grid cell the point falls into based on the voxel size.
808    ///
809    /// # Examples
810    ///
811    /// ```
812    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
813    /// use parry3d::shape::Voxels;
814    /// use parry3d::math::{Vector, IVector};
815    ///
816    /// let voxels = Voxels::new(
817    ///     Vector::new(1.0, 1.0, 1.0),
818    ///     &[IVector::new(0, 0, 0)],
819    /// );
820    ///
821    /// // Vector in first voxel (center at 0.5, 0.5, 0.5)
822    /// assert_eq!(voxels.voxel_at_point(Vector::new(0.3, 0.7, 0.2)), IVector::new(0, 0, 0));
823    ///
824    /// // Vector just inside second voxel boundary
825    /// assert_eq!(voxels.voxel_at_point(Vector::new(1.0, 0.0, 0.0)), IVector::new(1, 0, 0));
826    ///
827    /// // Negative coordinates work too
828    /// assert_eq!(voxels.voxel_at_point(Vector::new(-0.5, -0.5, -0.5)), IVector::new(-1, -1, -1));
829    /// # }
830    /// ```
831    ///
832    /// ```
833    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
834    /// use parry3d::shape::Voxels;
835    /// use parry3d::math::{Vector, IVector};
836    ///
837    /// // With non-uniform voxel size
838    /// let voxels = Voxels::new(
839    ///     Vector::new(2.0, 0.5, 1.0),
840    ///     &[],
841    /// );
842    ///
843    /// // X coordinate divided by 2.0, Y by 0.5, Z by 1.0
844    /// assert_eq!(voxels.voxel_at_point(Vector::new(3.0, 1.2, 0.8)), IVector::new(1, 2, 0));
845    /// # }
846    /// ```
847    pub fn voxel_at_point(&self, point: Vector) -> IVector {
848        vect_to_ivect((point / self.voxel_size).floor())
849    }
850
851    /// Gets the voxel at the given flat voxel index.
852    pub fn voxel_at_flat_id(&self, id: u32) -> Option<IVector> {
853        let vid = VoxelIndex::from_flat_id(id as usize);
854        let chunk_key = self.chunk_keys.get(vid.chunk_id)?;
855        if *chunk_key == VoxelsChunk::INVALID_CHUNK_KEY {
856            return None;
857        }
858
859        Some(VoxelsChunk::voxel_key_at_id(
860            *chunk_key,
861            vid.id_in_chunk as u32,
862        ))
863    }
864
865    /// The range of grid coordinates of voxels intersecting the given AABB.
866    ///
867    /// The returned range covers both empty and non-empty voxels, and is not limited to the
868    /// bounds defined by [`Self::domain`].
869    /// The range is semi, open, i.e., the range along each dimension `i` is understood as
870    /// the semi-open interval: `range[0][i]..range[1][i]`.
871    pub fn voxel_range_intersecting_local_aabb(&self, aabb: &Aabb) -> [IVector; 2] {
872        let mins = vect_to_ivect((aabb.mins / self.voxel_size).floor());
873        let maxs = vect_to_ivect((aabb.maxs / self.voxel_size).ceil());
874        [mins, maxs]
875    }
876
877    /// The AABB of a given range of voxels.
878    ///
879    /// The AABB is computed independently of [`Self::domain`] and independently of whether
880    /// the voxels contained within are empty or not.
881    pub fn voxel_range_aabb(&self, mins: IVector, maxs: IVector) -> Aabb {
882        Aabb {
883            mins: ivect_to_vect(mins) * self.voxel_size,
884            maxs: ivect_to_vect(maxs) * self.voxel_size,
885        }
886    }
887
888    /// Aligns the given AABB with the voxelized grid.
889    ///
890    /// The aligned is calculated such that the returned AABB has corners lying at the grid
891    /// intersections (i.e. matches voxel corners) and fully contains the input `aabb`.
892    pub fn align_aabb_to_grid(&self, aabb: &Aabb) -> Aabb {
893        let mins = (aabb.mins / self.voxel_size).floor() * self.voxel_size;
894        let maxs = (aabb.maxs / self.voxel_size).ceil() * self.voxel_size;
895        Aabb { mins, maxs }
896    }
897
898    /// Iterates through every voxel intersecting the given aabb.
899    ///
900    /// Returns the voxel’s linearized id, center, and state.
901    pub fn voxels_intersecting_local_aabb(
902        &self,
903        aabb: &Aabb,
904    ) -> impl Iterator<Item = VoxelData> + '_ {
905        let [mins, maxs] = self.voxel_range_intersecting_local_aabb(aabb);
906        self.voxels_in_range(mins, maxs)
907    }
908
909    /// The center point of all the voxels in this shape (including empty ones).
910    ///
911    /// The voxel data associated to each center is provided to determine what kind of voxel
912    /// it is (and, in particular, if it is empty or full).
913    pub fn voxels(&self) -> impl Iterator<Item = VoxelData> + '_ {
914        let aabb = self.chunk_bvh.root_aabb();
915        self.voxels_in_range(
916            self.voxel_at_point(aabb.mins),
917            self.voxel_at_point(aabb.maxs),
918        )
919    }
920
921    /// Iterate through the data of all the voxels within the given (semi-open) voxel grid indices.
922    ///
923    /// Note that this yields both empty and non-empty voxels within the range. This does not
924    /// include any voxel that falls outside [`Self::domain`].
925    pub fn voxels_in_range(
926        &self,
927        mins: IVector,
928        maxs: IVector,
929    ) -> impl Iterator<Item = VoxelData> + '_ {
930        let range_aabb = Aabb::new(self.voxel_center(mins), self.voxel_center(maxs));
931
932        self.chunk_bvh
933            .leaves(move |node: &BvhNode| node.aabb().intersects(&range_aabb))
934            .flat_map(move |chunk_id| {
935                let chunk = self.chunk_ref(chunk_id);
936                chunk.voxels_in_range(mins, maxs)
937            })
938    }
939
940    fn voxel_to_chunk_key(voxel_key: IVector) -> IVector {
941        fn div_floor(a: Int, b: usize) -> Int {
942            let sign = (a < 0) as Int;
943            (a + sign) / b as Int - sign
944        }
945
946        #[cfg(feature = "dim2")]
947        {
948            IVector::new(
949                div_floor(voxel_key.x, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
950                div_floor(voxel_key.y, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
951            )
952        }
953        #[cfg(feature = "dim3")]
954        {
955            IVector::new(
956                div_floor(voxel_key.x, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
957                div_floor(voxel_key.y, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
958                div_floor(voxel_key.z, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
959            )
960        }
961    }
962
963    /// Given a voxel key, returns the key of the voxel chunk that contains it, as well as the
964    /// linear index of the voxel within that chunk.
965    #[cfg(feature = "dim2")]
966    pub(super) fn chunk_key_and_id_in_chunk(voxel_key: IVector) -> (IVector, usize) {
967        let chunk_key = Self::voxel_to_chunk_key(voxel_key);
968        // NOTE: always positive since we subtracted the smallest possible key on that chunk.
969        let voxel_key_in_chunk = voxel_key - chunk_key * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int;
970        let id_in_chunk = (voxel_key_in_chunk.x
971            + voxel_key_in_chunk.y * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int)
972            as usize;
973        (chunk_key, id_in_chunk)
974    }
975
976    /// Given a voxel key, returns the key of the voxel chunk that contains it, as well as the
977    /// linear index of the voxel within that chunk.
978    #[cfg(feature = "dim3")]
979    pub(super) fn chunk_key_and_id_in_chunk(voxel_key: IVector) -> (IVector, usize) {
980        let chunk_key = Self::voxel_to_chunk_key(voxel_key);
981        // NOTE: always positive since we subtracted the smallest possible key on that chunk.
982        let voxel_key_in_chunk = voxel_key - chunk_key * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int;
983        let id_in_chunk = (voxel_key_in_chunk.x
984            + voxel_key_in_chunk.y * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int
985            + voxel_key_in_chunk.z
986                * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int
987                * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int) as usize;
988        (chunk_key, id_in_chunk)
989    }
990
991    /// The linearized index associated to the given voxel key.
992    pub fn linear_index(&self, voxel_key: IVector) -> Option<VoxelIndex> {
993        let (chunk_key, id_in_chunk) = Self::chunk_key_and_id_in_chunk(voxel_key);
994        let chunk_id = self.chunk_headers.get(&chunk_key)?.id;
995        Some(VoxelIndex {
996            chunk_id,
997            id_in_chunk,
998        })
999    }
1000
1001    /// The world-space center position of the voxel with the given grid coordinates.
1002    ///
1003    /// Returns the center point regardless of whether the voxel is actually filled.
1004    ///
1005    /// # Examples
1006    ///
1007    /// ```
1008    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
1009    /// use parry3d::shape::Voxels;
1010    /// use parry3d::math::{Vector, IVector};
1011    ///
1012    /// let voxels = Voxels::new(
1013    ///     Vector::new(1.0, 1.0, 1.0),
1014    ///     &[IVector::new(0, 0, 0)],
1015    /// );
1016    ///
1017    /// // Center of voxel at origin
1018    /// assert_eq!(voxels.voxel_center(IVector::new(0, 0, 0)), Vector::new(0.5, 0.5, 0.5));
1019    ///
1020    /// // Center of voxel at (1, 2, 3)
1021    /// assert_eq!(voxels.voxel_center(IVector::new(1, 2, 3)), Vector::new(1.5, 2.5, 3.5));
1022    /// # }
1023    /// ```
1024    pub fn voxel_center(&self, key: IVector) -> Vector {
1025        (ivect_to_vect(key) + Vector::splat(0.5)) * self.voxel_size
1026    }
1027}