@developerz.ai/ts23d 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/bug-hunter.md +21 -0
- package/.claude/agents/docs-author.md +13 -0
- package/.claude/agents/dx-auditor.md +14 -0
- package/.claude/agents/primitive-author.md +22 -0
- package/.claude/agents/recipe-author.md +36 -0
- package/.claude/agents/visual-reviewer.md +23 -0
- package/.claude/commands/feature.md +28 -0
- package/.claude/commands/new-recipe.md +19 -0
- package/.claude/commands/release.md +14 -0
- package/.claude/commands/verify.md +9 -0
- package/.claude/skills/author-recipe/SKILL.md +93 -0
- package/CHANGELOG.md +90 -0
- package/CLAUDE.md +85 -0
- package/LICENSE +21 -0
- package/README.md +385 -2
- package/dist/chunk-6b05mnmx.js +19 -0
- package/dist/chunk-6b05mnmx.js.map +11 -0
- package/dist/chunk-7rtfjh9f.js +45 -0
- package/dist/chunk-7rtfjh9f.js.map +10 -0
- package/dist/chunk-8j4c28jt.js +4085 -0
- package/dist/chunk-8j4c28jt.js.map +46 -0
- package/dist/chunk-d1fv6hrm.js +2532 -0
- package/dist/chunk-d1fv6hrm.js.map +43 -0
- package/dist/chunk-dbq4qf52.js +902 -0
- package/dist/chunk-dbq4qf52.js.map +25 -0
- package/dist/chunk-drna2rny.js +836 -0
- package/dist/chunk-drna2rny.js.map +17 -0
- package/dist/chunk-dxvypr7y.js +89 -0
- package/dist/chunk-dxvypr7y.js.map +11 -0
- package/dist/chunk-f6yme003.js +1589 -0
- package/dist/chunk-f6yme003.js.map +30 -0
- package/dist/chunk-hg7pf34j.js +465 -0
- package/dist/chunk-hg7pf34j.js.map +19 -0
- package/dist/chunk-jq4e17tj.js +1031 -0
- package/dist/chunk-jq4e17tj.js.map +21 -0
- package/dist/chunk-y63c6zcv.js +2668 -0
- package/dist/chunk-y63c6zcv.js.map +53 -0
- package/dist/cli/args.d.ts +30 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +348 -0
- package/dist/cli/bin.js.map +15 -0
- package/dist/cli/load.d.ts +2 -0
- package/dist/cli/manifest.d.ts +53 -0
- package/dist/cli/render-cmds.d.ts +12 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/texture-cache.d.ts +4 -0
- package/dist/core/asset.d.ts +10 -0
- package/dist/core/definition.d.ts +155 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/family.d.ts +23 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +69 -0
- package/dist/core/index.js.map +9 -0
- package/dist/core/mesh.d.ts +35 -0
- package/dist/core/part.d.ts +114 -0
- package/dist/core/png.d.ts +1 -0
- package/dist/core/rng.d.ts +12 -0
- package/dist/core/transform.d.ts +20 -0
- package/dist/core/vec.d.ts +11 -0
- package/dist/core/world.d.ts +16 -0
- package/dist/export/bin-writer.d.ts +16 -0
- package/dist/export/container.d.ts +3 -0
- package/dist/export/credits.d.ts +4 -0
- package/dist/export/glb-options.d.ts +48 -0
- package/dist/export/glb.d.ts +8 -0
- package/dist/export/hierarchy.d.ts +23 -0
- package/dist/export/index.d.ts +5 -0
- package/dist/export/index.js +40 -0
- package/dist/export/index.js.map +9 -0
- package/dist/export/lights.d.ts +8 -0
- package/dist/export/lod-coverage.d.ts +12 -0
- package/dist/export/materials.d.ts +23 -0
- package/dist/export/merge-pivots.d.ts +6 -0
- package/dist/export/metadata.d.ts +21 -0
- package/dist/export/motion.d.ts +9 -0
- package/dist/export/optimize-parts.d.ts +26 -0
- package/dist/export/primitive.d.ts +3 -0
- package/dist/export/tangents.d.ts +2 -0
- package/dist/export/textures.d.ts +22 -0
- package/dist/export/version.d.ts +2 -0
- package/dist/grammar/index.d.ts +2 -0
- package/dist/grammar/index.js +16 -0
- package/dist/grammar/index.js.map +9 -0
- package/dist/grammar/split.d.ts +21 -0
- package/dist/grammar/whyte.d.ts +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +9 -0
- package/dist/kinds/building.d.ts +44 -0
- package/dist/kinds/faces.d.ts +27 -0
- package/dist/kinds/head-armour.d.ts +5 -0
- package/dist/kinds/head-beard.d.ts +6 -0
- package/dist/kinds/head-bust.d.ts +5 -0
- package/dist/kinds/head-extras.d.ts +15 -0
- package/dist/kinds/head-face.d.ts +8 -0
- package/dist/kinds/head-garb.d.ts +4 -0
- package/dist/kinds/head-geom.d.ts +7 -0
- package/dist/kinds/head-hair.d.ts +4 -0
- package/dist/kinds/head-layout.d.ts +29 -0
- package/dist/kinds/head-mesh.d.ts +31 -0
- package/dist/kinds/head-mouth.d.ts +4 -0
- package/dist/kinds/head-params.d.ts +116 -0
- package/dist/kinds/head-presets.d.ts +19 -0
- package/dist/kinds/head-rim.d.ts +10 -0
- package/dist/kinds/head-tone.d.ts +10 -0
- package/dist/kinds/head-torso.d.ts +35 -0
- package/dist/kinds/head.d.ts +39 -0
- package/dist/kinds/hull-lines.d.ts +65 -0
- package/dist/kinds/hull-skin.d.ts +6 -0
- package/dist/kinds/hull.d.ts +14 -0
- package/dist/kinds/index.d.ts +16 -0
- package/dist/kinds/index.js +78 -0
- package/dist/kinds/index.js.map +9 -0
- package/dist/kinds/mass.d.ts +15 -0
- package/dist/kinds/openings.d.ts +30 -0
- package/dist/kinds/planned.d.ts +6 -0
- package/dist/kinds/roof-chimney.d.ts +16 -0
- package/dist/kinds/roof-shape.d.ts +64 -0
- package/dist/kinds/roof.d.ts +31 -0
- package/dist/kinds/vegetation-fronds.d.ts +15 -0
- package/dist/kinds/vegetation-impostor.d.ts +9 -0
- package/dist/kinds/vegetation-leaves.d.ts +15 -0
- package/dist/kinds/vegetation-mesh.d.ts +21 -0
- package/dist/kinds/vegetation-params.d.ts +90 -0
- package/dist/kinds/vegetation-presets.d.ts +20 -0
- package/dist/kinds/vegetation-skeleton.d.ts +20 -0
- package/dist/kinds/vegetation.d.ts +42 -0
- package/dist/kinds/vehicle.d.ts +42 -0
- package/dist/material/cc0.d.ts +4 -0
- package/dist/material/define.d.ts +34 -0
- package/dist/material/generator.d.ts +4 -0
- package/dist/material/index.d.ts +9 -0
- package/dist/material/index.js +70 -0
- package/dist/material/index.js.map +9 -0
- package/dist/material/library.d.ts +7 -0
- package/dist/material/material-def.d.ts +30 -0
- package/dist/material/paint/bond.d.ts +3 -0
- package/dist/material/paint/canvas.d.ts +39 -0
- package/dist/material/paint/cell.d.ts +4 -0
- package/dist/material/paint/cells-round.d.ts +5 -0
- package/dist/material/paint/cells.d.ts +7 -0
- package/dist/material/paint/compose.d.ts +5 -0
- package/dist/material/paint/concrete.d.ts +3 -0
- package/dist/material/paint/courses.d.ts +31 -0
- package/dist/material/paint/decode-png.d.ts +6 -0
- package/dist/material/paint/default-sheet.d.ts +4 -0
- package/dist/material/paint/fabric.d.ts +4 -0
- package/dist/material/paint/foliage-broad.d.ts +5 -0
- package/dist/material/paint/foliage-common.d.ts +35 -0
- package/dist/material/paint/foliage-compound.d.ts +4 -0
- package/dist/material/paint/foliage-conifer.d.ts +5 -0
- package/dist/material/paint/foliage-raster.d.ts +26 -0
- package/dist/material/paint/foliage.d.ts +3 -0
- package/dist/material/paint/image-strip.d.ts +4 -0
- package/dist/material/paint/kit.d.ts +5 -0
- package/dist/material/paint/masks.d.ts +3 -0
- package/dist/material/paint/metal.d.ts +3 -0
- package/dist/material/paint/noise.d.ts +9 -0
- package/dist/material/paint/normal.d.ts +8 -0
- package/dist/material/paint/orm.d.ts +8 -0
- package/dist/material/paint/planks.d.ts +3 -0
- package/dist/material/paint/plates.d.ts +3 -0
- package/dist/material/paint/shapes.d.ts +8 -0
- package/dist/material/paint/slate.d.ts +3 -0
- package/dist/material/paint/stamp.d.ts +3 -0
- package/dist/material/paint/strip.d.ts +3 -0
- package/dist/material/paint/surface.d.ts +5 -0
- package/dist/material/paint/types.d.ts +65 -0
- package/dist/material/paint/validate.d.ts +3 -0
- package/dist/material/palette.d.ts +14 -0
- package/dist/material/shader-godot.d.ts +2 -0
- package/dist/material/shader-three.d.ts +2 -0
- package/dist/material/shader.d.ts +46 -0
- package/dist/material/slots.d.ts +5 -0
- package/dist/material/texture-source.d.ts +34 -0
- package/dist/material/trim-sheet.d.ts +38 -0
- package/dist/neural/generator.d.ts +25 -0
- package/dist/neural/images.d.ts +17 -0
- package/dist/neural/index.d.ts +2 -0
- package/dist/neural/index.js +14 -0
- package/dist/neural/index.js.map +9 -0
- package/dist/passes/bake-ao.d.ts +17 -0
- package/dist/passes/grime.d.ts +15 -0
- package/dist/passes/index.d.ts +6 -0
- package/dist/passes/index.js +22 -0
- package/dist/passes/index.js.map +9 -0
- package/dist/passes/livery.d.ts +19 -0
- package/dist/passes/optimize-cache.d.ts +15 -0
- package/dist/passes/optimize-weld.d.ts +15 -0
- package/dist/passes/optimize.d.ts +28 -0
- package/dist/passes/ray-grid.d.ts +23 -0
- package/dist/passes/weighted-normals.d.ts +8 -0
- package/dist/primitives/beam.d.ts +17 -0
- package/dist/primitives/bevel-box.d.ts +13 -0
- package/dist/primitives/box.d.ts +16 -0
- package/dist/primitives/cell.d.ts +20 -0
- package/dist/primitives/check.d.ts +12 -0
- package/dist/primitives/closed.d.ts +11 -0
- package/dist/primitives/erosion.d.ts +22 -0
- package/dist/primitives/font-5x7.d.ts +3 -0
- package/dist/primitives/heightfield.d.ts +16 -0
- package/dist/primitives/index.d.ts +23 -0
- package/dist/primitives/index.js +81 -0
- package/dist/primitives/index.js.map +9 -0
- package/dist/primitives/lathe-facets.d.ts +8 -0
- package/dist/primitives/lathe.d.ts +26 -0
- package/dist/primitives/lattice.d.ts +30 -0
- package/dist/primitives/loft.d.ts +15 -0
- package/dist/primitives/mc-table.d.ts +2 -0
- package/dist/primitives/member.d.ts +19 -0
- package/dist/primitives/noise.d.ts +34 -0
- package/dist/primitives/planar.d.ts +10 -0
- package/dist/primitives/prism.d.ts +13 -0
- package/dist/primitives/sculpt-edit.d.ts +56 -0
- package/dist/primitives/sculpt-tree.d.ts +23 -0
- package/dist/primitives/sculpt.d.ts +27 -0
- package/dist/primitives/sdf-grid.d.ts +17 -0
- package/dist/primitives/sdf-ops.d.ts +37 -0
- package/dist/primitives/sdf.d.ts +21 -0
- package/dist/primitives/strip-uv.d.ts +11 -0
- package/dist/primitives/surface-nets.d.ts +7 -0
- package/dist/primitives/sweep.d.ts +19 -0
- package/dist/primitives/terrain-color.d.ts +30 -0
- package/dist/primitives/terrain-grid.d.ts +36 -0
- package/dist/primitives/text-layout.d.ts +22 -0
- package/dist/primitives/text.d.ts +27 -0
- package/dist/primitives/tiled.d.ts +22 -0
- package/dist/primitives/transport.d.ts +17 -0
- package/dist/primitives/triangulate.d.ts +2 -0
- package/dist/primitives/value-noise.d.ts +10 -0
- package/dist/qa/checks.d.ts +11 -0
- package/dist/qa/clash.d.ts +2 -0
- package/dist/qa/floating.d.ts +5 -0
- package/dist/qa/gates.d.ts +12 -0
- package/dist/qa/geometry.d.ts +21 -0
- package/dist/qa/index.d.ts +12 -0
- package/dist/qa/index.js +78 -0
- package/dist/qa/index.js.map +9 -0
- package/dist/qa/inside.d.ts +12 -0
- package/dist/qa/inspect.d.ts +18 -0
- package/dist/qa/lods.d.ts +12 -0
- package/dist/qa/measures.d.ts +4 -0
- package/dist/qa/occlusion.d.ts +3 -0
- package/dist/qa/sanity.d.ts +7 -0
- package/dist/qa/types.d.ts +12 -0
- package/dist/render/beauty.d.ts +40 -0
- package/dist/render/camera.d.ts +33 -0
- package/dist/render/depth-map.d.ts +38 -0
- package/dist/render/font.d.ts +1 -0
- package/dist/render/ground.d.ts +13 -0
- package/dist/render/index.d.ts +10 -0
- package/dist/render/index.js +47 -0
- package/dist/render/index.js.map +9 -0
- package/dist/render/lighting.d.ts +27 -0
- package/dist/render/lod-strip.d.ts +5 -0
- package/dist/render/metrics.d.ts +37 -0
- package/dist/render/options.d.ts +28 -0
- package/dist/render/photo-match.d.ts +19 -0
- package/dist/render/raster.d.ts +25 -0
- package/dist/render/resolve.d.ts +11 -0
- package/dist/render/review-prompt.d.ts +2 -0
- package/dist/render/review-warnings.d.ts +34 -0
- package/dist/render/review.d.ts +24 -0
- package/dist/render/scene.d.ts +18 -0
- package/dist/render/shade.d.ts +22 -0
- package/dist/render/sheet.d.ts +15 -0
- package/dist/render/texture.d.ts +15 -0
- package/dist/render/thumbnail.d.ts +8 -0
- package/dist/render/vis-buffer.d.ts +18 -0
- package/dist/testing/assert.d.ts +21 -0
- package/dist/testing/compiled.d.ts +15 -0
- package/dist/testing/hash.d.ts +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +134 -0
- package/dist/testing/index.js.map +13 -0
- package/dist/testing/snapshot.d.ts +13 -0
- package/docs/README.md +56 -0
- package/docs/architecture/README.md +68 -0
- package/docs/architecture/adding-a-kind.md +44 -0
- package/docs/architecture/adding-a-primitive.md +49 -0
- package/docs/architecture/errors.md +47 -0
- package/docs/architecture/glb-production.md +214 -0
- package/docs/architecture/gltf-capabilities.md +117 -0
- package/docs/architecture/materials.md +151 -0
- package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
- package/docs/decisions/002-typescript-not-json.md +22 -0
- package/docs/decisions/003-shared-trim-sheet.md +21 -0
- package/docs/decisions/004-z-up-metres.md +21 -0
- package/docs/decisions/005-zero-runtime-deps.md +21 -0
- package/docs/decisions/006-single-package-subpath-exports.md +20 -0
- package/docs/decisions/007-cpu-contact-sheets.md +21 -0
- package/docs/decisions/008-surface-materials.md +21 -0
- package/docs/decisions/009-gltf-front-axis.md +21 -0
- package/docs/decisions/README.md +15 -0
- package/docs/idea/01-vision.md +117 -0
- package/docs/idea/02-dsl.md +343 -0
- package/docs/idea/03-ir.md +97 -0
- package/docs/idea/04-materials.md +114 -0
- package/docs/idea/05-agent-loop.md +141 -0
- package/docs/idea/06-qa.md +101 -0
- package/docs/idea/07-what-fits.md +45 -0
- package/docs/idea/08-neural-adapters.md +78 -0
- package/docs/idea/09-kinds.md +105 -0
- package/docs/idea/10-sculpting.md +166 -0
- package/docs/idea/11-materials-authoring.md +132 -0
- package/docs/idea/12-blender-bridge.md +136 -0
- package/docs/idea/13-asset-pipeline-research.md +89 -0
- package/docs/landscape.md +49 -0
- package/docs/plans/roadmap.md +139 -0
- package/examples/build-with-bun.ts +41 -0
- package/llms.txt +47 -0
- package/package.json +100 -4
- package/recipes/README.md +38 -0
- package/recipes/asteroid/README.md +46 -0
- package/recipes/asteroid/asteroid.ts +336 -0
- package/recipes/boulders/README.md +28 -0
- package/recipes/boulders/boulders.ts +117 -0
- package/recipes/boxcar/README.md +53 -0
- package/recipes/boxcar/boxcar.ts +1000 -0
- package/recipes/cargo_container_scifi/README.md +52 -0
- package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
- package/recipes/dropship/README.md +84 -0
- package/recipes/dropship/dropship.ts +1144 -0
- package/recipes/eiffel_tower/README.md +110 -0
- package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
- package/recipes/goblin_head/README.md +75 -0
- package/recipes/goblin_head/goblin_head.ts +38 -0
- package/recipes/human_head/README.md +68 -0
- package/recipes/human_head/human_head.ts +38 -0
- package/recipes/loco_440/README.md +32 -0
- package/recipes/loco_440/loco_440.ts +1404 -0
- package/recipes/material_swatches/README.md +21 -0
- package/recipes/material_swatches/material_swatches.ts +189 -0
- package/recipes/mountain_range/README.md +45 -0
- package/recipes/mountain_range/mountain_range.ts +128 -0
- package/recipes/oak/README.md +30 -0
- package/recipes/oak/oak.ts +19 -0
- package/recipes/orc_head/README.md +78 -0
- package/recipes/orc_head/orc_head.ts +41 -0
- package/recipes/palm/README.md +30 -0
- package/recipes/palm/palm.ts +19 -0
- package/recipes/pine/README.md +29 -0
- package/recipes/pine/pine.ts +19 -0
- package/recipes/signal_gantry/README.md +36 -0
- package/recipes/signal_gantry/signal_gantry.ts +522 -0
- package/recipes/station_halt/README.md +39 -0
- package/recipes/station_halt/station_halt.ts +722 -0
- package/recipes/t34/README.md +44 -0
- package/recipes/t34/t34.ts +1068 -0
- package/recipes/tiger_1/README.md +75 -0
- package/recipes/tiger_1/tiger_1.ts +1370 -0
- package/recipes/water_tower/README.md +40 -0
- package/recipes/water_tower/water_tower.ts +542 -0
- package/src/cli/args.ts +125 -0
- package/src/cli/bin.ts +7 -0
- package/src/cli/load.ts +17 -0
- package/src/cli/manifest.ts +95 -0
- package/src/cli/render-cmds.ts +66 -0
- package/src/cli/run.ts +192 -0
- package/src/cli/texture-cache.ts +24 -0
- package/src/core/asset.ts +129 -0
- package/src/core/definition.ts +156 -0
- package/src/core/errors.ts +50 -0
- package/src/core/family.ts +50 -0
- package/src/core/index.ts +66 -0
- package/src/core/mesh.ts +114 -0
- package/src/core/part.ts +130 -0
- package/src/core/png.ts +49 -0
- package/src/core/rng.ts +49 -0
- package/src/core/transform.ts +90 -0
- package/src/core/vec.ts +26 -0
- package/src/core/world.ts +80 -0
- package/src/export/bin-writer.ts +79 -0
- package/src/export/container.ts +35 -0
- package/src/export/credits.ts +25 -0
- package/src/export/glb-options.ts +53 -0
- package/src/export/glb.ts +187 -0
- package/src/export/hierarchy.ts +122 -0
- package/src/export/index.ts +34 -0
- package/src/export/lights.ts +49 -0
- package/src/export/lod-coverage.ts +71 -0
- package/src/export/materials.ts +126 -0
- package/src/export/merge-pivots.ts +146 -0
- package/src/export/metadata.ts +62 -0
- package/src/export/motion.ts +70 -0
- package/src/export/optimize-parts.ts +80 -0
- package/src/export/primitive.ts +35 -0
- package/src/export/tangents.ts +75 -0
- package/src/export/textures.ts +88 -0
- package/src/export/version.ts +6 -0
- package/src/grammar/index.ts +4 -0
- package/src/grammar/split.ts +87 -0
- package/src/grammar/whyte.ts +20 -0
- package/src/index.ts +14 -0
- package/src/kinds/building.ts +153 -0
- package/src/kinds/faces.ts +138 -0
- package/src/kinds/head-armour.ts +181 -0
- package/src/kinds/head-beard.ts +133 -0
- package/src/kinds/head-bust.ts +184 -0
- package/src/kinds/head-extras.ts +150 -0
- package/src/kinds/head-face.ts +242 -0
- package/src/kinds/head-garb.ts +181 -0
- package/src/kinds/head-geom.ts +31 -0
- package/src/kinds/head-hair.ts +193 -0
- package/src/kinds/head-layout.ts +63 -0
- package/src/kinds/head-mesh.ts +195 -0
- package/src/kinds/head-mouth.ts +111 -0
- package/src/kinds/head-params.ts +162 -0
- package/src/kinds/head-presets.ts +155 -0
- package/src/kinds/head-rim.ts +128 -0
- package/src/kinds/head-tone.ts +52 -0
- package/src/kinds/head-torso.ts +230 -0
- package/src/kinds/head.ts +176 -0
- package/src/kinds/hull-lines.ts +160 -0
- package/src/kinds/hull-skin.ts +83 -0
- package/src/kinds/hull.ts +209 -0
- package/src/kinds/index.ts +63 -0
- package/src/kinds/mass.ts +110 -0
- package/src/kinds/openings.ts +141 -0
- package/src/kinds/planned.ts +12 -0
- package/src/kinds/roof-chimney.ts +100 -0
- package/src/kinds/roof-shape.ts +230 -0
- package/src/kinds/roof.ts +250 -0
- package/src/kinds/vegetation-fronds.ts +126 -0
- package/src/kinds/vegetation-impostor.ts +118 -0
- package/src/kinds/vegetation-leaves.ts +168 -0
- package/src/kinds/vegetation-mesh.ts +174 -0
- package/src/kinds/vegetation-params.ts +147 -0
- package/src/kinds/vegetation-presets.ts +184 -0
- package/src/kinds/vegetation-skeleton.ts +168 -0
- package/src/kinds/vegetation.ts +199 -0
- package/src/kinds/vehicle.ts +144 -0
- package/src/material/cc0.ts +41 -0
- package/src/material/define.ts +130 -0
- package/src/material/generator.ts +63 -0
- package/src/material/index.ts +60 -0
- package/src/material/library.ts +196 -0
- package/src/material/material-def.ts +84 -0
- package/src/material/paint/bond.ts +60 -0
- package/src/material/paint/canvas.ts +101 -0
- package/src/material/paint/cell.ts +39 -0
- package/src/material/paint/cells-round.ts +119 -0
- package/src/material/paint/cells.ts +111 -0
- package/src/material/paint/compose.ts +53 -0
- package/src/material/paint/concrete.ts +62 -0
- package/src/material/paint/courses.ts +85 -0
- package/src/material/paint/decode-png.ts +106 -0
- package/src/material/paint/default-sheet.ts +73 -0
- package/src/material/paint/fabric.ts +63 -0
- package/src/material/paint/foliage-broad.ts +126 -0
- package/src/material/paint/foliage-common.ts +93 -0
- package/src/material/paint/foliage-compound.ts +135 -0
- package/src/material/paint/foliage-conifer.ts +143 -0
- package/src/material/paint/foliage-raster.ts +196 -0
- package/src/material/paint/foliage.ts +23 -0
- package/src/material/paint/image-strip.ts +88 -0
- package/src/material/paint/kit.ts +37 -0
- package/src/material/paint/masks.ts +46 -0
- package/src/material/paint/metal.ts +109 -0
- package/src/material/paint/noise.ts +52 -0
- package/src/material/paint/normal.ts +52 -0
- package/src/material/paint/orm.ts +108 -0
- package/src/material/paint/planks.ts +101 -0
- package/src/material/paint/plates.ts +72 -0
- package/src/material/paint/shapes.ts +28 -0
- package/src/material/paint/slate.ts +52 -0
- package/src/material/paint/stamp.ts +40 -0
- package/src/material/paint/strip.ts +48 -0
- package/src/material/paint/surface.ts +60 -0
- package/src/material/paint/types.ts +94 -0
- package/src/material/paint/validate.ts +74 -0
- package/src/material/palette.ts +46 -0
- package/src/material/shader-godot.ts +77 -0
- package/src/material/shader-three.ts +103 -0
- package/src/material/shader.ts +100 -0
- package/src/material/slots.ts +18 -0
- package/src/material/texture-source.ts +86 -0
- package/src/material/trim-sheet.ts +166 -0
- package/src/neural/generator.ts +37 -0
- package/src/neural/images.ts +27 -0
- package/src/neural/index.ts +10 -0
- package/src/passes/bake-ao.ts +183 -0
- package/src/passes/grime.ts +37 -0
- package/src/passes/index.ts +8 -0
- package/src/passes/livery.ts +66 -0
- package/src/passes/optimize-cache.ts +175 -0
- package/src/passes/optimize-weld.ts +147 -0
- package/src/passes/optimize.ts +85 -0
- package/src/passes/ray-grid.ts +237 -0
- package/src/passes/weighted-normals.ts +125 -0
- package/src/primitives/beam.ts +60 -0
- package/src/primitives/bevel-box.ts +140 -0
- package/src/primitives/box.ts +90 -0
- package/src/primitives/cell.ts +115 -0
- package/src/primitives/check.ts +49 -0
- package/src/primitives/closed.ts +74 -0
- package/src/primitives/erosion.ts +183 -0
- package/src/primitives/font-5x7.ts +117 -0
- package/src/primitives/heightfield.ts +101 -0
- package/src/primitives/index.ts +56 -0
- package/src/primitives/lathe-facets.ts +45 -0
- package/src/primitives/lathe.ts +174 -0
- package/src/primitives/lattice.ts +131 -0
- package/src/primitives/loft.ts +174 -0
- package/src/primitives/mc-table.ts +41 -0
- package/src/primitives/member.ts +130 -0
- package/src/primitives/noise.ts +157 -0
- package/src/primitives/planar.ts +55 -0
- package/src/primitives/prism.ts +85 -0
- package/src/primitives/sculpt-edit.ts +212 -0
- package/src/primitives/sculpt-tree.ts +226 -0
- package/src/primitives/sculpt.ts +80 -0
- package/src/primitives/sdf-grid.ts +131 -0
- package/src/primitives/sdf-ops.ts +145 -0
- package/src/primitives/sdf.ts +83 -0
- package/src/primitives/strip-uv.ts +26 -0
- package/src/primitives/surface-nets.ts +161 -0
- package/src/primitives/sweep.ts +162 -0
- package/src/primitives/terrain-color.ts +85 -0
- package/src/primitives/terrain-grid.ts +137 -0
- package/src/primitives/text-layout.ts +85 -0
- package/src/primitives/text.ts +197 -0
- package/src/primitives/tiled.ts +93 -0
- package/src/primitives/transport.ts +85 -0
- package/src/primitives/triangulate.ts +42 -0
- package/src/primitives/value-noise.ts +62 -0
- package/src/qa/checks.ts +110 -0
- package/src/qa/clash.ts +177 -0
- package/src/qa/floating.ts +63 -0
- package/src/qa/gates.ts +126 -0
- package/src/qa/geometry.ts +187 -0
- package/src/qa/index.ts +28 -0
- package/src/qa/inside.ts +114 -0
- package/src/qa/inspect.ts +107 -0
- package/src/qa/lods.ts +97 -0
- package/src/qa/measures.ts +67 -0
- package/src/qa/occlusion.ts +166 -0
- package/src/qa/sanity.ts +137 -0
- package/src/qa/types.ts +17 -0
- package/src/render/beauty.ts +115 -0
- package/src/render/camera.ts +142 -0
- package/src/render/depth-map.ts +191 -0
- package/src/render/font.ts +76 -0
- package/src/render/ground.ts +87 -0
- package/src/render/index.ts +34 -0
- package/src/render/lighting.ts +121 -0
- package/src/render/lod-strip.ts +34 -0
- package/src/render/metrics.ts +147 -0
- package/src/render/options.ts +81 -0
- package/src/render/photo-match.ts +28 -0
- package/src/render/raster.ts +143 -0
- package/src/render/resolve.ts +109 -0
- package/src/render/review-prompt.ts +59 -0
- package/src/render/review-warnings.ts +92 -0
- package/src/render/review.ts +103 -0
- package/src/render/scene.ts +119 -0
- package/src/render/shade.ts +204 -0
- package/src/render/sheet.ts +88 -0
- package/src/render/texture.ts +97 -0
- package/src/render/thumbnail.ts +75 -0
- package/src/render/vis-buffer.ts +119 -0
- package/src/testing/assert.ts +115 -0
- package/src/testing/compiled.ts +31 -0
- package/src/testing/hash.ts +14 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/snapshot.ts +37 -0
- package/src/types/gltf-validator.d.ts +13 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Samples a signed-distance field on a cubic grid for the meshers. The field is clipped to the
|
|
2
|
+
// bounds box (max with the box's own distance), and the grid has a one-cell positive border, so
|
|
3
|
+
// every surface comes out closed. Sparse: one coarse sample per 8³-cell block; a block whose
|
|
4
|
+
// centre is farther from the surface than `lipschitz` × its half-diagonal cannot hold any of it,
|
|
5
|
+
// so its points take that sign without evaluating the field (a head at 4 mm meshes in ~1 s).
|
|
6
|
+
|
|
7
|
+
import type { Vec3 } from '../core/index.ts';
|
|
8
|
+
import { invalid } from './check.ts';
|
|
9
|
+
import type { Field } from './sdf-ops.ts';
|
|
10
|
+
|
|
11
|
+
export interface Grid {
|
|
12
|
+
/** Points per axis. */
|
|
13
|
+
readonly nx: number;
|
|
14
|
+
readonly ny: number;
|
|
15
|
+
readonly nz: number;
|
|
16
|
+
/** Position of point (0, 0, 0) and the cell edge, metres. */
|
|
17
|
+
readonly origin: Vec3;
|
|
18
|
+
readonly h: number;
|
|
19
|
+
readonly values: Float32Array;
|
|
20
|
+
/** The clipped field the grid sampled (normals take its gradient). */
|
|
21
|
+
readonly field: Field;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Grid points above this are refused (≈ 80 MB of samples). */
|
|
25
|
+
export const MAX_POINTS = 1 << 24;
|
|
26
|
+
const BLOCK = 4;
|
|
27
|
+
|
|
28
|
+
export function sampleGrid(
|
|
29
|
+
raw: Field,
|
|
30
|
+
bounds: readonly [Vec3, Vec3],
|
|
31
|
+
h: number,
|
|
32
|
+
lipschitz: number,
|
|
33
|
+
symmetricY = false,
|
|
34
|
+
): Grid {
|
|
35
|
+
const [lo0, hi0] = bounds;
|
|
36
|
+
if (!Number.isFinite(h) || h <= 0)
|
|
37
|
+
invalid(
|
|
38
|
+
`sdf resolution is ${h}`,
|
|
39
|
+
'pass the cell edge in metres, e.g. ctx.pick([0.004, 0.012, 0.03])',
|
|
40
|
+
);
|
|
41
|
+
if (
|
|
42
|
+
![0, 1, 2].every((k) => Number.isFinite(lo0[k] ?? Number.NaN) && (hi0[k] ?? 0) > (lo0[k] ?? 0))
|
|
43
|
+
)
|
|
44
|
+
invalid(
|
|
45
|
+
`sdf bounds [${lo0.join(', ')}] → [${hi0.join(', ')}] are empty or inverted`,
|
|
46
|
+
'give bounds as [min, max] corners with max > min on every axis, in metres',
|
|
47
|
+
);
|
|
48
|
+
// Mirror-symmetric shapes: bounds ±m cells in y, so y = 0 is a grid plane and every sample
|
|
49
|
+
// has its mirror twin at exactly (j − m − 1) · h — the mesh comes out symmetric.
|
|
50
|
+
const m = Math.ceil(Math.max(Math.abs(lo0[1]), Math.abs(hi0[1])) / h);
|
|
51
|
+
const lo: Vec3 = symmetricY ? [lo0[0], -m * h, lo0[2]] : lo0;
|
|
52
|
+
const hi: Vec3 = symmetricY ? [hi0[0], m * h, hi0[2]] : hi0;
|
|
53
|
+
const size = (k: number): number => Math.ceil(((hi[k] ?? 0) - (lo[k] ?? 0)) / h) + 3;
|
|
54
|
+
const [nx, ny, nz] = [size(0), symmetricY ? 2 * m + 3 : size(1), size(2)];
|
|
55
|
+
const yAt = (j: number): number => (symmetricY ? (j - m - 1) * h : lo[1] - h + j * h);
|
|
56
|
+
if (nx * ny * nz > MAX_POINTS) {
|
|
57
|
+
const vol = [0, 1, 2].reduce((v, k) => v * ((hi[k] ?? 0) - (lo[k] ?? 0)), 1);
|
|
58
|
+
invalid(
|
|
59
|
+
`sdf grid ${nx}×${ny}×${nz} exceeds ${MAX_POINTS} points at ${h} m cells`,
|
|
60
|
+
`use cells ≥ ${Math.cbrt(vol / MAX_POINTS).toPrecision(2)} m, or tighten the bounds`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const cx = (lo[0] + hi[0]) / 2;
|
|
64
|
+
const cy = (lo[1] + hi[1]) / 2;
|
|
65
|
+
const cz = (lo[2] + hi[2]) / 2;
|
|
66
|
+
const [bx, by, bz] = [(hi[0] - lo[0]) / 2, (hi[1] - lo[1]) / 2, (hi[2] - lo[2]) / 2];
|
|
67
|
+
const field: Field = (p) => {
|
|
68
|
+
const qx = Math.abs(p[0] - cx) - bx;
|
|
69
|
+
const qy = Math.abs(p[1] - cy) - by;
|
|
70
|
+
const qz = Math.abs(p[2] - cz) - bz;
|
|
71
|
+
const box =
|
|
72
|
+
Math.hypot(Math.max(qx, 0), Math.max(qy, 0), Math.max(qz, 0)) +
|
|
73
|
+
Math.min(Math.max(qx, qy, qz), 0);
|
|
74
|
+
const f = raw(p);
|
|
75
|
+
if (Number.isNaN(f))
|
|
76
|
+
invalid(
|
|
77
|
+
`sdf field returned NaN at [${p.join(', ')}]`,
|
|
78
|
+
'make the field return a finite distance everywhere in bounds',
|
|
79
|
+
);
|
|
80
|
+
return Math.max(f, box);
|
|
81
|
+
};
|
|
82
|
+
const origin: Vec3 = [lo[0] - h, symmetricY ? -(m + 1) * h : lo[1] - h, lo[2] - h];
|
|
83
|
+
const values = new Float32Array(nx * ny * nz);
|
|
84
|
+
const exact = new Uint8Array(nx * ny * nz);
|
|
85
|
+
const [kx, ky, kz] = [
|
|
86
|
+
Math.ceil((nx - 1) / BLOCK),
|
|
87
|
+
Math.ceil((ny - 1) / BLOCK),
|
|
88
|
+
Math.ceil((nz - 1) / BLOCK),
|
|
89
|
+
];
|
|
90
|
+
const coarse = new Float32Array(kx * ky * kz);
|
|
91
|
+
const p: [number, number, number] = [0, 0, 0];
|
|
92
|
+
const floor = h * 0.01;
|
|
93
|
+
const reach = lipschitz * BLOCK * h * Math.sqrt(3) * 0.5 + h;
|
|
94
|
+
for (let k = 0; k < kz; k++)
|
|
95
|
+
for (let j = 0; j < ky; j++)
|
|
96
|
+
for (let i = 0; i < kx; i++) {
|
|
97
|
+
p[0] = origin[0] + (i + 0.5) * BLOCK * h;
|
|
98
|
+
p[1] = origin[1] + (j + 0.5) * BLOCK * h;
|
|
99
|
+
p[2] = origin[2] + (k + 0.5) * BLOCK * h;
|
|
100
|
+
const v = field(p);
|
|
101
|
+
coarse[i + kx * (j + ky * k)] = v;
|
|
102
|
+
if (Math.abs(v) > reach) continue;
|
|
103
|
+
// Near the surface: every point of this block (faces included) is sampled exactly.
|
|
104
|
+
for (let z = k * BLOCK; z <= Math.min(nz - 1, (k + 1) * BLOCK); z++)
|
|
105
|
+
for (let y = j * BLOCK; y <= Math.min(ny - 1, (j + 1) * BLOCK); y++)
|
|
106
|
+
for (let x = i * BLOCK; x <= Math.min(nx - 1, (i + 1) * BLOCK); x++)
|
|
107
|
+
exact[x + nx * (y + ny * z)] = 1;
|
|
108
|
+
}
|
|
109
|
+
for (let z = 0; z < nz; z++)
|
|
110
|
+
for (let y = 0; y < ny; y++)
|
|
111
|
+
for (let x = 0; x < nx; x++) {
|
|
112
|
+
const id = x + nx * (y + ny * z);
|
|
113
|
+
if (exact[id]) {
|
|
114
|
+
p[0] = origin[0] + x * h;
|
|
115
|
+
p[1] = yAt(y);
|
|
116
|
+
p[2] = origin[2] + z * h;
|
|
117
|
+
const f = field(p);
|
|
118
|
+
// A sample (near) zero puts vertices of several cells on one grid point: marching cubes
|
|
119
|
+
// then emits slivers. Keep |value| ≥ 1% of a cell (moves the surface ≤ h / 100).
|
|
120
|
+
values[id] = Math.abs(f) >= floor ? f : f < 0 ? -floor : floor;
|
|
121
|
+
} else {
|
|
122
|
+
const b =
|
|
123
|
+
Math.min(kx - 1, Math.floor(x / BLOCK)) +
|
|
124
|
+
kx *
|
|
125
|
+
(Math.min(ky - 1, Math.floor(y / BLOCK)) +
|
|
126
|
+
ky * Math.min(kz - 1, Math.floor(z / BLOCK)));
|
|
127
|
+
values[id] = coarse[b] ?? 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { nx, ny, nz, origin, h, values, field };
|
|
131
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Signed-distance building blocks for `sdf`: exact shape distances (Quílez's formulas), boolean
|
|
2
|
+
// and smooth combinators (noise for `displace`: value-noise.ts). Negative = inside, metres.
|
|
3
|
+
// A field is called once per grid sample, so shapes do scalar maths only (no Vec3 allocation);
|
|
4
|
+
// fields must not keep the `p` they are given (the mesher reuses it).
|
|
5
|
+
|
|
6
|
+
import type { Vec3 } from '../core/index.ts';
|
|
7
|
+
|
|
8
|
+
export type Field = (p: Vec3) => number;
|
|
9
|
+
|
|
10
|
+
const clamp01 = (x: number): number => (x < 0 ? 0 : Math.min(x, 1));
|
|
11
|
+
|
|
12
|
+
/** Shape distances in the shape's own frame (centre/base at the origin). */
|
|
13
|
+
export const shapes = {
|
|
14
|
+
sphere: (x: number, y: number, z: number, r: number): number =>
|
|
15
|
+
Math.sqrt(x * x + y * y + z * z) - r,
|
|
16
|
+
ellipsoid(x: number, y: number, z: number, rx: number, ry: number, rz: number): number {
|
|
17
|
+
const k0 = Math.sqrt((x / rx) ** 2 + (y / ry) ** 2 + (z / rz) ** 2);
|
|
18
|
+
const k1 = Math.sqrt((x / (rx * rx)) ** 2 + (y / (ry * ry)) ** 2 + (z / (rz * rz)) ** 2);
|
|
19
|
+
return k1 === 0 ? -Math.min(rx, ry, rz) : (k0 * (k0 - 1)) / k1;
|
|
20
|
+
},
|
|
21
|
+
/** Box of half extents (hx, hy, hz) with edges rounded by `r`. */
|
|
22
|
+
roundBox(x: number, y: number, z: number, hx: number, hy: number, hz: number, r: number): number {
|
|
23
|
+
const qx = Math.abs(x) - hx + r;
|
|
24
|
+
const qy = Math.abs(y) - hy + r;
|
|
25
|
+
const qz = Math.abs(z) - hz + r;
|
|
26
|
+
const ox = Math.max(qx, 0);
|
|
27
|
+
const oy = Math.max(qy, 0);
|
|
28
|
+
const oz = Math.max(qz, 0);
|
|
29
|
+
return Math.sqrt(ox * ox + oy * oy + oz * oz) + Math.min(Math.max(qx, qy, qz), 0) - r;
|
|
30
|
+
},
|
|
31
|
+
/** Segment (0,0,0) → (0,0,len) with radius r. */
|
|
32
|
+
capsule(x: number, y: number, z: number, len: number, r: number): number {
|
|
33
|
+
const t = len > 0 ? Math.min(Math.max(z, 0), len) : 0;
|
|
34
|
+
return Math.sqrt(x * x + y * y + (z - t) * (z - t)) - r;
|
|
35
|
+
},
|
|
36
|
+
/** Capped cone standing on z = 0: base radius r1, top radius r2, height h. */
|
|
37
|
+
cone(x: number, y: number, z: number, r1: number, r2: number, h: number): number {
|
|
38
|
+
const hh = h / 2;
|
|
39
|
+
const qx = Math.sqrt(x * x + y * y);
|
|
40
|
+
const qy = z - hh;
|
|
41
|
+
const cax = qx - Math.min(qx, qy < 0 ? r1 : r2);
|
|
42
|
+
const cay = Math.abs(qy) - hh;
|
|
43
|
+
const k2x = r2 - r1;
|
|
44
|
+
const k2y = 2 * hh;
|
|
45
|
+
const t = clamp01(((r2 - qx) * k2x + (hh - qy) * k2y) / (k2x * k2x + k2y * k2y));
|
|
46
|
+
const cbx = qx - r2 + k2x * t;
|
|
47
|
+
const cby = qy - hh + k2y * t;
|
|
48
|
+
const s = cbx < 0 && cay < 0 ? -1 : 1;
|
|
49
|
+
return s * Math.sqrt(Math.min(cax * cax + cay * cay, cbx * cbx + cby * cby));
|
|
50
|
+
},
|
|
51
|
+
/** Ring in the XY plane: major radius R, tube radius r. */
|
|
52
|
+
torus(x: number, y: number, z: number, R: number, r: number): number {
|
|
53
|
+
const q = Math.sqrt(x * x + y * y) - R;
|
|
54
|
+
return Math.sqrt(q * q + z * z) - r;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Polynomial smooth-min weight: 1 → all `a`, 0 → all `b`; k = 0 is a hard min. */
|
|
59
|
+
export const smoothWeight = (a: number, b: number, k: number): number =>
|
|
60
|
+
k > 0 ? clamp01(0.5 + (0.5 * (b - a)) / k) : a < b ? 1 : 0;
|
|
61
|
+
|
|
62
|
+
/** Field constructors and combinators, world space. */
|
|
63
|
+
export const sd = {
|
|
64
|
+
sphere:
|
|
65
|
+
(at: Vec3, r: number): Field =>
|
|
66
|
+
(p) =>
|
|
67
|
+
shapes.sphere(p[0] - at[0], p[1] - at[1], p[2] - at[2], r),
|
|
68
|
+
ellipsoid:
|
|
69
|
+
(at: Vec3, radii: Vec3): Field =>
|
|
70
|
+
(p) =>
|
|
71
|
+
shapes.ellipsoid(p[0] - at[0], p[1] - at[1], p[2] - at[2], radii[0], radii[1], radii[2]),
|
|
72
|
+
/** Axis-aligned box of full `size` centred on `at`, edges rounded by `round`. */
|
|
73
|
+
box:
|
|
74
|
+
(at: Vec3, size: Vec3, round = 0): Field =>
|
|
75
|
+
(p) =>
|
|
76
|
+
shapes.roundBox(
|
|
77
|
+
p[0] - at[0],
|
|
78
|
+
p[1] - at[1],
|
|
79
|
+
p[2] - at[2],
|
|
80
|
+
size[0] / 2,
|
|
81
|
+
size[1] / 2,
|
|
82
|
+
size[2] / 2,
|
|
83
|
+
round,
|
|
84
|
+
),
|
|
85
|
+
capsule(a: Vec3, b: Vec3, r: number): Field {
|
|
86
|
+
const bx = b[0] - a[0];
|
|
87
|
+
const by = b[1] - a[1];
|
|
88
|
+
const bz = b[2] - a[2];
|
|
89
|
+
const bb = bx * bx + by * by + bz * bz;
|
|
90
|
+
return (p) => {
|
|
91
|
+
const px = p[0] - a[0];
|
|
92
|
+
const py = p[1] - a[1];
|
|
93
|
+
const pz = p[2] - a[2];
|
|
94
|
+
const h = bb > 0 ? clamp01((px * bx + py * by + pz * bz) / bb) : 0;
|
|
95
|
+
return Math.sqrt((px - bx * h) ** 2 + (py - by * h) ** 2 + (pz - bz * h) ** 2) - r;
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
cone:
|
|
99
|
+
(at: Vec3, r1: number, r2: number, h: number): Field =>
|
|
100
|
+
(p) =>
|
|
101
|
+
shapes.cone(p[0] - at[0], p[1] - at[1], p[2] - at[2], r1, r2, h),
|
|
102
|
+
torus:
|
|
103
|
+
(at: Vec3, R: number, r: number): Field =>
|
|
104
|
+
(p) =>
|
|
105
|
+
shapes.torus(p[0] - at[0], p[1] - at[1], p[2] - at[2], R, r),
|
|
106
|
+
union:
|
|
107
|
+
(...fs: readonly Field[]): Field =>
|
|
108
|
+
(p) => {
|
|
109
|
+
let d = Number.POSITIVE_INFINITY;
|
|
110
|
+
for (const f of fs) d = Math.min(d, f(p));
|
|
111
|
+
return d;
|
|
112
|
+
},
|
|
113
|
+
intersect:
|
|
114
|
+
(a: Field, b: Field): Field =>
|
|
115
|
+
(p) =>
|
|
116
|
+
Math.max(a(p), b(p)),
|
|
117
|
+
/** a minus b. */
|
|
118
|
+
subtract:
|
|
119
|
+
(a: Field, b: Field): Field =>
|
|
120
|
+
(p) =>
|
|
121
|
+
Math.max(a(p), -b(p)),
|
|
122
|
+
/** Union with a fillet of radius ~k metres between the two. */
|
|
123
|
+
smoothUnion:
|
|
124
|
+
(k: number, a: Field, b: Field): Field =>
|
|
125
|
+
(p) => {
|
|
126
|
+
const da = a(p);
|
|
127
|
+
const db = b(p);
|
|
128
|
+
const h = smoothWeight(da, db, k);
|
|
129
|
+
return db + (da - db) * h - k * h * (1 - h);
|
|
130
|
+
},
|
|
131
|
+
/** a minus b with a rounded crease of ~k metres. */
|
|
132
|
+
smoothSubtract:
|
|
133
|
+
(k: number, a: Field, b: Field): Field =>
|
|
134
|
+
(p) => {
|
|
135
|
+
const da = a(p);
|
|
136
|
+
const db = -b(p);
|
|
137
|
+
const h = smoothWeight(-da, -db, k);
|
|
138
|
+
return da + (db - da) * (1 - h) + k * h * (1 - h);
|
|
139
|
+
},
|
|
140
|
+
/** Push the surface out/in by `amount` metres × noise (∈ [−1, 1]). */
|
|
141
|
+
displace:
|
|
142
|
+
(f: Field, noise: Field, amount: number): Field =>
|
|
143
|
+
(p) =>
|
|
144
|
+
f(p) + amount * noise(p),
|
|
145
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// A signed-distance field meshed into a closed surface: rocks, boulders, terrain lumps, sculpted
|
|
2
|
+
// heads (sculpt.ts builds the field). SMOOTH normals from the field gradient (central differences),
|
|
3
|
+
// shared vertices. `resolution` is the cell edge in METRES, the LOD knob:
|
|
4
|
+
// `ctx.pick([0.01, 0.03, 0.08])`. Default mesher is surface nets (see surface-nets.ts for why);
|
|
5
|
+
// `mesher: 'cubes'` for marching cubes. UVs: planar along the normal's dominant axis, U at true
|
|
6
|
+
// metres, V mirrored every band so it never leaves the strip.
|
|
7
|
+
// triangles = 2 × grid edges the surface crosses (nets) ≈ 2 · area / resolution² · 1.5
|
|
8
|
+
|
|
9
|
+
import { type Mesh, MeshBuilder, type Vec3 } from '../core/index.ts';
|
|
10
|
+
import { type StripId, stripRect } from '../material/index.ts';
|
|
11
|
+
import { invalid } from './check.ts';
|
|
12
|
+
import { sampleGrid } from './sdf-grid.ts';
|
|
13
|
+
import { mirroredV, stripU, stripVMetres } from './strip-uv.ts';
|
|
14
|
+
import { marchingCubes, surfaceNets } from './surface-nets.ts';
|
|
15
|
+
|
|
16
|
+
/** One colour, or one per vertex from its position and unit normal (moss on top, dark in cracks). */
|
|
17
|
+
export type SdfColor = Vec3 | ((p: Vec3, n: Vec3) => Vec3);
|
|
18
|
+
|
|
19
|
+
export interface SdfOptions {
|
|
20
|
+
/** Signed distance in metres: negative inside. Must not keep the `p` it is given. */
|
|
21
|
+
readonly field: (p: Vec3) => number;
|
|
22
|
+
/** [min, max] corners; the surface is clipped to this box, so it is always closed. */
|
|
23
|
+
readonly bounds: readonly [Vec3, Vec3];
|
|
24
|
+
/** Cell edge in metres (the LOD knob). */
|
|
25
|
+
readonly resolution: number;
|
|
26
|
+
readonly color: SdfColor;
|
|
27
|
+
readonly strip?: StripId;
|
|
28
|
+
readonly mesher?: 'nets' | 'cubes';
|
|
29
|
+
/** Upper bound on |∇field| (1 for exact distances). Raise it for strongly displaced fields so
|
|
30
|
+
* the sparse pass never skips a block that holds surface. Default 1.25. */
|
|
31
|
+
readonly lipschitz?: number;
|
|
32
|
+
/** 'y': the field is mirror-symmetric across y = 0 — snap the grid so the mesh is too. */
|
|
33
|
+
readonly symmetry?: 'y';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function sdf(o: SdfOptions): Mesh {
|
|
37
|
+
const lip = o.lipschitz ?? 1.25;
|
|
38
|
+
if (!(lip >= 1)) invalid(`sdf lipschitz is ${lip}`, 'use ≥ 1 (1 = exact distance field)');
|
|
39
|
+
const g = sampleGrid(o.field, o.bounds, o.resolution, lip, o.symmetry === 'y');
|
|
40
|
+
const raw = o.mesher === 'cubes' ? marchingCubes(g) : surfaceNets(g);
|
|
41
|
+
if (raw.tris.length === 0)
|
|
42
|
+
invalid(
|
|
43
|
+
'sdf field has no surface inside its bounds (never crosses zero)',
|
|
44
|
+
'return negative distances inside the shape, positive outside, and make bounds contain it',
|
|
45
|
+
);
|
|
46
|
+
const strip = o.strip ?? 'plain';
|
|
47
|
+
const rect = stripRect(strip);
|
|
48
|
+
const vm = stripVMetres(strip);
|
|
49
|
+
const e = g.h * 0.5;
|
|
50
|
+
const q: [number, number, number] = [0, 0, 0];
|
|
51
|
+
const at = (x: number, y: number, z: number): number => {
|
|
52
|
+
q[0] = x;
|
|
53
|
+
q[1] = y;
|
|
54
|
+
q[2] = z;
|
|
55
|
+
return g.field(q);
|
|
56
|
+
};
|
|
57
|
+
const b = new MeshBuilder();
|
|
58
|
+
for (let i = 0; i < raw.pos.length; i += 3) {
|
|
59
|
+
const x = raw.pos[i] ?? 0;
|
|
60
|
+
const y = raw.pos[i + 1] ?? 0;
|
|
61
|
+
const z = raw.pos[i + 2] ?? 0;
|
|
62
|
+
const gx = at(x + e, y, z) - at(x - e, y, z);
|
|
63
|
+
const gy = at(x, y + e, z) - at(x, y - e, z);
|
|
64
|
+
const gz = at(x, y, z + e) - at(x, y, z - e);
|
|
65
|
+
const gl = Math.sqrt(gx * gx + gy * gy + gz * gz);
|
|
66
|
+
const n: Vec3 = gl > 0 ? [gx / gl, gy / gl, gz / gl] : [0, 0, 1];
|
|
67
|
+
const ax = Math.abs(n[0]);
|
|
68
|
+
const ay = Math.abs(n[1]);
|
|
69
|
+
const az = Math.abs(n[2]);
|
|
70
|
+
// Project onto the plane facing the normal's dominant axis; V runs with world up on walls.
|
|
71
|
+
const [s, t] = az >= ax && az >= ay ? [x, y] : ax >= ay ? [y, z] : [x, z];
|
|
72
|
+
const p: Vec3 = [x, y, z];
|
|
73
|
+
b.vertex({
|
|
74
|
+
p,
|
|
75
|
+
n,
|
|
76
|
+
uv: [stripU(rect, s), mirroredV(rect, t, vm)],
|
|
77
|
+
color: typeof o.color === 'function' ? o.color(p, n) : o.color,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; i < raw.tris.length; i += 3)
|
|
81
|
+
b.tri(raw.tris[i] ?? 0, raw.tris[i + 1] ?? 0, raw.tris[i + 2] ?? 0);
|
|
82
|
+
return b.build();
|
|
83
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// True-scale UV mapping onto the trim sheet. U tiles across a strip through the REPEAT sampler
|
|
2
|
+
// (metres / rect.metres); V cannot wrap (the next strip sits there), so V maps one strip band of
|
|
3
|
+
// `vMetres` world metres onto the rect, half a texel inside its edges against bleeding.
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_SHEET, type StripId, TRIM_SHEET_SIZE, type UvRect } from '../material/index.ts';
|
|
6
|
+
|
|
7
|
+
/** Half a texel at the house sheet size: keeps bilinear samples inside the region. */
|
|
8
|
+
export const HALF_TEXEL = 0.5 / TRIM_SHEET_SIZE;
|
|
9
|
+
|
|
10
|
+
/** World metres one strip band covers along V on the house sheet (`scaleMetres`). */
|
|
11
|
+
export const stripVMetres = (id: StripId): number => DEFAULT_SHEET.strips[id]?.scaleMetres ?? 1;
|
|
12
|
+
|
|
13
|
+
/** U for a run of `metres` along the strip. */
|
|
14
|
+
export const stripU = (rect: UvRect, metres: number): number =>
|
|
15
|
+
rect.u0 + (metres / rect.metres) * (rect.u1 - rect.u0);
|
|
16
|
+
|
|
17
|
+
/** V for a fraction 0..1 of one band. */
|
|
18
|
+
export const stripV = (rect: UvRect, frac: number): number =>
|
|
19
|
+
rect.v0 + HALF_TEXEL + frac * (rect.v1 - rect.v0 - 2 * HALF_TEXEL);
|
|
20
|
+
|
|
21
|
+
/** V for any metre value, mirrored every band: continuous, never leaves the strip (organic). */
|
|
22
|
+
export function mirroredV(rect: UvRect, metres: number, vMetres: number): number {
|
|
23
|
+
const t = metres / vMetres;
|
|
24
|
+
const m = t - 2 * Math.floor(t / 2);
|
|
25
|
+
return stripV(rect, m <= 1 ? m : 2 - m);
|
|
26
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Meshers over a sampled grid (inside = value < 0). Both return positions + triangle indices
|
|
2
|
+
// wound CCW seen from outside; sdf.ts adds normals, UVs and colour.
|
|
3
|
+
// nets: naive surface nets — one vertex per surface cell (mean of its edge crossings), one quad
|
|
4
|
+
// per sign-changing grid edge: triangles = 2 × crossing edges. Even, well-shaped
|
|
5
|
+
// triangles, ~half of marching cubes' count, smooth by construction: the organic default.
|
|
6
|
+
// cubes: marching cubes (Lorensen & Cline) — vertices on the grid edges, the classic table.
|
|
7
|
+
|
|
8
|
+
import { TRI } from './mc-table.ts';
|
|
9
|
+
import type { Grid } from './sdf-grid.ts';
|
|
10
|
+
|
|
11
|
+
export interface RawSurface {
|
|
12
|
+
readonly pos: number[];
|
|
13
|
+
readonly tris: number[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Cube corners (Bourke numbering) and the 12 edges as corner pairs.
|
|
17
|
+
const CORNER = [0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1];
|
|
18
|
+
const EDGE = [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7];
|
|
19
|
+
|
|
20
|
+
export function surfaceNets(g: Grid): RawSurface {
|
|
21
|
+
const { nx, ny, nz, values: v, h, origin: o } = g;
|
|
22
|
+
const cells = new Int32Array((nx - 1) * (ny - 1) * (nz - 1)).fill(-1);
|
|
23
|
+
const pos: number[] = [];
|
|
24
|
+
const c = new Float64Array(8);
|
|
25
|
+
for (let z = 0; z < nz - 1; z++)
|
|
26
|
+
for (let y = 0; y < ny - 1; y++)
|
|
27
|
+
for (let x = 0; x < nx - 1; x++) {
|
|
28
|
+
let mask = 0;
|
|
29
|
+
for (let k = 0; k < 8; k++) {
|
|
30
|
+
const val =
|
|
31
|
+
v[
|
|
32
|
+
x +
|
|
33
|
+
(CORNER[k * 3] ?? 0) +
|
|
34
|
+
nx * (y + (CORNER[k * 3 + 1] ?? 0) + ny * (z + (CORNER[k * 3 + 2] ?? 0)))
|
|
35
|
+
] ?? 1;
|
|
36
|
+
c[k] = val;
|
|
37
|
+
if (val < 0) mask |= 1 << k;
|
|
38
|
+
}
|
|
39
|
+
if (mask === 0 || mask === 255) continue;
|
|
40
|
+
let sx = 0;
|
|
41
|
+
let sy = 0;
|
|
42
|
+
let sz = 0;
|
|
43
|
+
let n = 0;
|
|
44
|
+
for (let e = 0; e < 12; e++) {
|
|
45
|
+
const a = EDGE[e * 2] ?? 0;
|
|
46
|
+
const b = EDGE[e * 2 + 1] ?? 0;
|
|
47
|
+
const va = c[a] ?? 0;
|
|
48
|
+
const vb = c[b] ?? 0;
|
|
49
|
+
if (va < 0 === vb < 0) continue;
|
|
50
|
+
const t = va / (va - vb);
|
|
51
|
+
sx += (CORNER[a * 3] ?? 0) + t * ((CORNER[b * 3] ?? 0) - (CORNER[a * 3] ?? 0));
|
|
52
|
+
sy +=
|
|
53
|
+
(CORNER[a * 3 + 1] ?? 0) + t * ((CORNER[b * 3 + 1] ?? 0) - (CORNER[a * 3 + 1] ?? 0));
|
|
54
|
+
sz +=
|
|
55
|
+
(CORNER[a * 3 + 2] ?? 0) + t * ((CORNER[b * 3 + 2] ?? 0) - (CORNER[a * 3 + 2] ?? 0));
|
|
56
|
+
n++;
|
|
57
|
+
}
|
|
58
|
+
cells[x + (nx - 1) * (y + (ny - 1) * z)] = pos.length / 3;
|
|
59
|
+
pos.push(o[0] + (x + sx / n) * h, o[1] + (y + sy / n) * h, o[2] + (z + sz / n) * h);
|
|
60
|
+
}
|
|
61
|
+
const tris: number[] = [];
|
|
62
|
+
const cell = (x: number, y: number, z: number): number =>
|
|
63
|
+
cells[x + (nx - 1) * (y + (ny - 1) * z)] ?? 0;
|
|
64
|
+
const P = (i: number, k: number): number => pos[i * 3 + k] ?? 0;
|
|
65
|
+
/** Unit normal of triangle (a, b, c) written into n. */
|
|
66
|
+
const n1 = new Float64Array(3);
|
|
67
|
+
const n2 = new Float64Array(3);
|
|
68
|
+
const normal = (a: number, b: number, c: number, n: Float64Array): void => {
|
|
69
|
+
const ex = P(b, 0) - P(a, 0);
|
|
70
|
+
const ey = P(b, 1) - P(a, 1);
|
|
71
|
+
const ez = P(b, 2) - P(a, 2);
|
|
72
|
+
const fx = P(c, 0) - P(a, 0);
|
|
73
|
+
const fy = P(c, 1) - P(a, 1);
|
|
74
|
+
const fz = P(c, 2) - P(a, 2);
|
|
75
|
+
const x = ey * fz - ez * fy;
|
|
76
|
+
const y = ez * fx - ex * fz;
|
|
77
|
+
const z = ex * fy - ey * fx;
|
|
78
|
+
const l = Math.sqrt(x * x + y * y + z * z) || 1;
|
|
79
|
+
n[0] = x / l;
|
|
80
|
+
n[1] = y / l;
|
|
81
|
+
n[2] = z / l;
|
|
82
|
+
};
|
|
83
|
+
const flatness = (): number =>
|
|
84
|
+
(n1[0] ?? 0) * (n2[0] ?? 0) + (n1[1] ?? 0) * (n2[1] ?? 0) + (n1[2] ?? 0) * (n2[2] ?? 0);
|
|
85
|
+
const quad = (a: number, b: number, cc: number, d: number, out: boolean): void => {
|
|
86
|
+
const [q0, q1, q2, q3] = out ? [a, b, cc, d] : [a, d, cc, b];
|
|
87
|
+
// Split along the diagonal whose two triangles fold least (no creases on thin features).
|
|
88
|
+
normal(q0, q1, q2, n1);
|
|
89
|
+
normal(q0, q2, q3, n2);
|
|
90
|
+
const along02 = flatness();
|
|
91
|
+
normal(q0, q1, q3, n1);
|
|
92
|
+
normal(q1, q2, q3, n2);
|
|
93
|
+
if (along02 >= flatness()) tris.push(q0, q1, q2, q0, q2, q3);
|
|
94
|
+
else tris.push(q0, q1, q3, q1, q2, q3);
|
|
95
|
+
};
|
|
96
|
+
for (let z = 1; z < nz - 1; z++)
|
|
97
|
+
for (let y = 1; y < ny - 1; y++)
|
|
98
|
+
for (let x = 1; x < nx - 1; x++) {
|
|
99
|
+
const inside = (v[x + nx * (y + ny * z)] ?? 1) < 0;
|
|
100
|
+
// Edge +X: cells around it in the (y, z) plane, CCW seen from +X.
|
|
101
|
+
if (x < nx - 1 && inside !== (v[x + 1 + nx * (y + ny * z)] ?? 1) < 0)
|
|
102
|
+
quad(cell(x, y - 1, z - 1), cell(x, y, z - 1), cell(x, y, z), cell(x, y - 1, z), inside);
|
|
103
|
+
if (y < ny - 1 && inside !== (v[x + nx * (y + 1 + ny * z)] ?? 1) < 0)
|
|
104
|
+
quad(cell(x - 1, y, z - 1), cell(x - 1, y, z), cell(x, y, z), cell(x, y, z - 1), inside);
|
|
105
|
+
if (z < nz - 1 && inside !== (v[x + nx * (y + ny * (z + 1))] ?? 1) < 0)
|
|
106
|
+
quad(cell(x - 1, y - 1, z), cell(x, y - 1, z), cell(x, y, z), cell(x - 1, y, z), inside);
|
|
107
|
+
}
|
|
108
|
+
return { pos, tris };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function marchingCubes(g: Grid): RawSurface {
|
|
112
|
+
const { nx, ny, nz, values: v, h, origin: o } = g;
|
|
113
|
+
const shared = new Map<number, number>();
|
|
114
|
+
const pos: number[] = [];
|
|
115
|
+
const tris: number[] = [];
|
|
116
|
+
for (let z = 0; z < nz - 1; z++)
|
|
117
|
+
for (let y = 0; y < ny - 1; y++)
|
|
118
|
+
for (let x = 0; x < nx - 1; x++) {
|
|
119
|
+
let mask = 0;
|
|
120
|
+
for (let k = 0; k < 8; k++)
|
|
121
|
+
if (
|
|
122
|
+
(v[
|
|
123
|
+
x +
|
|
124
|
+
(CORNER[k * 3] ?? 0) +
|
|
125
|
+
nx * (y + (CORNER[k * 3 + 1] ?? 0) + ny * (z + (CORNER[k * 3 + 2] ?? 0)))
|
|
126
|
+
] ?? 1) < 0
|
|
127
|
+
)
|
|
128
|
+
mask |= 1 << k;
|
|
129
|
+
const list = TRI[mask] ?? [];
|
|
130
|
+
for (let t = 0; t < list.length; t += 3)
|
|
131
|
+
for (const j of [0, 2, 1]) {
|
|
132
|
+
const e = list[t + j] ?? 0;
|
|
133
|
+
const a = EDGE[e * 2] ?? 0;
|
|
134
|
+
const b = EDGE[e * 2 + 1] ?? 0;
|
|
135
|
+
const ax = x + (CORNER[a * 3] ?? 0);
|
|
136
|
+
const ay = y + (CORNER[a * 3 + 1] ?? 0);
|
|
137
|
+
const az = z + (CORNER[a * 3 + 2] ?? 0);
|
|
138
|
+
const bx = x + (CORNER[b * 3] ?? 0);
|
|
139
|
+
const by = y + (CORNER[b * 3 + 1] ?? 0);
|
|
140
|
+
const bz = z + (CORNER[b * 3 + 2] ?? 0);
|
|
141
|
+
const ia = ax + nx * (ay + ny * az);
|
|
142
|
+
const ib = bx + nx * (by + ny * bz);
|
|
143
|
+
const lowId = Math.min(ia, ib);
|
|
144
|
+
const key = lowId * 3 + (ax !== bx ? 0 : ay !== by ? 1 : 2);
|
|
145
|
+
let id = shared.get(key);
|
|
146
|
+
if (id === undefined) {
|
|
147
|
+
const va = v[ia] ?? 0;
|
|
148
|
+
const s = va / (va - (v[ib] ?? 0));
|
|
149
|
+
id = pos.length / 3;
|
|
150
|
+
pos.push(
|
|
151
|
+
o[0] + (ax + s * (bx - ax)) * h,
|
|
152
|
+
o[1] + (ay + s * (by - ay)) * h,
|
|
153
|
+
o[2] + (az + s * (bz - az)) * h,
|
|
154
|
+
);
|
|
155
|
+
shared.set(key, id);
|
|
156
|
+
}
|
|
157
|
+
tris.push(id);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { pos, tris };
|
|
161
|
+
}
|