use super::combinators::{sdf_smooth_union, sdf_union};
use super::helpers::{scale3, sub3};
use super::operators::sdf_normal;
use super::primitives::{sdf_box, sdf_capsule, sdf_cylinder, sdf_plane, sdf_sphere, sdf_torus};
use super::ray_marcher::{RayMarchHit, RayMarcher};
#[derive(Debug, Clone)]
pub struct SdfObject {
pub name: String,
pub translation: [f64; 3],
pub scale: f64,
pub kind: SdfKind,
}
#[derive(Debug, Clone)]
pub enum SdfKind {
Sphere(f64),
Box([f64; 3]),
Capsule([f64; 3], [f64; 3], f64),
Cylinder(f64, f64),
Torus(f64, f64),
Plane([f64; 3], f64),
}
impl SdfObject {
pub fn sphere(name: &str, radius: f64, translation: [f64; 3]) -> Self {
Self {
name: name.to_string(),
translation,
scale: 1.0,
kind: SdfKind::Sphere(radius),
}
}
pub fn box_shape(name: &str, half_extents: [f64; 3], translation: [f64; 3]) -> Self {
Self {
name: name.to_string(),
translation,
scale: 1.0,
kind: SdfKind::Box(half_extents),
}
}
pub fn evaluate(&self, p: [f64; 3]) -> f64 {
let local = scale3(sub3(p, self.translation), 1.0 / self.scale);
let raw = match &self.kind {
SdfKind::Sphere(r) => sdf_sphere(local, *r),
SdfKind::Box(b) => sdf_box(local, *b),
SdfKind::Capsule(a, b, r) => sdf_capsule(local, *a, *b, *r),
SdfKind::Cylinder(r, h) => sdf_cylinder(local, *r, *h),
SdfKind::Torus(r1, r2) => sdf_torus(local, *r1, *r2),
SdfKind::Plane(n, d) => sdf_plane(local, *n, *d),
};
raw * self.scale
}
}
#[derive(Debug, Clone, Default)]
pub struct SdfScene {
pub objects: Vec<SdfObject>,
pub blend_k: f64,
}
impl SdfScene {
pub fn new() -> Self {
Self {
objects: Vec::new(),
blend_k: 0.0,
}
}
pub fn add(&mut self, obj: SdfObject) {
self.objects.push(obj);
}
pub fn evaluate(&self, p: [f64; 3]) -> f64 {
if self.objects.is_empty() {
return f64::MAX;
}
let mut d = self.objects[0].evaluate(p);
for obj in &self.objects[1..] {
let di = obj.evaluate(p);
d = if self.blend_k > 0.0 {
sdf_smooth_union(d, di, self.blend_k)
} else {
sdf_union(d, di)
};
}
d
}
pub fn ray_cast(&self, origin: [f64; 3], dir: [f64; 3]) -> Option<RayMarchHit> {
let marcher = RayMarcher::new();
marcher.march(&|p| self.evaluate(p), origin, dir)
}
pub fn normal(&self, p: [f64; 3]) -> [f64; 3] {
sdf_normal(&|q| self.evaluate(q), p, 1e-4)
}
}