bevy_sculpter/
density_field.rs

1//! Density field storage and operations for SDF-based volumetric data.
2//!
3//! The [`DensityField`] component stores signed distance field (SDF) values on a 3D grid.
4//! Negative values represent the interior of a shape, positive values the exterior,
5//! and the zero-crossing defines the surface.
6
7use crate::{DENSITY_FIELD_SIZE, FIELD_VOLUME, field::Field};
8use bevy::prelude::*;
9
10/// A 3D grid of signed distance field (SDF) values.
11///
12/// Each voxel stores a floating-point density value where:
13/// - **Negative** = inside the surface
14/// - **Positive** = outside the surface  
15/// - **Zero** = on the surface
16///
17/// The field is stored as a flat `Vec<f32>` in X-Y-Z order (X varies fastest).
18///
19/// # Example
20///
21/// ```
22/// use bevy_sculpter::prelude::*;
23///
24/// let mut field = DensityField::new();
25///
26/// // Set a single voxel to be inside
27/// field.set(16, 16, 16, -1.0);
28///
29/// // Query a voxel
30/// let density = field.get(16, 16, 16);
31/// assert!(density < 0.0); // Inside
32/// ```
33#[derive(Component, Clone, Deref, DerefMut, Debug)]
34pub struct DensityField(pub Vec<f32>);
35
36impl Default for DensityField {
37    fn default() -> Self {
38        Self(vec![1.0; FIELD_VOLUME]) // All outside
39    }
40}
41
42impl Field<f32> for DensityField {
43    const SIZE: UVec3 = DENSITY_FIELD_SIZE;
44    const DEFAULT: f32 = 1.0; // Outside = exterior
45
46    #[inline]
47    fn data(&self) -> &[f32] {
48        &self.0
49    }
50
51    #[inline]
52    fn data_mut(&mut self) -> &mut [f32] {
53        &mut self.0
54    }
55}
56
57impl DensityField {
58    /// Creates a new density field with all voxels set to exterior (1.0).
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// Creates a density field with all voxels set to the given value.
64    ///
65    /// # Arguments
66    /// * `value` - The density value for all voxels (negative = inside, positive = outside)
67    pub fn filled(value: f32) -> Self {
68        Self(vec![value; FIELD_VOLUME])
69    }
70
71    // =========================================================================
72    // SDF-specific operations (not part of generic Field trait)
73    // =========================================================================
74
75    /// Checks if a voxel is inside the surface (negative density).
76    #[inline]
77    pub fn is_inside(&self, x: u32, y: u32, z: u32) -> bool {
78        self.get(x, y, z) < 0.0
79    }
80
81    /// Checks if a voxel is outside the surface (positive density).
82    #[inline]
83    pub fn is_outside(&self, x: u32, y: u32, z: u32) -> bool {
84        self.get(x, y, z) > 0.0
85    }
86
87    /// Checks if a voxel is on the surface (near zero density).
88    #[inline]
89    pub fn is_surface(&self, x: u32, y: u32, z: u32, threshold: f32) -> bool {
90        self.get(x, y, z).abs() <= threshold
91    }
92
93    // ... rest of the SDF-specific methods (nearest_interior, raycasting, redistancing, etc.)
94    // remain unchanged ...
95}
96
97/// Result of finding the nearest interior point in a density field.
98#[derive(Clone, Copy, Debug)]
99pub struct NearestInteriorResult {
100    /// Grid coordinates of the nearest interior voxel.
101    pub grid_pos: UVec3,
102    /// World-space position of the nearest interior voxel center.
103    pub world_pos: Vec3,
104    /// The density value at this point (negative = inside).
105    pub density: f32,
106    /// Squared distance from query point to this voxel (in grid space).
107    pub distance_sq: f32,
108}
109
110// ... rest of the file (raycasting, redistancing, etc.) remains the same ...
111
112/// Marker component indicating this chunk needs remeshing.
113#[derive(Component, Clone, Copy, Default, Debug)]
114pub struct GenerateMesh;