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;