alice-sdf 5.1.0

ALICE-SDF: 3D/Spatial Data Specialist — Store laws, not polygons. GLSL/WGSL/HLSL transpile, SVO, GPU marching cubes, texture-fit, procedural CSG. Optional bridges to alice-physics / alice-codec / libasp / alice-cache behind features.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
//! # ALICE-SDF
//!
//! Signed distance functions for Rust. A shape is a tree of primitives, CSG
//! operations, transforms and modifiers ([`SdfNode`]). The crate
//! evaluates the tree on the CPU (scalar, SIMD, BVH and JIT evaluators),
//! transpiles it to GLSL, WGSL, HLSL and Metal, and turns it into meshes and file
//! formats.
//!
//! The CPU evaluators return bit-identical distances for the same tree and point
//! on every platform CI tests: every transcendental goes through
//! [`alice-det-math`](https://crates.io/crates/alice-det-math) and `a * b + c` is
//! never fused. The GPU shaders agree with the CPU within a tolerance.
//!
//! ## Example
//!
//! ```rust
//! use alice_sdf::prelude::*;
//!
//! // A unit sphere minus a box (box3d takes full extents)
//! let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(1.0, 1.0, 1.0));
//!
//! // Signed distance: negative inside, zero on the surface, positive outside
//! let p = Vec3::new(0.9, 0.0, 0.0);
//! let d = eval(&shape, p);
//! assert!(d < 0.0);
//!
//! // Compiled to bytecode for repeated evaluation, with the same bits
//! let compiled = CompiledSdf::compile(&shape);
//! assert_eq!(eval_compiled(&compiled, p).to_bits(), d.to_bits());
//!
//! // A triangle mesh by marching cubes
//! let mesh = sdf_to_mesh(
//!     &shape,
//!     Vec3::splat(-1.5),
//!     Vec3::splat(1.5),
//!     &MarchingCubesConfig::default(),
//! );
//! assert!(!mesh.indices.is_empty());
//! ```
//!
//! ## Cargo features
//!
//! The `physics`, `codec` and `sdf-cache` features link crates licensed
//! `AGPL-3.0-or-later`; the default features and all others do not.
//!
//! <!-- readme-sync: features -->
//! | Feature | Default | Description |
//! |---------|:-------:|-------------|
//! | `cli` | yes | The `alice-sdf` command-line tool (clap). |
//! | `image` | | PNG / JPEG decoding for heightmaps and texture fitting. |
//! | `texture-fit` | | Fit a bitmap texture with procedural noise formulas. Implies `image` and `cli`. |
//! | `jit` | | Native evaluation through Cranelift (scalar and 8-wide). |
//! | `gpu` | | GPU evaluation and WGSL output through wgpu. |
//! | `gpu-mesh` | | Marching cubes on the GPU. Implies `gpu`. |
//! | `volume` | | Bake a field into a 3D texture. Implies `gpu`. |
//! | `glsl` | | GLSL output (Unity, OpenGL, Vulkan, Shadertoy). |
//! | `hlsl` | | HLSL output (Unreal Engine, DirectX). |
//! | `msl` | | Metal Shading Language output, converted from WGSL with naga. Implies `gpu`. |
//! | `blinkscript` | | BlinkScript output for Nuke. Implies `hlsl`. |
//! | `rust` | | Dependency-free Rust source (`fn sdf`, `fn normal`) bit-identical to `eval_compiled`, for `build.rs`. |
//! | `all-shaders` | | `gpu`, `glsl`, `hlsl`, `msl` and `blinkscript`. |
//! | `svo` | | Sparse voxel octree. |
//! | `svo-gpu` | | Sparse voxel octree with GPU support. Implies `svo` and `gpu`. |
//! | `destruction` | | Voxel destruction. |
//! | `terrain` | | Heightmap terrain with erosion and caves. |
//! | `gi` | | Cone-traced global illumination. Implies `svo`. |
//! | `aaa` | | `volume`, `gpu-mesh`, `svo-gpu`, `destruction`, `terrain` and `gi`. |
//! | `ffi` | | C ABI for C, C++, C#, Unity and Unreal Engine. |
//! | `unity` | | `ffi` and `glsl`. |
//! | `unreal` | | `ffi`, `hlsl`, `glsl` and `gpu`: what the Unreal Engine plugin calls. |
//! | `python` | | Python bindings (PyO3 + NumPy). |
//! | `godot` | | Godot 4 GDExtension. |
//! | `wasm` | | WebAssembly bindings through `wasm-bindgen` (built for `wasm32` only). |
//! | `openvdb` | | OpenVDB float grid input and output. |
//! | `physics` | | **AGPL.** SDF colliders and simulation modifiers for [`alice-physics`](https://crates.io/crates/alice-physics). |
//! | `codec` | | **AGPL.** Volume compression with [`alice-codec`](https://crates.io/crates/alice-codec). |
//! | `sdf-cache` | | **AGPL.** Evaluation cache with [`alice-cache`](https://crates.io/crates/alice-cache). |
//! | `asp` | | ALICE Streaming Protocol packets with [`libasp`](https://crates.io/crates/libasp) (its default features only). |
//! | `font` | | Glyph outlines from `alice-font`. Inert on crates.io: also needs `--cfg alice_font_bridge` and a local `alice-font`. |
//!
//! Module overview: <https://github.com/ext-sakamoro/ALICE-SDF/blob/main/docs/MODULES.md>

