#![warn(missing_docs)]
pub mod animation;
pub mod cache;
pub mod collision;
pub mod compiled;
mod crispy;
pub mod eval;
pub mod fidelity;
pub mod interval;
pub mod io;
pub mod llm_schema;
pub mod material;
pub mod mesh;
pub mod modifiers;
pub mod morphology;
pub mod neural;
pub mod npr;
pub mod operations;
pub mod optimize;
pub mod primitives;
mod raw_minmax;
pub mod raycast;
pub mod soa;
pub mod tight_aabb;
pub mod transforms;
pub mod types;
pub mod autodiff;
pub mod constraint;
pub mod diff;
pub mod heatmap;
pub mod incremental;
pub mod measure;
pub mod sdf2d;
pub mod shell;
pub mod validity;
#[cfg(all(feature = "font", alice_font_bridge))]
pub mod font_bridge;
#[cfg(feature = "python")]
pub mod python;
#[cfg(feature = "godot")]
pub mod godot;
#[cfg(feature = "ffi")]
pub mod ffi;
#[cfg(feature = "codec")]
pub mod codec_bridge;
#[cfg(feature = "physics")]
pub mod physics_bridge;
#[cfg(feature = "physics")]
pub mod sim_bridge;
#[cfg(feature = "physics")]
pub mod live_sdf;
#[cfg(feature = "asp")]
pub mod asp_bridge;
#[cfg(feature = "sdf-cache")]
pub mod cache_bridge;
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
pub mod wasm;
#[cfg(feature = "texture-fit")]
pub mod texture;
#[cfg(feature = "volume")]
pub mod volume;
#[cfg(feature = "svo")]
pub mod svo;
#[cfg(feature = "destruction")]
pub mod destruction;
#[cfg(feature = "terrain")]
pub mod terrain;
#[cfg(feature = "gi")]
pub mod gi;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub use alice_det_math::SEMANTICS_ID;
pub mod prelude {
pub use crate::animation::{
morph, AnimatedSdf, Interpolation, Keyframe, LoopMode, Timeline, Track,
};
pub use crate::autodiff::{
eval_hessian, eval_with_gradient, gaussian_curvature, mean_curvature, principal_curvatures,
Dual, Dual3,
};
#[cfg(feature = "codec")]
pub use crate::codec_bridge::{
compress_sdf, compression_ratio, decode_sdf_volume, decompress_sdf, encode_sdf_volume,
volume_stats, voxelize_sdf, voxelize_sdf_uniform, CompressResult, EncodeConfig, SdfVolume,
VolumeStats,
};
pub use crate::collision::{
compute_manifold, sdf_ccd, sdf_closest_point, sdf_collide, sdf_distance, sdf_overlap,
ContactManifold, SdfContact,
};
pub use crate::compiled::{
eval_compiled, eval_compiled_batch_simd, eval_compiled_batch_simd_parallel,
eval_compiled_batch_soa, eval_compiled_batch_soa_parallel, eval_compiled_bvh,
eval_compiled_distance_and_normal, eval_compiled_normal, eval_compiled_simd,
get_scene_aabb, AabbPacked, CompileError, CompiledSdf, CompiledSdfBvh, RefitError, Vec3x8,
};
pub use crate::constraint::{
Constraint, ConstraintKind, ConstraintSolver, ParamId, SolveResult,
};
#[cfg(feature = "destruction")]
pub use crate::destruction::{
carve, carve_batch, generate_debris, voronoi_fracture, CarveShape, ChunkMesh, DebrisConfig,
DebrisPiece, DestructionResult, FractureConfig, FracturePiece, MutableVoxelGrid,
};
pub use crate::diff::{
apply_patch, invert_patch, merge_patches, tree_diff, tree_hash, DiffError, DiffOp,
TreePatch,
};
pub use crate::eval::{
eval, eval_batch, eval_batch_parallel, eval_gradient, eval_grid, eval_material,
eval_normal, gradient, normal,
};
#[cfg(feature = "gi")]
pub use crate::gi::{
bake_irradiance_grid, cone_trace, direct_lighting, sky_color, trace_hemisphere,
BakeGiConfig, ConeTraceConfig, ConeTraceResult, DirectionalLight, IrradianceGrid,
IrradianceProbe, PointLight,
};
pub use crate::heatmap::{
generate_heatmap, heatmap_to_rgba, ColorMap, Heatmap, HeatmapConfig, SlicePlane,
};
pub use crate::incremental::{IncrementalError, InstructionSlot, ParamDependencyIndex};
pub use crate::io::{
export_alembic, export_fbx, export_glb, export_glb_bytes, export_gltf_json, export_nanite,
export_nanite_json, export_nanite_with_config, export_obj, export_usda, get_info,
import_fbx, import_glb, import_glb_bytes, import_obj, load, load_asdf, load_asdf_json,
save, save_asdf, save_asdf_json, AlembicConfig, FbxConfig, FbxFormat, FbxUpAxis,
GltfConfig, NaniteExportConfig, ObjConfig, UsdConfig, UsdUpAxis,
};
#[cfg(feature = "physics")]
pub use crate::live_sdf::{LiveMesh, LiveMeshConfig, LiveSdf};
pub use crate::material::{
material_lerp, Material, MaterialLibrary, StandardMaterials, TextureSlot,
};
pub use crate::measure::{
estimate_center_of_mass, estimate_surface_area, estimate_volume, AreaEstimate,
CenterOfMass, VolumeEstimate,
};
#[allow(deprecated)] pub use crate::mesh::point_cloud_to_sdf;
pub use crate::mesh::{
adaptive_marching_cubes,
apply_uvs,
compute_aabb,
compute_acmr,
compute_bounding_sphere,
compute_convex_hull,
compute_quality,
convex_decomposition,
decimate,
deduplicate_vertices,
detect_primitive,
dual_contouring,
dual_contouring_compiled,
extract_hermite,
generate_lightmap_uvs,
generate_lightmap_uvs_fast,
generate_lod_chain,
generate_lod_chain_decimated,
generate_nanite_mesh,
marching_cubes,
mesh_to_sdf,
mesh_to_sdf_exact,
optimize_vertex_cache,
primitives_to_csg,
sdf_to_mesh,
simplify_collision,
uv_unwrap,
validate_mesh,
AdaptiveConfig,
BoundingSphere,
CollisionAabb,
CollisionMesh,
ConvexDecomposition,
ConvexHull,
DecimateConfig,
DecimationLodConfig,
DualContouringConfig,
FittedPrimitive,
FittingConfig,
HermiteConfig,
HermitePoint,
LodChain,
LodConfig,
LodSelector,
MarchingCubesConfig,
Mesh,
MeshBvh,
MeshQuality,
MeshRepair,
MeshSdf,
MeshSignMode,
MeshToSdfConfig,
MeshToSdfStrategy,
MeshValidation,
NaniteCluster,
NaniteConfig,
NaniteMesh,
PointCloudSdf,
PointCloudSdfConfig,
Triangle,
UvChart,
UvUnwrapConfig,
UvUnwrapResult,
Vertex,
VhacdConfig,
};
#[cfg(feature = "gpu")]
pub use crate::mesh::{
gpu_marching_cubes, gpu_marching_cubes_from_shader, GpuMarchingCubesConfig,
};
pub use crate::modifiers::*;
pub use crate::npr::compiled_color::{
ColorOp, CompiledColorPipeline, NprBatchContext8, NprColorBatch8,
};
pub use crate::npr::composition::{bloom_toon, chromatic_offsets, vignette};
pub use crate::npr::distortion::{hand_drawn_jitter, line_boil, sketch_wobble};
pub use crate::npr::dsl::{NprColorContext, NprColorNode, PaletteSource};
pub use crate::npr::dsl_shader::{
transpile_npr_color_node, NprShaderContext, NprShaderSnippet,
};
pub use crate::npr::hatch::{cross_hatch, hatch_lines, paper_grain, pencil_shade};
pub use crate::npr::motion::{impact_flash, speed_line};
pub use crate::npr::noise::{
fbm, HashNoise, NoiseField, PerlinNoise, SimplexNoise, WorleyNoise,
};
pub use crate::npr::outline::{
composite_outline, curvature_outline, depth_step_outline, distance_field_outline,
distance_field_outline_soft,
};
pub use crate::npr::palette::{palette_gradient, season_palette, time_of_day};
pub use crate::npr::rim::{fresnel_rim, procedural_matcap, stylized_specular};
#[cfg(any(feature = "glsl", feature = "hlsl", feature = "gpu"))]
pub use crate::npr::scene_composer::SceneShaderBuilder;
pub use crate::npr::sdf_integration::{
curvature_outline_from_node, distance_outline_from_node, soft_toon_shade_from_node,
toon_shade_from_node,
};
pub use crate::npr::shader_glue::{
full_helpers_for, helpers_for, palette_helpers_for, ShaderLanguage, NPR_GLSL_HELPERS,
NPR_GLSL_PALETTE_HELPERS, NPR_HLSL_HELPERS, NPR_HLSL_PALETTE_HELPERS, NPR_WGSL_HELPERS,
NPR_WGSL_PALETTE_HELPERS,
};
pub use crate::npr::sky::{
distance_color_quantize, light_shaft_beam, puffy_cloud_layer, sky_gradient_bands, sun_disc,
};
pub use crate::npr::toon::{posterize_color, soft_toon_ramp, toon_ramp, two_tone};
pub use crate::npr::{NprColor, NprInput};
pub use crate::operations::*;
pub use crate::primitives::*;
pub use crate::raycast::{
ambient_occlusion, raycast, raycast_batch, raymarch, raymarch_with_config, soft_shadow,
RaymarchConfig, RaymarchResult,
};
pub use crate::sdf2d::{eval_2d, eval_2d_batch, eval_2d_normal, Sdf2dNode};
pub use crate::shell::{
eval_shell, eval_shell_batch, eval_shell_batch_parallel, eval_shell_compiled,
eval_shell_compiled_batch_parallel, eval_shell_gradient, shell_node, ShellConfig,
};
#[cfg(feature = "physics")]
pub use crate::sim_bridge::{attach_physics, simulate_sdf, SimulatedSdf};
#[cfg(all(feature = "physics", feature = "gpu"))]
pub use crate::sim_bridge::{gpu_mesh_with_physics, GpuPhysicsBundle};
pub use crate::soa::{SoADistances, SoAPoints};
#[cfg(feature = "svo")]
pub use crate::svo::{
build_svo, build_svo_compiled, linearize_svo, svo_nearest_surface, svo_query_point,
svo_ray_query, LinearizedSvo, SparseVoxelOctree, SvoBuildConfig, SvoChunk, SvoNode,
SvoRayHit, SvoStreamingCache,
};
#[cfg(all(feature = "terrain", feature = "image"))]
pub use crate::terrain::HeightmapImageConfig;
#[cfg(feature = "terrain")]
pub use crate::terrain::{
erode, generate_cave_sdf, terrain_sdf, CaveConfig, ClipmapLevel, ClipmapMesh,
ClipmapTerrain, ErosionConfig, Heightmap, SplatLayer, Splatmap, TerrainConfig,
};
pub use crate::transforms::*;
pub use crate::types::{Aabb, Hit, Ray, SdfCategory, SdfMetadata, SdfNode, SdfTree};
#[cfg(feature = "volume")]
pub use crate::volume::{
bake_volume, bake_volume_with_normals, export_dds_3d, export_raw, generate_mip_chain,
BakeChannels, BakeConfig, Volume3D, VoxelDistGrad,
};
pub use glam::{Quat, Vec3};
}
pub use cache::{
compute_cache_key, hash_sdf_node, CacheConfig, ChunkCoord, ChunkedCacheConfig,
ChunkedMeshCache, MeshCache, MeshCacheKey,
};
pub use eval::eval;
pub use io::{load, save};
pub use mesh::sdf_to_mesh;
pub use types::{SdfNode, SdfTree};
#[cfg(doctest)]
struct RemovedIn5;
#[cfg(test)]
mod tests {
use super::prelude::*;
#[test]
fn test_basic_workflow() {
let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(0.5, 0.5, 0.5));
let d = eval(&shape, Vec3::ZERO);
assert!(d > 0.0);
let d_surface = eval(&shape, Vec3::new(1.0, 0.0, 0.0));
assert!(d_surface.abs() < 0.01);
let tree = SdfTree::new(shape);
assert!(tree.node_count() > 0);
}
#[test]
fn test_complex_tree() {
let shape = SdfNode::sphere(1.0)
.smooth_union(SdfNode::cylinder(0.5, 2.0), 0.2)
.subtract(SdfNode::box3d(0.5, 0.5, 0.5).repeat_infinite(1.0, 1.0, 1.0))
.twist(0.5)
.scale(2.0);
assert!(shape.node_count() >= 6);
}
#[test]
fn test_mesh_generation() {
let sphere = SdfNode::sphere(1.0);
let config = MarchingCubesConfig {
resolution: 8,
iso_level: 0.0,
compute_normals: true,
..Default::default()
};
let mesh = sdf_to_mesh(&sphere, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
assert!(mesh.vertex_count() > 0);
assert!(mesh.triangle_count() > 0);
}
#[test]
fn test_raymarching() {
let sphere = SdfNode::sphere(1.0);
let ray = Ray::new(Vec3::new(-5.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0));
let hit = raycast(&sphere, ray, 10.0);
assert!(hit.is_some());
let hit = hit.unwrap();
assert!((hit.distance - 4.0).abs() < 0.1);
}
#[test]
fn test_production_stress_high_res() {
let shape = SdfNode::sphere(1.0).smooth_union(SdfNode::box3d(0.5, 0.5, 0.5), 0.2);
let config = MarchingCubesConfig {
resolution: 64,
iso_level: 0.0,
compute_normals: true,
..Default::default()
};
let mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
assert!(
mesh.vertex_count() > 1000,
"Expected >1000 vertices, got {}",
mesh.vertex_count()
);
assert!(
mesh.triangle_count() > 500,
"Expected >500 triangles, got {}",
mesh.triangle_count()
);
}
#[test]
fn test_production_multi_material_pipeline() {
let shape = SdfNode::sphere(1.0);
let config = MarchingCubesConfig {
resolution: 16,
iso_level: 0.0,
compute_normals: true,
..Default::default()
};
let mut mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
let half = mesh.vertices.len() / 2;
for v in mesh.vertices[half..].iter_mut() {
v.material_id = 1;
}
let acmr_before = compute_acmr(&mesh, 32);
optimize_vertex_cache(&mut mesh);
let acmr_after = compute_acmr(&mesh, 32);
assert!(acmr_after <= acmr_before + 0.01);
let mut mat_lib = MaterialLibrary::new();
mat_lib.add(
Material::metal("Chrome", 0.9, 0.9, 0.9, 0.2)
.with_albedo_map("textures/chrome_albedo.png")
.with_normal_map("textures/chrome_normal.png"),
);
let path = std::env::temp_dir().join("alice_stress_multi_mat.glb");
export_glb(&mesh, &path, &GltfConfig::aaa(), Some(&mat_lib)).unwrap();
let bytes = std::fs::read(&path).unwrap();
assert!(bytes.len() > 100);
assert_eq!(&bytes[0..4], &0x46546C67u32.to_le_bytes());
std::fs::remove_file(&path).ok();
}
#[test]
fn test_production_collision_pipeline() {
let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(0.5, 0.5, 0.5));
let config = MarchingCubesConfig {
resolution: 16,
iso_level: 0.0,
compute_normals: true,
..Default::default()
};
let mesh = sdf_to_mesh(&shape, Vec3::splat(-2.0), Vec3::splat(2.0), &config);
let aabb = compute_aabb(&mesh);
assert!(aabb.contains(Vec3::new(0.5, 0.0, 0.0)));
let bs = compute_bounding_sphere(&mesh);
assert!(bs.radius > 0.5);
let hull = compute_convex_hull(&mesh);
assert!(hull.vertices.len() >= 4);
assert!(hull.indices.len() >= 12);
let simplified = simplify_collision(&mesh, 8);
assert!(simplified.vertices.len() < mesh.vertices.len());
}
#[test]
fn test_compiled_vs_interpreted() {
let shape = SdfNode::sphere(1.0)
.smooth_union(
SdfNode::cylinder(0.3, 1.5).rotate_euler(1.57, 0.0, 0.0),
0.2,
)
.subtract(SdfNode::box3d(0.4, 0.4, 0.4))
.translate(0.5, 0.0, 0.0);
let compiled = CompiledSdf::compile(&shape);
let test_points = [
Vec3::ZERO,
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.5, 0.5, 0.5),
Vec3::new(-1.0, -1.0, -1.0),
Vec3::new(0.25, 0.25, 0.25),
Vec3::new(2.0, 0.0, 0.0),
];
for p in test_points {
let d_interp = eval(&shape, p);
let d_compiled = eval_compiled(&compiled, p);
assert!(
(d_interp - d_compiled).abs() < 0.001,
"Mismatch at {:?}: interp={}, compiled={}",
p,
d_interp,
d_compiled
);
}
}
}