use crate::compiled::CompiledSdf;
use crate::physics_bridge::CompiledSdfField;
use alice_physics::sdf_collider::SdfField;
use alice_physics::sim_modifier::{ModifiedSdf, PhysicsModifier};
use std::sync::Arc;
const DEFAULT_HALF_EXTENT: f32 = 5.0;
pub struct SimulatedSdf {
inner: ModifiedSdf,
compiled: Arc<CompiledSdf>,
bounds_min: (f32, f32, f32),
bounds_max: (f32, f32, f32),
}
impl SimulatedSdf {
pub fn new(compiled: CompiledSdf) -> Self {
Self::from_arc(Arc::new(compiled))
}
pub fn from_arc(arc: Arc<CompiledSdf>) -> Self {
let field = CompiledSdfField::from_arc(Arc::clone(&arc));
let modified = ModifiedSdf::new(Box::new(field));
let e = DEFAULT_HALF_EXTENT;
Self {
inner: modified,
compiled: arc,
bounds_min: (-e, -e, -e),
bounds_max: (e, e, e),
}
}
pub const fn with_bounds(mut self, min: (f32, f32, f32), max: (f32, f32, f32)) -> Self {
self.bounds_min = min;
self.bounds_max = max;
self
}
pub fn compiled(&self) -> &CompiledSdf {
&self.compiled
}
pub const fn bounds(&self) -> ((f32, f32, f32), (f32, f32, f32)) {
(self.bounds_min, self.bounds_max)
}
pub fn add_modifier(&mut self, modifier: Box<dyn PhysicsModifier>) {
self.inner.add_modifier(modifier);
}
pub fn modifier_count(&self) -> usize {
self.inner.modifier_count()
}
pub fn clear_modifiers(&mut self) {
self.inner.clear_modifiers();
}
pub fn update(&mut self, dt: f32) {
self.inner.update(dt);
}
pub fn distance(&self, x: f32, y: f32, z: f32) -> f32 {
self.inner.distance(x, y, z)
}
pub fn normal(&self, x: f32, y: f32, z: f32) -> (f32, f32, f32) {
self.inner.normal(x, y, z)
}
pub fn modifier_mut(&mut self, index: usize) -> Option<&mut dyn PhysicsModifier> {
self.inner.modifier_mut(index)
}
pub fn add_thermal(
&mut self,
config: alice_physics::thermal::ThermalConfig,
resolution: usize,
) -> usize {
let modifier = alice_physics::thermal::ThermalModifier::new(
config,
resolution,
self.bounds_min,
self.bounds_max,
);
let idx = self.modifier_count();
self.add_modifier(Box::new(modifier));
idx
}
pub fn add_pressure(
&mut self,
config: alice_physics::pressure::PressureConfig,
resolution: usize,
) -> usize {
let modifier = alice_physics::pressure::PressureModifier::new(
config,
resolution,
self.bounds_min,
self.bounds_max,
);
let idx = self.modifier_count();
self.add_modifier(Box::new(modifier));
idx
}
pub fn add_erosion(
&mut self,
config: alice_physics::erosion::ErosionConfig,
resolution: usize,
) -> usize {
let modifier = alice_physics::erosion::ErosionModifier::new(
config,
resolution,
self.bounds_min,
self.bounds_max,
);
let idx = self.modifier_count();
self.add_modifier(Box::new(modifier));
idx
}
pub fn add_fracture(
&mut self,
config: alice_physics::fracture::FractureConfig,
resolution: usize,
) -> usize {
let modifier = alice_physics::fracture::FractureModifier::new(
config,
resolution,
self.bounds_min,
self.bounds_max,
);
let idx = self.modifier_count();
self.add_modifier(Box::new(modifier));
idx
}
pub fn add_phase_change(
&mut self,
config: alice_physics::phase_change::PhaseChangeConfig,
resolution: usize,
) -> usize {
let modifier = alice_physics::phase_change::PhaseChangeModifier::new(
config,
resolution,
self.bounds_min,
self.bounds_max,
);
let idx = self.modifier_count();
self.add_modifier(Box::new(modifier));
idx
}
}
pub fn simulate_sdf(node: &crate::types::SdfNode) -> SimulatedSdf {
let compiled = CompiledSdf::compile(node);
SimulatedSdf::new(compiled)
}
#[cfg(feature = "gpu")]
pub struct GpuPhysicsBundle {
pub mesh: crate::mesh::Mesh,
pub sim: SimulatedSdf,
}
#[cfg(feature = "gpu")]
pub fn gpu_mesh_with_physics(
node: &crate::types::SdfNode,
bounds_min: glam::Vec3,
bounds_max: glam::Vec3,
mc_config: &crate::mesh::gpu_marching_cubes::GpuMarchingCubesConfig,
) -> Result<GpuPhysicsBundle, crate::compiled::GpuError> {
use crate::mesh::gpu_marching_cubes::gpu_marching_cubes;
let mesh = gpu_marching_cubes(node, bounds_min, bounds_max, mc_config)?;
let compiled = CompiledSdf::compile(node);
let sim = SimulatedSdf::new(compiled).with_bounds(
(bounds_min.x, bounds_min.y, bounds_min.z),
(bounds_max.x, bounds_max.y, bounds_max.z),
);
Ok(GpuPhysicsBundle { mesh, sim })
}
pub fn attach_physics(
node: &crate::types::SdfNode,
bounds_min: (f32, f32, f32),
bounds_max: (f32, f32, f32),
) -> SimulatedSdf {
let compiled = CompiledSdf::compile(node);
SimulatedSdf::new(compiled).with_bounds(bounds_min, bounds_max)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::SdfNode;
use alice_physics::pressure::PressureConfig;
use alice_physics::thermal::ThermalConfig;
#[test]
fn test_simulated_sdf_no_modifiers() {
let node = SdfNode::sphere(1.0);
let sim = simulate_sdf(&node);
let d = sim.distance(2.0, 0.0, 0.0);
assert!((d - 1.0).abs() < 0.01, "No modifiers: d={}", d);
}
#[test]
fn test_simulated_sdf_with_thermal() {
let node = SdfNode::sphere(1.0);
let mut sim = simulate_sdf(&node);
let _idx = sim.add_thermal(ThermalConfig::default(), 8);
assert_eq!(sim.modifier_count(), 1);
let d = sim.distance(2.0, 0.0, 0.0);
assert!((d - 1.0).abs() < 0.1, "No heat: d={}", d);
}
#[test]
fn test_simulated_sdf_with_pressure() {
let node = SdfNode::sphere(1.0);
let mut sim = simulate_sdf(&node);
sim.add_pressure(PressureConfig::default(), 8);
assert_eq!(sim.modifier_count(), 1);
sim.update(0.016);
let d = sim.distance(2.0, 0.0, 0.0);
assert!((d - 1.0).abs() < 0.1, "No pressure: d={}", d);
}
#[test]
fn test_simulated_sdf_multiple_modifiers() {
let node = SdfNode::sphere(1.0);
let mut sim = simulate_sdf(&node);
sim.add_thermal(ThermalConfig::default(), 8);
sim.add_pressure(PressureConfig::default(), 8);
assert_eq!(sim.modifier_count(), 2);
for _ in 0..10 {
sim.update(0.016);
}
let d = sim.distance(2.0, 0.0, 0.0);
assert!(d > 0.0, "Should still be outside: d={}", d);
}
#[test]
fn test_attach_physics() {
let node = SdfNode::sphere(1.0);
let sim = attach_physics(&node, (-3.0, -3.0, -3.0), (3.0, 3.0, 3.0));
let (bmin, bmax) = sim.bounds();
assert_eq!(bmin, (-3.0, -3.0, -3.0));
assert_eq!(bmax, (3.0, 3.0, 3.0));
let d = sim.distance(2.0, 0.0, 0.0);
assert!((d - 1.0).abs() < 0.01, "attach_physics d={}", d);
}
#[test]
fn test_attach_physics_with_modifiers() {
let node = SdfNode::sphere(1.0);
let mut sim = attach_physics(&node, (-2.0, -2.0, -2.0), (2.0, 2.0, 2.0));
sim.add_thermal(ThermalConfig::default(), 8);
assert_eq!(sim.modifier_count(), 1);
sim.update(0.016);
let d = sim.distance(2.0, 0.0, 0.0);
assert!(d > 0.0, "Should still be outside: d={}", d);
}
#[test]
fn test_simulated_sdf_custom_bounds() {
let node = SdfNode::sphere(1.0);
let sim = SimulatedSdf::new(CompiledSdf::compile(&node))
.with_bounds((-2.0, -2.0, -2.0), (2.0, 2.0, 2.0));
let (bmin, bmax) = sim.bounds();
assert_eq!(bmin, (-2.0, -2.0, -2.0));
assert_eq!(bmax, (2.0, 2.0, 2.0));
}
}