@developerz.ai/ts23d 0.0.0-stage → 0.1.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 +79 -0
- package/CLAUDE.md +85 -0
- package/LICENSE +21 -0
- package/README.md +380 -2
- package/dist/chunk-11v28977.js +1575 -0
- package/dist/chunk-11v28977.js.map +30 -0
- package/dist/chunk-1ypfs7ga.js +3571 -0
- package/dist/chunk-1ypfs7ga.js.map +42 -0
- package/dist/chunk-4ddzsqtc.js +464 -0
- package/dist/chunk-4ddzsqtc.js.map +19 -0
- package/dist/chunk-a8fxakcj.js +19 -0
- package/dist/chunk-a8fxakcj.js.map +11 -0
- package/dist/chunk-gcyp9b4d.js +755 -0
- package/dist/chunk-gcyp9b4d.js.map +22 -0
- package/dist/chunk-kgvbpqqw.js +44 -0
- package/dist/chunk-kgvbpqqw.js.map +10 -0
- package/dist/chunk-kvcep7ft.js +1919 -0
- package/dist/chunk-kvcep7ft.js.map +43 -0
- package/dist/chunk-ravbb06q.js +89 -0
- package/dist/chunk-ravbb06q.js.map +11 -0
- package/dist/chunk-w1zyhj40.js +1031 -0
- package/dist/chunk-w1zyhj40.js.map +21 -0
- package/dist/chunk-wtqhq529.js +2532 -0
- package/dist/chunk-wtqhq529.js.map +43 -0
- package/dist/chunk-xnt2sdhf.js +836 -0
- package/dist/chunk-xnt2sdhf.js.map +17 -0
- package/dist/cli/args.d.ts +24 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +307 -0
- package/dist/cli/bin.js.map +14 -0
- package/dist/cli/load.d.ts +2 -0
- package/dist/cli/manifest.d.ts +49 -0
- package/dist/cli/render-cmds.d.ts +12 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/core/asset.d.ts +10 -0
- package/dist/core/definition.d.ts +148 -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/glb.d.ts +38 -0
- package/dist/export/index.d.ts +4 -0
- package/dist/export/index.js +26 -0
- package/dist/export/index.js.map +9 -0
- package/dist/export/lights.d.ts +8 -0
- package/dist/export/materials.d.ts +23 -0
- package/dist/export/merge-pivots.d.ts +6 -0
- package/dist/export/metadata.d.ts +19 -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 +411 -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 +9 -0
- package/dist/kinds/head-beard.d.ts +6 -0
- package/dist/kinds/head-bust.d.ts +4 -0
- package/dist/kinds/head-extras.d.ts +15 -0
- package/dist/kinds/head-face.d.ts +6 -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 +27 -0
- package/dist/kinds/head-params.d.ts +108 -0
- package/dist/kinds/head-presets.d.ts +19 -0
- package/dist/kinds/head-rim.d.ts +10 -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/generator.d.ts +4 -0
- package/dist/material/index.d.ts +4 -0
- package/dist/material/index.js +38 -0
- package/dist/material/index.js.map +9 -0
- package/dist/material/paint/bond.d.ts +3 -0
- package/dist/material/paint/canvas.d.ts +32 -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 +2 -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/masks.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 +35 -0
- package/dist/material/paint/validate.d.ts +3 -0
- package/dist/material/palette.d.ts +12 -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/trim-sheet.d.ts +22 -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 +20 -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 +52 -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 +80 -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/landscape.md +49 -0
- package/docs/plans/roadmap.md +127 -0
- package/examples/build-with-bun.ts +41 -0
- package/llms.txt +47 -0
- package/package.json +99 -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 +46 -0
- package/recipes/boxcar/boxcar.ts +916 -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 +62 -0
- package/recipes/goblin_head/goblin_head.ts +36 -0
- package/recipes/human_head/README.md +64 -0
- package/recipes/human_head/human_head.ts +37 -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 +183 -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 +66 -0
- package/recipes/orc_head/orc_head.ts +39 -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 +112 -0
- package/src/cli/bin.ts +7 -0
- package/src/cli/load.ts +17 -0
- package/src/cli/manifest.ts +89 -0
- package/src/cli/render-cmds.ts +66 -0
- package/src/cli/run.ts +168 -0
- package/src/core/asset.ts +128 -0
- package/src/core/definition.ts +149 -0
- package/src/core/errors.ts +45 -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/glb.ts +200 -0
- package/src/export/index.ts +18 -0
- package/src/export/lights.ts +49 -0
- package/src/export/materials.ts +126 -0
- package/src/export/merge-pivots.ts +146 -0
- package/src/export/metadata.ts +59 -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 +151 -0
- package/src/kinds/head-beard.ts +133 -0
- package/src/kinds/head-bust.ts +221 -0
- package/src/kinds/head-extras.ts +150 -0
- package/src/kinds/head-face.ts +222 -0
- package/src/kinds/head-geom.ts +31 -0
- package/src/kinds/head-hair.ts +152 -0
- package/src/kinds/head-layout.ts +63 -0
- package/src/kinds/head-mesh.ts +157 -0
- package/src/kinds/head-params.ts +153 -0
- package/src/kinds/head-presets.ts +152 -0
- package/src/kinds/head-rim.ts +128 -0
- package/src/kinds/head.ts +171 -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/generator.ts +56 -0
- package/src/material/index.ts +29 -0
- package/src/material/paint/bond.ts +60 -0
- package/src/material/paint/canvas.ts +91 -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 +100 -0
- package/src/material/paint/default-sheet.ts +61 -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/masks.ts +44 -0
- package/src/material/paint/noise.ts +52 -0
- package/src/material/paint/normal.ts +52 -0
- package/src/material/paint/orm.ts +95 -0
- package/src/material/paint/planks.ts +56 -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 +42 -0
- package/src/material/paint/surface.ts +60 -0
- package/src/material/paint/types.ts +51 -0
- package/src/material/paint/validate.ts +58 -0
- package/src/material/palette.ts +43 -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/trim-sheet.ts +100 -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 +95 -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 +199 -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,56 @@
|
|
|
1
|
+
// Procedural material generator: paints the shared trim sheet (albedo, normal, ORM) from code —
|
|
2
|
+
// brick bonds, plank grain, riveted plates, slate, concrete, glass, decal cells and cutout foliage
|
|
3
|
+
// cards — at a fixed seed, so the texture is as deterministic as the meshes. Supplied PNGs (reference
|
|
4
|
+
// photos, kubeez.com generations) can stamp cells; they are inputs, never the only path.
|
|
5
|
+
//
|
|
6
|
+
// Conventions: V (image row) grows with world up, as box/planar map faces; strips tile seamlessly
|
|
7
|
+
// along U and span `stripRect().metres` there and `scaleMetres` along V; albedo is near-greyscale
|
|
8
|
+
// sRGB (vertex colour tints it) except foliage and stamped cells, which carry colour + alpha.
|
|
9
|
+
|
|
10
|
+
import { createRng } from '../core/index.ts';
|
|
11
|
+
import { createCanvas } from './paint/canvas.ts';
|
|
12
|
+
import { paintCell } from './paint/cell.ts';
|
|
13
|
+
import { blankSheet, composeRegion } from './paint/compose.ts';
|
|
14
|
+
import { DEFAULT_SHEET } from './paint/default-sheet.ts';
|
|
15
|
+
import { paintStrip } from './paint/strip.ts';
|
|
16
|
+
import type { PaintedSheet, SheetRecipe } from './paint/types.ts';
|
|
17
|
+
import { validateSheet, validateStrip } from './paint/validate.ts';
|
|
18
|
+
import { CELLS, cellRect, STRIPS, stripRect } from './trim-sheet.ts';
|
|
19
|
+
|
|
20
|
+
export { DEFAULT_SHEET } from './paint/default-sheet.ts';
|
|
21
|
+
export type {
|
|
22
|
+
CellImage,
|
|
23
|
+
PaintedSheet,
|
|
24
|
+
SheetRecipe,
|
|
25
|
+
StripKind,
|
|
26
|
+
StripRecipe,
|
|
27
|
+
} from './paint/types.ts';
|
|
28
|
+
|
|
29
|
+
const FALLBACK = { kind: 'noise', scaleMetres: 1, roughness: 0.6 } as const;
|
|
30
|
+
|
|
31
|
+
export function paintTrimSheet(recipe: SheetRecipe = DEFAULT_SHEET): PaintedSheet {
|
|
32
|
+
validateSheet(recipe);
|
|
33
|
+
const size = recipe.size ?? 1024;
|
|
34
|
+
const sheet = blankSheet(size);
|
|
35
|
+
const rng = createRng(recipe.seed);
|
|
36
|
+
const seedOf = (name: string): number => rng.fork(name).int(0, 0x7fffffff);
|
|
37
|
+
const pad = Math.max(2, size / 256);
|
|
38
|
+
for (const id of STRIPS) {
|
|
39
|
+
const r = recipe.strips[id] ?? DEFAULT_SHEET.strips[id] ?? FALLBACK;
|
|
40
|
+
validateStrip(`strips.${id}`, r);
|
|
41
|
+
const rect = stripRect(id);
|
|
42
|
+
const h = Math.round((rect.v1 - rect.v0) * size);
|
|
43
|
+
const c = createCanvas(size, h, rect.metres, r.scaleMetres, true);
|
|
44
|
+
paintStrip(c, r, seedOf(id));
|
|
45
|
+
composeRegion(sheet, c, 0, Math.round(rect.v0 * size), pad);
|
|
46
|
+
}
|
|
47
|
+
for (const id of CELLS) {
|
|
48
|
+
const e = recipe.cells?.[id] ?? DEFAULT_SHEET.cells?.[id] ?? FALLBACK;
|
|
49
|
+
const rect = cellRect(id);
|
|
50
|
+
const w = Math.round((rect.u1 - rect.u0) * size);
|
|
51
|
+
const c = createCanvas(w, w, rect.metres, rect.metres, false);
|
|
52
|
+
paintCell(c, id, e, seedOf(id));
|
|
53
|
+
composeRegion(sheet, c, Math.round(rect.u0 * size), Math.round(rect.v0 * size), pad);
|
|
54
|
+
}
|
|
55
|
+
return sheet;
|
|
56
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Public API of @ts23d/material: the shared trim-sheet layout, the procedural sheet painter,
|
|
2
|
+
// palettes and engine shader sources.
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
type CellImage,
|
|
6
|
+
DEFAULT_SHEET,
|
|
7
|
+
type PaintedSheet,
|
|
8
|
+
paintTrimSheet,
|
|
9
|
+
type SheetRecipe,
|
|
10
|
+
type StripKind,
|
|
11
|
+
type StripRecipe,
|
|
12
|
+
} from './generator.ts';
|
|
13
|
+
export { definePalette, hex, type Palette, palettes } from './palette.ts';
|
|
14
|
+
export { godotShaderSource, type ShaderHints, threeMaterialSource } from './shader.ts';
|
|
15
|
+
export {
|
|
16
|
+
CELLS,
|
|
17
|
+
type CellId,
|
|
18
|
+
cellRect,
|
|
19
|
+
DECAL_CELLS,
|
|
20
|
+
type DecalCellId,
|
|
21
|
+
FOLIAGE_CELLS,
|
|
22
|
+
type FoliageCellId,
|
|
23
|
+
isFoliage,
|
|
24
|
+
STRIPS,
|
|
25
|
+
type StripId,
|
|
26
|
+
stripRect,
|
|
27
|
+
TRIM_SHEET_SIZE,
|
|
28
|
+
type UvRect,
|
|
29
|
+
} from './trim-sheet.ts';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Brick and stone bond (kind 'bond'): staggered courses, recessed mortar with chipped edges,
|
|
2
|
+
// per-unit tone and tilt, face pitting. Stone is the same bond with `irregular` > 0 and bigger,
|
|
3
|
+
// rougher units.
|
|
4
|
+
// params: course 0.075 · length 0.225 · mortar 0.01 · depth 0.006 · bevel 0.004 · irregular 0
|
|
5
|
+
// stagger 0.5 · relief 0.0008 (face roughness amplitude, m) · variation 0.3 (tone spread)
|
|
6
|
+
|
|
7
|
+
import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
|
|
8
|
+
import { Courses } from './courses.ts';
|
|
9
|
+
import { hash2 } from './noise.ts';
|
|
10
|
+
import type { StripRecipe } from './types.ts';
|
|
11
|
+
|
|
12
|
+
export function paintBond(c: Canvas, r: StripRecipe, seed: number): void {
|
|
13
|
+
const p = r.params;
|
|
14
|
+
const mortar = param(p, 'mortar', 0.01);
|
|
15
|
+
const depth = param(p, 'depth', 0.006);
|
|
16
|
+
const bevel = param(p, 'bevel', 0.004);
|
|
17
|
+
const relief = param(p, 'relief', 0.0008);
|
|
18
|
+
const variation = param(p, 'variation', 0.3);
|
|
19
|
+
const course = param(p, 'course', 0.075);
|
|
20
|
+
const length = param(p, 'length', 0.225);
|
|
21
|
+
const courses = new Courses(c, {
|
|
22
|
+
course,
|
|
23
|
+
length,
|
|
24
|
+
irregular: param(p, 'irregular', 0),
|
|
25
|
+
stagger: param(p, 'stagger', 0.5),
|
|
26
|
+
seed,
|
|
27
|
+
});
|
|
28
|
+
const pu = c.metresU / c.w;
|
|
29
|
+
const pv = c.metresV / c.h;
|
|
30
|
+
const half = mortar / 2;
|
|
31
|
+
for (let y = 0; y < c.h; y++) {
|
|
32
|
+
const ym = (y + 0.5) * pv;
|
|
33
|
+
const k = courses.at(ym);
|
|
34
|
+
const dy = Math.min(ym - courses.y0, courses.y1 - ym);
|
|
35
|
+
for (let x = 0; x < c.w; x++) {
|
|
36
|
+
const i = y * c.w + x;
|
|
37
|
+
const xm = (x + 0.5) * pu;
|
|
38
|
+
const unit = courses.id[x] ?? 0;
|
|
39
|
+
const chip = noiseAt(c, xm, ym, 0.02, 0.02, seed + 7, 3) * mortar * 0.6;
|
|
40
|
+
const d = Math.min(courses.dx[x] ?? 0, dy) + chip;
|
|
41
|
+
const fine = noiseAt(c, xm, ym, 0.006, 0.006, seed + 11, 2);
|
|
42
|
+
if (d < half) {
|
|
43
|
+
c.height[i] = -depth + fine * 0.0006;
|
|
44
|
+
c.tone[i] = 0.95 + 0.04 * fine;
|
|
45
|
+
c.rough[i] = Math.min(1, r.roughness + 0.08);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const h = hash2(k, unit, seed);
|
|
49
|
+
const h2 = hash2(unit, k, seed + 3);
|
|
50
|
+
const big = noiseAt(c, xm, ym, length * 0.6, course * 0.8, seed + 5, 3);
|
|
51
|
+
const edge = smooth(0, bevel, d - half);
|
|
52
|
+
const tilt = ((h2 - 0.5) * (courses.ux[x] ?? 0) * 0.02 + (h - 0.5) * 0.001) * edge;
|
|
53
|
+
const pit = fine > 0.55 ? (fine - 0.55) * 0.004 : 0;
|
|
54
|
+
c.height[i] = -depth * (1 - edge) + relief * big + tilt - pit;
|
|
55
|
+
c.tone[i] =
|
|
56
|
+
(0.62 - variation * 0.5 + variation * h) * (1 + 0.15 * big + 0.05 * fine) - pit * 40;
|
|
57
|
+
c.rough[i] = r.roughness + 0.06 * fine;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// The painting surface for one sheet region (a strip or a cell): float layers in real metres.
|
|
2
|
+
// Painters write height (metres of relief, + is out of the surface), linear grey tone,
|
|
3
|
+
// roughness and metalness; `finish` turns them into normal + ORM; `compose` copies into the sheet.
|
|
4
|
+
|
|
5
|
+
import { fbm } from './noise.ts';
|
|
6
|
+
|
|
7
|
+
export interface Canvas {
|
|
8
|
+
readonly w: number;
|
|
9
|
+
readonly h: number;
|
|
10
|
+
/** World metres the canvas spans along U (x) and V (y). Row y grows with world up (+V). */
|
|
11
|
+
readonly metresU: number;
|
|
12
|
+
readonly metresV: number;
|
|
13
|
+
/** Strips wrap along U (tile seamlessly); cells clamp. */
|
|
14
|
+
readonly wrapU: boolean;
|
|
15
|
+
readonly height: Float32Array;
|
|
16
|
+
readonly tone: Float32Array;
|
|
17
|
+
readonly rough: Float32Array;
|
|
18
|
+
readonly metal: Float32Array;
|
|
19
|
+
/** Extra occlusion multiplied into the cavity AO (foliage darkens toward the twig). */
|
|
20
|
+
readonly occl: Float32Array;
|
|
21
|
+
/** sRGB RGBA override (stamped images, coloured foliage); null when painted greyscale. */
|
|
22
|
+
rgba: Uint8Array | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createCanvas(
|
|
26
|
+
w: number,
|
|
27
|
+
h: number,
|
|
28
|
+
metresU: number,
|
|
29
|
+
metresV: number,
|
|
30
|
+
wrapU: boolean,
|
|
31
|
+
): Canvas {
|
|
32
|
+
const n = w * h;
|
|
33
|
+
return {
|
|
34
|
+
w,
|
|
35
|
+
h,
|
|
36
|
+
metresU,
|
|
37
|
+
metresV,
|
|
38
|
+
wrapU,
|
|
39
|
+
height: new Float32Array(n),
|
|
40
|
+
tone: new Float32Array(n).fill(1),
|
|
41
|
+
rough: new Float32Array(n).fill(0.8),
|
|
42
|
+
metal: new Float32Array(n),
|
|
43
|
+
occl: new Float32Array(n).fill(1),
|
|
44
|
+
rgba: null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Metres per pixel along x and y. */
|
|
49
|
+
export const pxU = (c: Canvas): number => c.metresU / c.w;
|
|
50
|
+
export const pxV = (c: Canvas): number => c.metresV / c.h;
|
|
51
|
+
|
|
52
|
+
/** Noise period (lattice cells across the canvas width) for a feature size in metres. */
|
|
53
|
+
export function periodFor(c: Canvas, featureMetres: number): number {
|
|
54
|
+
return c.wrapU ? Math.max(1, Math.round(c.metresU / featureMetres)) : 1 << 20;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* fBm sampled at metres (xm, ym) with feature size `sizeU` along U and `sizeV` along V,
|
|
59
|
+
* snapped so it tiles along U. Returns [-1, 1].
|
|
60
|
+
*/
|
|
61
|
+
export function noiseAt(
|
|
62
|
+
c: Canvas,
|
|
63
|
+
xm: number,
|
|
64
|
+
ym: number,
|
|
65
|
+
sizeU: number,
|
|
66
|
+
sizeV: number,
|
|
67
|
+
seed: number,
|
|
68
|
+
octaves = 4,
|
|
69
|
+
): number {
|
|
70
|
+
// Strips are coarse along V: never ask for a feature finer than ~1.5 texels on either axis,
|
|
71
|
+
// or it aliases into streaks. Scale both axes together so the feature keeps its aspect.
|
|
72
|
+
const finest = 1.5 * Math.max(c.metresU / c.w, c.metresV / c.h);
|
|
73
|
+
const k = Math.max(1, finest / Math.min(sizeU, sizeV));
|
|
74
|
+
const period = periodFor(c, sizeU * k);
|
|
75
|
+
const cell = c.wrapU ? c.metresU / period : sizeU * k;
|
|
76
|
+
return fbm(xm / cell, ym / (sizeV * k), period, seed, octaves);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Read a parameter with a default, rejecting nothing (validation happens in the generator). */
|
|
80
|
+
export const param = (
|
|
81
|
+
params: Readonly<Record<string, number>> | undefined,
|
|
82
|
+
key: string,
|
|
83
|
+
fallback: number,
|
|
84
|
+
): number => params?.[key] ?? fallback;
|
|
85
|
+
|
|
86
|
+
export const clamp01 = (v: number): number => Math.min(1, Math.max(0, v));
|
|
87
|
+
|
|
88
|
+
export const smooth = (e0: number, e1: number, v: number): number => {
|
|
89
|
+
const t = clamp01((v - e0) / (e1 - e0));
|
|
90
|
+
return t * t * (3 - 2 * t);
|
|
91
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Paint one cell: a stamped image, a foliage card (its own painter; the recipe's roughness scales
|
|
2
|
+
// the leaves), or a decal motif over a backdrop of the recipe's kind.
|
|
3
|
+
|
|
4
|
+
import { type CellId, isFoliage } from '../trim-sheet.ts';
|
|
5
|
+
import type { Canvas } from './canvas.ts';
|
|
6
|
+
import { paintDoorPanel, paintSign, paintWindowSash } from './cells.ts';
|
|
7
|
+
import { paintClock, paintPorthole, paintSmokebox } from './cells-round.ts';
|
|
8
|
+
import { FOLIAGE_PAINTERS } from './foliage.ts';
|
|
9
|
+
import { stampImage } from './stamp.ts';
|
|
10
|
+
import { paintStrip } from './strip.ts';
|
|
11
|
+
import type { CellImage, StripRecipe } from './types.ts';
|
|
12
|
+
|
|
13
|
+
type Motif = (c: Canvas, r: StripRecipe, seed: number) => void;
|
|
14
|
+
const MOTIFS: Readonly<Record<string, Motif>> = {
|
|
15
|
+
window_sash: paintWindowSash,
|
|
16
|
+
door_panel: paintDoorPanel,
|
|
17
|
+
clock: paintClock,
|
|
18
|
+
smokebox: paintSmokebox,
|
|
19
|
+
sign: paintSign,
|
|
20
|
+
porthole: paintPorthole,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Roughness the foliage painter assumes; a recipe's own roughness rescales it. */
|
|
24
|
+
const LEAF_ROUGHNESS = 0.6;
|
|
25
|
+
|
|
26
|
+
export function paintCell(c: Canvas, id: CellId, entry: StripRecipe | CellImage, seed: number) {
|
|
27
|
+
if ('image' in entry) {
|
|
28
|
+
stampImage(c, entry.image);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (isFoliage(id)) {
|
|
32
|
+
FOLIAGE_PAINTERS[id](c, seed);
|
|
33
|
+
const k = entry.roughness / LEAF_ROUGHNESS;
|
|
34
|
+
for (let i = 0; i < c.rough.length; i++) c.rough[i] = Math.min(1, (c.rough[i] ?? 0.6) * k);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
paintStrip(c, entry, seed);
|
|
38
|
+
MOTIFS[id]?.(c, entry, seed + 1);
|
|
39
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Round decal cells (1 m each), centred, over the cell recipe's backdrop: station clock (bezel,
|
|
2
|
+
// minute and hour ticks, hands at 10:10), locomotive smokebox door (flanged, bolted rim, hinge
|
|
3
|
+
// straps, centre dart with handle), porthole (bolted brass ring, dark glass). Row y grows with
|
|
4
|
+
// world up, so 12 o'clock is +y.
|
|
5
|
+
|
|
6
|
+
import { type Canvas, noiseAt, smooth } from './canvas.ts';
|
|
7
|
+
import { PANE_TONE } from './cells.ts';
|
|
8
|
+
import { boltRing, dome, segment } from './shapes.ts';
|
|
9
|
+
import type { StripRecipe } from './types.ts';
|
|
10
|
+
|
|
11
|
+
const TAU = Math.PI * 2;
|
|
12
|
+
|
|
13
|
+
/** Hand from the centre at clock position `hours` (0..12), length `len`. */
|
|
14
|
+
function hand(px: number, py: number, hours: number, len: number): number {
|
|
15
|
+
const a = (hours / 12) * TAU;
|
|
16
|
+
return segment(px, py, 0, 0, Math.sin(a) * len, Math.cos(a) * len);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function paintClock(c: Canvas, r: StripRecipe, _seed: number): void {
|
|
20
|
+
const pu = c.metresU / c.w;
|
|
21
|
+
const pv = c.metresV / c.h;
|
|
22
|
+
for (let y = 0; y < c.h; y++) {
|
|
23
|
+
const py = (y + 0.5) * pv - 0.5;
|
|
24
|
+
for (let x = 0; x < c.w; x++) {
|
|
25
|
+
const px = (x + 0.5) * pu - 0.5;
|
|
26
|
+
const i = y * c.w + x;
|
|
27
|
+
const d = Math.hypot(px, py);
|
|
28
|
+
if (d > 0.48) continue;
|
|
29
|
+
const i0 = i;
|
|
30
|
+
if (d > 0.42) {
|
|
31
|
+
c.height[i0] = 0.012 * dome(Math.abs(d - 0.45), 0.03);
|
|
32
|
+
c.tone[i0] = 0.55;
|
|
33
|
+
c.metal[i0] = 1;
|
|
34
|
+
c.rough[i0] = 0.35;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const a = Math.atan2(px, py);
|
|
38
|
+
const minute = Math.abs((((a / TAU) * 60 + 60.5) % 1) - 0.5) * (TAU / 60) * d;
|
|
39
|
+
const hour = Math.abs((((a / TAU) * 12 + 12.5) % 1) - 0.5) * (TAU / 12) * d;
|
|
40
|
+
const tickM = d > 0.38 && d < 0.41 && minute < 0.003 ? 1 : 0;
|
|
41
|
+
const tickH = d > 0.32 && d < 0.41 && hour < 0.008 ? 1 : 0;
|
|
42
|
+
const hands = Math.min(
|
|
43
|
+
hand(px, py, 10 + 10 / 60, 0.22) - 0.012,
|
|
44
|
+
hand(px, py, 2, 0.34) - 0.007,
|
|
45
|
+
);
|
|
46
|
+
const ink = Math.max(tickM, tickH, hands < 0 ? 1 : 0, d < 0.02 ? 1 : 0);
|
|
47
|
+
c.height[i0] = -0.004 + 0.002 * ink;
|
|
48
|
+
c.tone[i0] = ink ? 0.08 : 0.96;
|
|
49
|
+
c.rough[i0] = ink ? 0.5 : r.roughness;
|
|
50
|
+
c.metal[i0] = 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function paintSmokebox(c: Canvas, r: StripRecipe, seed: number): void {
|
|
56
|
+
const pu = c.metresU / c.w;
|
|
57
|
+
const pv = c.metresV / c.h;
|
|
58
|
+
const metal = r.metalness ?? 0;
|
|
59
|
+
for (let y = 0; y < c.h; y++) {
|
|
60
|
+
const py = (y + 0.5) * pv - 0.5;
|
|
61
|
+
for (let x = 0; x < c.w; x++) {
|
|
62
|
+
const px = (x + 0.5) * pu - 0.5;
|
|
63
|
+
const i = y * c.w + x;
|
|
64
|
+
const d = Math.hypot(px, py);
|
|
65
|
+
if (d > 0.47) continue;
|
|
66
|
+
const pit = noiseAt(c, px + 0.5, py + 0.5, 0.03, 0.03, seed, 3);
|
|
67
|
+
// Slightly dished door, a raised flange, a bolt ring just inside it.
|
|
68
|
+
let h = 0.01 * (1 - (d / 0.47) ** 2) + 0.0004 * pit;
|
|
69
|
+
const flange = dome(Math.abs(d - 0.45), 0.02);
|
|
70
|
+
h += 0.006 * flange;
|
|
71
|
+
const bolt = dome(boltRing(px, py, 0.415, 28), 0.011);
|
|
72
|
+
h += 0.006 * bolt;
|
|
73
|
+
// Two hinge straps from the left rim to the dart's line, ending in a rounded tip.
|
|
74
|
+
const strap = Math.min(
|
|
75
|
+
segment(px, py, -0.47, 0.17, 0.12, 0.17),
|
|
76
|
+
segment(px, py, -0.47, -0.17, 0.12, -0.17),
|
|
77
|
+
);
|
|
78
|
+
const strapH = smooth(0.024, 0.018, strap);
|
|
79
|
+
h = Math.max(h, 0.014 * strapH + h * (1 - strapH));
|
|
80
|
+
const boss = dome(d, 0.05);
|
|
81
|
+
const handle = smooth(0.012, 0.008, segment(px, py, -0.13, 0, 0.13, 0));
|
|
82
|
+
h += 0.02 * boss + 0.012 * handle;
|
|
83
|
+
c.height[i] = h;
|
|
84
|
+
c.tone[i] =
|
|
85
|
+
(0.5 + 0.05 * pit) * (1 + 0.5 * Math.max(bolt, handle, strapH * 0.6, flange, boss));
|
|
86
|
+
c.rough[i] = r.roughness - 0.15 * Math.max(bolt, handle, boss);
|
|
87
|
+
c.metal[i] = metal + (1 - metal) * Math.max(handle, boss) * 0.8;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function paintPorthole(c: Canvas, r: StripRecipe, seed: number): void {
|
|
93
|
+
const pu = c.metresU / c.w;
|
|
94
|
+
const pv = c.metresV / c.h;
|
|
95
|
+
for (let y = 0; y < c.h; y++) {
|
|
96
|
+
const py = (y + 0.5) * pv - 0.5;
|
|
97
|
+
for (let x = 0; x < c.w; x++) {
|
|
98
|
+
const px = (x + 0.5) * pu - 0.5;
|
|
99
|
+
const i = y * c.w + x;
|
|
100
|
+
const d = Math.hypot(px, py);
|
|
101
|
+
if (d > 0.44) continue;
|
|
102
|
+
if (d < 0.29) {
|
|
103
|
+
const sky = noiseAt(c, px + 0.5, py + 0.5, 0.5, 0.5, seed, 2);
|
|
104
|
+
c.height[i] = -0.012;
|
|
105
|
+
c.tone[i] = PANE_TONE + 0.04 * (py + 0.3) + 0.02 * sky;
|
|
106
|
+
c.rough[i] = 0.05;
|
|
107
|
+
c.metal[i] = 0;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const ring = dome(Math.abs(d - 0.365), 0.075);
|
|
111
|
+
const bolt = dome(boltRing(px, py, 0.405, 10, Math.PI / 10), 0.018);
|
|
112
|
+
const lip = dome(Math.abs(d - 0.3), 0.012);
|
|
113
|
+
c.height[i] = 0.016 * ring + 0.008 * bolt + 0.004 * lip;
|
|
114
|
+
c.tone[i] = 0.75 + 0.15 * ring;
|
|
115
|
+
c.rough[i] = Math.max(0.2, r.roughness - 0.2 * ring);
|
|
116
|
+
c.metal[i] = 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Rectangular decal cells (1 m each), painted over a backdrop of the cell recipe's own kind:
|
|
2
|
+
// window sash (glazing bars, dark panes the shaders' window glow keys on), four-panel door with
|
|
3
|
+
// raised-and-fielded panels, sign blank with a moulded border and corner screws. Row y grows with
|
|
4
|
+
// world up, so "bottom rail" is low y.
|
|
5
|
+
// params (window_sash): cols 2 · rows 2 (panes per sash) · frame 0.07 · bar 0.022
|
|
6
|
+
// params (door_panel): stile 0.12
|
|
7
|
+
|
|
8
|
+
import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
|
|
9
|
+
import { dome, rect } from './shapes.ts';
|
|
10
|
+
import type { StripRecipe } from './types.ts';
|
|
11
|
+
|
|
12
|
+
/** Pane tone: dark enough that `luma < 0.3` marks glass for window glow. */
|
|
13
|
+
export const PANE_TONE = 0.12;
|
|
14
|
+
|
|
15
|
+
export function paintWindowSash(c: Canvas, r: StripRecipe, seed: number): void {
|
|
16
|
+
const cols = param(r.params, 'cols', 2);
|
|
17
|
+
const rows = param(r.params, 'rows', 2);
|
|
18
|
+
const frame = param(r.params, 'frame', 0.07);
|
|
19
|
+
const bar = param(r.params, 'bar', 0.022);
|
|
20
|
+
const meet = 0.5;
|
|
21
|
+
const pu = c.metresU / c.w;
|
|
22
|
+
const pv = c.metresV / c.h;
|
|
23
|
+
for (let y = 0; y < c.h; y++) {
|
|
24
|
+
const ym = (y + 0.5) * pv;
|
|
25
|
+
const lower = ym < meet;
|
|
26
|
+
const y0 = lower ? frame : meet + bar;
|
|
27
|
+
const y1 = lower ? meet - bar : 1 - frame;
|
|
28
|
+
for (let x = 0; x < c.w; x++) {
|
|
29
|
+
const xm = (x + 0.5) * pu;
|
|
30
|
+
const i = y * c.w + x;
|
|
31
|
+
// Distance into a pane: inside the sash opening, away from every glazing bar.
|
|
32
|
+
const fx = ((xm - frame) / (1 - 2 * frame)) * cols;
|
|
33
|
+
const fy = ((ym - y0) / (y1 - y0)) * rows;
|
|
34
|
+
const inX = Math.min(fx - Math.floor(fx), Math.ceil(fx) - fx) * ((1 - 2 * frame) / cols);
|
|
35
|
+
const inY = Math.min(fy - Math.floor(fy), Math.ceil(fy) - fy) * ((y1 - y0) / rows);
|
|
36
|
+
const open = -rect(xm, ym, frame, y0, 1 - frame, y1);
|
|
37
|
+
const pane = Math.min(open, inX - bar / 2, inY - bar / 2);
|
|
38
|
+
const wood = noiseAt(c, xm, ym, 0.3, 0.02, seed, 3);
|
|
39
|
+
if (pane > 0) {
|
|
40
|
+
const sky = noiseAt(c, xm, ym, 0.6, 0.6, seed + 1, 2);
|
|
41
|
+
c.height[i] = -0.012;
|
|
42
|
+
c.tone[i] = PANE_TONE + 0.05 * (ym - y0) + 0.03 * sky;
|
|
43
|
+
c.rough[i] = 0.05;
|
|
44
|
+
c.metal[i] = 0;
|
|
45
|
+
} else {
|
|
46
|
+
const bevel = smooth(0, 0.01, -pane);
|
|
47
|
+
c.height[i] = -0.012 * (1 - bevel) + 0.0003 * wood;
|
|
48
|
+
c.tone[i] = 0.9 + 0.03 * wood;
|
|
49
|
+
c.rough[i] = r.roughness;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function paintDoorPanel(c: Canvas, r: StripRecipe, seed: number): void {
|
|
56
|
+
const stile = param(r.params, 'stile', 0.12);
|
|
57
|
+
const muntin = stile * 0.7;
|
|
58
|
+
const pu = c.metresU / c.w;
|
|
59
|
+
const pv = c.metresV / c.h;
|
|
60
|
+
// Four panels: two tall above the lock rail, two short below. [x0, y0, x1, y1].
|
|
61
|
+
const midX = 0.5;
|
|
62
|
+
const panels = [
|
|
63
|
+
[stile, 0.2, midX - muntin / 2, 0.42],
|
|
64
|
+
[midX + muntin / 2, 0.2, 1 - stile, 0.42],
|
|
65
|
+
[stile, 0.58, midX - muntin / 2, 1 - stile],
|
|
66
|
+
[midX + muntin / 2, 0.58, 1 - stile, 1 - stile],
|
|
67
|
+
] as const;
|
|
68
|
+
for (let y = 0; y < c.h; y++) {
|
|
69
|
+
const ym = (y + 0.5) * pv;
|
|
70
|
+
for (let x = 0; x < c.w; x++) {
|
|
71
|
+
const xm = (x + 0.5) * pu;
|
|
72
|
+
const i = y * c.w + x;
|
|
73
|
+
let d = 1;
|
|
74
|
+
for (const [x0, y0, x1, y1] of panels) d = Math.min(d, rect(xm, ym, x0, y0, x1, y1));
|
|
75
|
+
const grain = noiseAt(c, xm, ym, 0.02, 0.4, seed, 3);
|
|
76
|
+
// Recess, then a bevel rising to the raised field 0.04 m in from the panel edge.
|
|
77
|
+
const recess = d < 0 ? 1 : 1 - smooth(0, 0.006, d);
|
|
78
|
+
const field = smooth(0.005, 0.04, -d);
|
|
79
|
+
c.height[i] = -0.012 * recess + 0.008 * field + 0.0003 * grain;
|
|
80
|
+
c.tone[i] = (0.8 + 0.08 * grain) * (d < 0 && d > -0.008 ? 0.65 : d < 0 ? 0.92 : 1);
|
|
81
|
+
c.rough[i] = r.roughness + 0.05 * grain;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function paintSign(c: Canvas, r: StripRecipe, _seed: number): void {
|
|
87
|
+
const border = param(r.params, 'border', 0.05);
|
|
88
|
+
const pu = c.metresU / c.w;
|
|
89
|
+
const pv = c.metresV / c.h;
|
|
90
|
+
const screws = [0.07, 0.93];
|
|
91
|
+
for (let y = 0; y < c.h; y++) {
|
|
92
|
+
const ym = (y + 0.5) * pv;
|
|
93
|
+
for (let x = 0; x < c.w; x++) {
|
|
94
|
+
const xm = (x + 0.5) * pu;
|
|
95
|
+
const i = y * c.w + x;
|
|
96
|
+
const inner = -rect(xm, ym, border, border, 1 - border, 1 - border);
|
|
97
|
+
const edge = Math.min(xm, ym, 1 - xm, 1 - ym);
|
|
98
|
+
const moulding = inner < 0 ? dome(Math.abs(-inner - border / 2), border / 2) : 0;
|
|
99
|
+
let screw = 0;
|
|
100
|
+
for (const sx of screws)
|
|
101
|
+
for (const sy of screws) screw = Math.max(screw, dome(Math.hypot(xm - sx, ym - sy), 0.012));
|
|
102
|
+
c.height[i] = 0.006 * moulding + 0.002 * screw - 0.002 * (1 - smooth(0, 0.004, edge));
|
|
103
|
+
c.tone[i] = inner > 0 ? 0.95 : 0.85 + 0.1 * moulding;
|
|
104
|
+
if (screw > 0) {
|
|
105
|
+
c.tone[i] = 0.6;
|
|
106
|
+
c.metal[i] = 1;
|
|
107
|
+
c.rough[i] = 0.35;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Finish a region canvas (AO, normal, ORM, albedo) and blit it into the sheet. Edge texels are
|
|
2
|
+
// replicated `pad` deep (rows for strips, rows + columns for cells) so the first mip levels never
|
|
3
|
+
// sample a neighbouring region.
|
|
4
|
+
|
|
5
|
+
import type { Canvas } from './canvas.ts';
|
|
6
|
+
import { sobelNormals } from './normal.ts';
|
|
7
|
+
import { cavityOcclusion, packAlbedo, packOrm } from './orm.ts';
|
|
8
|
+
import type { PaintedSheet } from './types.ts';
|
|
9
|
+
|
|
10
|
+
/** An empty sheet: white albedo (vertex colour reads true), flat normal, AO 1, rough 0.8. */
|
|
11
|
+
export function blankSheet(size: number): PaintedSheet {
|
|
12
|
+
const n = size * size * 4;
|
|
13
|
+
const albedo = new Uint8Array(n).fill(255);
|
|
14
|
+
const normal = new Uint8Array(n);
|
|
15
|
+
const orm = new Uint8Array(n);
|
|
16
|
+
const flat = new Uint8Array([128, 128, 255, 255]);
|
|
17
|
+
const base = new Uint8Array([255, 204, 0, 255]);
|
|
18
|
+
for (let i = 0; i < n; i += 4) {
|
|
19
|
+
normal.set(flat, i);
|
|
20
|
+
orm.set(base, i);
|
|
21
|
+
}
|
|
22
|
+
return { size, albedo, normal, orm };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function composeRegion(
|
|
26
|
+
sheet: PaintedSheet,
|
|
27
|
+
c: Canvas,
|
|
28
|
+
x0: number,
|
|
29
|
+
y0: number,
|
|
30
|
+
pad: number,
|
|
31
|
+
): void {
|
|
32
|
+
const ao = cavityOcclusion(c);
|
|
33
|
+
const maps = [
|
|
34
|
+
[sheet.albedo, packAlbedo(c, ao)],
|
|
35
|
+
[sheet.normal, sobelNormals(c)],
|
|
36
|
+
[sheet.orm, packOrm(c, ao)],
|
|
37
|
+
] as const;
|
|
38
|
+
const padX = c.wrapU ? 0 : pad;
|
|
39
|
+
for (let y = 0; y < c.h; y++) {
|
|
40
|
+
const sy = Math.min(c.h - 1 - pad, Math.max(pad, y));
|
|
41
|
+
const dst = ((y0 + y) * sheet.size + x0) * 4;
|
|
42
|
+
const src = sy * c.w;
|
|
43
|
+
const l = (src + padX) * 4;
|
|
44
|
+
const r = (src + c.w - 1 - padX) * 4;
|
|
45
|
+
for (const [to, from] of maps) {
|
|
46
|
+
to.set(from.subarray(l, r + 4), dst + padX * 4);
|
|
47
|
+
for (let x = 0; x < padX; x++) {
|
|
48
|
+
to.set(from.subarray(l, l + 4), dst + x * 4);
|
|
49
|
+
to.set(from.subarray(r, r + 4), dst + (c.w - 1 - x) * 4);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Cast concrete (kind 'concrete'): mottled cement tone, air-void pores, pour-lift lines and
|
|
2
|
+
// formwork tie holes on a grid.
|
|
3
|
+
// params: pores 0.35 (fraction of 8 mm cells holding a void) · ties 0.6 (tie grid, m; 0 = none)
|
|
4
|
+
// lift 0.6 (pour lift spacing, m; 0 = none) · mottle 0.1
|
|
5
|
+
|
|
6
|
+
import { type Canvas, noiseAt, param, periodFor, smooth } from './canvas.ts';
|
|
7
|
+
import { hash2 } from './noise.ts';
|
|
8
|
+
import type { StripRecipe } from './types.ts';
|
|
9
|
+
|
|
10
|
+
const PORE_CELL = 0.008;
|
|
11
|
+
|
|
12
|
+
export function paintConcrete(c: Canvas, r: StripRecipe, seed: number): void {
|
|
13
|
+
const p = r.params;
|
|
14
|
+
const pores = param(p, 'pores', 0.35);
|
|
15
|
+
const ties = param(p, 'ties', 0.6);
|
|
16
|
+
const lift = param(p, 'lift', 0.6);
|
|
17
|
+
const mottle = param(p, 'mottle', 0.1);
|
|
18
|
+
const pu = c.metresU / c.w;
|
|
19
|
+
const pv = c.metresV / c.h;
|
|
20
|
+
const cellM = Math.max(PORE_CELL, 2.5 * Math.max(pu, pv));
|
|
21
|
+
const pPer = periodFor(c, cellM);
|
|
22
|
+
const pCell = c.wrapU ? c.metresU / pPer : cellM;
|
|
23
|
+
const tPer = ties > 0 ? periodFor(c, ties) : 1;
|
|
24
|
+
const tCell = c.wrapU ? c.metresU / tPer : ties;
|
|
25
|
+
for (let y = 0; y < c.h; y++) {
|
|
26
|
+
const ym = (y + 0.5) * pv;
|
|
27
|
+
const liftD = lift > 0 ? Math.abs(ym - Math.round(ym / lift) * lift) : 1;
|
|
28
|
+
const liftGroove = 1 - smooth(0, 0.003, liftD);
|
|
29
|
+
const liftBand = lift > 0 ? hash2(Math.floor(ym / lift), 0, seed + 4) - 0.5 : 0;
|
|
30
|
+
for (let x = 0; x < c.w; x++) {
|
|
31
|
+
const i = y * c.w + x;
|
|
32
|
+
const xm = (x + 0.5) * pu;
|
|
33
|
+
const big = noiseAt(c, xm, ym, 0.25, 0.25, seed, 4);
|
|
34
|
+
const fine = noiseAt(c, xm, ym, 0.004, 0.004, seed + 2, 2);
|
|
35
|
+
const ix = Math.floor(xm / pCell) % pPer;
|
|
36
|
+
const iy = Math.floor(ym / pCell);
|
|
37
|
+
let pit = 0;
|
|
38
|
+
if (hash2(ix, iy, seed + 5) < pores) {
|
|
39
|
+
const cx = (ix + 0.2 + 0.6 * hash2(ix, iy, seed + 6)) * pCell;
|
|
40
|
+
const cy = (iy + 0.2 + 0.6 * hash2(ix, iy, seed + 7)) * pCell;
|
|
41
|
+
const rad = pCell * (0.12 + 0.25 * hash2(ix, iy, seed + 8));
|
|
42
|
+
const d = Math.hypot(xm - cx, ym - cy);
|
|
43
|
+
pit = d < rad ? 1 - d / rad : 0;
|
|
44
|
+
}
|
|
45
|
+
let tie = 0;
|
|
46
|
+
if (ties > 0) {
|
|
47
|
+
const tx = xm - (Math.floor(xm / tCell) + 0.5) * tCell;
|
|
48
|
+
const ty = ym - (Math.floor(ym / ties) + 0.5) * ties;
|
|
49
|
+
const d = Math.hypot(tx, ty);
|
|
50
|
+
tie = d < 0.012 ? 1 : 0;
|
|
51
|
+
}
|
|
52
|
+
c.height[i] =
|
|
53
|
+
0.0005 * big + 0.00015 * fine - 0.0015 * pit - 0.006 * tie - 0.0012 * liftGroove;
|
|
54
|
+
c.tone[i] =
|
|
55
|
+
(0.78 + mottle * big + 0.05 * liftBand) *
|
|
56
|
+
(1 + 0.04 * fine) *
|
|
57
|
+
(1 - 0.4 * pit) *
|
|
58
|
+
(1 - 0.5 * tie);
|
|
59
|
+
c.rough[i] = r.roughness + 0.05 * fine + 0.05 * big;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|