@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,50 @@
|
|
|
1
|
+
// The one error type. Every failure carries a stable code, the cause, and the exact fix — the
|
|
2
|
+
// reader is usually an agent, and an error it can act on is worth ten it has to interpret.
|
|
3
|
+
|
|
4
|
+
export type ErrorCode =
|
|
5
|
+
| 'T3D_NOT_IMPLEMENTED'
|
|
6
|
+
| 'T3D_GRAMMAR_SYNTAX'
|
|
7
|
+
| 'T3D_GRAMMAR_OVERFLOW'
|
|
8
|
+
| 'T3D_PART_DUPLICATE'
|
|
9
|
+
| 'T3D_PART_UNKNOWN'
|
|
10
|
+
| 'T3D_RECIPE_INVALID'
|
|
11
|
+
| 'T3D_NO_ASSET_EXPORT'
|
|
12
|
+
| 'T3D_QA_FAILED'
|
|
13
|
+
| 'T3D_RENDER_OPTIONS'
|
|
14
|
+
| 'T3D_AMBIENT_RANDOM'
|
|
15
|
+
| 'T3D_RELEASE'
|
|
16
|
+
| 'T3D_BENCH'
|
|
17
|
+
| 'T3D_MATERIAL_INVALID'
|
|
18
|
+
| 'T3D_MATERIAL_UNKNOWN'
|
|
19
|
+
| 'T3D_MATERIAL_CONFLICT'
|
|
20
|
+
| 'T3D_SHEET_FULL'
|
|
21
|
+
| 'T3D_TEXTURE_MISSING';
|
|
22
|
+
|
|
23
|
+
export interface ErrorDetails {
|
|
24
|
+
readonly cause: string;
|
|
25
|
+
readonly fix: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class Ts23dError extends Error {
|
|
29
|
+
readonly code: ErrorCode;
|
|
30
|
+
readonly fix: string;
|
|
31
|
+
|
|
32
|
+
constructor(code: ErrorCode, details: ErrorDetails) {
|
|
33
|
+
super(`${code}: ${details.cause}`);
|
|
34
|
+
this.name = 'Ts23dError';
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.fix = details.fix;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toJSON(): { code: ErrorCode; message: string; fix: string } {
|
|
40
|
+
return { code: this.code, message: this.message, fix: this.fix };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Placeholder for a planned seam. Names the plan so the agent knows where the work is tracked. */
|
|
45
|
+
export function notImplemented(what: string, plan = 'docs/plans/roadmap.md'): never {
|
|
46
|
+
throw new Ts23dError('T3D_NOT_IMPLEMENTED', {
|
|
47
|
+
cause: `${what} is declared but not built yet`,
|
|
48
|
+
fix: `implement it — the spec and its slice are in ${plan}`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Parametric families: one build function, many assets. A variant is a name plus the parameters
|
|
2
|
+
// that differ — "T-34 1941 vs 1943 turret", "4-4-0 vs 2-6-0", "oak vs birch" — so a new model is a
|
|
3
|
+
// one-key change, not a copied file. Families extend families: `base.extend({...})` layers new
|
|
4
|
+
// defaults and can wrap the parent's build to add or replace detail.
|
|
5
|
+
|
|
6
|
+
import { type Asset, type AssetDefinition, type BuildContext, defineAsset } from './asset.ts';
|
|
7
|
+
|
|
8
|
+
type Shared = Omit<AssetDefinition, 'name' | 'build'>;
|
|
9
|
+
|
|
10
|
+
export interface FamilyDefinition<P extends object> extends Shared {
|
|
11
|
+
/** Defaults for every parameter; variants override a subset. */
|
|
12
|
+
readonly defaults: P;
|
|
13
|
+
build(ctx: BuildContext, params: Readonly<P>): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Variant<P> extends Partial<Shared> {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly params?: Partial<P>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Family<P extends object> {
|
|
22
|
+
readonly defaults: Readonly<P>;
|
|
23
|
+
/** Make one asset of this family. */
|
|
24
|
+
variant(v: Variant<P>): Asset;
|
|
25
|
+
/** Derive a family: new defaults (may add parameters) and optionally a build that wraps ours. */
|
|
26
|
+
extend<Q extends object>(
|
|
27
|
+
o: {
|
|
28
|
+
readonly defaults: Q;
|
|
29
|
+
readonly build?: (ctx: BuildContext, params: Readonly<P & Q>, base: () => void) => void;
|
|
30
|
+
} & Partial<Shared>,
|
|
31
|
+
): Family<P & Q>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function defineFamily<P extends object>(def: FamilyDefinition<P>): Family<P> {
|
|
35
|
+
const { defaults, build, ...shared } = def;
|
|
36
|
+
return {
|
|
37
|
+
defaults,
|
|
38
|
+
variant: ({ name, params, ...over }) => {
|
|
39
|
+
const p = { ...defaults, ...params } as P;
|
|
40
|
+
return defineAsset({ ...shared, ...over, name, build: (ctx) => build(ctx, p) });
|
|
41
|
+
},
|
|
42
|
+
extend: ({ defaults: more, build: wrap, ...over }) =>
|
|
43
|
+
defineFamily<P & typeof more>({
|
|
44
|
+
...shared,
|
|
45
|
+
...over,
|
|
46
|
+
defaults: { ...defaults, ...more },
|
|
47
|
+
build: (ctx, p) => (wrap ? wrap(ctx, p, () => build(ctx, p)) : build(ctx, p)),
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Public API of @ts23d/core: errors, RNG, vector math, the mesh IR and the asset DSL root.
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
type Asset,
|
|
5
|
+
type AssetDefinition,
|
|
6
|
+
BUDGETS,
|
|
7
|
+
type Budget,
|
|
8
|
+
type BuildContext,
|
|
9
|
+
type Check,
|
|
10
|
+
type CheckFinding,
|
|
11
|
+
type CompiledAsset,
|
|
12
|
+
compile,
|
|
13
|
+
defineAsset,
|
|
14
|
+
type Emitter,
|
|
15
|
+
type EmitterOptions,
|
|
16
|
+
isAsset,
|
|
17
|
+
type Lod,
|
|
18
|
+
type Measure,
|
|
19
|
+
type Motion,
|
|
20
|
+
type Part,
|
|
21
|
+
type PartOptions,
|
|
22
|
+
type RealDimensions,
|
|
23
|
+
type Slide,
|
|
24
|
+
type Socket,
|
|
25
|
+
type SocketOptions,
|
|
26
|
+
type Spin,
|
|
27
|
+
type Surface,
|
|
28
|
+
type Sway,
|
|
29
|
+
type UseOptions,
|
|
30
|
+
} from './asset.ts';
|
|
31
|
+
export { type ErrorCode, type ErrorDetails, notImplemented, Ts23dError } from './errors.ts';
|
|
32
|
+
export { defineFamily, type Family, type FamilyDefinition, type Variant } from './family.ts';
|
|
33
|
+
export {
|
|
34
|
+
type Bounds,
|
|
35
|
+
boundsOf,
|
|
36
|
+
type Mesh,
|
|
37
|
+
MeshBuilder,
|
|
38
|
+
merge,
|
|
39
|
+
translate,
|
|
40
|
+
triangleCount,
|
|
41
|
+
type Vertex,
|
|
42
|
+
} from './mesh.ts';
|
|
43
|
+
export { encodePng } from './png.ts';
|
|
44
|
+
export { createRng, type Rng } from './rng.ts';
|
|
45
|
+
export { type Affine, affine, applyAffine, frame, type Placement, place } from './transform.ts';
|
|
46
|
+
export {
|
|
47
|
+
add,
|
|
48
|
+
cross,
|
|
49
|
+
dot,
|
|
50
|
+
length,
|
|
51
|
+
normalize,
|
|
52
|
+
type Quat,
|
|
53
|
+
quatFromAxisAngle,
|
|
54
|
+
scale,
|
|
55
|
+
sub,
|
|
56
|
+
type Vec2,
|
|
57
|
+
type Vec3,
|
|
58
|
+
} from './vec.ts';
|
|
59
|
+
export {
|
|
60
|
+
rotateZ,
|
|
61
|
+
toWorld,
|
|
62
|
+
type WorldTransform,
|
|
63
|
+
worldMesh,
|
|
64
|
+
worldMeshes,
|
|
65
|
+
worldTransforms,
|
|
66
|
+
} from './world.ts';
|
package/src/core/mesh.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// The IR: a triangle mesh with per-vertex normal, UV (into the shared trim sheet) and colour.
|
|
2
|
+
// Every primitive emits one; passes and back ends consume one. Nothing else crosses stages.
|
|
3
|
+
|
|
4
|
+
import type { Vec2, Vec3 } from './vec.ts';
|
|
5
|
+
|
|
6
|
+
export interface Bounds {
|
|
7
|
+
readonly min: Vec3;
|
|
8
|
+
readonly max: Vec3;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Mesh {
|
|
12
|
+
readonly positions: Float32Array;
|
|
13
|
+
readonly normals: Float32Array;
|
|
14
|
+
readonly uvs: Float32Array;
|
|
15
|
+
readonly colors: Float32Array;
|
|
16
|
+
readonly indices: Uint32Array;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Vertex {
|
|
20
|
+
readonly p: Vec3;
|
|
21
|
+
readonly n: Vec3;
|
|
22
|
+
readonly uv: Vec2;
|
|
23
|
+
readonly color: Vec3;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class MeshBuilder {
|
|
27
|
+
private readonly p: number[] = [];
|
|
28
|
+
private readonly n: number[] = [];
|
|
29
|
+
private readonly uv: number[] = [];
|
|
30
|
+
private readonly c: number[] = [];
|
|
31
|
+
private readonly idx: number[] = [];
|
|
32
|
+
|
|
33
|
+
vertex(v: Vertex): number {
|
|
34
|
+
this.p.push(...v.p);
|
|
35
|
+
this.n.push(...v.n);
|
|
36
|
+
this.uv.push(...v.uv);
|
|
37
|
+
this.c.push(...v.color);
|
|
38
|
+
return this.p.length / 3 - 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
tri(a: number, b: number, c: number): this {
|
|
42
|
+
this.idx.push(a, b, c);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Counter-clockwise quad a-b-c-d as seen from the side its normal faces. */
|
|
47
|
+
quad(a: number, b: number, c: number, d: number): this {
|
|
48
|
+
return this.tri(a, b, c).tri(a, c, d);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
build(): Mesh {
|
|
52
|
+
return {
|
|
53
|
+
positions: new Float32Array(this.p),
|
|
54
|
+
normals: new Float32Array(this.n),
|
|
55
|
+
uvs: new Float32Array(this.uv),
|
|
56
|
+
colors: new Float32Array(this.c),
|
|
57
|
+
indices: new Uint32Array(this.idx),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const triangleCount = (m: Mesh): number => m.indices.length / 3;
|
|
63
|
+
|
|
64
|
+
export function boundsOf(m: Mesh): Bounds {
|
|
65
|
+
const min = [Infinity, Infinity, Infinity];
|
|
66
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
67
|
+
for (let i = 0; i < m.positions.length; i += 3) {
|
|
68
|
+
for (let k = 0; k < 3; k++) {
|
|
69
|
+
const v = m.positions[i + k] ?? 0;
|
|
70
|
+
if (v < (min[k] ?? 0)) min[k] = v;
|
|
71
|
+
if (v > (max[k] ?? 0)) max[k] = v;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
min: [min[0] ?? 0, min[1] ?? 0, min[2] ?? 0],
|
|
76
|
+
max: [max[0] ?? 0, max[1] ?? 0, max[2] ?? 0],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Bake a translation into a mesh (parts are authored in parent space, then offset). */
|
|
81
|
+
export function translate(m: Mesh, by: Vec3): Mesh {
|
|
82
|
+
const positions = new Float32Array(m.positions);
|
|
83
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
84
|
+
positions[i] = (positions[i] ?? 0) + by[0];
|
|
85
|
+
positions[i + 1] = (positions[i + 1] ?? 0) + by[1];
|
|
86
|
+
positions[i + 2] = (positions[i + 2] ?? 0) + by[2];
|
|
87
|
+
}
|
|
88
|
+
return { ...m, positions };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function merge(meshes: readonly Mesh[]): Mesh {
|
|
92
|
+
// Preallocated from summed lengths: no intermediate arrays, no spread (which overflows the
|
|
93
|
+
// call stack on very large inputs).
|
|
94
|
+
const sum = (f: (m: Mesh) => number): number => meshes.reduce((t, m) => t + f(m), 0);
|
|
95
|
+
const positions = new Float32Array(sum((m) => m.positions.length));
|
|
96
|
+
const normals = new Float32Array(sum((m) => m.normals.length));
|
|
97
|
+
const uvs = new Float32Array(sum((m) => m.uvs.length));
|
|
98
|
+
const colors = new Float32Array(sum((m) => m.colors.length));
|
|
99
|
+
const indices = new Uint32Array(sum((m) => m.indices.length));
|
|
100
|
+
let v = 0;
|
|
101
|
+
let uv = 0;
|
|
102
|
+
let ix = 0;
|
|
103
|
+
for (const m of meshes) {
|
|
104
|
+
positions.set(m.positions, v * 3);
|
|
105
|
+
normals.set(m.normals, v * 3);
|
|
106
|
+
colors.set(m.colors, v * 3);
|
|
107
|
+
uvs.set(m.uvs, uv);
|
|
108
|
+
for (let i = 0; i < m.indices.length; i++) indices[ix + i] = (m.indices[i] ?? 0) + v;
|
|
109
|
+
v += m.positions.length / 3;
|
|
110
|
+
uv += m.uvs.length;
|
|
111
|
+
ix += m.indices.length;
|
|
112
|
+
}
|
|
113
|
+
return { positions, normals, uvs, colors, indices };
|
|
114
|
+
}
|
package/src/core/part.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// The part vocabulary: LOD, motion, emitters, sockets, surfaces and the Part record itself —
|
|
2
|
+
// what a recipe can say about one named node.
|
|
3
|
+
|
|
4
|
+
import type { Mesh } from './mesh.ts';
|
|
5
|
+
import type { Vec3 } from './vec.ts';
|
|
6
|
+
|
|
7
|
+
/** 0 = hero detail, 1 = mid, 2 = far. A recipe reads it and drops detail; it never decimates. */
|
|
8
|
+
export type Lod = 0 | 1 | 2;
|
|
9
|
+
|
|
10
|
+
export interface Spin {
|
|
11
|
+
readonly kind: 'spin';
|
|
12
|
+
/** Rotation axis in the part's local space. */
|
|
13
|
+
readonly axis: Vec3;
|
|
14
|
+
/** Revolutions per second of the baked loop. */
|
|
15
|
+
readonly rps: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Slide {
|
|
19
|
+
readonly kind: 'slide';
|
|
20
|
+
/** Peak offset of a sinusoidal back-and-forth (piston rods, sliders). */
|
|
21
|
+
readonly offset: Vec3;
|
|
22
|
+
readonly hz: number;
|
|
23
|
+
/** Move only from rest to rest + offset and back (a sliding door), not to both sides. */
|
|
24
|
+
readonly oneWay?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Sway {
|
|
28
|
+
readonly kind: 'sway';
|
|
29
|
+
/** Rotation axis in the part's local space. */
|
|
30
|
+
readonly axis: Vec3;
|
|
31
|
+
/** Peak angle either side of rest: turret scan, gun elevation, wind in a crown, a door ajar. */
|
|
32
|
+
readonly degrees: number;
|
|
33
|
+
readonly hz: number;
|
|
34
|
+
/** Swing only from rest to +degrees and back (a signal arm dropping, a hatch opening). */
|
|
35
|
+
readonly oneWay?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type Motion = Spin | Slide | Sway;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A particle source the engine spawns at runtime: exhaust smoke, loco steam, track dust, muzzle
|
|
42
|
+
* flash, chimney smoke, fire. Exported as an empty named node carrying `extras.ts23d.emitter`,
|
|
43
|
+
* so it moves with its parent (a turret, a stack) and any engine can bind its own system to it.
|
|
44
|
+
*/
|
|
45
|
+
export interface Emitter {
|
|
46
|
+
readonly kind: 'smoke' | 'steam' | 'dust' | 'muzzle' | 'fire' | 'sparks';
|
|
47
|
+
/** Emission direction in the parent's space; default up (+Z). */
|
|
48
|
+
readonly dir?: Vec3;
|
|
49
|
+
/** Particles per second at full intensity. */
|
|
50
|
+
readonly rate: number;
|
|
51
|
+
/** Seconds a particle lives. */
|
|
52
|
+
readonly life: number;
|
|
53
|
+
/** Start and end size in metres. */
|
|
54
|
+
readonly size: readonly [number, number];
|
|
55
|
+
/** Linear sRGB start colour; engines fade to transparent. */
|
|
56
|
+
readonly color?: Vec3;
|
|
57
|
+
/** Initial speed m/s along `dir`, with cone spread in degrees. */
|
|
58
|
+
readonly speed?: number;
|
|
59
|
+
readonly spreadDeg?: number;
|
|
60
|
+
/** 'always' (chimney), 'moving' (track dust), 'event' (muzzle — fired by game code). */
|
|
61
|
+
readonly when?: 'always' | 'moving' | 'event';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A named attachment point game code binds to: a seat, a weapon mount, a camera, a sound source,
|
|
66
|
+
* a hardpoint, a spawn or interaction point. An empty node with `extras.ts23d.socket`.
|
|
67
|
+
*/
|
|
68
|
+
export interface Socket {
|
|
69
|
+
readonly kind: 'seat' | 'mount' | 'camera' | 'audio' | 'attach' | 'spawn' | 'interact' | 'light';
|
|
70
|
+
/** Facing direction in the parent's space; default +X (forward). */
|
|
71
|
+
readonly dir?: Vec3;
|
|
72
|
+
/** Free-form, engine-facing: `{ clip: 'engine_idle' }`, `{ caliber: 88 }`, `{ fov: 60 }`. */
|
|
73
|
+
readonly data?: Readonly<Record<string, string | number | boolean>>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface SocketOptions extends Socket {
|
|
77
|
+
readonly at?: Vec3;
|
|
78
|
+
readonly parent?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface EmitterOptions extends Emitter {
|
|
82
|
+
readonly at?: Vec3;
|
|
83
|
+
readonly parent?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* How a part's surface renders. Everything stays on ONE shared texture set; the GLB carries at
|
|
88
|
+
* most four materials (one per surface in use), so batching still holds:
|
|
89
|
+
* opaque — the default trim-sheet material
|
|
90
|
+
* cutout — alpha-tested, double-sided: leaves, fences, grilles, ash
|
|
91
|
+
* glass — clear: transmission + IOR 1.5 + thin volume (windows, bottles, lamp lenses)
|
|
92
|
+
* translucent — tinted, partly see-through: plastic bottles, liquids, paper, smoke-stained glass
|
|
93
|
+
* Tint comes from vertex colour, as everywhere else.
|
|
94
|
+
*/
|
|
95
|
+
export type Surface = 'opaque' | 'cutout' | 'glass' | 'translucent';
|
|
96
|
+
|
|
97
|
+
export interface PartOptions {
|
|
98
|
+
/** Pivot in the parent's space; the mesh is authored relative to it. */
|
|
99
|
+
readonly at?: Vec3;
|
|
100
|
+
readonly parent?: string;
|
|
101
|
+
/** Rotation about local Z, radians. A part that also moves goes inside a yawed group. */
|
|
102
|
+
readonly yaw?: number;
|
|
103
|
+
readonly motion?: Motion;
|
|
104
|
+
/** Parts this moving part may legitimately pass through (an axle in its bearing). QA `clashes`. */
|
|
105
|
+
readonly clearOf?: readonly string[];
|
|
106
|
+
readonly surface?: Surface;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface UseOptions {
|
|
110
|
+
/** Prefix for every instanced part: `house_3` → `house_3_roof`, `house_3_door_0`. */
|
|
111
|
+
readonly prefix: string;
|
|
112
|
+
readonly at?: Vec3;
|
|
113
|
+
readonly yaw?: number;
|
|
114
|
+
readonly parent?: string;
|
|
115
|
+
/** Per-instance seed, so ten copies of one recipe are ten variants. */
|
|
116
|
+
readonly seed?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface Part {
|
|
120
|
+
readonly name: string;
|
|
121
|
+
readonly mesh: Mesh | null;
|
|
122
|
+
readonly at: Vec3;
|
|
123
|
+
readonly parent: string | null;
|
|
124
|
+
readonly yaw: number;
|
|
125
|
+
readonly motion: Motion | null;
|
|
126
|
+
readonly clearOf?: readonly string[];
|
|
127
|
+
readonly emitter?: Emitter;
|
|
128
|
+
readonly socket?: Socket;
|
|
129
|
+
readonly surface?: Surface;
|
|
130
|
+
}
|
package/src/core/png.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// RGBA → PNG bytes: contact sheets, painted trim sheets, embedded GLB textures. Zero dependencies beyond node:zlib (present in Bun and Node).
|
|
2
|
+
|
|
3
|
+
import { deflateSync } from 'node:zlib';
|
|
4
|
+
|
|
5
|
+
const CRC = new Uint32Array(256).map((_, n) => {
|
|
6
|
+
let c = n;
|
|
7
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
8
|
+
return c >>> 0;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function crc32(bytes: Uint8Array): number {
|
|
12
|
+
let c = 0xffffffff;
|
|
13
|
+
for (const b of bytes) c = (CRC[(c ^ b) & 0xff] ?? 0) ^ (c >>> 8);
|
|
14
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function chunk(type: string, data: Uint8Array): Uint8Array {
|
|
18
|
+
const out = new Uint8Array(12 + data.length);
|
|
19
|
+
const dv = new DataView(out.buffer);
|
|
20
|
+
dv.setUint32(0, data.length);
|
|
21
|
+
out.set(new TextEncoder().encode(type), 4);
|
|
22
|
+
out.set(data, 8);
|
|
23
|
+
dv.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function encodePng(width: number, height: number, rgba: Uint8Array): Uint8Array {
|
|
28
|
+
const raw = new Uint8Array(height * (width * 4 + 1));
|
|
29
|
+
for (let y = 0; y < height; y++)
|
|
30
|
+
raw.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width * 4 + 1) + 1);
|
|
31
|
+
const ihdr = new Uint8Array(13);
|
|
32
|
+
const dv = new DataView(ihdr.buffer);
|
|
33
|
+
dv.setUint32(0, width);
|
|
34
|
+
dv.setUint32(4, height);
|
|
35
|
+
ihdr.set([8, 6, 0, 0, 0], 8);
|
|
36
|
+
const parts = [
|
|
37
|
+
new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]),
|
|
38
|
+
chunk('IHDR', ihdr),
|
|
39
|
+
chunk('IDAT', new Uint8Array(deflateSync(raw))),
|
|
40
|
+
chunk('IEND', new Uint8Array()),
|
|
41
|
+
];
|
|
42
|
+
const out = new Uint8Array(parts.reduce((s, p) => s + p.length, 0));
|
|
43
|
+
let o = 0;
|
|
44
|
+
for (const p of parts) {
|
|
45
|
+
out.set(p, o);
|
|
46
|
+
o += p.length;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
package/src/core/rng.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Seeded, forkable PRNG (mulberry32). The only entropy a recipe may read: same seed, same bytes.
|
|
2
|
+
|
|
3
|
+
export interface Rng {
|
|
4
|
+
/** Uniform in [0, 1). */
|
|
5
|
+
next(): number;
|
|
6
|
+
range(min: number, max: number): number;
|
|
7
|
+
int(min: number, maxInclusive: number): number;
|
|
8
|
+
pick<T>(items: readonly T[]): T;
|
|
9
|
+
/** An independent stream keyed by name, so adding a draw in one part never shifts another. */
|
|
10
|
+
fork(name: string): Rng;
|
|
11
|
+
/** A colour varied by ±amount per channel (relative): ten bales, ten slightly different straws. */
|
|
12
|
+
jitter(color: readonly [number, number, number], amount: number): [number, number, number];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function hash(seed: number, name: string): number {
|
|
16
|
+
let h = seed ^ 0x9e3779b9;
|
|
17
|
+
for (let i = 0; i < name.length; i++) h = Math.imul(h ^ name.charCodeAt(i), 0x85ebca6b);
|
|
18
|
+
return (h ^ (h >>> 13)) >>> 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createRng(seed: number): Rng {
|
|
22
|
+
let state = seed >>> 0;
|
|
23
|
+
const next = (): number => {
|
|
24
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
25
|
+
let t = state;
|
|
26
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
27
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
28
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
next,
|
|
32
|
+
range: (min, max) => min + (max - min) * next(),
|
|
33
|
+
int: (min, max) => min + Math.floor(next() * (max - min + 1)),
|
|
34
|
+
pick: <T>(items: readonly T[]): T => {
|
|
35
|
+
const item = items[Math.floor(next() * items.length)];
|
|
36
|
+
if (item === undefined) throw new RangeError('pick() from an empty list');
|
|
37
|
+
return item;
|
|
38
|
+
},
|
|
39
|
+
fork: (name) => createRng(hash(seed, name)),
|
|
40
|
+
jitter: (c, amount) => {
|
|
41
|
+
const k = 1 + (next() * 2 - 1) * amount;
|
|
42
|
+
return [
|
|
43
|
+
c[0] * k * (1 + (next() - 0.5) * amount * 0.4),
|
|
44
|
+
c[1] * k,
|
|
45
|
+
c[2] * k * (1 + (next() - 0.5) * amount * 0.4),
|
|
46
|
+
];
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Rigid transforms on meshes: place a sub-mesh anywhere at any orientation, then merge. This is
|
|
2
|
+
// how detail is authored — build a rivet/hatch/bracket once in local space, place it N times.
|
|
3
|
+
|
|
4
|
+
import type { Mesh } from './mesh.ts';
|
|
5
|
+
import type { Vec3 } from './vec.ts';
|
|
6
|
+
|
|
7
|
+
/** Row-major 3×3 rotation(·scale) + translation. */
|
|
8
|
+
export interface Affine {
|
|
9
|
+
readonly m: readonly [number, number, number, number, number, number, number, number, number];
|
|
10
|
+
readonly t: Vec3;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Placement {
|
|
14
|
+
readonly at?: Vec3;
|
|
15
|
+
/** Radians about Z (heading), then Y (pitch), then X (roll). */
|
|
16
|
+
readonly yaw?: number;
|
|
17
|
+
readonly pitch?: number;
|
|
18
|
+
readonly roll?: number;
|
|
19
|
+
readonly scale?: number | Vec3;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function affine(p: Placement): Affine {
|
|
23
|
+
const [cy, sy] = [Math.cos(p.yaw ?? 0), Math.sin(p.yaw ?? 0)];
|
|
24
|
+
const [cp, sp] = [Math.cos(p.pitch ?? 0), Math.sin(p.pitch ?? 0)];
|
|
25
|
+
const [cr, sr] = [Math.cos(p.roll ?? 0), Math.sin(p.roll ?? 0)];
|
|
26
|
+
const [kx, ky, kz]: Vec3 =
|
|
27
|
+
typeof p.scale === 'number' ? [p.scale, p.scale, p.scale] : (p.scale ?? [1, 1, 1]);
|
|
28
|
+
// R = Rz(yaw) · Ry(pitch) · Rx(roll), columns scaled by (kx, ky, kz).
|
|
29
|
+
return {
|
|
30
|
+
m: [
|
|
31
|
+
cy * cp * kx,
|
|
32
|
+
(cy * sp * sr - sy * cr) * ky,
|
|
33
|
+
(cy * sp * cr + sy * sr) * kz,
|
|
34
|
+
sy * cp * kx,
|
|
35
|
+
(sy * sp * sr + cy * cr) * ky,
|
|
36
|
+
(sy * sp * cr - cy * sr) * kz,
|
|
37
|
+
-sp * kx,
|
|
38
|
+
cp * sr * ky,
|
|
39
|
+
cp * cr * kz,
|
|
40
|
+
],
|
|
41
|
+
t: p.at ?? [0, 0, 0],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A basis from an explicit frame: local X→u, Y→v, Z→w (all world vectors). */
|
|
46
|
+
export const frame = (u: Vec3, v: Vec3, w: Vec3, at: Vec3 = [0, 0, 0]): Affine => ({
|
|
47
|
+
m: [u[0], v[0], w[0], u[1], v[1], w[1], u[2], v[2], w[2]],
|
|
48
|
+
t: at,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export function applyAffine(mesh: Mesh, a: Affine): Mesh {
|
|
52
|
+
const { m, t } = a;
|
|
53
|
+
const positions = new Float32Array(mesh.positions.length);
|
|
54
|
+
const normals = new Float32Array(mesh.normals.length);
|
|
55
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
56
|
+
const x = mesh.positions[i] ?? 0;
|
|
57
|
+
const y = mesh.positions[i + 1] ?? 0;
|
|
58
|
+
const z = mesh.positions[i + 2] ?? 0;
|
|
59
|
+
positions[i] = m[0] * x + m[1] * y + m[2] * z + t[0];
|
|
60
|
+
positions[i + 1] = m[3] * x + m[4] * y + m[5] * z + t[1];
|
|
61
|
+
positions[i + 2] = m[6] * x + m[7] * y + m[8] * z + t[2];
|
|
62
|
+
const nx = mesh.normals[i] ?? 0;
|
|
63
|
+
const ny = mesh.normals[i + 1] ?? 0;
|
|
64
|
+
const nz = mesh.normals[i + 2] ?? 0;
|
|
65
|
+
// Rigid + uniform scale: rotating the normal and renormalising is exact.
|
|
66
|
+
const rx = m[0] * nx + m[1] * ny + m[2] * nz;
|
|
67
|
+
const ry = m[3] * nx + m[4] * ny + m[5] * nz;
|
|
68
|
+
const rz = m[6] * nx + m[7] * ny + m[8] * nz;
|
|
69
|
+
const l = Math.hypot(rx, ry, rz) || 1;
|
|
70
|
+
normals[i] = rx / l;
|
|
71
|
+
normals[i + 1] = ry / l;
|
|
72
|
+
normals[i + 2] = rz / l;
|
|
73
|
+
}
|
|
74
|
+
// A mirror (det < 0) turns counter-clockwise triangles clockwise: flip each one back, or
|
|
75
|
+
// engines cull the outside and draw the inside.
|
|
76
|
+
const det =
|
|
77
|
+
m[0] * (m[4] * m[8] - m[5] * m[7]) -
|
|
78
|
+
m[1] * (m[3] * m[8] - m[5] * m[6]) +
|
|
79
|
+
m[2] * (m[3] * m[7] - m[4] * m[6]);
|
|
80
|
+
if (det >= 0) return { ...mesh, positions, normals };
|
|
81
|
+
const indices = mesh.indices.slice();
|
|
82
|
+
for (let t = 0; t + 2 < indices.length; t += 3) {
|
|
83
|
+
const b = indices[t + 1] ?? 0;
|
|
84
|
+
indices[t + 1] = indices[t + 2] ?? 0;
|
|
85
|
+
indices[t + 2] = b;
|
|
86
|
+
}
|
|
87
|
+
return { ...mesh, positions, normals, indices };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const place = (mesh: Mesh, p: Placement): Mesh => applyAffine(mesh, affine(p));
|
package/src/core/vec.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Minimal vector math. Units are metres, Z is up, +X is an asset's "front" (docs/idea/03-ir.md).
|
|
2
|
+
|
|
3
|
+
export type Vec2 = readonly [number, number];
|
|
4
|
+
export type Vec3 = readonly [number, number, number];
|
|
5
|
+
export type Quat = readonly [number, number, number, number];
|
|
6
|
+
|
|
7
|
+
export const add = (a: Vec3, b: Vec3): Vec3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
8
|
+
export const sub = (a: Vec3, b: Vec3): Vec3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
9
|
+
export const scale = (a: Vec3, s: number): Vec3 => [a[0] * s, a[1] * s, a[2] * s];
|
|
10
|
+
export const dot = (a: Vec3, b: Vec3): number => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
11
|
+
export const cross = (a: Vec3, b: Vec3): Vec3 => [
|
|
12
|
+
a[1] * b[2] - a[2] * b[1],
|
|
13
|
+
a[2] * b[0] - a[0] * b[2],
|
|
14
|
+
a[0] * b[1] - a[1] * b[0],
|
|
15
|
+
];
|
|
16
|
+
export const length = (a: Vec3): number => Math.hypot(a[0], a[1], a[2]);
|
|
17
|
+
export const normalize = (a: Vec3): Vec3 => {
|
|
18
|
+
const l = length(a);
|
|
19
|
+
return l === 0 ? [0, 0, 0] : scale(a, 1 / l);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function quatFromAxisAngle(axis: Vec3, radians: number): Quat {
|
|
23
|
+
const [x, y, z] = normalize(axis);
|
|
24
|
+
const s = Math.sin(radians / 2);
|
|
25
|
+
return [x * s, y * s, z * s, Math.cos(radians / 2)];
|
|
26
|
+
}
|