#![warn(missing_docs)]
// The clippy policy (pedantic + nursery with documented exceptions) lives in
// Cargo.toml [lints.clippy] so tests, benches and examples share it.

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;
pub mod raycast;
pub mod soa;
pub mod tight_aabb;
pub mod transforms;
pub mod types;

// New modules: analysis, 2D, and bridges
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;

// `font` alone is inert on crates.io (no `alice-font` dep declared);
// the bridge needs `--cfg alice_font_bridge` + a local path dep.
#[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;

/// Library version
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

/// The numeric semantics the CPU evaluators compute distances with
///
/// Re-exported from `alice-det-math`, which derives it from the bit pins of its
/// kernels: it changes exactly when one of them would return different bits for
/// the same input. A caller that stores or identifies distances evaluated by
/// this crate (a baked field, a recorded collision trace, a content hash of a
/// tree plus its samples) can mix it in, so a change of arithmetic shows up as
/// a different identifier instead of a silent mismatch. `tests/test_det_golden.rs`
/// pins its value, so moving to another `alice-det-math` is visible here too.
pub use alice_det_math::SEMANTICS_ID;

/// Prelude - commonly used types and functions
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)] // re-export kept until the next major
    pub use crate::mesh::point_cloud_to_sdf;
    pub use crate::mesh::{
        // Adaptive marching cubes
        adaptive_marching_cubes,
        apply_uvs,
        compute_aabb,
        compute_acmr,
        compute_bounding_sphere,
        compute_convex_hull,
        compute_quality,
        convex_decomposition,
        // Mesh decimation
        decimate,
        deduplicate_vertices,
        detect_primitive,
        // Dual Contouring
        dual_contouring,
        dual_contouring_compiled,
        extract_hermite,
        // Lightmap UV generation
        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,
        // Vertex cache optimization
        optimize_vertex_cache,
        primitives_to_csg,
        sdf_to_mesh,
        simplify_collision,
        // UV unwrapping (LSCM)
        uv_unwrap,
        validate_mesh,
        AdaptiveConfig,
        BoundingSphere,
        // Physics collision primitives
        CollisionAabb,
        CollisionMesh,
        ConvexDecomposition,
        ConvexHull,
        DecimateConfig,
        DecimationLodConfig,
        DualContouringConfig,
        // Primitive fitting
        FittedPrimitive,
        FittingConfig,
        HermiteConfig,
        // Hermite data extraction
        HermitePoint,
        // LOD generation
        LodChain,
        LodConfig,
        LodSelector,
        MarchingCubesConfig,
        Mesh,
        MeshBvh,
        MeshQuality,
        MeshRepair,
        MeshSdf,
        MeshSignMode,
        MeshToSdfConfig,
        MeshToSdfStrategy,
        // Manifold mesh validation & repair
        MeshValidation,
        // Nanite clusters
        NaniteCluster,
        NaniteConfig,
        NaniteMesh,
        // Point cloud SDF
        PointCloudSdf,
        PointCloudSdfConfig,
        Triangle,
        UvChart,
        UvUnwrapConfig,
        UvUnwrapResult,
        Vertex,
        // Convex decomposition (V-HACD)
        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};
}

