1#![forbid(unsafe_code)]
5#![allow(
6 clippy::enum_glob_use, )]
8
9use macaw::Vec3;
10
11pub use saft_sdf::*;
12
13mod program;
14pub use program::*;
15
16mod compiler;
17pub use compiler::*;
18
19mod graph;
20pub use graph::*;
21
22mod grid3;
23pub use grid3::*;
24
25mod mesh;
26pub use mesh::*;
27
28mod marching_cubes;
29pub use marching_cubes::*;
30
31pub mod sphere_tracing;
32
33mod trace;
34pub use trace::*;
35
36mod codegen;
37pub use codegen::*;
38
39pub use macaw::BoundingBox;
40#[derive(Clone, Copy, Debug, PartialEq)]
41#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
42#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
43pub struct MeshOptions {
44 pub mean_resolution: f32,
48
49 pub max_resolution: f32,
53 pub min_resolution: f32,
54}
55
56impl MeshOptions {
57 pub fn low() -> Self {
58 Self {
59 mean_resolution: 32.0,
60 max_resolution: 64.0,
61 min_resolution: 8.0,
62 }
63 }
64}
65
66impl Default for MeshOptions {
67 fn default() -> Self {
68 Self {
69 mean_resolution: 64.0,
70 max_resolution: 128.0,
71 min_resolution: 8.0,
72 }
73 }
74}
75
76pub fn transform_positions_in_place(
77 mesh: &mut TriangleMesh,
78 world_from_grid_f: impl Fn(Vec3) -> Vec3 + Send + Sync,
79) {
80 #[cfg(feature = "with_rayon")]
81 {
82 use rayon::prelude::*;
83
84 mesh.positions.par_iter_mut().for_each(|p| {
85 *p = world_from_grid_f((*p).into()).into();
87 });
88 }
89
90 #[cfg(not(feature = "with_rayon"))]
91 {
92 mesh.positions.iter_mut().for_each(|p| {
93 *p = world_from_grid_f((*p).into()).into();
95 });
96 }
97}
98
99pub fn gather_colors_in_place(
100 mesh: &mut TriangleMesh,
101 color_world: impl Fn(Vec3) -> Vec3 + Send + Sync,
102) {
103 #[cfg(feature = "with_rayon")]
104 {
105 use rayon::prelude::*;
106
107 mesh.colors = mesh
108 .positions
109 .par_iter()
110 .map(|p| color_world(Vec3::new(p[0], p[1], p[2])).into())
111 .collect();
112 }
113
114 #[cfg(not(feature = "with_rayon"))]
115 {
116 mesh.colors = mesh
117 .positions
118 .iter()
119 .map(|p| color_world(Vec3::new(p[0], p[1], p[2])).into())
120 .collect();
121 }
122}
123
124pub fn mesh_from_sdf_func(
125 bb: &BoundingBox,
126 resolution: [usize; 3],
127 sd_world: impl Fn(Vec3) -> f32 + Send + Sync,
128 color_world: impl Fn(Vec3) -> Vec3 + Send + Sync,
129) -> Result<TriangleMesh, Error> {
130 use macaw::*;
131
132 let world_from_grid_scale = bb.size().x / (resolution[0] as f32 - 1.0);
133 let grid_from_world_scale = 1.0 / world_from_grid_scale;
134
135 let world_from_grid_f = |pos_in_grid: Vec3| bb.min + world_from_grid_scale * pos_in_grid;
136
137 let world_from_grid_i = |pos_in_grid: Index3| {
138 let pos_in_grid = Vec3::new(
139 pos_in_grid[0] as f32,
140 pos_in_grid[1] as f32,
141 pos_in_grid[2] as f32,
142 );
143 world_from_grid_f(pos_in_grid)
144 };
145
146 let sd_in_grid = |pos_in_grid| {
147 let pos_in_world = world_from_grid_i(pos_in_grid);
148 grid_from_world_scale * sd_world(pos_in_world)
149 };
150
151 let mut grid = Grid3::<f32>::new(resolution);
152 grid.set_truncated(sd_in_grid, 2.0);
153
154 if !grid.data()[grid.data().len() / 2].is_finite() {
156 return Err(Error::EvaluatedToNaN);
157 }
158
159 let mut mesh = grid.marching_cubes();
160
161 transform_positions_in_place(&mut mesh, world_from_grid_f);
162 gather_colors_in_place(&mut mesh, color_world);
163
164 Ok(mesh)
165}
166
167pub fn mesh_from_sdf_program(
168 program: &Program,
169 bb: &BoundingBox,
170 resolution: [usize; 3],
171) -> Result<TriangleMesh, Error> {
172 let color_func = |pos_in_world| {
173 let mut rgbd_context = Interpreter::new_context(&program.opcodes, &program.constants);
174 Interpreter::<RgbWithDistance>::interpret(&mut rgbd_context, pos_in_world)
175 .unwrap()
176 .material()
177 .rgb()
178 };
179
180 let d_func = |pos_in_world| {
181 let mut d_context = Interpreter::new_context(&program.opcodes, &program.constants);
182 Interpreter::<f32>::interpret(&mut d_context, pos_in_world)
183 .unwrap()
184 .distance()
185 };
186
187 mesh_from_sdf_func(bb, resolution, d_func, color_func)
188}
189
190pub fn mesh_from_sdf(
191 graph: &Graph,
192 node: NodeId,
193 options: MeshOptions,
194) -> Result<TriangleMesh, Error> {
195 let (bb, resolution) = sdf_bb_and_resolution(graph.bounding_box(node), options);
196 let program = compile(graph, node);
197
198 mesh_from_sdf_program(&program, &bb, resolution)
199}
200
201pub fn sdf_bb_and_resolution(bb: BoundingBox, options: MeshOptions) -> (BoundingBox, [usize; 3]) {
203 assert!(bb.is_finite(), "Bad saft bounding box: {:?}", bb);
204 assert!(bb.volume() > 0.0, "Bad saft bounding box: {:?}", bb);
205
206 let grid_padding = 1.0;
208
209 let grid_from_world_scale = options.mean_resolution / bb.volume().cbrt();
211 let padding = grid_padding / grid_from_world_scale;
212 let bb = bb.expanded(Vec3::splat(padding));
213
214 let grid_from_world_scale = options.mean_resolution / bb.volume().cbrt();
216
217 let resolution = [
218 grid_from_world_scale * bb.size().x,
219 grid_from_world_scale * bb.size().y,
220 grid_from_world_scale * bb.size().z,
221 ];
222
223 let max_side = resolution[0].max(resolution[1]).max(resolution[2]);
224 let max_factor = if max_side > options.max_resolution {
225 options.max_resolution / max_side
226 } else {
227 1.0
228 };
229 let min_side = resolution[0].min(resolution[1]).min(resolution[2]);
230 let min_factor = if min_side < options.min_resolution {
231 options.min_resolution / min_side
232 } else {
233 1.0
234 };
235
236 let factor = min_factor.max(max_factor);
238
239 let grid_resolution = [
240 (factor * resolution[0]).ceil() as usize,
241 (factor * resolution[1]).ceil() as usize,
242 (factor * resolution[2]).ceil() as usize,
243 ];
244
245 (bb, grid_resolution)
254}
255
256pub fn surface_distance_to(graph: &Graph, node: NodeId, pos: Vec3) -> f32 {
257 let program = compile(graph, node);
258 let mut d_context = Interpreter::new_context(&program.opcodes, &program.constants);
259 Interpreter::<f32>::interpret(&mut d_context, pos).unwrap()
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn it_works() {
268 let mut graph = Graph::default();
269 let node = graph.sphere(Vec3::default(), 1.0);
270
271 let node2 = graph.sphere(Vec3::default(), 0.5);
272 let node3 = graph.op_translate(node2, -Vec3::X);
273 let node4 = graph.op_translate(node2, Vec3::X);
274 let node5 = graph.op_translate(node2, Vec3::Y);
275
276 let union_node = graph.op_union_multi(vec![node, node3, node4, node5]);
277
278 let program = compile(&graph, union_node);
279 let mut grid = Grid3::<f32>::new([64, 64, 64]);
280 grid.set_truncated_sync(
281 |pos| {
282 let mut context = Interpreter::new_context(&program.opcodes, &program.constants);
283
284 let pos = Vec3::new(pos[0] as f32, pos[1] as f32, pos[2] as f32);
285 Interpreter::<f32>::interpret(&mut context, pos).unwrap()
286 },
287 2.0,
288 );
289
290 let mut grid2 = Grid3::new([64, 64, 64]);
291 grid2.set_truncated(
292 |pos| {
293 let pos = Vec3::new(pos[0] as f32, pos[1] as f32, pos[2] as f32);
294 sd_op_union(
295 sd_sphere(pos, Vec3::default(), 1.0),
296 sd_op_union(
297 sd_sphere(pos + Vec3::X, Vec3::default(), 0.5),
298 sd_op_union(
299 sd_sphere(pos - Vec3::X, Vec3::default(), 0.5),
300 sd_sphere(pos - Vec3::Y, Vec3::default(), 0.5),
301 ),
302 ),
303 )
304 },
305 2.0,
306 );
307
308 assert!(grid == grid2);
310 }
311}