@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,119 @@
|
|
|
1
|
+
// Visibility buffer: perspective-rasterise every triangle once, keeping only the nearest triangle
|
|
2
|
+
// id and its perspective-correct barycentrics per pixel. Shading then runs exactly once per pixel
|
|
3
|
+
// (deferred), so overdraw on an 88k-triangle tank costs a depth compare, not a lighting model.
|
|
4
|
+
|
|
5
|
+
import type { PerspectiveCamera } from './camera.ts';
|
|
6
|
+
import type { Scene } from './scene.ts';
|
|
7
|
+
|
|
8
|
+
export interface VisBuffer {
|
|
9
|
+
readonly width: number;
|
|
10
|
+
readonly height: number;
|
|
11
|
+
/** Triangle id per pixel, −1 = background. */
|
|
12
|
+
readonly tri: Int32Array;
|
|
13
|
+
/** Perspective-correct barycentrics of vertices 1 and 2 (vertex 0 = 1 − b1 − b2). */
|
|
14
|
+
readonly b1: Float32Array;
|
|
15
|
+
readonly b2: Float32Array;
|
|
16
|
+
/** 1 / view depth (larger = nearer). */
|
|
17
|
+
readonly invw: Float32Array;
|
|
18
|
+
/** Per-triangle texture mip level from its texel : pixel footprint. */
|
|
19
|
+
readonly mip: Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Return false to discard a fragment (alpha-tested cutouts). */
|
|
23
|
+
export type AlphaTest = (tri: number, b1: number, b2: number, mip: number) => boolean;
|
|
24
|
+
|
|
25
|
+
export function rasteriseVis(
|
|
26
|
+
s: Scene,
|
|
27
|
+
cam: PerspectiveCamera,
|
|
28
|
+
texSize: number,
|
|
29
|
+
alpha?: AlphaTest,
|
|
30
|
+
): VisBuffer {
|
|
31
|
+
const W = cam.width;
|
|
32
|
+
const H = cam.height;
|
|
33
|
+
const n = W * H;
|
|
34
|
+
const tri = new Int32Array(n).fill(-1);
|
|
35
|
+
const b1 = new Float32Array(n);
|
|
36
|
+
const b2 = new Float32Array(n);
|
|
37
|
+
const invw = new Float32Array(n);
|
|
38
|
+
const nv = s.positions.length / 3;
|
|
39
|
+
const sx = new Float32Array(nv);
|
|
40
|
+
const sy = new Float32Array(nv);
|
|
41
|
+
const iw = new Float32Array(nv);
|
|
42
|
+
const P = s.positions;
|
|
43
|
+
const [ex, ey, ez] = cam.eye;
|
|
44
|
+
for (let v = 0; v < nv; v++) {
|
|
45
|
+
const dx = (P[v * 3] ?? 0) - ex;
|
|
46
|
+
const dy = (P[v * 3 + 1] ?? 0) - ey;
|
|
47
|
+
const dz = (P[v * 3 + 2] ?? 0) - ez;
|
|
48
|
+
const w = dx * cam.f[0] + dy * cam.f[1] + dz * cam.f[2];
|
|
49
|
+
const q = w > 1e-4 ? 1 / w : -1;
|
|
50
|
+
iw[v] = q;
|
|
51
|
+
sx[v] = cam.cx + (dx * cam.r[0] + dy * cam.r[1] + dz * cam.r[2]) * q * cam.focal;
|
|
52
|
+
sy[v] = cam.cy - (dx * cam.u[0] + dy * cam.u[1] + dz * cam.u[2]) * q * cam.focal;
|
|
53
|
+
}
|
|
54
|
+
const mip = new Uint8Array(s.triangles);
|
|
55
|
+
const I = s.indices;
|
|
56
|
+
const UV = s.uvs;
|
|
57
|
+
for (let t = 0; t < s.triangles; t++) {
|
|
58
|
+
const a = I[t * 3] ?? 0;
|
|
59
|
+
const b = I[t * 3 + 1] ?? 0;
|
|
60
|
+
const c = I[t * 3 + 2] ?? 0;
|
|
61
|
+
const qa = iw[a] ?? -1;
|
|
62
|
+
const qb = iw[b] ?? -1;
|
|
63
|
+
const qc = iw[c] ?? -1;
|
|
64
|
+
if (qa < 0 || qb < 0 || qc < 0) continue;
|
|
65
|
+
const ax = sx[a] ?? 0;
|
|
66
|
+
const ay = sy[a] ?? 0;
|
|
67
|
+
const bx = sx[b] ?? 0;
|
|
68
|
+
const by = sy[b] ?? 0;
|
|
69
|
+
const cx = sx[c] ?? 0;
|
|
70
|
+
const cy = sy[c] ?? 0;
|
|
71
|
+
const area = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
72
|
+
if (Math.abs(area) < 1e-12) continue;
|
|
73
|
+
const uvArea = Math.abs(
|
|
74
|
+
((UV[b * 2] ?? 0) - (UV[a * 2] ?? 0)) * ((UV[c * 2 + 1] ?? 0) - (UV[a * 2 + 1] ?? 0)) -
|
|
75
|
+
((UV[b * 2 + 1] ?? 0) - (UV[a * 2 + 1] ?? 0)) * ((UV[c * 2] ?? 0) - (UV[a * 2] ?? 0)),
|
|
76
|
+
);
|
|
77
|
+
const lod = 0.5 * Math.log2((uvArea * texSize * texSize) / Math.abs(area) + 1e-12);
|
|
78
|
+
const m = Math.max(0, Math.min(10, Math.floor(lod + 0.25)));
|
|
79
|
+
mip[t] = m;
|
|
80
|
+
const lx = ax < bx ? Math.min(ax, cx) : Math.min(bx, cx);
|
|
81
|
+
const hx = ax > bx ? Math.max(ax, cx) : Math.max(bx, cx);
|
|
82
|
+
const ly = ay < by ? Math.min(ay, cy) : Math.min(by, cy);
|
|
83
|
+
const hy = ay > by ? Math.max(ay, cy) : Math.max(by, cy);
|
|
84
|
+
const minX = lx < 0 ? 0 : Math.floor(lx);
|
|
85
|
+
const maxX = hx > W - 1 ? W - 1 : Math.ceil(hx);
|
|
86
|
+
const minY = ly < 0 ? 0 : Math.floor(ly);
|
|
87
|
+
const maxY = hy > H - 1 ? H - 1 : Math.ceil(hy);
|
|
88
|
+
if (minX > maxX || minY > maxY) continue;
|
|
89
|
+
const inv = 1 / area;
|
|
90
|
+
const cut = alpha !== undefined && s.surface[t] === 1;
|
|
91
|
+
// Affine edge functions, stepped per pixel.
|
|
92
|
+
const A0 = (by - cy) * inv;
|
|
93
|
+
const B0 = (cx - bx) * inv;
|
|
94
|
+
const A1 = (cy - ay) * inv;
|
|
95
|
+
const B1 = (ax - cx) * inv;
|
|
96
|
+
const C0 = (bx * cy - by * cx) * inv;
|
|
97
|
+
const C1 = (cx * ay - cy * ax) * inv;
|
|
98
|
+
for (let y = minY; y <= maxY; y++) {
|
|
99
|
+
const py = y + 0.5;
|
|
100
|
+
let w0 = C0 + A0 * (minX + 0.5) + B0 * py;
|
|
101
|
+
let w1 = C1 + A1 * (minX + 0.5) + B1 * py;
|
|
102
|
+
for (let x = minX; x <= maxX; x++, w0 += A0, w1 += A1) {
|
|
103
|
+
const w2 = 1 - w0 - w1;
|
|
104
|
+
if (w0 < 0 || w1 < 0 || w2 < 0) continue;
|
|
105
|
+
const z = w0 * qa + w1 * qb + w2 * qc;
|
|
106
|
+
const i = y * W + x;
|
|
107
|
+
if (z <= (invw[i] ?? 0)) continue;
|
|
108
|
+
const pb = (w1 * qb) / z;
|
|
109
|
+
const pc = (w2 * qc) / z;
|
|
110
|
+
if (cut && alpha && !alpha(t, pb, pc, m)) continue;
|
|
111
|
+
invw[i] = z;
|
|
112
|
+
tri[i] = t;
|
|
113
|
+
b1[i] = pb;
|
|
114
|
+
b2[i] = pc;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { width: W, height: H, tri, b1, b2, invw, mip };
|
|
119
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Assertions for a recipe's own `bun test` (or any runner): each compiles the asset, checks one
|
|
2
|
+
// property, and throws a Ts23dError with the findings and a fix — no test framework imported.
|
|
3
|
+
// it('passes QA', () => assertQaPass(tiger_1));
|
|
4
|
+
// it('has its pivots', () => assertParts(tiger_1, ['turret', 'gun', 'wheel_l_*']));
|
|
5
|
+
|
|
6
|
+
import type { Asset, Lod, Measure } from '../core/index.ts';
|
|
7
|
+
import {
|
|
8
|
+
clashes,
|
|
9
|
+
DEFAULT_GATES,
|
|
10
|
+
DEFAULT_LOD_GATES,
|
|
11
|
+
type Gate,
|
|
12
|
+
glob,
|
|
13
|
+
type LodGate,
|
|
14
|
+
measure,
|
|
15
|
+
runLodQa,
|
|
16
|
+
runQa,
|
|
17
|
+
} from '../qa/index.ts';
|
|
18
|
+
import { compileLods, failOn, type LodOptions } from './compiled.ts';
|
|
19
|
+
|
|
20
|
+
export interface QaOptions extends LodOptions {
|
|
21
|
+
/** Per-LOD gates. Default the build's DEFAULT_GATES. */
|
|
22
|
+
readonly gates?: readonly Gate[];
|
|
23
|
+
/** Chain gates over all compiled LODs. Default DEFAULT_LOD_GATES. */
|
|
24
|
+
readonly lodGates?: readonly LodGate[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The same gate `t3d build` runs: every LOD passes QA, plus the LOD-chain gates. */
|
|
28
|
+
export function assertQaPass(asset: Asset, o: QaOptions = {}): void {
|
|
29
|
+
const lods = compileLods(asset, o);
|
|
30
|
+
const found = lods.flatMap((a) =>
|
|
31
|
+
runQa(a, o.gates ?? DEFAULT_GATES).map((finding) => ({ finding, lod: a.lod })),
|
|
32
|
+
);
|
|
33
|
+
const chain = lods.length > 1 ? runLodQa(lods, o.lodGates ?? DEFAULT_LOD_GATES) : [];
|
|
34
|
+
failOn(`${asset.name} QA`, [...found, ...chain.map((finding) => ({ finding }))]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Triangles per LOD at or under `caps` (default: the asset's own budget). */
|
|
38
|
+
export function assertBudget(
|
|
39
|
+
asset: Asset,
|
|
40
|
+
caps: readonly [number, number, number] = asset.budget.triangles,
|
|
41
|
+
o: LodOptions = {},
|
|
42
|
+
): void {
|
|
43
|
+
failOn(
|
|
44
|
+
`${asset.name} budget`,
|
|
45
|
+
compileLods(asset, o).flatMap((a) => {
|
|
46
|
+
const tris = a.parts.reduce((s, p) => s + (p.mesh ? p.mesh.indices.length / 3 : 0), 0);
|
|
47
|
+
const cap = caps[a.lod];
|
|
48
|
+
return tris > cap
|
|
49
|
+
? [
|
|
50
|
+
{
|
|
51
|
+
lod: a.lod,
|
|
52
|
+
finding: {
|
|
53
|
+
gate: 'budget',
|
|
54
|
+
message: `${tris} triangles > ${cap}`,
|
|
55
|
+
fix: 'drop detail at this LOD (ctx.lod / ctx.pick) — never raise a cap to make a test pass',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
: [];
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Each selector (name or glob) matches at least one part at every LOD checked. */
|
|
65
|
+
export function assertParts(asset: Asset, selectors: readonly string[], o: LodOptions = {}): void {
|
|
66
|
+
failOn(
|
|
67
|
+
`${asset.name} parts`,
|
|
68
|
+
compileLods(asset, o).flatMap((a) =>
|
|
69
|
+
selectors
|
|
70
|
+
.filter((s) => !a.parts.some((p) => glob(s).test(p.name)))
|
|
71
|
+
.map((s) => ({
|
|
72
|
+
lod: a.lod,
|
|
73
|
+
finding: {
|
|
74
|
+
gate: 'parts',
|
|
75
|
+
message: `no part matches '${s}'`,
|
|
76
|
+
fix: 'engines bind to these names: add the part/group at this LOD (gate its geometry, not its pivot)',
|
|
77
|
+
},
|
|
78
|
+
})),
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** One spec dimension at LOD0, the way the `measures` gate checks `real.measures`. */
|
|
84
|
+
export function assertMeasure(asset: Asset, m: Measure, o: { lod?: Lod } = {}): number {
|
|
85
|
+
const [a] = compileLods(asset, { lods: [o.lod ?? 0] });
|
|
86
|
+
const got = a ? measure(a, m) : null;
|
|
87
|
+
const tol = m.tolerance ?? 0.02;
|
|
88
|
+
const bad = got === null || Math.abs(got - m.value) / m.value > tol;
|
|
89
|
+
failOn(
|
|
90
|
+
`${asset.name} measure`,
|
|
91
|
+
bad
|
|
92
|
+
? [
|
|
93
|
+
{
|
|
94
|
+
finding: {
|
|
95
|
+
gate: 'measure',
|
|
96
|
+
message:
|
|
97
|
+
got === null
|
|
98
|
+
? `'${m.label}': no part matches ${JSON.stringify(m.parts ?? m.between)}`
|
|
99
|
+
: `${m.label}: ${got.toFixed(3)} m on ${m.axis.toUpperCase()}, want ${m.value} m (±${tol * 100}%)`,
|
|
100
|
+
fix: 'rebuild the part from the spec number; never edit the spec to match the model',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
: [],
|
|
105
|
+
);
|
|
106
|
+
return got ?? Number.NaN;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Every moving part sweeps clear of everything else (the `clashes` gate), at every LOD. */
|
|
110
|
+
export function assertMotionClear(asset: Asset, o: LodOptions = {}): void {
|
|
111
|
+
failOn(
|
|
112
|
+
`${asset.name} motion`,
|
|
113
|
+
compileLods(asset, o).flatMap((a) => clashes(a).map((finding) => ({ finding, lod: a.lod }))),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Shared plumbing for the assertions: compile an asset at the LODs asked for, and turn QA
|
|
2
|
+
// findings into one thrown Ts23dError an agent can act on (code, every finding, the first fix).
|
|
3
|
+
|
|
4
|
+
import { type Asset, type CompiledAsset, compile, type Lod, Ts23dError } from '../core/index.ts';
|
|
5
|
+
import type { Finding } from '../qa/index.ts';
|
|
6
|
+
|
|
7
|
+
export interface LodOptions {
|
|
8
|
+
/** LODs to check. Default all three. */
|
|
9
|
+
readonly lods?: readonly Lod[];
|
|
10
|
+
readonly seed?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ALL_LODS: readonly Lod[] = [0, 1, 2];
|
|
14
|
+
|
|
15
|
+
export const compileLods = (asset: Asset, o: LodOptions = {}): CompiledAsset[] =>
|
|
16
|
+
(o.lods ?? ALL_LODS).map((lod) =>
|
|
17
|
+
compile(asset, { lod, ...(o.seed !== undefined ? { seed: o.seed } : {}) }),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const describeFinding = (f: Finding, lod?: Lod): string =>
|
|
21
|
+
`${lod !== undefined ? `LOD${lod} ` : ''}${f.gate}${f.part ? ` [${f.part}]` : ''}: ${f.message}`;
|
|
22
|
+
|
|
23
|
+
/** Throw T3D_QA_FAILED listing every finding; no-op when there are none. */
|
|
24
|
+
export function failOn(what: string, found: ReadonlyArray<{ finding: Finding; lod?: Lod }>): void {
|
|
25
|
+
if (found.length === 0) return;
|
|
26
|
+
const lines = found.map(({ finding, lod }) => ` - ${describeFinding(finding, lod)}`);
|
|
27
|
+
throw new Ts23dError('T3D_QA_FAILED', {
|
|
28
|
+
cause: `${what}: ${found.length} finding(s)\n${lines.join('\n')}`,
|
|
29
|
+
fix: found[0]?.finding.fix ?? 'read the findings above',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// A stable content hash for snapshot tests: 64-bit FNV-1a (two 32-bit lanes), pure JS, so the
|
|
2
|
+
// same bytes give the same hex string on Bun, Node and the browser — no runtime hash API needed.
|
|
3
|
+
|
|
4
|
+
export function hashBytes(bytes: Uint8Array): string {
|
|
5
|
+
let h1 = 0x811c9dc5;
|
|
6
|
+
let h2 = 0xcbf29ce4;
|
|
7
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
8
|
+
const b = bytes[i] ?? 0;
|
|
9
|
+
h1 = Math.imul(h1 ^ b, 0x01000193);
|
|
10
|
+
h2 = Math.imul(h2 ^ b ^ (h1 >>> 24), 0x01000193);
|
|
11
|
+
}
|
|
12
|
+
const hex = (n: number): string => (n >>> 0).toString(16).padStart(8, '0');
|
|
13
|
+
return `${hex(h2)}${hex(h1)}`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Public API of /testing: assertions for users' own recipe tests (`bun test`, vitest, node:test —
|
|
2
|
+
// framework-agnostic: each throws a Ts23dError with findings and a fix, or returns quietly).
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
assertBudget,
|
|
6
|
+
assertMeasure,
|
|
7
|
+
assertMotionClear,
|
|
8
|
+
assertParts,
|
|
9
|
+
assertQaPass,
|
|
10
|
+
type QaOptions,
|
|
11
|
+
} from './assert.ts';
|
|
12
|
+
export { ALL_LODS, compileLods, describeFinding, type LodOptions } from './compiled.ts';
|
|
13
|
+
export { hashBytes } from './hash.ts';
|
|
14
|
+
export { assertDeterministic, type SnapshotOptions, snapshotHash } from './snapshot.ts';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Determinism and snapshot helpers: the GLB an asset compiles to, hashed. Pin a hash in a test to
|
|
2
|
+
// catch any unintended geometry change; compile twice to prove the recipe has no ambient state.
|
|
3
|
+
|
|
4
|
+
import { type Asset, compile, type Lod, Ts23dError } from '../core/index.ts';
|
|
5
|
+
import { exportGlb, type GlbOptions } from '../export/index.ts';
|
|
6
|
+
import type { LodOptions } from './compiled.ts';
|
|
7
|
+
import { ALL_LODS } from './compiled.ts';
|
|
8
|
+
import { hashBytes } from './hash.ts';
|
|
9
|
+
|
|
10
|
+
export interface SnapshotOptions {
|
|
11
|
+
readonly lod?: Lod;
|
|
12
|
+
readonly seed?: number;
|
|
13
|
+
/** Exporter options; default the plain optimised GLB (no textures, no clips). */
|
|
14
|
+
readonly glb?: GlbOptions;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 16-hex-digit content hash of the asset's GLB at one LOD. */
|
|
18
|
+
export function snapshotHash(asset: Asset, o: SnapshotOptions = {}): string {
|
|
19
|
+
const a = compile(asset, {
|
|
20
|
+
lod: o.lod ?? 0,
|
|
21
|
+
...(o.seed !== undefined ? { seed: o.seed } : {}),
|
|
22
|
+
});
|
|
23
|
+
return hashBytes(exportGlb(a, o.glb ?? {}).glb);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Compile + export twice per LOD; the bytes must match. */
|
|
27
|
+
export function assertDeterministic(asset: Asset, o: LodOptions = {}): void {
|
|
28
|
+
for (const lod of o.lods ?? ALL_LODS) {
|
|
29
|
+
const once = snapshotHash(asset, { lod, ...(o.seed !== undefined ? { seed: o.seed } : {}) });
|
|
30
|
+
const twice = snapshotHash(asset, { lod, ...(o.seed !== undefined ? { seed: o.seed } : {}) });
|
|
31
|
+
if (once !== twice)
|
|
32
|
+
throw new Ts23dError('T3D_QA_FAILED', {
|
|
33
|
+
cause: `${asset.name} LOD${lod} is not byte-deterministic (${once} vs ${twice})`,
|
|
34
|
+
fix: 'remove ambient state from build(): read ctx.rng/ctx.seed only — no Math.random, Date, or module-level counters',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Minimal types for the dev-only Khronos validator (it ships none).
|
|
2
|
+
declare module 'gltf-validator' {
|
|
3
|
+
export function validateBytes(
|
|
4
|
+
data: Uint8Array,
|
|
5
|
+
options?: Record<string, unknown>,
|
|
6
|
+
): Promise<{
|
|
7
|
+
issues: {
|
|
8
|
+
numErrors: number;
|
|
9
|
+
numWarnings: number;
|
|
10
|
+
messages: { severity: number; code: string; pointer?: string; message: string }[];
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
}
|