@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,86 @@
|
|
|
1
|
+
// Image-backed strips: a CC0 PBR texture set (Poly Haven, ambientCG or a local file) declared as
|
|
2
|
+
// data — URL + sha256 per map, licence, author page, real-world size. Compile and paint stay
|
|
3
|
+
// offline: bytes come only from a content-addressed cache the caller provides (`provideTextures`,
|
|
4
|
+
// filled by `bun run textures`); a missing map throws T3D_TEXTURE_MISSING with that command.
|
|
5
|
+
|
|
6
|
+
import { Ts23dError } from '../core/index.ts';
|
|
7
|
+
|
|
8
|
+
export interface TextureFile {
|
|
9
|
+
/** Download URL (https) or, for `source: 'file'`, a path relative to the working directory. */
|
|
10
|
+
readonly url: string;
|
|
11
|
+
/** Lower-case hex sha256 of the file's bytes: the cache key and the integrity check. */
|
|
12
|
+
readonly sha256: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TextureSource {
|
|
16
|
+
readonly source: 'polyhaven' | 'ambientcg' | 'file';
|
|
17
|
+
/** The set's id at the source, e.g. 'wood_planks_grey'. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** Only public-domain sets are accepted, so a generated asset never inherits a licence duty. */
|
|
20
|
+
readonly license: 'CC0-1.0';
|
|
21
|
+
/** Human page crediting the set (goes into the GLB's XMP credits). */
|
|
22
|
+
readonly page: string;
|
|
23
|
+
readonly author?: string;
|
|
24
|
+
/** Real-world size the image covers, metres (U × V). Poly Haven lists it as `dimensions`. */
|
|
25
|
+
readonly metres: readonly [number, number];
|
|
26
|
+
readonly maps: {
|
|
27
|
+
/** sRGB base colour. */
|
|
28
|
+
readonly albedo: TextureFile;
|
|
29
|
+
/** Tangent-space normal, OpenGL convention (+Y up in the image; Poly Haven `nor_gl`). */
|
|
30
|
+
readonly normal?: TextureFile;
|
|
31
|
+
/** Packed AO / roughness / metalness (Poly Haven `arm`, glTF ORM order). */
|
|
32
|
+
readonly arm?: TextureFile;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** PNG bytes by sha256. */
|
|
37
|
+
export type TextureCache = ReadonlyMap<string, Uint8Array>;
|
|
38
|
+
|
|
39
|
+
const SHA = /^[0-9a-f]{64}$/;
|
|
40
|
+
const provided = new Map<string, Uint8Array>();
|
|
41
|
+
|
|
42
|
+
/** Hand verified texture bytes to the painter (e.g. from `loadTextureCache()` in scripts). */
|
|
43
|
+
export function provideTextures(cache: TextureCache): void {
|
|
44
|
+
for (const [k, v] of cache) provided.set(k, v);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function textureBytes(file: TextureFile, where: string): Uint8Array {
|
|
48
|
+
const hit = provided.get(file.sha256);
|
|
49
|
+
if (hit) return hit;
|
|
50
|
+
throw new Ts23dError('T3D_TEXTURE_MISSING', {
|
|
51
|
+
cause: `${where}: texture ${file.url} (sha256 ${file.sha256.slice(0, 12)}…) is not in the cache`,
|
|
52
|
+
fix: 'run `bun run textures <module defining the material>` once (downloads + verifies into node_modules/.cache/ts23d/textures), then provideTextures(await loadTextureCache()) before painting',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function validateTexture(id: string, t: TextureSource): void {
|
|
57
|
+
const fail = (cause: string, fix: string): never => {
|
|
58
|
+
throw new Ts23dError('T3D_MATERIAL_INVALID', { cause: `material '${id}': ${cause}`, fix });
|
|
59
|
+
};
|
|
60
|
+
if (!['polyhaven', 'ambientcg', 'file'].includes(t.source))
|
|
61
|
+
fail(`texture.source '${t.source}'`, "use 'polyhaven', 'ambientcg' or 'file'");
|
|
62
|
+
if (t.license !== 'CC0-1.0')
|
|
63
|
+
fail(
|
|
64
|
+
`texture.license '${t.license}'`,
|
|
65
|
+
'only CC0-1.0 sets are accepted (Poly Haven and ambientCG are CC0)',
|
|
66
|
+
);
|
|
67
|
+
if (!(t.metres.length === 2 && t.metres.every((m) => Number.isFinite(m) && m > 0)))
|
|
68
|
+
fail(`texture.metres [${t.metres}]`, 'give the real size the image covers, e.g. [1.5, 1.5]');
|
|
69
|
+
for (const [map, f] of Object.entries(t.maps)) {
|
|
70
|
+
if (!f) continue;
|
|
71
|
+
if (!SHA.test(f.sha256))
|
|
72
|
+
fail(
|
|
73
|
+
`texture.maps.${map}.sha256 '${f.sha256}'`,
|
|
74
|
+
'lower-case hex sha256 of the file (sha256sum)',
|
|
75
|
+
);
|
|
76
|
+
if (!/\.png$/i.test(f.url))
|
|
77
|
+
fail(
|
|
78
|
+
`texture.maps.${map}.url '${f.url}'`,
|
|
79
|
+
'use the PNG download (8- or 16-bit); JPEG is not decoded',
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** One credit line for the GLB's XMP and the docs. */
|
|
85
|
+
export const creditOf = (t: TextureSource): string =>
|
|
86
|
+
`${t.id}${t.author ? ` by ${t.author}` : ''} (${t.source}, ${t.license}) ${t.page}`;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// The shared trim sheet: ONE 2048² atlas (albedo + normal + ORM) every asset maps into, so every
|
|
2
|
+
// asset is one material and one draw call. Strips tile along U; cells are fixed decals.
|
|
3
|
+
// Layout is data here; `paintTrimSheet` (generator.ts) paints it. V grows with world up on
|
|
4
|
+
// box/planar faces. V 0–0.875 holds 28 strip rows (64 px each at 2048²): the 8 original strips,
|
|
5
|
+
// 16 physically distinct materials (woods, metals, rubber, canvas, leather, plastic) and 4 slots
|
|
6
|
+
// for project materials (`defineMaterial`). V 0.875–1 holds 128 px cells: decals, then foliage.
|
|
7
|
+
|
|
8
|
+
import { Ts23dError } from '../core/index.ts';
|
|
9
|
+
import { customIndex } from './slots.ts';
|
|
10
|
+
|
|
11
|
+
/** The original eight strips: greyscale, tinted by vertex colour. */
|
|
12
|
+
export type ClassicStripId =
|
|
13
|
+
| 'plain'
|
|
14
|
+
| 'brick'
|
|
15
|
+
| 'stone'
|
|
16
|
+
| 'slate'
|
|
17
|
+
| 'iron'
|
|
18
|
+
| 'wood'
|
|
19
|
+
| 'glass'
|
|
20
|
+
| 'concrete';
|
|
21
|
+
/** Material strips with measured colour: use a near-white vertex colour (`NATURAL`) for truth. */
|
|
22
|
+
export type MaterialStripId =
|
|
23
|
+
| 'oak'
|
|
24
|
+
| 'pine'
|
|
25
|
+
| 'wood_weathered'
|
|
26
|
+
| 'wood_painted'
|
|
27
|
+
| 'iron_rust'
|
|
28
|
+
| 'iron_cast'
|
|
29
|
+
| 'steel_painted'
|
|
30
|
+
| 'steel_galvanised'
|
|
31
|
+
| 'brass'
|
|
32
|
+
| 'copper'
|
|
33
|
+
| 'aluminium'
|
|
34
|
+
| 'chrome'
|
|
35
|
+
| 'rubber'
|
|
36
|
+
| 'canvas'
|
|
37
|
+
| 'leather'
|
|
38
|
+
| 'plastic';
|
|
39
|
+
export type BuiltinStripId = ClassicStripId | MaterialStripId;
|
|
40
|
+
/** A project material's id, as returned by `defineMaterial` (a branded string). */
|
|
41
|
+
export type CustomStripId = string & { readonly __ts23dMaterial: true };
|
|
42
|
+
export type StripId = BuiltinStripId | CustomStripId;
|
|
43
|
+
export type DecalCellId = 'window_sash' | 'door_panel' | 'clock' | 'smokebox' | 'sign' | 'porthole';
|
|
44
|
+
/** Cutout leaf cards: the anchor (twig base) is the cell's bottom-centre, growth toward +V. */
|
|
45
|
+
export type FoliageCellId =
|
|
46
|
+
| 'foliage_broadleaf'
|
|
47
|
+
| 'foliage_maple'
|
|
48
|
+
| 'foliage_maple_autumn'
|
|
49
|
+
| 'foliage_birch'
|
|
50
|
+
| 'foliage_acacia'
|
|
51
|
+
| 'foliage_palm'
|
|
52
|
+
| 'foliage_spruce'
|
|
53
|
+
| 'foliage_spruce_snow'
|
|
54
|
+
| 'foliage_willow'
|
|
55
|
+
| 'foliage_reeds'
|
|
56
|
+
| 'foliage_needles';
|
|
57
|
+
export type CellId = DecalCellId | FoliageCellId;
|
|
58
|
+
|
|
59
|
+
export interface UvRect {
|
|
60
|
+
readonly u0: number;
|
|
61
|
+
readonly v0: number;
|
|
62
|
+
readonly u1: number;
|
|
63
|
+
readonly v1: number;
|
|
64
|
+
/** Metres of world surface one full rect covers, so texel density is equal across assets. */
|
|
65
|
+
readonly metres: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Strip rows in the atlas: built-ins first, then `CUSTOM_STRIP_SLOTS` project materials. */
|
|
69
|
+
export const STRIP_ROWS = 28;
|
|
70
|
+
const STRIP_H = 1 / 32;
|
|
71
|
+
const classic: readonly ClassicStripId[] = [
|
|
72
|
+
'plain',
|
|
73
|
+
'brick',
|
|
74
|
+
'stone',
|
|
75
|
+
'slate',
|
|
76
|
+
'iron',
|
|
77
|
+
'wood',
|
|
78
|
+
'glass',
|
|
79
|
+
'concrete',
|
|
80
|
+
];
|
|
81
|
+
const material: readonly MaterialStripId[] = [
|
|
82
|
+
'oak',
|
|
83
|
+
'pine',
|
|
84
|
+
'wood_weathered',
|
|
85
|
+
'wood_painted',
|
|
86
|
+
'iron_rust',
|
|
87
|
+
'iron_cast',
|
|
88
|
+
'steel_painted',
|
|
89
|
+
'steel_galvanised',
|
|
90
|
+
'brass',
|
|
91
|
+
'copper',
|
|
92
|
+
'aluminium',
|
|
93
|
+
'chrome',
|
|
94
|
+
'rubber',
|
|
95
|
+
'canvas',
|
|
96
|
+
'leather',
|
|
97
|
+
'plastic',
|
|
98
|
+
];
|
|
99
|
+
const strips: readonly BuiltinStripId[] = [...classic, ...material];
|
|
100
|
+
export const CUSTOM_STRIP_SLOTS = STRIP_ROWS - strips.length;
|
|
101
|
+
const decals: readonly DecalCellId[] = [
|
|
102
|
+
'window_sash',
|
|
103
|
+
'door_panel',
|
|
104
|
+
'clock',
|
|
105
|
+
'smokebox',
|
|
106
|
+
'sign',
|
|
107
|
+
'porthole',
|
|
108
|
+
];
|
|
109
|
+
const foliage: readonly FoliageCellId[] = [
|
|
110
|
+
'foliage_broadleaf',
|
|
111
|
+
'foliage_maple',
|
|
112
|
+
'foliage_maple_autumn',
|
|
113
|
+
'foliage_birch',
|
|
114
|
+
'foliage_acacia',
|
|
115
|
+
'foliage_palm',
|
|
116
|
+
'foliage_spruce',
|
|
117
|
+
'foliage_spruce_snow',
|
|
118
|
+
'foliage_willow',
|
|
119
|
+
'foliage_reeds',
|
|
120
|
+
'foliage_needles',
|
|
121
|
+
];
|
|
122
|
+
const cells: readonly CellId[] = [...decals, ...foliage];
|
|
123
|
+
/** Cells are 1/16 of the sheet, 16 per row from V 0.875: decals at 0–5, foliage from slot 8. */
|
|
124
|
+
const CELL = 1 / 16;
|
|
125
|
+
const CELL_V0 = STRIP_ROWS * STRIP_H;
|
|
126
|
+
const FOLIAGE_SLOT = 8;
|
|
127
|
+
const slotOf = (id: CellId): number => {
|
|
128
|
+
const d = decals.indexOf(id as DecalCellId);
|
|
129
|
+
return d >= 0 ? d : FOLIAGE_SLOT + foliage.indexOf(id as FoliageCellId);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const TRIM_SHEET_SIZE = 2048;
|
|
133
|
+
|
|
134
|
+
/** Atlas row of a strip id, or −1 when it is neither built in nor defined. */
|
|
135
|
+
export function stripRow(id: string): number {
|
|
136
|
+
const b = strips.indexOf(id as BuiltinStripId);
|
|
137
|
+
if (b >= 0) return b;
|
|
138
|
+
const c = customIndex(id);
|
|
139
|
+
return c >= 0 ? strips.length + c : -1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function stripRect(id: StripId): UvRect {
|
|
143
|
+
const i = stripRow(id);
|
|
144
|
+
if (i < 0)
|
|
145
|
+
throw new Ts23dError('T3D_MATERIAL_UNKNOWN', {
|
|
146
|
+
cause: `strip '${id}' is neither built in nor defined`,
|
|
147
|
+
fix: `use a built-in strip (${strips.join(', ')}) or register it first with defineMaterial({ id: '${id}', ... })`,
|
|
148
|
+
});
|
|
149
|
+
return { u0: 0, v0: i * STRIP_H, u1: 1, v1: (i + 1) * STRIP_H, metres: 2 };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function cellRect(id: CellId): UvRect {
|
|
153
|
+
const i = slotOf(id);
|
|
154
|
+
const u0 = (i % 16) * CELL;
|
|
155
|
+
const v0 = CELL_V0 + Math.floor(i / 16) * CELL;
|
|
156
|
+
return { u0, v0, u1: u0 + CELL, v1: v0 + CELL, metres: 1 };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Every built-in strip, classic first. */
|
|
160
|
+
export const STRIPS = strips;
|
|
161
|
+
export const CLASSIC_STRIPS = classic;
|
|
162
|
+
export const MATERIAL_STRIPS = material;
|
|
163
|
+
export const CELLS = cells;
|
|
164
|
+
export const DECAL_CELLS = decals;
|
|
165
|
+
export const FOLIAGE_CELLS = foliage;
|
|
166
|
+
export const isFoliage = (id: CellId): id is FoliageCellId => id.startsWith('foliage_');
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Optional seam for organic shapes the recipe approach handles poorly: people, animals, sculpted
|
|
2
|
+
// rocks. Runs an open-weight image/text-to-3D model on the USER's GPU (TRELLIS, Hunyuan3D, SF3D…)
|
|
3
|
+
// behind a local HTTP endpoint, then wraps the result as an ordinary part. Never a core dependency.
|
|
4
|
+
//
|
|
5
|
+
// Determinism: a generated mesh is cached by (model, prompt/image hash, seed) under
|
|
6
|
+
// .ts23d/neural/ and committed or published like any asset, so CI rebuilds without a GPU.
|
|
7
|
+
|
|
8
|
+
import { type Mesh, notImplemented, type Vec3 } from '../core/index.ts';
|
|
9
|
+
|
|
10
|
+
export interface GenerateRequest {
|
|
11
|
+
readonly prompt?: string;
|
|
12
|
+
/** PNG/JPEG bytes of a reference or concept image. */
|
|
13
|
+
readonly image?: Uint8Array;
|
|
14
|
+
readonly seed: number;
|
|
15
|
+
/** Fit the result into this box (metres), Z up, base on the ground. */
|
|
16
|
+
readonly fit: Vec3;
|
|
17
|
+
readonly targetTriangles: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface MeshGenerator {
|
|
21
|
+
/** e.g. 'trellis-2', 'hunyuan3d-2.1', 'sf3d'. Part of the cache key. */
|
|
22
|
+
readonly model: string;
|
|
23
|
+
generate(request: GenerateRequest): Promise<Mesh>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Talks to a local server (e.g. a ComfyUI / gradio wrapper) at `endpoint`. Planned: M5. */
|
|
27
|
+
export function localGenerator(_o: { model: string; endpoint: string }): MeshGenerator {
|
|
28
|
+
return notImplemented(
|
|
29
|
+
'localGenerator (local GPU model adapter)',
|
|
30
|
+
'docs/idea/08-neural-adapters.md',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Import an existing GLB (a CC0 base mesh, a sculpt, a generator's output) as a part. Planned: M5. */
|
|
35
|
+
export function importGlb(_bytes: Uint8Array, _o: { fit: Vec3; recolor?: Vec3 }): Mesh {
|
|
36
|
+
return notImplemented('importGlb', 'docs/idea/08-neural-adapters.md');
|
|
37
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Image sources for the material generator and the agent loop: reference/concept images, decals,
|
|
2
|
+
// texture source photos. kubeez.com is the recommended hosted source; any function returning PNG
|
|
3
|
+
// bytes fits. Results are cached by (source, prompt, seed) like generated meshes. Planned: M5.
|
|
4
|
+
|
|
5
|
+
import { notImplemented } from '../core/index.ts';
|
|
6
|
+
|
|
7
|
+
export interface ImageRequest {
|
|
8
|
+
readonly prompt: string;
|
|
9
|
+
readonly seed: number;
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly height: number;
|
|
12
|
+
/** e.g. 'decal', 'tileable', 'reference', 'concept'. */
|
|
13
|
+
readonly purpose: 'decal' | 'tileable' | 'reference' | 'concept';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ImageSource {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
generate(request: ImageRequest): Promise<Uint8Array>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** kubeez.com image generation. Reads the key from the caller, never from ambient env. */
|
|
22
|
+
export function kubeezImages(_o: { apiKey: string; model?: string }): ImageSource {
|
|
23
|
+
return notImplemented(
|
|
24
|
+
'kubeezImages (kubeez.com image source)',
|
|
25
|
+
'docs/idea/08-neural-adapters.md',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Public API of /neural: optional AI adapters — local-GPU mesh generators for organic shapes,
|
|
2
|
+
// mesh import, and image sources (kubeez.com) for references and materials. Never required.
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
type GenerateRequest,
|
|
6
|
+
importGlb,
|
|
7
|
+
localGenerator,
|
|
8
|
+
type MeshGenerator,
|
|
9
|
+
} from './generator.ts';
|
|
10
|
+
export { type ImageRequest, type ImageSource, kubeezImages } from './images.ts';
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Ambient occlusion baked into vertex colour: the cheapest big look upgrade for a vertex-coloured
|
|
2
|
+
// asset — creases, wheel wells and the gap under a turret darken, as a lit render would. Each
|
|
3
|
+
// vertex fires a fixed cosine-weighted hemisphere of rays (Fibonacci spiral, no randomness) at the
|
|
4
|
+
// asset's own triangles (and optionally the ground plane); the hit fraction darkens its colour.
|
|
5
|
+
|
|
6
|
+
import { type CompiledAsset, type Mesh, merge, type Vec3, worldMeshes } from '../core/index.ts';
|
|
7
|
+
import { RayGrid } from './ray-grid.ts';
|
|
8
|
+
|
|
9
|
+
export interface AoOptions {
|
|
10
|
+
/** Rays per vertex. Default 24. */
|
|
11
|
+
readonly samples?: number;
|
|
12
|
+
/** Occluders farther than this (metres) don't count. Default 1. */
|
|
13
|
+
readonly radius?: number;
|
|
14
|
+
/** 0 = no change, 1 = fully occluded vertices go black. Default 0.6. */
|
|
15
|
+
readonly strength?: number;
|
|
16
|
+
/** Treat z = 0 as a ground plane (contact shadow under the asset). Default true. */
|
|
17
|
+
readonly ground?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const GOLDEN = Math.PI * (3 - Math.sqrt(5));
|
|
21
|
+
|
|
22
|
+
/** Fixed cosine-weighted directions around +Z. */
|
|
23
|
+
function hemisphere(n: number): Vec3[] {
|
|
24
|
+
return Array.from({ length: n }, (_, i) => {
|
|
25
|
+
const r = Math.sqrt((i + 0.5) / n);
|
|
26
|
+
const phi = i * GOLDEN;
|
|
27
|
+
return [r * Math.cos(phi), r * Math.sin(phi), Math.sqrt(Math.max(0, 1 - r * r))] as Vec3;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Orthonormal frame with `n` as its Z (Frisvad / Duff et al. branchless basis). */
|
|
32
|
+
function frame(n: Vec3): [Vec3, Vec3] {
|
|
33
|
+
const s = n[2] >= 0 ? 1 : -1;
|
|
34
|
+
const a = -1 / (s + n[2]);
|
|
35
|
+
const b = n[0] * n[1] * a;
|
|
36
|
+
return [
|
|
37
|
+
[1 + s * n[0] * n[0] * a, s * b, -s * n[0]],
|
|
38
|
+
[b, s + n[1] * n[1] * a, -n[1]],
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Vertices sharing a position (0.1 mm) and normal (0.001) share one result: welded seams and
|
|
44
|
+
* duplicated soup vertices cost one hemisphere, not many. Open addressing on the six integers.
|
|
45
|
+
*/
|
|
46
|
+
class Memo {
|
|
47
|
+
private readonly keys: Float64Array;
|
|
48
|
+
private readonly vals: Float64Array;
|
|
49
|
+
private readonly used: Uint8Array;
|
|
50
|
+
private readonly mask: number;
|
|
51
|
+
|
|
52
|
+
constructor(n: number) {
|
|
53
|
+
let cap = 16;
|
|
54
|
+
while (cap < n * 2) cap *= 2;
|
|
55
|
+
this.mask = cap - 1;
|
|
56
|
+
this.keys = new Float64Array(cap * 6);
|
|
57
|
+
this.vals = new Float64Array(cap);
|
|
58
|
+
this.used = new Uint8Array(cap);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Slot for the key; `found` tells whether it already holds a value. */
|
|
62
|
+
slot(k: Float64Array): { at: number; found: boolean } {
|
|
63
|
+
let h = 2166136261;
|
|
64
|
+
for (let i = 0; i < 6; i++) h = Math.imul(h ^ ((k[i] ?? 0) | 0), 16777619);
|
|
65
|
+
let at = (h >>> 0) & this.mask;
|
|
66
|
+
for (;;) {
|
|
67
|
+
if (!this.used[at]) return { at, found: false };
|
|
68
|
+
let same = true;
|
|
69
|
+
for (let i = 0; i < 6 && same; i++) same = this.keys[at * 6 + i] === k[i];
|
|
70
|
+
if (same) return { at, found: true };
|
|
71
|
+
at = (at + 1) & this.mask;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get(at: number): number {
|
|
76
|
+
return this.vals[at] ?? 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
set(at: number, k: Float64Array, v: number): void {
|
|
80
|
+
this.used[at] = 1;
|
|
81
|
+
this.keys.set(k, at * 6);
|
|
82
|
+
this.vals[at] = v;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Per-vertex visibility (1 = open sky, 0 = buried) of `mesh` against `grid`. */
|
|
87
|
+
function visibility(mesh: Mesh, grid: RayGrid, o: AoOptions): Float32Array {
|
|
88
|
+
const hemi = hemisphere(o.samples ?? 24);
|
|
89
|
+
const D = Float64Array.from(hemi.flat());
|
|
90
|
+
const count = hemi.length;
|
|
91
|
+
const radius = o.radius ?? 1;
|
|
92
|
+
const ground = o.ground ?? true;
|
|
93
|
+
const P = mesh.positions;
|
|
94
|
+
const N = mesh.normals;
|
|
95
|
+
const out = new Float32Array(P.length / 3);
|
|
96
|
+
const memo = new Memo(out.length);
|
|
97
|
+
const key = new Float64Array(6);
|
|
98
|
+
// Lift off the surface so a ray never hits the face it starts on.
|
|
99
|
+
const bias = 2e-3;
|
|
100
|
+
for (let v = 0; v < out.length; v++) {
|
|
101
|
+
const px = P[v * 3] ?? 0;
|
|
102
|
+
const py = P[v * 3 + 1] ?? 0;
|
|
103
|
+
const pz = P[v * 3 + 2] ?? 0;
|
|
104
|
+
const rx = N[v * 3] ?? 0;
|
|
105
|
+
const ry = N[v * 3 + 1] ?? 0;
|
|
106
|
+
const rz = N[v * 3 + 2] ?? 1;
|
|
107
|
+
const len = Math.hypot(rx, ry, rz) || 1;
|
|
108
|
+
const n: Vec3 = [rx / len, ry / len, rz / len];
|
|
109
|
+
key[0] = Math.round(px * 1e4);
|
|
110
|
+
key[1] = Math.round(py * 1e4);
|
|
111
|
+
key[2] = Math.round(pz * 1e4);
|
|
112
|
+
key[3] = Math.round(n[0] * 1e3);
|
|
113
|
+
key[4] = Math.round(n[1] * 1e3);
|
|
114
|
+
key[5] = Math.round(n[2] * 1e3);
|
|
115
|
+
const s = memo.slot(key);
|
|
116
|
+
if (s.found) {
|
|
117
|
+
out[v] = memo.get(s.at);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const [t, b] = frame(n);
|
|
121
|
+
const ox = px + n[0] * bias;
|
|
122
|
+
const oy = py + n[1] * bias;
|
|
123
|
+
const oz = pz + n[2] * bias;
|
|
124
|
+
let blocked = 0;
|
|
125
|
+
for (let i = 0; i < count; i++) {
|
|
126
|
+
const d0 = D[i * 3] ?? 0;
|
|
127
|
+
const d1 = D[i * 3 + 1] ?? 0;
|
|
128
|
+
const d2 = D[i * 3 + 2] ?? 0;
|
|
129
|
+
const wx = t[0] * d0 + b[0] * d1 + n[0] * d2;
|
|
130
|
+
const wy = t[1] * d0 + b[1] * d1 + n[1] * d2;
|
|
131
|
+
const wz = t[2] * d0 + b[2] * d1 + n[2] * d2;
|
|
132
|
+
if (ground && wz < -1e-6 && oz >= 0 && -oz / wz <= radius) blocked++;
|
|
133
|
+
else if (grid.hit(ox, oy, oz, wx, wy, wz, radius)) blocked++;
|
|
134
|
+
}
|
|
135
|
+
const vis = 1 - blocked / count;
|
|
136
|
+
memo.set(s.at, key, vis);
|
|
137
|
+
out[v] = vis;
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const darken = (mesh: Mesh, vis: Float32Array, strength: number): Mesh => {
|
|
143
|
+
const colors = new Float32Array(mesh.colors);
|
|
144
|
+
for (let v = 0; v < vis.length; v++) {
|
|
145
|
+
const f = 1 - strength * (1 - (vis[v] ?? 1));
|
|
146
|
+
for (let k = 0; k < 3; k++) colors[v * 3 + k] = (colors[v * 3 + k] ?? 0) * f;
|
|
147
|
+
}
|
|
148
|
+
return { ...mesh, colors };
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/** Bake AO of one mesh against itself (or `occluder`, in the same space). */
|
|
152
|
+
// Callers bake several parts against one occluder (a head's skin, jaw and eyes against the
|
|
153
|
+
// whole head): index each occluder once. Meshes are immutable, so the grid stays valid.
|
|
154
|
+
const grids = new WeakMap<Mesh, RayGrid>();
|
|
155
|
+
const gridOf = (m: Mesh): RayGrid => {
|
|
156
|
+
const hit = grids.get(m);
|
|
157
|
+
if (hit) return hit;
|
|
158
|
+
const g = new RayGrid(m);
|
|
159
|
+
grids.set(m, g);
|
|
160
|
+
return g;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export function bakeAo(mesh: Mesh, o: AoOptions = {}, occluder: Mesh = mesh): Mesh {
|
|
164
|
+
return darken(mesh, visibility(mesh, gridOf(occluder), o), o.strength ?? 0.6);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Bake AO across a whole compiled asset: every part is occluded by every other (a wheel darkens
|
|
169
|
+
* the hull behind it). Colours only change, so parts keep their local meshes and pivots.
|
|
170
|
+
*/
|
|
171
|
+
export function bakeAoAsset(asset: CompiledAsset, o: AoOptions = {}): CompiledAsset {
|
|
172
|
+
const world = worldMeshes(asset.parts);
|
|
173
|
+
if (world.size === 0) return asset;
|
|
174
|
+
const grid = new RayGrid(merge([...world.values()]));
|
|
175
|
+
const strength = o.strength ?? 0.6;
|
|
176
|
+
return {
|
|
177
|
+
...asset,
|
|
178
|
+
parts: asset.parts.map((p) => {
|
|
179
|
+
const w = world.get(p.name);
|
|
180
|
+
return p.mesh && w ? { ...p, mesh: darken(p.mesh, visibility(w, grid, o), strength) } : p;
|
|
181
|
+
}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Grime: darken vertex colour near the ground and in named zones (soot, mud, dust). A pass over a
|
|
2
|
+
// finished mesh, so recipes stay about shape; wear is applied once, the same way everywhere.
|
|
3
|
+
|
|
4
|
+
import type { Mesh, Vec3 } from '../core/index.ts';
|
|
5
|
+
|
|
6
|
+
export interface GrimeZone {
|
|
7
|
+
readonly at: Vec3;
|
|
8
|
+
readonly radius: number;
|
|
9
|
+
/** 0..1 darkening at the centre. */
|
|
10
|
+
readonly strength: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GrimeOptions {
|
|
14
|
+
/** Height (m) below which mud/dust builds up toward the ground. */
|
|
15
|
+
readonly ground?: number;
|
|
16
|
+
/** Darkening at z = 0. */
|
|
17
|
+
readonly groundStrength?: number;
|
|
18
|
+
readonly zones?: readonly GrimeZone[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function grime(mesh: Mesh, o: GrimeOptions): Mesh {
|
|
22
|
+
const colors = new Float32Array(mesh.colors);
|
|
23
|
+
const g = o.ground ?? 0;
|
|
24
|
+
const gs = o.groundStrength ?? 0.3;
|
|
25
|
+
for (let i = 0; i < mesh.positions.length / 3; i++) {
|
|
26
|
+
const x = mesh.positions[i * 3] ?? 0;
|
|
27
|
+
const y = mesh.positions[i * 3 + 1] ?? 0;
|
|
28
|
+
const z = mesh.positions[i * 3 + 2] ?? 0;
|
|
29
|
+
let f = g > 0 && z < g ? 1 - gs * (1 - Math.max(0, z) / g) : 1;
|
|
30
|
+
for (const zone of o.zones ?? []) {
|
|
31
|
+
const d = Math.hypot(x - zone.at[0], y - zone.at[1], z - zone.at[2]) / zone.radius;
|
|
32
|
+
if (d < 1) f *= 1 - zone.strength * (1 - d);
|
|
33
|
+
}
|
|
34
|
+
for (let k = 0; k < 3; k++) colors[i * 3 + k] = (colors[i * 3 + k] ?? 1) * f;
|
|
35
|
+
}
|
|
36
|
+
return { ...mesh, colors };
|
|
37
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Public API of /passes: whole-mesh post-processing applied after geometry — wear, optimisation.
|
|
2
|
+
|
|
3
|
+
export { type AoOptions, bakeAo, bakeAoAsset } from './bake-ao.ts';
|
|
4
|
+
export { type GrimeOptions, type GrimeZone, grime } from './grime.ts';
|
|
5
|
+
export { type Livery, type LiveryRule, livery } from './livery.ts';
|
|
6
|
+
export { type OptimizeOptions, type OptimizeReport, optimize } from './optimize.ts';
|
|
7
|
+
export { RayGrid } from './ray-grid.ts';
|
|
8
|
+
export { type WeightedNormalsOptions, weightedNormals } from './weighted-normals.ts';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Livery: paint a finished mesh by rules instead of by hand. Base colour, then ordered rules —
|
|
2
|
+
// the last matching rule wins — keyed on height bands, an along-length range, a side-elevation
|
|
3
|
+
// polygon (stripes, wings, numbers' backgrounds), and surface orientation (roof, front, back,
|
|
4
|
+
// sides, underside). One call turns a grey loco into "Daylight", a tank into its camo scheme.
|
|
5
|
+
|
|
6
|
+
import type { Mesh, Vec2, Vec3 } from '../core/index.ts';
|
|
7
|
+
|
|
8
|
+
export interface LiveryRule {
|
|
9
|
+
readonly color: Vec3;
|
|
10
|
+
/** Height band [z0, z1] (metres). */
|
|
11
|
+
readonly z?: readonly [number, number];
|
|
12
|
+
/** Along-length band [x0, x1] (metres, +X forward). */
|
|
13
|
+
readonly x?: readonly [number, number];
|
|
14
|
+
/** Polygon in the side elevation (x, z): the region a stripe or panel covers. */
|
|
15
|
+
readonly poly?: readonly Vec2[];
|
|
16
|
+
/** Only faces whose normal points this way (n·axis > 0.5; roof: n.z > 0.55). */
|
|
17
|
+
readonly facing?: 'roof' | 'under' | 'front' | 'back' | 'side';
|
|
18
|
+
/** Multiply into the existing colour instead of replacing it (weathering, shading). */
|
|
19
|
+
readonly multiply?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Livery {
|
|
23
|
+
readonly base?: Vec3;
|
|
24
|
+
readonly rules: readonly LiveryRule[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function inPoly(poly: readonly Vec2[], x: number, z: number): boolean {
|
|
28
|
+
let inside = false;
|
|
29
|
+
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
30
|
+
const [xi, zi] = poly[i] ?? [0, 0];
|
|
31
|
+
const [xj, zj] = poly[j] ?? [0, 0];
|
|
32
|
+
if (zi > z !== zj > z && x < ((xj - xi) * (z - zi)) / (zj - zi) + xi) inside = !inside;
|
|
33
|
+
}
|
|
34
|
+
return inside;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function faces(f: NonNullable<LiveryRule['facing']>, n: Vec3): boolean {
|
|
38
|
+
if (f === 'roof') return n[2] > 0.55;
|
|
39
|
+
if (f === 'under') return n[2] < -0.55;
|
|
40
|
+
if (f === 'front') return n[0] > 0.5;
|
|
41
|
+
if (f === 'back') return n[0] < -0.5;
|
|
42
|
+
return Math.abs(n[1]) > 0.5;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function livery(mesh: Mesh, l: Livery): Mesh {
|
|
46
|
+
const colors = new Float32Array(mesh.colors);
|
|
47
|
+
for (let i = 0; i < mesh.positions.length / 3; i++) {
|
|
48
|
+
const x = mesh.positions[i * 3] ?? 0;
|
|
49
|
+
const z = mesh.positions[i * 3 + 2] ?? 0;
|
|
50
|
+
const n: Vec3 = [
|
|
51
|
+
mesh.normals[i * 3] ?? 0,
|
|
52
|
+
mesh.normals[i * 3 + 1] ?? 0,
|
|
53
|
+
mesh.normals[i * 3 + 2] ?? 0,
|
|
54
|
+
];
|
|
55
|
+
let c: Vec3 = l.base ?? [colors[i * 3] ?? 1, colors[i * 3 + 1] ?? 1, colors[i * 3 + 2] ?? 1];
|
|
56
|
+
for (const r of l.rules) {
|
|
57
|
+
if (r.z && (z < r.z[0] || z > r.z[1])) continue;
|
|
58
|
+
if (r.x && (x < r.x[0] || x > r.x[1])) continue;
|
|
59
|
+
if (r.poly && !inPoly(r.poly, x, z)) continue;
|
|
60
|
+
if (r.facing && !faces(r.facing, n)) continue;
|
|
61
|
+
c = r.multiply ? [c[0] * r.color[0], c[1] * r.color[1], c[2] * r.color[2]] : r.color;
|
|
62
|
+
}
|
|
63
|
+
colors.set(c, i * 3);
|
|
64
|
+
}
|
|
65
|
+
return { ...mesh, colors };
|
|
66
|
+
}
|