Struct VoxelSet

Source
pub struct VoxelSet {
    pub origin: Point<f32>,
    pub scale: f32,
    /* private fields */
}
Expand description

A sparse set of filled voxels resulting from voxelization.

VoxelSet is a memory-efficient storage format that only contains voxels marked as “filled” during the voxelization process. This is much more efficient than storing a dense 3D array for shapes that are mostly empty or have a hollow interior.

§Structure

Each VoxelSet contains:

  • A list of filled voxels with their grid coordinates
  • The origin point and scale factor for converting grid coordinates to world space
  • Optional metadata tracking which primitives (triangles/segments) intersect each voxel

§Grid Coordinates vs World Coordinates

Voxels are stored with integer grid coordinates (i, j, k). To convert to world-space coordinates, use:

world_position = origin + (i, j, k) * scale

The get_voxel_point() method does this conversion for you.

§Memory Layout

Unlike VoxelizedVolume which stores a dense 3D array, VoxelSet uses sparse storage:

  • Only filled voxels are stored (typically much fewer than total grid cells)
  • Memory usage is O(filled_voxels) instead of O(resolution^3)
  • Ideal for shapes with low surface-to-volume ratio

§Example: Basic Usage

use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Cuboid;
use nalgebra::Vector3;

// Create and voxelize a shape
let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
let (vertices, indices) = cuboid.to_trimesh();

let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    10,                      // resolution
    FillMode::SurfaceOnly,   // hollow surface only
    false,                   // no primitive mapping
);

// Query the voxel set
println!("Number of voxels: {}", voxels.len());
println!("Voxel size: {}", voxels.scale);
println!("Total volume: {}", voxels.compute_volume());

// Access voxels
for voxel in voxels.voxels() {
    let world_pos = voxels.get_voxel_point(voxel);
    println!("Voxel at {:?}", world_pos);
}

§Example: Volume Computation

use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Ball;
///
let ball = Ball::new(1.0);
let (vertices, indices) = ball.to_trimesh(20, 20);

// Voxelize with interior filling
let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    20,
    FillMode::FloodFill { detect_cavities: false },
    false,
);

// Compute approximate volume
let voxel_volume = voxels.compute_volume();
let expected_volume = 4.0 / 3.0 * std::f32::consts::PI;
println!("Voxel volume: {:.3}, Expected: {:.3}", voxel_volume, expected_volume);

Fields§

§origin: Point<f32>

The 3D origin of this voxel-set.

§scale: f32

The scale factor between the voxel integer coordinates and their actual float world-space coordinates.

Implementations§

Source§

impl VoxelSet

Source

pub fn new() -> Self

Creates a new empty set of voxels.

Source

pub fn voxel_volume(&self) -> f32

The volume of a single voxel of this voxel set.

Source

pub fn with_voxel_size( points: &[Point<f32>], indices: &[[u32; 3]], voxel_size: f32, fill_mode: FillMode, keep_voxel_to_primitives_map: bool, ) -> Self

Voxelizes a shape by specifying the physical size of each voxel.

This creates a voxelized representation of a shape defined by its boundary:

  • In 3D: A triangle mesh (vertices and triangle indices)
  • In 2D: A polyline (vertices and segment indices)

The resolution is automatically determined based on the shape’s bounding box and the requested voxel size.

§Parameters
  • points - Vertex buffer defining the boundary of the shape. These are the vertices of the triangle mesh (3D) or polyline (2D).

  • indices - Index buffer defining the boundary primitives:

    • 3D: Each element is [v0, v1, v2] defining a triangle
    • 2D: Each element is [v0, v1] defining a line segment
  • voxel_size - The physical size (edge length) of each cubic voxel. Smaller values give higher resolution but use more memory.

  • fill_mode - Controls which voxels are marked as filled:

  • keep_voxel_to_primitives_map - If true, stores which primitives (triangles or segments) intersect each voxel. Required for compute_exact_convex_hull() and compute_primitive_intersections(), but uses additional memory.

§Returns

A sparse VoxelSet containing only the filled voxels.

§Example
use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Ball;

let ball = Ball::new(2.0);  // radius = 2.0
let (vertices, indices) = ball.to_trimesh(20, 20);

