1#![warn(missing_docs)]
84pub mod animation;
88pub mod cache;
89pub mod collision;
90pub mod compiled;
91mod crispy;
92pub mod eval;
93pub mod fidelity;
94pub mod interval;
95pub mod io;
96pub mod llm_schema;
97pub mod material;
98pub mod mesh;
99pub mod modifiers;
100pub mod morphology;
101pub mod neural;
102pub mod npr;
103pub mod operations;
104pub mod optimize;
105pub mod primitives;
106mod raw_minmax;
107pub mod raycast;
108pub mod soa;
109pub mod tight_aabb;
110pub mod transforms;
111pub mod types;
112
113pub mod autodiff;
115pub mod constraint;
116pub mod diff;
117pub mod heatmap;
118pub mod incremental;
119pub mod measure;
120pub mod sdf2d;
121pub mod shell;
122pub mod validity;
123
124#[cfg(all(feature = "font", alice_font_bridge))]
127pub mod font_bridge;
128
129#[cfg(feature = "python")]
130pub mod python;
131
132#[cfg(feature = "godot")]
133pub mod godot;
134
135#[cfg(feature = "ffi")]
136pub mod ffi;
137
138#[cfg(feature = "codec")]
139pub mod codec_bridge;
140
141#[cfg(feature = "physics")]
142pub mod physics_bridge;
143
144#[cfg(feature = "physics")]
145pub mod sim_bridge;
146
147#[cfg(feature = "physics")]
148pub mod live_sdf;
149
150#[cfg(feature = "asp")]
151pub mod asp_bridge;
152
153#[cfg(feature = "sdf-cache")]
154pub mod cache_bridge;
155
156#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
157pub mod wasm;
158
159#[cfg(feature = "texture-fit")]
160pub mod texture;
161
162#[cfg(feature = "volume")]
163pub mod volume;
164
165#[cfg(feature = "svo")]
166pub mod svo;
167
168#[cfg(feature = "destruction")]
169pub mod destruction;
170
171#[cfg(feature = "terrain")]
172pub mod terrain;
173
174#[cfg(feature = "gi")]
175pub mod gi;
176
177pub const VERSION: &str = env!("CARGO_PKG_VERSION");
179
180pub use alice_det_math::SEMANTICS_ID;
190
191pub mod prelude {
193 pub use crate::animation::{
194 morph, AnimatedSdf, Interpolation, Keyframe, LoopMode, Timeline, Track,
195 };
196 pub use crate::autodiff::{
197 eval_hessian, eval_with_gradient, gaussian_curvature, mean_curvature, principal_curvatures,
198 Dual, Dual3,
199 };
200 #[cfg(feature = "codec")]
201 pub use crate::codec_bridge::{
202 compress_sdf, compression_ratio, decode_sdf_volume, decompress_sdf, encode_sdf_volume,
203 volume_stats, voxelize_sdf, voxelize_sdf_uniform, CompressResult, EncodeConfig, SdfVolume,
204 VolumeStats,
205 };
206 pub use crate::collision::{
207 compute_manifold, sdf_ccd, sdf_closest_point, sdf_collide, sdf_distance, sdf_overlap,
208 ContactManifold, SdfContact,
209 };
210 pub use crate::compiled::{
211 eval_compiled, eval_compiled_batch_simd, eval_compiled_batch_simd_parallel,
212 eval_compiled_batch_soa, eval_compiled_batch_soa_parallel, eval_compiled_bvh,
213 eval_compiled_distance_and_normal, eval_compiled_normal, eval_compiled_simd,
214 get_scene_aabb, AabbPacked, CompileError, CompiledSdf, CompiledSdfBvh, RefitError, Vec3x8,
215 };
216 pub use crate::constraint::{
217 Constraint, ConstraintKind, ConstraintSolver, ParamId, SolveResult,
218 };
219 #[cfg(feature = "destruction")]
220 pub use crate::destruction::{
221 carve, carve_batch, generate_debris, voronoi_fracture, CarveShape, ChunkMesh, DebrisConfig,
222 DebrisPiece, DestructionResult, FractureConfig, FracturePiece, MutableVoxelGrid,
223 };
224 pub use crate::diff::{
225 apply_patch, invert_patch, merge_patches, tree_diff, tree_hash, DiffError, DiffOp,
226 TreePatch,
227 };
228 pub use crate::eval::{
229 eval, eval_batch, eval_batch_parallel, eval_gradient, eval_grid, eval_material,
230 eval_normal, gradient, normal,
231 };
232 #[cfg(feature = "gi")]
233 pub use crate::gi::{
234 bake_irradiance_grid, cone_trace, direct_lighting, sky_color, trace_hemisphere,
235 BakeGiConfig, ConeTraceConfig, ConeTraceResult, DirectionalLight, IrradianceGrid,
236 IrradianceProbe, PointLight,
237 };
238 pub use crate::heatmap::{
239 generate_heatmap, heatmap_to_rgba, ColorMap, Heatmap, HeatmapConfig, SlicePlane,
240 };
241 pub use crate::incremental::{IncrementalError, InstructionSlot, ParamDependencyIndex};
242 pub use crate::io::{
243 export_alembic, export_fbx, export_glb, export_glb_bytes, export_gltf_json, export_nanite,
244 export_nanite_json, export_nanite_with_config, export_obj, export_usda, get_info,
245 import_fbx, import_glb, import_glb_bytes, import_obj, load, load_asdf, load_asdf_json,
246 save, save_asdf, save_asdf_json, AlembicConfig, FbxConfig, FbxFormat, FbxUpAxis,
247 GltfConfig, NaniteExportConfig, ObjConfig, UsdConfig, UsdUpAxis,
248 };
249 #[cfg(feature = "physics")]
250 pub use crate::live_sdf::{LiveMesh, LiveMeshConfig, LiveSdf};
251 pub use crate::material::{
252 material_lerp, Material, MaterialLibrary, StandardMaterials, TextureSlot,
253 };
254 pub use crate::measure::{
255 estimate_center_of_mass, estimate_surface_area, estimate_volume, AreaEstimate,
256 CenterOfMass, VolumeEstimate,
257 };
258 #[allow(deprecated)] pub use crate::mesh::point_cloud_to_sdf;
260 pub use crate::mesh::{
261 adaptive_marching_cubes,
263 apply_uvs,
264 compute_aabb,
265 compute_acmr,
266 compute_bounding_sphere,
267 compute_convex_hull,
268 compute_quality,
269 convex_decomposition,
270 decimate,
272 deduplicate_vertices,
273 detect_primitive,
274 dual_contouring,
276 dual_contouring_compiled,
277 extract_hermite,
278 generate_lightmap_uvs,
280 generate_lightmap_uvs_fast,
281 generate_lod_chain,
282 generate_lod_chain_decimated,
283 generate_nanite_mesh,
284 marching_cubes,
285 mesh_to_sdf,
286 mesh_to_sdf_exact,
287 optimize_vertex_cache,
289 primitives_to_csg,
290 sdf_to_mesh,
291 simplify_collision,
292 uv_unwrap,
294 validate_mesh,
295 AdaptiveConfig,
296 BoundingSphere,
297 CollisionAabb,
299 CollisionMesh,
300 ConvexDecomposition,
301 ConvexHull,
302 DecimateConfig,
303 DecimationLodConfig,
304 DualContouringConfig,
305 FittedPrimitive,
307 FittingConfig,
308 HermiteConfig,
309 HermitePoint,
311 LodChain,
313 LodConfig,
314 LodSelector,
315 MarchingCubesConfig,
316 Mesh,
317 MeshBvh,
318 MeshQuality,
319 MeshRepair,
320 MeshSdf,
321 MeshSignMode,
322 MeshToSdfConfig,
323 MeshToSdfStrategy,
324 MeshValidation,
326 NaniteCluster,
328 NaniteConfig,
329 NaniteMesh,
330 PointCloudSdf,
332 PointCloudSdfConfig,
333 Triangle,
334 UvChart,
335 UvUnwrapConfig,
336 UvUnwrapResult,
337 Vertex,
338 VhacdConfig,
340 };
341 #[cfg(feature = "gpu")]
342 pub use crate::mesh::{
343 gpu_marching_cubes, gpu_marching_cubes_from_shader, GpuMarchingCubesConfig,
344 };
345 pub use crate::modifiers::*;
346 pub use crate::npr::compiled_color::{
347 ColorOp, CompiledColorPipeline, NprBatchContext8, NprColorBatch8,
348 };
349 pub use crate::npr::composition::{bloom_toon, chromatic_offsets, vignette};
350 pub use crate::npr::distortion::{hand_drawn_jitter, line_boil, sketch_wobble};
351 pub use crate::npr::dsl::{NprColorContext, NprColorNode, PaletteSource};
352 pub use crate::npr::dsl_shader::{
353 transpile_npr_color_node, NprShaderContext, NprShaderSnippet,
354 };
355 pub use crate::npr::hatch::{cross_hatch, hatch_lines, paper_grain, pencil_shade};
356 pub use crate::npr::motion::{impact_flash, speed_line};
357 pub use crate::npr::noise::{
358 fbm, HashNoise, NoiseField, PerlinNoise, SimplexNoise, WorleyNoise,
359 };
360 pub use crate::npr::outline::{
361 composite_outline, curvature_outline, depth_step_outline, distance_field_outline,
362 distance_field_outline_soft,
363 };
364 pub use crate::npr::palette::{palette_gradient, season_palette, time_of_day};
365 pub use crate::npr::rim::{fresnel_rim, procedural_matcap, stylized_specular};
366 #[cfg(any(feature = "glsl", feature = "hlsl", feature = "gpu"))]
367 pub use crate::npr::scene_composer::SceneShaderBuilder;
368 pub use crate::npr::sdf_integration::{
369 curvature_outline_from_node, distance_outline_from_node, soft_toon_shade_from_node,
370 toon_shade_from_node,
371 };
372 pub use crate::npr::shader_glue::{
373 full_helpers_for, helpers_for, palette_helpers_for, ShaderLanguage, NPR_GLSL_HELPERS,
374 NPR_GLSL_PALETTE_HELPERS, NPR_HLSL_HELPERS, NPR_HLSL_PALETTE_HELPERS, NPR_WGSL_HELPERS,
375 NPR_WGSL_PALETTE_HELPERS,
376 };
377 pub use crate::npr::sky::{
378 distance_color_quantize, light_shaft_beam, puffy_cloud_layer, sky_gradient_bands, sun_disc,
379 };
380 pub use crate::npr::toon::{posterize_color, soft_toon_ramp, toon_ramp, two_tone};
381 pub use crate::npr::{NprColor, NprInput};
382 pub use crate::operations::*;
383 pub use crate::primitives::*;
384 pub use crate::raycast::{
385 ambient_occlusion, raycast, raycast_batch, raymarch, raymarch_with_config, soft_shadow,
386 RaymarchConfig, RaymarchResult,
387 };
388 pub use crate::sdf2d::{eval_2d, eval_2d_batch, eval_2d_normal, Sdf2dNode};
389 pub use crate::shell::{
390 eval_shell, eval_shell_batch, eval_shell_batch_parallel, eval_shell_compiled,
391 eval_shell_compiled_batch_parallel, eval_shell_gradient, shell_node, ShellConfig,
392 };
393 #[cfg(feature = "physics")]
394 pub use crate::sim_bridge::{attach_physics, simulate_sdf, SimulatedSdf};
395 #[cfg(all(feature = "physics", feature = "gpu"))]
396 pub use crate::sim_bridge::{gpu_mesh_with_physics, GpuPhysicsBundle};
397 pub use crate::soa::{SoADistances, SoAPoints};
398 #[cfg(feature = "svo")]
399 pub use crate::svo::{
400 build_svo, build_svo_compiled, linearize_svo, svo_nearest_surface, svo_query_point,
401 svo_ray_query, LinearizedSvo, SparseVoxelOctree, SvoBuildConfig, SvoChunk, SvoNode,
402 SvoRayHit, SvoStreamingCache,
403 };
404 #[cfg(all(feature = "terrain", feature = "image"))]
405 pub use crate::terrain::HeightmapImageConfig;
406 #[cfg(feature = "terrain")]
407 pub use crate::terrain::{
408 erode, generate_cave_sdf, terrain_sdf, CaveConfig, ClipmapLevel, ClipmapMesh,
409 ClipmapTerrain, ErosionConfig, Heightmap, SplatLayer, Splatmap, TerrainConfig,
410 };
411 pub use crate::transforms::*;
412 pub use crate::types::{Aabb, Hit, Ray, SdfCategory, SdfMetadata, SdfNode, SdfTree};
413 #[cfg(feature = "volume")]
414 pub use crate::volume::{
415 bake_volume, bake_volume_with_normals, export_dds_3d, export_raw, generate_mip_chain,
416 BakeChannels, BakeConfig, Volume3D, VoxelDistGrad,
417 };
418 pub use glam::{Quat, Vec3};
419}
420
421pub use cache::{
423 compute_cache_key, hash_sdf_node, CacheConfig, ChunkCoord, ChunkedCacheConfig,
424 ChunkedMeshCache, MeshCache, MeshCacheKey,
425};
426pub use eval::eval;
427pub use io::{load, save};
428pub use mesh::sdf_to_mesh;
429pub use types::{SdfNode, SdfTree};
430
431#[cfg(doctest)]
472struct RemovedIn5;
473
474#[cfg(test)]
475mod tests {
476 use super::prelude::*;
477
478 #[test]
479 fn test_basic_workflow() {
480 let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(0.5, 0.5, 0.5));
482
483 let d = eval(&shape, Vec3::ZERO);
485 assert!(d > 0.0); let d_surface = eval(&shape, Vec3::new(1.0, 0.0, 0.0));
489 assert!(d_surface.abs() < 0.01);
490
491 let tree = SdfTree::new(shape);
493 assert!(tree.node_count() > 0);
494 }
495
496 #[test]
497 fn test_complex_tree() {
498 let shape = SdfNode::sphere(1.0)
499 .smooth_union(SdfNode::cylinder(0.5, 2.0), 0.2)
500 .subtract(SdfNode::box3d(0.5, 0.5, 0.5).repeat_infinite(1.0, 1.0, 1.0))
501 .twist(0.5)
502 .scale(2.0);
503
504 assert!(shape.node_count() >= 6);
505 }
506
507 #[test]
508 fn test_mesh_generation() {
509 let sphere = SdfNode::sphere(1.0);
510 let config = MarchingCubesConfig {
511 resolution: 8,
512 iso_level: 0.0,
513 compute_normals: true,
514 ..Default::default()
515 };
516
517 let mesh = sdf_to_mesh(&sphere, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
518 assert!(mesh.vertex_count() > 0);
519 assert!(mesh.triangle_count() > 0);
520 }
521
522 #[test]
523 fn test_raymarching() {
524 let sphere = SdfNode::sphere(1.0);
525 let ray = Ray::new(Vec3::new(-5.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0));
526
527 let hit = raycast(&sphere, ray, 10.0);
528 assert!(hit.is_some());
529
530 let hit = hit.unwrap();
531 assert!((hit.distance - 4.0).abs() < 0.1);
532 }
533
534 #[test]
535 fn test_production_stress_high_res() {
536 let shape = SdfNode::sphere(1.0).smooth_union(SdfNode::box3d(0.5, 0.5, 0.5), 0.2);
538 let config = MarchingCubesConfig {
539 resolution: 64,
540 iso_level: 0.0,
541 compute_normals: true,
542 ..Default::default()
543 };
544 let mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
545
546 assert!(
548 mesh.vertex_count() > 1000,
549 "Expected >1000 vertices, got {}",
550 mesh.vertex_count()
551 );
552 assert!(
553 mesh.triangle_count() > 500,
554 "Expected >500 triangles, got {}",
555 mesh.triangle_count()
556 );
557 }
558
559 #[test]
560 fn test_production_multi_material_pipeline() {
561 let shape = SdfNode::sphere(1.0);
563 let config = MarchingCubesConfig {
564 resolution: 16,
565 iso_level: 0.0,
566 compute_normals: true,
567 ..Default::default()
568 };
569 let mut mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
570
571 let half = mesh.vertices.len() / 2;
573 for v in mesh.vertices[half..].iter_mut() {
574 v.material_id = 1;
575 }
576
577 let acmr_before = compute_acmr(&mesh, 32);
579 optimize_vertex_cache(&mut mesh);
580 let acmr_after = compute_acmr(&mesh, 32);
581 assert!(acmr_after <= acmr_before + 0.01);
582
583 let mut mat_lib = MaterialLibrary::new();
585 mat_lib.add(
586 Material::metal("Chrome", 0.9, 0.9, 0.9, 0.2)
587 .with_albedo_map("textures/chrome_albedo.png")
588 .with_normal_map("textures/chrome_normal.png"),
589 );
590
591 let path = std::env::temp_dir().join("alice_stress_multi_mat.glb");
593 export_glb(&mesh, &path, &GltfConfig::aaa(), Some(&mat_lib)).unwrap();
594
595 let bytes = std::fs::read(&path).unwrap();
596 assert!(bytes.len() > 100);
597 assert_eq!(&bytes[0..4], &0x46546C67u32.to_le_bytes());
599
600 std::fs::remove_file(&path).ok();
601 }
602
603 #[test]
604 fn test_production_collision_pipeline() {
605 let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(0.5, 0.5, 0.5));
607 let config = MarchingCubesConfig {
608 resolution: 16,
609 iso_level: 0.0,
610 compute_normals: true,
611 ..Default::default()
612 };
613 let mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
614
615 let aabb = compute_aabb(&mesh);
617 assert!(aabb.contains(Vec3::new(0.5, 0.0, 0.0)));
618
619 let bs = compute_bounding_sphere(&mesh);
621 assert!(bs.radius > 0.5);
622
623 let hull = compute_convex_hull(&mesh);
625 assert!(hull.vertices.len() >= 4);
626 assert!(hull.indices.len() >= 12);
627
628 let simplified = simplify_collision(&mesh, 8);
630 assert!(simplified.vertices.len() < mesh.vertices.len());
631 }
632
633 #[test]
634 fn test_compiled_vs_interpreted() {
635 let shape = SdfNode::sphere(1.0)
637 .smooth_union(
638 SdfNode::cylinder(0.3, 1.5).rotate_euler(1.57, 0.0, 0.0),
639 0.2,
640 )
641 .subtract(SdfNode::box3d(0.4, 0.4, 0.4))
642 .translate(0.5, 0.0, 0.0);
643
644 let compiled = CompiledSdf::compile(&shape);
645
646 let test_points = [
648 Vec3::ZERO,
649 Vec3::new(1.0, 0.0, 0.0),
650 Vec3::new(0.5, 0.5, 0.5),
651 Vec3::new(-1.0, -1.0, -1.0),
652 Vec3::new(0.25, 0.25, 0.25),
653 Vec3::new(2.0, 0.0, 0.0),
654 ];
655
656 for p in test_points {
657 let d_interp = eval(&shape, p);
658 let d_compiled = eval_compiled(&compiled, p);
659 assert!(
660 (d_interp - d_compiled).abs() < 0.001,
661 "Mismatch at {:?}: interp={}, compiled={}",
662 p,
663 d_interp,
664 d_compiled
665 );
666 }
667 }
668}