oxiphysics_geometry/signed_distance_field/
scene.rs1use super::combinators::{sdf_smooth_union, sdf_union};
7use super::helpers::{scale3, sub3};
8use super::operators::sdf_normal;
9use super::primitives::{sdf_box, sdf_capsule, sdf_cylinder, sdf_plane, sdf_sphere, sdf_torus};
10use super::ray_marcher::{RayMarchHit, RayMarcher};
11
12#[derive(Debug, Clone)]
18pub struct SdfObject {
19 pub name: String,
21 pub translation: [f64; 3],
23 pub scale: f64,
25 pub kind: SdfKind,
27}
28
29#[derive(Debug, Clone)]
31pub enum SdfKind {
32 Sphere(f64),
34 Box([f64; 3]),
36 Capsule([f64; 3], [f64; 3], f64),
38 Cylinder(f64, f64),
40 Torus(f64, f64),
42 Plane([f64; 3], f64),
44}
45
46impl SdfObject {
47 pub fn sphere(name: &str, radius: f64, translation: [f64; 3]) -> Self {
49 Self {
50 name: name.to_string(),
51 translation,
52 scale: 1.0,
53 kind: SdfKind::Sphere(radius),
54 }
55 }
56
57 pub fn box_shape(name: &str, half_extents: [f64; 3], translation: [f64; 3]) -> Self {
59 Self {
60 name: name.to_string(),
61 translation,
62 scale: 1.0,
63 kind: SdfKind::Box(half_extents),
64 }
65 }
66
67 pub fn evaluate(&self, p: [f64; 3]) -> f64 {
69 let local = scale3(sub3(p, self.translation), 1.0 / self.scale);
71 let raw = match &self.kind {
72 SdfKind::Sphere(r) => sdf_sphere(local, *r),
73 SdfKind::Box(b) => sdf_box(local, *b),
74 SdfKind::Capsule(a, b, r) => sdf_capsule(local, *a, *b, *r),
75 SdfKind::Cylinder(r, h) => sdf_cylinder(local, *r, *h),
76 SdfKind::Torus(r1, r2) => sdf_torus(local, *r1, *r2),
77 SdfKind::Plane(n, d) => sdf_plane(local, *n, *d),
78 };
79 raw * self.scale
80 }
81}
82
83#[derive(Debug, Clone, Default)]
85pub struct SdfScene {
86 pub objects: Vec<SdfObject>,
88 pub blend_k: f64,
90}
91
92impl SdfScene {
93 pub fn new() -> Self {
95 Self {
96 objects: Vec::new(),
97 blend_k: 0.0,
98 }
99 }
100
101 pub fn add(&mut self, obj: SdfObject) {
103 self.objects.push(obj);
104 }
105
106 pub fn evaluate(&self, p: [f64; 3]) -> f64 {
108 if self.objects.is_empty() {
109 return f64::MAX;
110 }
111 let mut d = self.objects[0].evaluate(p);
112 for obj in &self.objects[1..] {
113 let di = obj.evaluate(p);
114 d = if self.blend_k > 0.0 {
115 sdf_smooth_union(d, di, self.blend_k)
116 } else {
117 sdf_union(d, di)
118 };
119 }
120 d
121 }
122
123 pub fn ray_cast(&self, origin: [f64; 3], dir: [f64; 3]) -> Option<RayMarchHit> {
125 let marcher = RayMarcher::new();
126 marcher.march(&|p| self.evaluate(p), origin, dir)
127 }
128
129 pub fn normal(&self, p: [f64; 3]) -> [f64; 3] {
131 sdf_normal(&|q| self.evaluate(q), p, 1e-4)
132 }
133}