// Create voxels with 0.1 unit size
let voxels = VoxelSet::with_voxel_size(
    &vertices,
    &indices,
    0.1,                    // each voxel is 0.1 x 0.1 x 0.1
    FillMode::SurfaceOnly,
    false,
);

println!("Created {} voxels", voxels.len());
println!("Each voxel has volume {}", voxels.voxel_volume());
Source

pub fn voxelize( points: &[Point<f32>], indices: &[[u32; 3]], resolution: u32, fill_mode: FillMode, keep_voxel_to_primitives_map: bool, ) -> Self

Voxelizes a shape by specifying the grid resolution along the longest axis.

This creates a voxelized representation of a shape defined by its boundary:

  • In 3D: A triangle mesh (vertices and triangle indices)
  • In 2D: A polyline (vertices and segment indices)

The voxel size is automatically computed to fit the specified number of subdivisions along the shape’s longest axis, while maintaining cubic (or square in 2D) voxels. Other axes will have proportionally determined resolutions.

§Parameters
  • points - Vertex buffer defining the boundary of the shape. These are the vertices of the triangle mesh (3D) or polyline (2D).

  • indices - Index buffer defining the boundary primitives:

    • 3D: Each element is [v0, v1, v2] defining a triangle
    • 2D: Each element is [v0, v1] defining a line segment
  • resolution - Number of voxel subdivisions along the longest axis of the shape’s bounding box. Higher values give more detail but use more memory. For example, resolution = 10 creates approximately 10 voxels along the longest dimension.

  • fill_mode - Controls which voxels are marked as filled:

  • keep_voxel_to_primitives_map - If true, stores which primitives (triangles or segments) intersect each voxel. Required for compute_exact_convex_hull() and compute_primitive_intersections(), but uses additional memory.

§Returns

A sparse VoxelSet containing only the filled voxels.

§Example
use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Cuboid;
use nalgebra::Vector3;

// Create a cuboid: 2 units wide (x), 1 unit tall (y), 0.5 units deep (z)
let cuboid = Cuboid::new(Vector3::new(1.0, 0.5, 0.25));
let (vertices, indices) = cuboid.to_trimesh();

// Voxelize with 20 subdivisions along the longest axis (x = 2.0)
// Other axes will be proportionally subdivided to maintain cubic voxels
let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    20,                           // 20 voxels along x-axis
    FillMode::FloodFill {
        detect_cavities: false,
    },
    false,
);

println!("Created {} voxels", voxels.len());
println!("Voxel scale: {}", voxels.scale);  // automatically computed
println!("Total volume: {}", voxels.compute_volume());
§Choosing Resolution
  • Low (5-10): Fast, coarse approximation, good for rough collision proxies
  • Medium (10-30): Balanced detail and performance, suitable for most use cases
  • High (50+): Fine detail, high memory usage, used for precise volume computation
Source

pub fn min_bb_voxels(&self) -> Point<u32>

The minimal coordinates of the integer bounding-box of the voxels in this set.

Source

pub fn max_bb_voxels(&self) -> Point<u32>

The maximal coordinates of the integer bounding-box of the voxels in this set.

Source

pub fn compute_volume(&self) -> f32

Computes the total volume occupied by all voxels in this set.

This calculates the approximate volume by multiplying the number of filled voxels by the volume of each individual voxel. The result is an approximation of the volume of the original shape.

§Accuracy

The accuracy depends on the voxelization resolution:

  • Higher resolution (smaller voxels) → more accurate volume
  • Lower resolution (larger voxels) → faster but less accurate
§Example
use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Ball;

let ball = Ball::new(1.0);
let (vertices, indices) = ball.to_trimesh(20, 20);

let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    30,  // Higher resolution for better accuracy
    FillMode::FloodFill { detect_cavities: false },
    false,
);

let voxel_volume = voxels.compute_volume();
let expected_volume = 4.0 / 3.0 * std::f32::consts::PI * 1.0_f32.powi(3);

println!("Voxel volume: {:.3}", voxel_volume);
println!("Expected volume: {:.3}", expected_volume);
println!("Error: {:.1}%", ((voxel_volume - expected_volume).abs() / expected_volume * 100.0));
Source

pub fn get_voxel_point(&self, voxel: &Voxel) -> Point<f32>