// Re-exports for convenience
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};

/// Items removed or made crate-private in 5.0.0 stay out of the public API.
/// Each block below must fail to compile (`cargo test --doc` runs them):
///
/// ```compile_fail
/// use alice_sdf::crispy::fnv1a_hash;
/// ```
///
/// ```compile_fail
/// let _ = alice_sdf::compiled::Vec3x8::zero();
/// ```
///
/// ```compile_fail
/// let _ = alice_sdf::primitives::eval_primitive_unchecked;
/// ```
///
/// ```compile_fail
/// use alice_sdf::mesh::bvh::Triangle;
/// ```
///
/// ```compile_fail
/// use alice_sdf::mesh::primitive_fitting::PrimitiveType;
/// ```
///
/// ```compile_fail
/// use alice_sdf::primitives::PrimitiveType;
/// ```
///
/// ```compile_fail
/// let _ = alice_sdf::primitives::eval_primitive;
/// ```
///
/// The replacements compile, and `mesh::Aabb` is `types::Aabb`:
///
/// ```
/// use alice_sdf::mesh::{BvhTriangle, FittedPrimitiveKind};
/// let a: alice_sdf::types::Aabb = alice_sdf::mesh::Aabb::empty();
/// let _ = (a, FittedPrimitiveKind::Sphere, std::mem::size_of::<BvhTriangle>());
/// let _ = alice_sdf::compiled::Vec3x8::splat(glam::Vec3::ONE);
/// let _ = alice_sdf::primitives::sdf_sphere;
/// ```
#[cfg(doctest)]
struct RemovedIn5;

#[cfg(test)]
mod tests {
    use super::prelude::*;

    #[test]
    fn test_basic_workflow() {
        // Create a shape: sphere with hole carved out
        let shape = SdfNode::sphere(1.0).subtract(SdfNode::box3d(0.5, 0.5, 0.5));

        // Evaluate at origin - box is inside sphere, so origin is carved out
        let d = eval(&shape, Vec3::ZERO);
        assert!(d > 0.0); // Outside (carved by box)

        // Evaluate at surface - should be near zero
        let d_surface = eval(&shape, Vec3::new(1.0, 0.0, 0.0));
        assert!(d_surface.abs() < 0.01);

        // Create tree
        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() {
        // High-resolution mesh generation (production scale)
        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);

        // Should produce a substantial mesh
        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() {
        // Full pipeline: SDF → mesh → optimize → glTF with multi-material
        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);

        // Assign materials to half the triangles
        let half = mesh.vertices.len() / 2;
        for v in mesh.vertices[half..].iter_mut() {
            v.material_id = 1;
        }

        // Optimize vertex cache
        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);

        // Create materials
        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"),
        );

        // Export glTF with multi-material
        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);
        // Verify it's valid GLB
        assert_eq!(&bytes[0..4], &0x46546C67u32.to_le_bytes());

        std::fs::remove_file(&path).ok();
    }

    #[test]
    fn test_production_collision_pipeline() {
        // Full pipeline: SDF → mesh → collision primitives
        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);

        // AABB
        let aabb = compute_aabb(&mesh);
        assert!(aabb.contains(Vec3::new(0.5, 0.0, 0.0)));

        // Bounding sphere
        let bs = compute_bounding_sphere(&mesh);
        assert!(bs.radius > 0.5);

        // Convex hull
        let hull = compute_convex_hull(&mesh);
        assert!(hull.vertices.len() >= 4);
        assert!(hull.indices.len() >= 12);

        // Simplified collision
        let simplified = simplify_collision(&mesh, 8);
        assert!(simplified.vertices.len() < mesh.vertices.len());
    }

    #[test]
    fn test_compiled_vs_interpreted() {
        // Complex shape to test
        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);

        // Test at many points
        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
            );
        }
    }
}