Converts voxel grid coordinates to world-space coordinates.

Given a voxel, this computes the world-space position of the voxel’s center. The conversion formula is:

world_position = origin + (voxel.coords + 0.5) * scale

Note that we add 0.5 to get the center of the voxel rather than its corner.

§Example
use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Cuboid;
use nalgebra::Vector3;

let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
let (vertices, indices) = cuboid.to_trimesh();

let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    10,
    FillMode::SurfaceOnly,
    false,
);

// Convert grid coordinates to world coordinates
for voxel in voxels.voxels() {
    let grid_coords = voxel.coords;
    let world_coords = voxels.get_voxel_point(voxel);

    println!("Grid: {:?} -> World: {:?}", grid_coords, world_coords);
}
Source

pub fn is_empty(&self) -> bool

Does this voxel not contain any element?

Source

pub fn len(&self) -> usize

The number of voxels in this set.

Source

pub fn voxels(&self) -> &[Voxel]

The set of voxels.

Source

pub fn compute_bb(&mut self)

Update the bounding box of this voxel set.

Source

pub fn compute_exact_convex_hull( &self, points: &[Point<f32>], indices: &[[u32; 3]], ) -> (Vec<Point<f32>>, Vec<[u32; 3]>)

Computes a precise convex hull by clipping primitives against voxel boundaries.

This method produces a more accurate convex hull than compute_convex_hull() by:

  1. Finding which primitives (triangles/segments) intersect each voxel
  2. Clipping those primitives to the voxel boundaries
  3. Computing the convex hull from the clipped geometry

This approach gives much tighter convex hulls, especially at lower resolutions, because it uses the actual intersection geometry rather than just voxel centers.

§Requirements

This method requires that the voxel set was created with keep_voxel_to_primitives_map = true. Otherwise, this method will panic.

§Parameters
  • points - The same vertex buffer used during voxelization
  • indices - The same index buffer used during voxelization
§Returns

In 2D: A vector of points forming the convex hull polygon In 3D: A tuple of (vertices, triangle_indices) forming the convex hull mesh

§Panics

Panics if this VoxelSet was created with keep_voxel_to_primitives_map = false.

§Example
use parry3d::transformation::voxelization::{FillMode, VoxelSet};
use parry3d::shape::Cuboid;
use nalgebra::Vector3;

let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
let (vertices, indices) = cuboid.to_trimesh();

// IMPORTANT: Set keep_voxel_to_primitives_map = true
let voxels = VoxelSet::voxelize(
    &vertices,
    &indices,
    10,
    FillMode::SurfaceOnly,
    true,  // Enable primitive mapping
);

// Compute exact convex hull using triangle clipping
let (hull_vertices, hull_indices) = voxels.compute_exact_convex_hull(&vertices, &indices);

println!("Exact convex hull: {} vertices, {} triangles",
         hull_vertices.len(), hull_indices.len());
§Comparison with compute_convex_hull()
  • compute_exact_convex_hull(): More accurate, requires primitive mapping, slower
  • compute_convex_hull(): Approximate, uses voxel centers, faster
Source

pub fn compute_primitive_intersections( &self, points: &[Point<f32>], indices: &[[u32; 3]], ) -> Vec<Point<f32>>

Computes the intersections between all the voxels of this voxel set, and all the primitives (triangle or segments) it intersected (as per the voxel-to-primitives-map computed during voxelization).

Panics if the voxelization was performed without setting the parameter voxel_to_primitives_map = true.

Source

pub fn compute_convex_hull( &self, sampling: u32, ) -> (Vec<Point<f32>>, Vec<[u32; 3]>)

Compute the convex-hull of the voxels in this set.

§Parameters
  • sampling - The convex-hull computation will ignore sampling voxels at regular intervals. Useful to save some computation times if an exact result isn’t need. Use 0 to make sure no voxel is being ignored.
Source

pub fn to_trimesh( &self, base_index: u32, is_on_surface: bool, ) -> (Vec<Point<f32>>, Vec<[u32; 3]>)

Convert self into a mesh, including only the voxels on the surface or only the voxel inside of the volume.

Trait Implementations§

Source§

impl Default for VoxelSet

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl From<VoxelizedVolume> for VoxelSet

Source§

fn from(shape: VoxelizedVolume) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.