@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.
Files changed (567) hide show
  1. package/.claude/agents/bug-hunter.md +21 -0
  2. package/.claude/agents/docs-author.md +13 -0
  3. package/.claude/agents/dx-auditor.md +14 -0
  4. package/.claude/agents/primitive-author.md +22 -0
  5. package/.claude/agents/recipe-author.md +36 -0
  6. package/.claude/agents/visual-reviewer.md +23 -0
  7. package/.claude/commands/feature.md +28 -0
  8. package/.claude/commands/new-recipe.md +19 -0
  9. package/.claude/commands/release.md +14 -0
  10. package/.claude/commands/verify.md +9 -0
  11. package/.claude/skills/author-recipe/SKILL.md +93 -0
  12. package/CHANGELOG.md +90 -0
  13. package/CLAUDE.md +85 -0
  14. package/LICENSE +21 -0
  15. package/README.md +385 -2
  16. package/dist/chunk-6b05mnmx.js +19 -0
  17. package/dist/chunk-6b05mnmx.js.map +11 -0
  18. package/dist/chunk-7rtfjh9f.js +45 -0
  19. package/dist/chunk-7rtfjh9f.js.map +10 -0
  20. package/dist/chunk-8j4c28jt.js +4085 -0
  21. package/dist/chunk-8j4c28jt.js.map +46 -0
  22. package/dist/chunk-d1fv6hrm.js +2532 -0
  23. package/dist/chunk-d1fv6hrm.js.map +43 -0
  24. package/dist/chunk-dbq4qf52.js +902 -0
  25. package/dist/chunk-dbq4qf52.js.map +25 -0
  26. package/dist/chunk-drna2rny.js +836 -0
  27. package/dist/chunk-drna2rny.js.map +17 -0
  28. package/dist/chunk-dxvypr7y.js +89 -0
  29. package/dist/chunk-dxvypr7y.js.map +11 -0
  30. package/dist/chunk-f6yme003.js +1589 -0
  31. package/dist/chunk-f6yme003.js.map +30 -0
  32. package/dist/chunk-hg7pf34j.js +465 -0
  33. package/dist/chunk-hg7pf34j.js.map +19 -0
  34. package/dist/chunk-jq4e17tj.js +1031 -0
  35. package/dist/chunk-jq4e17tj.js.map +21 -0
  36. package/dist/chunk-y63c6zcv.js +2668 -0
  37. package/dist/chunk-y63c6zcv.js.map +53 -0
  38. package/dist/cli/args.d.ts +30 -0
  39. package/dist/cli/bin.d.ts +2 -0
  40. package/dist/cli/bin.js +348 -0
  41. package/dist/cli/bin.js.map +15 -0
  42. package/dist/cli/load.d.ts +2 -0
  43. package/dist/cli/manifest.d.ts +53 -0
  44. package/dist/cli/render-cmds.d.ts +12 -0
  45. package/dist/cli/run.d.ts +1 -0
  46. package/dist/cli/texture-cache.d.ts +4 -0
  47. package/dist/core/asset.d.ts +10 -0
  48. package/dist/core/definition.d.ts +155 -0
  49. package/dist/core/errors.d.ts +17 -0
  50. package/dist/core/family.d.ts +23 -0
  51. package/dist/core/index.d.ts +9 -0
  52. package/dist/core/index.js +69 -0
  53. package/dist/core/index.js.map +9 -0
  54. package/dist/core/mesh.d.ts +35 -0
  55. package/dist/core/part.d.ts +114 -0
  56. package/dist/core/png.d.ts +1 -0
  57. package/dist/core/rng.d.ts +12 -0
  58. package/dist/core/transform.d.ts +20 -0
  59. package/dist/core/vec.d.ts +11 -0
  60. package/dist/core/world.d.ts +16 -0
  61. package/dist/export/bin-writer.d.ts +16 -0
  62. package/dist/export/container.d.ts +3 -0
  63. package/dist/export/credits.d.ts +4 -0
  64. package/dist/export/glb-options.d.ts +48 -0
  65. package/dist/export/glb.d.ts +8 -0
  66. package/dist/export/hierarchy.d.ts +23 -0
  67. package/dist/export/index.d.ts +5 -0
  68. package/dist/export/index.js +40 -0
  69. package/dist/export/index.js.map +9 -0
  70. package/dist/export/lights.d.ts +8 -0
  71. package/dist/export/lod-coverage.d.ts +12 -0
  72. package/dist/export/materials.d.ts +23 -0
  73. package/dist/export/merge-pivots.d.ts +6 -0
  74. package/dist/export/metadata.d.ts +21 -0
  75. package/dist/export/motion.d.ts +9 -0
  76. package/dist/export/optimize-parts.d.ts +26 -0
  77. package/dist/export/primitive.d.ts +3 -0
  78. package/dist/export/tangents.d.ts +2 -0
  79. package/dist/export/textures.d.ts +22 -0
  80. package/dist/export/version.d.ts +2 -0
  81. package/dist/grammar/index.d.ts +2 -0
  82. package/dist/grammar/index.js +16 -0
  83. package/dist/grammar/index.js.map +9 -0
  84. package/dist/grammar/split.d.ts +21 -0
  85. package/dist/grammar/whyte.d.ts +5 -0
  86. package/dist/index.d.ts +11 -0
  87. package/dist/index.js +457 -0
  88. package/dist/index.js.map +9 -0
  89. package/dist/kinds/building.d.ts +44 -0
  90. package/dist/kinds/faces.d.ts +27 -0
  91. package/dist/kinds/head-armour.d.ts +5 -0
  92. package/dist/kinds/head-beard.d.ts +6 -0
  93. package/dist/kinds/head-bust.d.ts +5 -0
  94. package/dist/kinds/head-extras.d.ts +15 -0
  95. package/dist/kinds/head-face.d.ts +8 -0
  96. package/dist/kinds/head-garb.d.ts +4 -0
  97. package/dist/kinds/head-geom.d.ts +7 -0
  98. package/dist/kinds/head-hair.d.ts +4 -0
  99. package/dist/kinds/head-layout.d.ts +29 -0
  100. package/dist/kinds/head-mesh.d.ts +31 -0
  101. package/dist/kinds/head-mouth.d.ts +4 -0
  102. package/dist/kinds/head-params.d.ts +116 -0
  103. package/dist/kinds/head-presets.d.ts +19 -0
  104. package/dist/kinds/head-rim.d.ts +10 -0
  105. package/dist/kinds/head-tone.d.ts +10 -0
  106. package/dist/kinds/head-torso.d.ts +35 -0
  107. package/dist/kinds/head.d.ts +39 -0
  108. package/dist/kinds/hull-lines.d.ts +65 -0
  109. package/dist/kinds/hull-skin.d.ts +6 -0
  110. package/dist/kinds/hull.d.ts +14 -0
  111. package/dist/kinds/index.d.ts +16 -0
  112. package/dist/kinds/index.js +78 -0
  113. package/dist/kinds/index.js.map +9 -0
  114. package/dist/kinds/mass.d.ts +15 -0
  115. package/dist/kinds/openings.d.ts +30 -0
  116. package/dist/kinds/planned.d.ts +6 -0
  117. package/dist/kinds/roof-chimney.d.ts +16 -0
  118. package/dist/kinds/roof-shape.d.ts +64 -0
  119. package/dist/kinds/roof.d.ts +31 -0
  120. package/dist/kinds/vegetation-fronds.d.ts +15 -0
  121. package/dist/kinds/vegetation-impostor.d.ts +9 -0
  122. package/dist/kinds/vegetation-leaves.d.ts +15 -0
  123. package/dist/kinds/vegetation-mesh.d.ts +21 -0
  124. package/dist/kinds/vegetation-params.d.ts +90 -0
  125. package/dist/kinds/vegetation-presets.d.ts +20 -0
  126. package/dist/kinds/vegetation-skeleton.d.ts +20 -0
  127. package/dist/kinds/vegetation.d.ts +42 -0
  128. package/dist/kinds/vehicle.d.ts +42 -0
  129. package/dist/material/cc0.d.ts +4 -0
  130. package/dist/material/define.d.ts +34 -0
  131. package/dist/material/generator.d.ts +4 -0
  132. package/dist/material/index.d.ts +9 -0
  133. package/dist/material/index.js +70 -0
  134. package/dist/material/index.js.map +9 -0
  135. package/dist/material/library.d.ts +7 -0
  136. package/dist/material/material-def.d.ts +30 -0
  137. package/dist/material/paint/bond.d.ts +3 -0
  138. package/dist/material/paint/canvas.d.ts +39 -0
  139. package/dist/material/paint/cell.d.ts +4 -0
  140. package/dist/material/paint/cells-round.d.ts +5 -0
  141. package/dist/material/paint/cells.d.ts +7 -0
  142. package/dist/material/paint/compose.d.ts +5 -0
  143. package/dist/material/paint/concrete.d.ts +3 -0
  144. package/dist/material/paint/courses.d.ts +31 -0
  145. package/dist/material/paint/decode-png.d.ts +6 -0
  146. package/dist/material/paint/default-sheet.d.ts +4 -0
  147. package/dist/material/paint/fabric.d.ts +4 -0
  148. package/dist/material/paint/foliage-broad.d.ts +5 -0
  149. package/dist/material/paint/foliage-common.d.ts +35 -0
  150. package/dist/material/paint/foliage-compound.d.ts +4 -0
  151. package/dist/material/paint/foliage-conifer.d.ts +5 -0
  152. package/dist/material/paint/foliage-raster.d.ts +26 -0
  153. package/dist/material/paint/foliage.d.ts +3 -0
  154. package/dist/material/paint/image-strip.d.ts +4 -0
  155. package/dist/material/paint/kit.d.ts +5 -0
  156. package/dist/material/paint/masks.d.ts +3 -0
  157. package/dist/material/paint/metal.d.ts +3 -0
  158. package/dist/material/paint/noise.d.ts +9 -0
  159. package/dist/material/paint/normal.d.ts +8 -0
  160. package/dist/material/paint/orm.d.ts +8 -0
  161. package/dist/material/paint/planks.d.ts +3 -0
  162. package/dist/material/paint/plates.d.ts +3 -0
  163. package/dist/material/paint/shapes.d.ts +8 -0
  164. package/dist/material/paint/slate.d.ts +3 -0
  165. package/dist/material/paint/stamp.d.ts +3 -0
  166. package/dist/material/paint/strip.d.ts +3 -0
  167. package/dist/material/paint/surface.d.ts +5 -0
  168. package/dist/material/paint/types.d.ts +65 -0
  169. package/dist/material/paint/validate.d.ts +3 -0
  170. package/dist/material/palette.d.ts +14 -0
  171. package/dist/material/shader-godot.d.ts +2 -0
  172. package/dist/material/shader-three.d.ts +2 -0
  173. package/dist/material/shader.d.ts +46 -0
  174. package/dist/material/slots.d.ts +5 -0
  175. package/dist/material/texture-source.d.ts +34 -0
  176. package/dist/material/trim-sheet.d.ts +38 -0
  177. package/dist/neural/generator.d.ts +25 -0
  178. package/dist/neural/images.d.ts +17 -0
  179. package/dist/neural/index.d.ts +2 -0
  180. package/dist/neural/index.js +14 -0
  181. package/dist/neural/index.js.map +9 -0
  182. package/dist/passes/bake-ao.d.ts +17 -0
  183. package/dist/passes/grime.d.ts +15 -0
  184. package/dist/passes/index.d.ts +6 -0
  185. package/dist/passes/index.js +22 -0
  186. package/dist/passes/index.js.map +9 -0
  187. package/dist/passes/livery.d.ts +19 -0
  188. package/dist/passes/optimize-cache.d.ts +15 -0
  189. package/dist/passes/optimize-weld.d.ts +15 -0
  190. package/dist/passes/optimize.d.ts +28 -0
  191. package/dist/passes/ray-grid.d.ts +23 -0
  192. package/dist/passes/weighted-normals.d.ts +8 -0
  193. package/dist/primitives/beam.d.ts +17 -0
  194. package/dist/primitives/bevel-box.d.ts +13 -0
  195. package/dist/primitives/box.d.ts +16 -0
  196. package/dist/primitives/cell.d.ts +20 -0
  197. package/dist/primitives/check.d.ts +12 -0
  198. package/dist/primitives/closed.d.ts +11 -0
  199. package/dist/primitives/erosion.d.ts +22 -0
  200. package/dist/primitives/font-5x7.d.ts +3 -0
  201. package/dist/primitives/heightfield.d.ts +16 -0
  202. package/dist/primitives/index.d.ts +23 -0
  203. package/dist/primitives/index.js +81 -0
  204. package/dist/primitives/index.js.map +9 -0
  205. package/dist/primitives/lathe-facets.d.ts +8 -0
  206. package/dist/primitives/lathe.d.ts +26 -0
  207. package/dist/primitives/lattice.d.ts +30 -0
  208. package/dist/primitives/loft.d.ts +15 -0
  209. package/dist/primitives/mc-table.d.ts +2 -0
  210. package/dist/primitives/member.d.ts +19 -0
  211. package/dist/primitives/noise.d.ts +34 -0
  212. package/dist/primitives/planar.d.ts +10 -0
  213. package/dist/primitives/prism.d.ts +13 -0
  214. package/dist/primitives/sculpt-edit.d.ts +56 -0
  215. package/dist/primitives/sculpt-tree.d.ts +23 -0
  216. package/dist/primitives/sculpt.d.ts +27 -0
  217. package/dist/primitives/sdf-grid.d.ts +17 -0
  218. package/dist/primitives/sdf-ops.d.ts +37 -0
  219. package/dist/primitives/sdf.d.ts +21 -0
  220. package/dist/primitives/strip-uv.d.ts +11 -0
  221. package/dist/primitives/surface-nets.d.ts +7 -0
  222. package/dist/primitives/sweep.d.ts +19 -0
  223. package/dist/primitives/terrain-color.d.ts +30 -0
  224. package/dist/primitives/terrain-grid.d.ts +36 -0
  225. package/dist/primitives/text-layout.d.ts +22 -0
  226. package/dist/primitives/text.d.ts +27 -0
  227. package/dist/primitives/tiled.d.ts +22 -0
  228. package/dist/primitives/transport.d.ts +17 -0
  229. package/dist/primitives/triangulate.d.ts +2 -0
  230. package/dist/primitives/value-noise.d.ts +10 -0
  231. package/dist/qa/checks.d.ts +11 -0
  232. package/dist/qa/clash.d.ts +2 -0
  233. package/dist/qa/floating.d.ts +5 -0
  234. package/dist/qa/gates.d.ts +12 -0
  235. package/dist/qa/geometry.d.ts +21 -0
  236. package/dist/qa/index.d.ts +12 -0
  237. package/dist/qa/index.js +78 -0
  238. package/dist/qa/index.js.map +9 -0
  239. package/dist/qa/inside.d.ts +12 -0
  240. package/dist/qa/inspect.d.ts +18 -0
  241. package/dist/qa/lods.d.ts +12 -0
  242. package/dist/qa/measures.d.ts +4 -0
  243. package/dist/qa/occlusion.d.ts +3 -0
  244. package/dist/qa/sanity.d.ts +7 -0
  245. package/dist/qa/types.d.ts +12 -0
  246. package/dist/render/beauty.d.ts +40 -0
  247. package/dist/render/camera.d.ts +33 -0
  248. package/dist/render/depth-map.d.ts +38 -0
  249. package/dist/render/font.d.ts +1 -0
  250. package/dist/render/ground.d.ts +13 -0
  251. package/dist/render/index.d.ts +10 -0
  252. package/dist/render/index.js +47 -0
  253. package/dist/render/index.js.map +9 -0
  254. package/dist/render/lighting.d.ts +27 -0
  255. package/dist/render/lod-strip.d.ts +5 -0
  256. package/dist/render/metrics.d.ts +37 -0
  257. package/dist/render/options.d.ts +28 -0
  258. package/dist/render/photo-match.d.ts +19 -0
  259. package/dist/render/raster.d.ts +25 -0
  260. package/dist/render/resolve.d.ts +11 -0
  261. package/dist/render/review-prompt.d.ts +2 -0
  262. package/dist/render/review-warnings.d.ts +34 -0
  263. package/dist/render/review.d.ts +24 -0
  264. package/dist/render/scene.d.ts +18 -0
  265. package/dist/render/shade.d.ts +22 -0
  266. package/dist/render/sheet.d.ts +15 -0
  267. package/dist/render/texture.d.ts +15 -0
  268. package/dist/render/thumbnail.d.ts +8 -0
  269. package/dist/render/vis-buffer.d.ts +18 -0
  270. package/dist/testing/assert.d.ts +21 -0
  271. package/dist/testing/compiled.d.ts +15 -0
  272. package/dist/testing/hash.d.ts +1 -0
  273. package/dist/testing/index.d.ts +4 -0
  274. package/dist/testing/index.js +134 -0
  275. package/dist/testing/index.js.map +13 -0
  276. package/dist/testing/snapshot.d.ts +13 -0
  277. package/docs/README.md +56 -0
  278. package/docs/architecture/README.md +68 -0
  279. package/docs/architecture/adding-a-kind.md +44 -0
  280. package/docs/architecture/adding-a-primitive.md +49 -0
  281. package/docs/architecture/errors.md +47 -0
  282. package/docs/architecture/glb-production.md +214 -0
  283. package/docs/architecture/gltf-capabilities.md +117 -0
  284. package/docs/architecture/materials.md +151 -0
  285. package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
  286. package/docs/decisions/002-typescript-not-json.md +22 -0
  287. package/docs/decisions/003-shared-trim-sheet.md +21 -0
  288. package/docs/decisions/004-z-up-metres.md +21 -0
  289. package/docs/decisions/005-zero-runtime-deps.md +21 -0
  290. package/docs/decisions/006-single-package-subpath-exports.md +20 -0
  291. package/docs/decisions/007-cpu-contact-sheets.md +21 -0
  292. package/docs/decisions/008-surface-materials.md +21 -0
  293. package/docs/decisions/009-gltf-front-axis.md +21 -0
  294. package/docs/decisions/README.md +15 -0
  295. package/docs/idea/01-vision.md +117 -0
  296. package/docs/idea/02-dsl.md +343 -0
  297. package/docs/idea/03-ir.md +97 -0
  298. package/docs/idea/04-materials.md +114 -0
  299. package/docs/idea/05-agent-loop.md +141 -0
  300. package/docs/idea/06-qa.md +101 -0
  301. package/docs/idea/07-what-fits.md +45 -0
  302. package/docs/idea/08-neural-adapters.md +78 -0
  303. package/docs/idea/09-kinds.md +105 -0
  304. package/docs/idea/10-sculpting.md +166 -0
  305. package/docs/idea/11-materials-authoring.md +132 -0
  306. package/docs/idea/12-blender-bridge.md +136 -0
  307. package/docs/idea/13-asset-pipeline-research.md +89 -0
  308. package/docs/landscape.md +49 -0
  309. package/docs/plans/roadmap.md +139 -0
  310. package/examples/build-with-bun.ts +41 -0
  311. package/llms.txt +47 -0
  312. package/package.json +100 -4
  313. package/recipes/README.md +38 -0
  314. package/recipes/asteroid/README.md +46 -0
  315. package/recipes/asteroid/asteroid.ts +336 -0
  316. package/recipes/boulders/README.md +28 -0
  317. package/recipes/boulders/boulders.ts +117 -0
  318. package/recipes/boxcar/README.md +53 -0
  319. package/recipes/boxcar/boxcar.ts +1000 -0
  320. package/recipes/cargo_container_scifi/README.md +52 -0
  321. package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
  322. package/recipes/dropship/README.md +84 -0
  323. package/recipes/dropship/dropship.ts +1144 -0
  324. package/recipes/eiffel_tower/README.md +110 -0
  325. package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
  326. package/recipes/goblin_head/README.md +75 -0
  327. package/recipes/goblin_head/goblin_head.ts +38 -0
  328. package/recipes/human_head/README.md +68 -0
  329. package/recipes/human_head/human_head.ts +38 -0
  330. package/recipes/loco_440/README.md +32 -0
  331. package/recipes/loco_440/loco_440.ts +1404 -0
  332. package/recipes/material_swatches/README.md +21 -0
  333. package/recipes/material_swatches/material_swatches.ts +189 -0
  334. package/recipes/mountain_range/README.md +45 -0
  335. package/recipes/mountain_range/mountain_range.ts +128 -0
  336. package/recipes/oak/README.md +30 -0
  337. package/recipes/oak/oak.ts +19 -0
  338. package/recipes/orc_head/README.md +78 -0
  339. package/recipes/orc_head/orc_head.ts +41 -0
  340. package/recipes/palm/README.md +30 -0
  341. package/recipes/palm/palm.ts +19 -0
  342. package/recipes/pine/README.md +29 -0
  343. package/recipes/pine/pine.ts +19 -0
  344. package/recipes/signal_gantry/README.md +36 -0
  345. package/recipes/signal_gantry/signal_gantry.ts +522 -0
  346. package/recipes/station_halt/README.md +39 -0
  347. package/recipes/station_halt/station_halt.ts +722 -0
  348. package/recipes/t34/README.md +44 -0
  349. package/recipes/t34/t34.ts +1068 -0
  350. package/recipes/tiger_1/README.md +75 -0
  351. package/recipes/tiger_1/tiger_1.ts +1370 -0
  352. package/recipes/water_tower/README.md +40 -0
  353. package/recipes/water_tower/water_tower.ts +542 -0
  354. package/src/cli/args.ts +125 -0
  355. package/src/cli/bin.ts +7 -0
  356. package/src/cli/load.ts +17 -0
  357. package/src/cli/manifest.ts +95 -0
  358. package/src/cli/render-cmds.ts +66 -0
  359. package/src/cli/run.ts +192 -0
  360. package/src/cli/texture-cache.ts +24 -0
  361. package/src/core/asset.ts +129 -0
  362. package/src/core/definition.ts +156 -0
  363. package/src/core/errors.ts +50 -0
  364. package/src/core/family.ts +50 -0
  365. package/src/core/index.ts +66 -0
  366. package/src/core/mesh.ts +114 -0
  367. package/src/core/part.ts +130 -0
  368. package/src/core/png.ts +49 -0
  369. package/src/core/rng.ts +49 -0
  370. package/src/core/transform.ts +90 -0
  371. package/src/core/vec.ts +26 -0
  372. package/src/core/world.ts +80 -0
  373. package/src/export/bin-writer.ts +79 -0
  374. package/src/export/container.ts +35 -0
  375. package/src/export/credits.ts +25 -0
  376. package/src/export/glb-options.ts +53 -0
  377. package/src/export/glb.ts +187 -0
  378. package/src/export/hierarchy.ts +122 -0
  379. package/src/export/index.ts +34 -0
  380. package/src/export/lights.ts +49 -0
  381. package/src/export/lod-coverage.ts +71 -0
  382. package/src/export/materials.ts +126 -0
  383. package/src/export/merge-pivots.ts +146 -0
  384. package/src/export/metadata.ts +62 -0
  385. package/src/export/motion.ts +70 -0
  386. package/src/export/optimize-parts.ts +80 -0
  387. package/src/export/primitive.ts +35 -0
  388. package/src/export/tangents.ts +75 -0
  389. package/src/export/textures.ts +88 -0
  390. package/src/export/version.ts +6 -0
  391. package/src/grammar/index.ts +4 -0
  392. package/src/grammar/split.ts +87 -0
  393. package/src/grammar/whyte.ts +20 -0
  394. package/src/index.ts +14 -0
  395. package/src/kinds/building.ts +153 -0
  396. package/src/kinds/faces.ts +138 -0
  397. package/src/kinds/head-armour.ts +181 -0
  398. package/src/kinds/head-beard.ts +133 -0
  399. package/src/kinds/head-bust.ts +184 -0
  400. package/src/kinds/head-extras.ts +150 -0
  401. package/src/kinds/head-face.ts +242 -0
  402. package/src/kinds/head-garb.ts +181 -0
  403. package/src/kinds/head-geom.ts +31 -0
  404. package/src/kinds/head-hair.ts +193 -0
  405. package/src/kinds/head-layout.ts +63 -0
  406. package/src/kinds/head-mesh.ts +195 -0
  407. package/src/kinds/head-mouth.ts +111 -0
  408. package/src/kinds/head-params.ts +162 -0
  409. package/src/kinds/head-presets.ts +155 -0
  410. package/src/kinds/head-rim.ts +128 -0
  411. package/src/kinds/head-tone.ts +52 -0
  412. package/src/kinds/head-torso.ts +230 -0
  413. package/src/kinds/head.ts +176 -0
  414. package/src/kinds/hull-lines.ts +160 -0
  415. package/src/kinds/hull-skin.ts +83 -0
  416. package/src/kinds/hull.ts +209 -0
  417. package/src/kinds/index.ts +63 -0
  418. package/src/kinds/mass.ts +110 -0
  419. package/src/kinds/openings.ts +141 -0
  420. package/src/kinds/planned.ts +12 -0
  421. package/src/kinds/roof-chimney.ts +100 -0
  422. package/src/kinds/roof-shape.ts +230 -0
  423. package/src/kinds/roof.ts +250 -0
  424. package/src/kinds/vegetation-fronds.ts +126 -0
  425. package/src/kinds/vegetation-impostor.ts +118 -0
  426. package/src/kinds/vegetation-leaves.ts +168 -0
  427. package/src/kinds/vegetation-mesh.ts +174 -0
  428. package/src/kinds/vegetation-params.ts +147 -0
  429. package/src/kinds/vegetation-presets.ts +184 -0
  430. package/src/kinds/vegetation-skeleton.ts +168 -0
  431. package/src/kinds/vegetation.ts +199 -0
  432. package/src/kinds/vehicle.ts +144 -0
  433. package/src/material/cc0.ts +41 -0
  434. package/src/material/define.ts +130 -0
  435. package/src/material/generator.ts +63 -0
  436. package/src/material/index.ts +60 -0
  437. package/src/material/library.ts +196 -0
  438. package/src/material/material-def.ts +84 -0
  439. package/src/material/paint/bond.ts +60 -0
  440. package/src/material/paint/canvas.ts +101 -0
  441. package/src/material/paint/cell.ts +39 -0
  442. package/src/material/paint/cells-round.ts +119 -0
  443. package/src/material/paint/cells.ts +111 -0
  444. package/src/material/paint/compose.ts +53 -0
  445. package/src/material/paint/concrete.ts +62 -0
  446. package/src/material/paint/courses.ts +85 -0
  447. package/src/material/paint/decode-png.ts +106 -0
  448. package/src/material/paint/default-sheet.ts +73 -0
  449. package/src/material/paint/fabric.ts +63 -0
  450. package/src/material/paint/foliage-broad.ts +126 -0
  451. package/src/material/paint/foliage-common.ts +93 -0
  452. package/src/material/paint/foliage-compound.ts +135 -0
  453. package/src/material/paint/foliage-conifer.ts +143 -0
  454. package/src/material/paint/foliage-raster.ts +196 -0
  455. package/src/material/paint/foliage.ts +23 -0
  456. package/src/material/paint/image-strip.ts +88 -0
  457. package/src/material/paint/kit.ts +37 -0
  458. package/src/material/paint/masks.ts +46 -0
  459. package/src/material/paint/metal.ts +109 -0
  460. package/src/material/paint/noise.ts +52 -0
  461. package/src/material/paint/normal.ts +52 -0
  462. package/src/material/paint/orm.ts +108 -0
  463. package/src/material/paint/planks.ts +101 -0
  464. package/src/material/paint/plates.ts +72 -0
  465. package/src/material/paint/shapes.ts +28 -0
  466. package/src/material/paint/slate.ts +52 -0
  467. package/src/material/paint/stamp.ts +40 -0
  468. package/src/material/paint/strip.ts +48 -0
  469. package/src/material/paint/surface.ts +60 -0
  470. package/src/material/paint/types.ts +94 -0
  471. package/src/material/paint/validate.ts +74 -0
  472. package/src/material/palette.ts +46 -0
  473. package/src/material/shader-godot.ts +77 -0
  474. package/src/material/shader-three.ts +103 -0
  475. package/src/material/shader.ts +100 -0
  476. package/src/material/slots.ts +18 -0
  477. package/src/material/texture-source.ts +86 -0
  478. package/src/material/trim-sheet.ts +166 -0
  479. package/src/neural/generator.ts +37 -0
  480. package/src/neural/images.ts +27 -0
  481. package/src/neural/index.ts +10 -0
  482. package/src/passes/bake-ao.ts +183 -0
  483. package/src/passes/grime.ts +37 -0
  484. package/src/passes/index.ts +8 -0
  485. package/src/passes/livery.ts +66 -0
  486. package/src/passes/optimize-cache.ts +175 -0
  487. package/src/passes/optimize-weld.ts +147 -0
  488. package/src/passes/optimize.ts +85 -0
  489. package/src/passes/ray-grid.ts +237 -0
  490. package/src/passes/weighted-normals.ts +125 -0
  491. package/src/primitives/beam.ts +60 -0
  492. package/src/primitives/bevel-box.ts +140 -0
  493. package/src/primitives/box.ts +90 -0
  494. package/src/primitives/cell.ts +115 -0
  495. package/src/primitives/check.ts +49 -0
  496. package/src/primitives/closed.ts +74 -0
  497. package/src/primitives/erosion.ts +183 -0
  498. package/src/primitives/font-5x7.ts +117 -0
  499. package/src/primitives/heightfield.ts +101 -0
  500. package/src/primitives/index.ts +56 -0
  501. package/src/primitives/lathe-facets.ts +45 -0
  502. package/src/primitives/lathe.ts +174 -0
  503. package/src/primitives/lattice.ts +131 -0
  504. package/src/primitives/loft.ts +174 -0
  505. package/src/primitives/mc-table.ts +41 -0
  506. package/src/primitives/member.ts +130 -0
  507. package/src/primitives/noise.ts +157 -0
  508. package/src/primitives/planar.ts +55 -0
  509. package/src/primitives/prism.ts +85 -0
  510. package/src/primitives/sculpt-edit.ts +212 -0
  511. package/src/primitives/sculpt-tree.ts +226 -0
  512. package/src/primitives/sculpt.ts +80 -0
  513. package/src/primitives/sdf-grid.ts +131 -0
  514. package/src/primitives/sdf-ops.ts +145 -0
  515. package/src/primitives/sdf.ts +83 -0
  516. package/src/primitives/strip-uv.ts +26 -0
  517. package/src/primitives/surface-nets.ts +161 -0
  518. package/src/primitives/sweep.ts +162 -0
  519. package/src/primitives/terrain-color.ts +85 -0
  520. package/src/primitives/terrain-grid.ts +137 -0
  521. package/src/primitives/text-layout.ts +85 -0
  522. package/src/primitives/text.ts +197 -0
  523. package/src/primitives/tiled.ts +93 -0
  524. package/src/primitives/transport.ts +85 -0
  525. package/src/primitives/triangulate.ts +42 -0
  526. package/src/primitives/value-noise.ts +62 -0
  527. package/src/qa/checks.ts +110 -0
  528. package/src/qa/clash.ts +177 -0
  529. package/src/qa/floating.ts +63 -0
  530. package/src/qa/gates.ts +126 -0
  531. package/src/qa/geometry.ts +187 -0
  532. package/src/qa/index.ts +28 -0
  533. package/src/qa/inside.ts +114 -0
  534. package/src/qa/inspect.ts +107 -0
  535. package/src/qa/lods.ts +97 -0
  536. package/src/qa/measures.ts +67 -0
  537. package/src/qa/occlusion.ts +166 -0
  538. package/src/qa/sanity.ts +137 -0
  539. package/src/qa/types.ts +17 -0
  540. package/src/render/beauty.ts +115 -0
  541. package/src/render/camera.ts +142 -0
  542. package/src/render/depth-map.ts +191 -0
  543. package/src/render/font.ts +76 -0
  544. package/src/render/ground.ts +87 -0
  545. package/src/render/index.ts +34 -0
  546. package/src/render/lighting.ts +121 -0
  547. package/src/render/lod-strip.ts +34 -0
  548. package/src/render/metrics.ts +147 -0
  549. package/src/render/options.ts +81 -0
  550. package/src/render/photo-match.ts +28 -0
  551. package/src/render/raster.ts +143 -0
  552. package/src/render/resolve.ts +109 -0
  553. package/src/render/review-prompt.ts +59 -0
  554. package/src/render/review-warnings.ts +92 -0
  555. package/src/render/review.ts +103 -0
  556. package/src/render/scene.ts +119 -0
  557. package/src/render/shade.ts +204 -0
  558. package/src/render/sheet.ts +88 -0
  559. package/src/render/texture.ts +97 -0
  560. package/src/render/thumbnail.ts +75 -0
  561. package/src/render/vis-buffer.ts +119 -0
  562. package/src/testing/assert.ts +115 -0
  563. package/src/testing/compiled.ts +31 -0
  564. package/src/testing/hash.ts +14 -0
  565. package/src/testing/index.ts +14 -0
  566. package/src/testing/snapshot.ts +37 -0
  567. package/src/types/gltf-validator.d.ts +13 -0
@@ -0,0 +1,37 @@
1
+ // The helper kit a project painter (`defineMaterial({ paint })`) receives, and the post-check that
2
+ // its layers stayed finite: a NaN written by user code fails loudly instead of corrupting a sheet.
3
+
4
+ import { Ts23dError } from '../../core/index.ts';
5
+ import { type Canvas, noiseAt, smooth } from './canvas.ts';
6
+ import { hash2 } from './noise.ts';
7
+ import type { PaintKit } from './types.ts';
8
+
9
+ export function paintKit(c: Canvas, seed: number): PaintKit {
10
+ const pu = c.metresU / c.w;
11
+ const pv = c.metresV / c.h;
12
+ return {
13
+ seed,
14
+ noise: (xm, ym, sizeU, sizeV, salt = 0, octaves = 4) =>
15
+ noiseAt(c, xm, ym, sizeU, sizeV, seed + salt * 7919, octaves),
16
+ hash: (a, b, salt = 0) => hash2(a, b, seed + salt),
17
+ smooth,
18
+ each(fn) {
19
+ for (let y = 0; y < c.h; y++) {
20
+ const ym = (y + 0.5) * pv;
21
+ for (let x = 0; x < c.w; x++) fn(y * c.w + x, (x + 0.5) * pu, ym);
22
+ }
23
+ },
24
+ };
25
+ }
26
+
27
+ /** Throw T3D_MATERIAL_INVALID if any layer holds a non-finite value. */
28
+ export function checkFinite(c: Canvas, where: string): void {
29
+ const layers = { height: c.height, tone: c.tone, rough: c.rough, metal: c.metal, mix: c.mix };
30
+ for (const [name, a] of Object.entries(layers))
31
+ for (let i = 0; i < a.length; i++)
32
+ if (!Number.isFinite(a[i] ?? 0))
33
+ throw new Ts23dError('T3D_MATERIAL_INVALID', {
34
+ cause: `${where}: the painter wrote ${a[i]} into '${name}' at texel ${i}`,
35
+ fix: 'write finite numbers only (guard divisions; clamp with Math.min/Math.max)',
36
+ });
37
+ }
@@ -0,0 +1,46 @@
1
+ // Weathering masks applied over a painted canvas: rust streaks (vertical runs from a patchy
2
+ // source, darker and rougher, killing metalness) and grime (dirt pooling toward the bottom of the
3
+ // region — world-down is low V — plus soft vertical rain streaks). Rust also pushes `mix` toward
4
+ // albedo2, which only shows on material strips that carry a colour pair.
5
+
6
+ import { type Canvas, noiseAt, smooth } from './canvas.ts';
7
+
8
+ export function rustStreaks(c: Canvas, amount: number, seed: number): void {
9
+ if (amount <= 0) return;
10
+ const pu = c.metresU / c.w;
11
+ const pv = c.metresV / c.h;
12
+ for (let y = 0; y < c.h; y++) {
13
+ const ym = (y + 0.5) * pv;
14
+ for (let x = 0; x < c.w; x++) {
15
+ const xm = (x + 0.5) * pu;
16
+ const run = noiseAt(c, xm, ym, 0.025, 0.6, seed, 3);
17
+ const patch = noiseAt(c, xm, ym, 0.3, 0.3, seed + 1, 2);
18
+ const m = amount * smooth(0.15, 0.6, run) * smooth(-0.1, 0.4, patch);
19
+ if (m <= 0) continue;
20
+ const i = y * c.w + x;
21
+ c.tone[i] = (c.tone[i] ?? 1) * (1 - 0.4 * m);
22
+ c.rough[i] = Math.min(1, (c.rough[i] ?? 0.8) + 0.2 * m);
23
+ c.metal[i] = (c.metal[i] ?? 0) * (1 - m);
24
+ c.mix[i] = Math.min(1, (c.mix[i] ?? 0) + m);
25
+ }
26
+ }
27
+ }
28
+
29
+ export function grime(c: Canvas, amount: number, seed: number): void {
30
+ if (amount <= 0) return;
31
+ const pu = c.metresU / c.w;
32
+ const pv = c.metresV / c.h;
33
+ for (let y = 0; y < c.h; y++) {
34
+ const ym = (y + 0.5) * pv;
35
+ const low = (1 - ym / c.metresV) ** 2;
36
+ for (let x = 0; x < c.w; x++) {
37
+ const xm = (x + 0.5) * pu;
38
+ const streak = smooth(0, 0.7, noiseAt(c, xm, ym, 0.05, 0.5, seed, 3));
39
+ const blot = 0.6 + 0.4 * noiseAt(c, xm, ym, 0.2, 0.2, seed + 1, 3);
40
+ const m = amount * Math.min(1, low * blot + 0.35 * streak);
41
+ const i = y * c.w + x;
42
+ c.tone[i] = (c.tone[i] ?? 1) * (1 - 0.3 * m);
43
+ c.rough[i] = Math.min(1, (c.rough[i] ?? 0.8) + 0.1 * m);
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,109 @@
1
+ // Bare and finished metal sheet (kind 'metal'): a continuous metallic base with polish mottling,
2
+ // then optional finishes, each a param in 0..1 — `brush` (fine streaks along U, brushed aluminium),
3
+ // `cast` (sand-cast pits and a rougher skin), `spangle` (galvanised zinc crystals), `oxide`
4
+ // (patches of rust / verdigris: blend to albedo2, dielectric, rough, slightly raised) and `paint`
5
+ // (a dielectric paint film over it whose `chips` reveal the oxidised metal). Rust runs down from
6
+ // the oxide (`streaks`).
7
+ // params: brush 0 · cast 0 · spangle 0 · oxide 0 · paint 0 · paintRough 0.45 · chips 0.25
8
+ // crystal 0.03 (spangle size, m) · streaks 0
9
+
10
+ import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
11
+ import { rustStreaks } from './masks.ts';
12
+ import { hash2 } from './noise.ts';
13
+ import type { StripRecipe } from './types.ts';
14
+
15
+ /** Nearest jittered grid point id on a `size` m lattice that wraps after `wrapU` metres. */
16
+ function crystal(xm: number, ym: number, size: number, cellsU: number, seed: number): number {
17
+ const gx = Math.floor(xm / size);
18
+ const gy = Math.floor(ym / size);
19
+ let best = 1e9;
20
+ let id = 0;
21
+ for (let j = -1; j <= 1; j++)
22
+ for (let i = -1; i <= 1; i++) {
23
+ const cx = (((gx + i) % cellsU) + cellsU) % cellsU;
24
+ const px = (gx + i + hash2(cx, gy + j, seed)) * size;
25
+ const py = (gy + j + hash2(cx, gy + j, seed + 1)) * size;
26
+ const d = (xm - px) ** 2 + (ym - py) ** 2;
27
+ if (d < best) {
28
+ best = d;
29
+ id = hash2(cx, gy + j, seed + 2) * 1e6;
30
+ }
31
+ }
32
+ return id;
33
+ }
34
+
35
+ export function paintMetal(c: Canvas, r: StripRecipe, seed: number): void {
36
+ const p = r.params;
37
+ const brush = param(p, 'brush', 0);
38
+ const cast = param(p, 'cast', 0);
39
+ const spangle = param(p, 'spangle', 0);
40
+ const oxide = param(p, 'oxide', 0);
41
+ const paint = param(p, 'paint', 0);
42
+ const paintRough = param(p, 'paintRough', 0.45);
43
+ const chips = param(p, 'chips', 0.25);
44
+ const size = param(p, 'crystal', 0.03);
45
+ const metal = r.metalness ?? 1;
46
+ const cellsU = Math.max(1, Math.round(c.metresU / size));
47
+ const cs = c.metresU / cellsU;
48
+ const pu = c.metresU / c.w;
49
+ const pv = c.metresV / c.h;
50
+ for (let y = 0; y < c.h; y++) {
51
+ const ym = (y + 0.5) * pv;
52
+ for (let x = 0; x < c.w; x++) {
53
+ const i = y * c.w + x;
54
+ const xm = (x + 0.5) * pu;
55
+ const big = noiseAt(c, xm, ym, 0.25, 0.25, seed, 4);
56
+ const fine = noiseAt(c, xm, ym, 0.01, 0.01, seed + 1, 2);
57
+ let tone = 1 + 0.05 * big;
58
+ let rough = r.roughness + 0.06 * big + 0.03 * fine;
59
+ let h = 0.00015 * big;
60
+ if (brush > 0) {
61
+ const s = noiseAt(c, xm, ym, 0.6, 0.002, seed + 2, 3);
62
+ tone *= 1 + 0.06 * brush * s;
63
+ rough += 0.08 * brush * s;
64
+ h += 0.00003 * brush * s;
65
+ }
66
+ if (cast > 0) {
67
+ const pit = noiseAt(c, xm, ym, 0.012, 0.012, seed + 3, 3);
68
+ const lump = noiseAt(c, xm, ym, 0.08, 0.08, seed + 4, 3);
69
+ h += cast * (0.0006 * pit + 0.0008 * lump);
70
+ rough += cast * (0.1 + 0.12 * pit);
71
+ tone *= 1 + cast * (0.16 * pit + 0.08 * lump - 0.04);
72
+ }
73
+ if (spangle > 0) {
74
+ const k = crystal(xm, ym, cs, cellsU, seed + 5);
75
+ const v = hash2(k, 7, seed) * 2 - 1;
76
+ tone *= 1 + 0.12 * spangle * v;
77
+ rough += 0.12 * spangle * v;
78
+ }
79
+ // Oxide: patchy, gathering in low-frequency blotches; dielectric, rough and a touch proud.
80
+ let ox = 0;
81
+ if (oxide > 0) {
82
+ const blot = noiseAt(c, xm, ym, 0.18, 0.18, seed + 6, 5);
83
+ const grain = noiseAt(c, xm, ym, 0.02, 0.02, seed + 7, 3);
84
+ ox = smooth(0.45, 0.75, blot * 0.5 + 0.5 + oxide - 0.5 + 0.15 * grain);
85
+ h += ox * 0.0004 * (1 + grain);
86
+ }
87
+ let m = metal * (1 - ox);
88
+ let mix = ox;
89
+ rough = rough * (1 - ox) + (0.88 + 0.06 * fine) * ox;
90
+ if (paint > 0) {
91
+ // Chips gather where the noise peaks; the film is everywhere else.
92
+ const n = noiseAt(c, xm, ym, 0.05, 0.05, seed + 8, 4);
93
+ const chip = smooth(0.42, 0.5, n * 0.5 + 0.5 - (1 - chips) * 0.5 + 0.1);
94
+ const film = paint * (1 - chip);
95
+ m *= 1 - film;
96
+ mix = mix * (1 - film) + chip * paint;
97
+ rough = rough * (1 - film) + (paintRough + 0.05 * big) * film;
98
+ tone = tone * (1 - film) + (0.96 + 0.03 * big) * film;
99
+ h -= chip * paint * 0.00015;
100
+ }
101
+ c.height[i] = h;
102
+ c.tone[i] = tone;
103
+ c.rough[i] = Math.min(1, Math.max(0, rough));
104
+ c.metal[i] = Math.min(1, Math.max(0, m));
105
+ c.mix[i] = mix;
106
+ }
107
+ }
108
+ rustStreaks(c, param(p, 'streaks', 0), seed + 77);
109
+ }
@@ -0,0 +1,52 @@
1
+ // Seeded integer-hash value noise and fBm, periodic along X so strips tile seamlessly along U.
2
+ // Pure functions of (coords, seed): no state, no allocation, safe in per-pixel loops.
3
+
4
+ /** Integer hash of a lattice point to [0, 1). */
5
+ export function hash2(ix: number, iy: number, seed: number): number {
6
+ let h = Math.imul(ix | 0, 0x27d4eb2d) ^ Math.imul(iy | 0, 0x165667b1) ^ seed;
7
+ h = Math.imul(h ^ (h >>> 15), 0x85ebca6b);
8
+ h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35);
9
+ return ((h ^ (h >>> 16)) >>> 0) / 4294967296;
10
+ }
11
+
12
+ /** Value noise in [0, 1] on a unit lattice, periodic in x with an integer `period` (cells). */
13
+ export function valueNoise(x: number, y: number, period: number, seed: number): number {
14
+ const xi = Math.floor(x);
15
+ const yi = Math.floor(y);
16
+ const fx = x - xi;
17
+ const fy = y - yi;
18
+ const sx = fx * fx * (3 - 2 * fx);
19
+ const sy = fy * fy * (3 - 2 * fy);
20
+ const x0 = ((xi % period) + period) % period;
21
+ const x1 = (x0 + 1) % period;
22
+ const a = hash2(x0, yi, seed);
23
+ const b = hash2(x1, yi, seed);
24
+ const c = hash2(x0, yi + 1, seed);
25
+ const d = hash2(x1, yi + 1, seed);
26
+ return a + (b - a) * sx + (c - a + (d - c - b + a) * sx) * sy;
27
+ }
28
+
29
+ /**
30
+ * Fractal Brownian motion in [-1, 1]: `octaves` layers of value noise, each at double frequency
31
+ * and `gain` amplitude. Doubling keeps the period an integer, so the sum stays tileable.
32
+ */
33
+ export function fbm(
34
+ x: number,
35
+ y: number,
36
+ period: number,
37
+ seed: number,
38
+ octaves = 4,
39
+ gain = 0.5,
40
+ ): number {
41
+ let sum = 0;
42
+ let amp = 1;
43
+ let norm = 0;
44
+ let f = 1;
45
+ for (let o = 0; o < octaves; o++) {
46
+ sum += amp * valueNoise(x * f, y * f, period * f, seed + o * 1013);
47
+ norm += amp;
48
+ amp *= gain;
49
+ f *= 2;
50
+ }
51
+ return (sum / norm) * 2 - 1;
52
+ }
@@ -0,0 +1,52 @@
1
+ // Normal from height: a Sobel gradient of the canvas height (metres) over its texel size (metres),
2
+ // so relief reads at true scale. glTF tangent space, +Y = toward decreasing V (OpenGL "green up").
3
+ // Wraps along U for strips, clamps for cells and along V.
4
+
5
+ import type { Canvas } from './canvas.ts';
6
+
7
+ /** Neighbour column indices, precomputed so the hot loop does no modulo. */
8
+ export function neighbours(n: number, wrap: boolean): { lo: Int32Array; hi: Int32Array } {
9
+ const lo = new Int32Array(n);
10
+ const hi = new Int32Array(n);
11
+ for (let i = 0; i < n; i++) {
12
+ lo[i] = i > 0 ? i - 1 : wrap ? n - 1 : 0;
13
+ hi[i] = i < n - 1 ? i + 1 : wrap ? 0 : n - 1;
14
+ }
15
+ return { lo, hi };
16
+ }
17
+
18
+ /** RGBA8 tangent-space normals for the canvas; `strength` scales the slope. */
19
+ export function sobelNormals(c: Canvas, strength = 1): Uint8Array {
20
+ const { w, h, height } = c;
21
+ const out = new Uint8Array(w * h * 4);
22
+ const cx = neighbours(w, c.wrapU);
23
+ const cy = neighbours(h, false);
24
+ const sx = strength / (8 * (c.metresU / w));
25
+ const sy = strength / (8 * (c.metresV / h));
26
+ for (let y = 0; y < h; y++) {
27
+ const r0 = (cy.lo[y] ?? 0) * w;
28
+ const r1 = y * w;
29
+ const r2 = (cy.hi[y] ?? 0) * w;
30
+ for (let x = 0; x < w; x++) {
31
+ const xl = cx.lo[x] ?? 0;
32
+ const xr = cx.hi[x] ?? 0;
33
+ const tl = height[r0 + xl] ?? 0;
34
+ const tc = height[r0 + x] ?? 0;
35
+ const tr = height[r0 + xr] ?? 0;
36
+ const ml = height[r1 + xl] ?? 0;
37
+ const mr = height[r1 + xr] ?? 0;
38
+ const bl = height[r2 + xl] ?? 0;
39
+ const bc = height[r2 + x] ?? 0;
40
+ const br = height[r2 + xr] ?? 0;
41
+ const gx = (tr + 2 * mr + br - tl - 2 * ml - bl) * sx;
42
+ const gy = (bl + 2 * bc + br - tl - 2 * tc - tr) * sy;
43
+ const inv = 1 / Math.sqrt(gx * gx + gy * gy + 1);
44
+ const o = (r1 + x) * 4;
45
+ out[o] = Math.round((-gx * inv * 0.5 + 0.5) * 255);
46
+ out[o + 1] = Math.round((gy * inv * 0.5 + 0.5) * 255);
47
+ out[o + 2] = Math.round((inv * 0.5 + 0.5) * 255);
48
+ out[o + 3] = 255;
49
+ }
50
+ }
51
+ return out;
52
+ }
@@ -0,0 +1,108 @@
1
+ // ORM + albedo packing. Occlusion comes from the height cavity (blurred height minus height: deep
2
+ // where a texel sits below its surroundings — mortar, seams, grain); roughness and metalness are
3
+ // the painted layers. Albedo is the linear tone, sRGB-encoded, with a mild cavity darkening baked
4
+ // in so engines that ignore the occlusion map still read the joints.
5
+
6
+ import type { Canvas } from './canvas.ts';
7
+
8
+ /** Depth (metres) below the local mean that counts as fully occluded. */
9
+ const AO_DEPTH = 0.006;
10
+ const AO_STRENGTH = 0.75;
11
+ /** Blur radius of the cavity estimate, metres. */
12
+ const AO_RADIUS = 0.02;
13
+
14
+ /** Separable sliding-window box blur: O(1) per texel. Wraps along x when `wrap`, else clamps. */
15
+ function boxBlur(
16
+ src: Float32Array,
17
+ w: number,
18
+ h: number,
19
+ rx: number,
20
+ ry: number,
21
+ wrap: boolean,
22
+ ): Float32Array {
23
+ const tmp = new Float32Array(src.length);
24
+ const out = new Float32Array(src.length);
25
+ const ix = (i: number): number =>
26
+ i < 0 ? (wrap ? (i % w) + w : 0) : i >= w ? (wrap ? i % w : w - 1) : i;
27
+ for (let y = 0; y < h; y++) {
28
+ const row = y * w;
29
+ let s = 0;
30
+ for (let k = -rx; k <= rx; k++) s += src[row + ix(k)] ?? 0;
31
+ for (let x = 0; x < w; x++) {
32
+ tmp[row + x] = s / (2 * rx + 1);
33
+ s += (src[row + ix(x + rx + 1)] ?? 0) - (src[row + ix(x - rx)] ?? 0);
34
+ }
35
+ }
36
+ const iy = (j: number): number => (j < 0 ? 0 : j >= h ? h - 1 : j);
37
+ for (let x = 0; x < w; x++) {
38
+ let s = 0;
39
+ for (let k = -ry; k <= ry; k++) s += tmp[iy(k) * w + x] ?? 0;
40
+ for (let y = 0; y < h; y++) {
41
+ out[y * w + x] = s / (2 * ry + 1);
42
+ s += (tmp[iy(y + ry + 1) * w + x] ?? 0) - (tmp[iy(y - ry) * w + x] ?? 0);
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+
48
+ /** Per-texel ambient occlusion in [1 - AO_STRENGTH, 1]. */
49
+ export function cavityOcclusion(c: Canvas): Float32Array {
50
+ const rx = Math.max(1, Math.round(AO_RADIUS / (c.metresU / c.w)));
51
+ const ry = Math.max(1, Math.round(AO_RADIUS / (c.metresV / c.h)));
52
+ const blur = boxBlur(boxBlur(c.height, c.w, c.h, rx, ry, c.wrapU), c.w, c.h, rx, ry, c.wrapU);
53
+ const ao = new Float32Array(c.height.length);
54
+ for (let i = 0; i < ao.length; i++) {
55
+ const cavity = (blur[i] ?? 0) - (c.height[i] ?? 0);
56
+ const t = cavity <= 0 ? 0 : cavity >= AO_DEPTH ? 1 : cavity / AO_DEPTH;
57
+ ao[i] = (1 - AO_STRENGTH * t) * (c.occl[i] ?? 1);
58
+ }
59
+ return ao;
60
+ }
61
+
62
+ export const SRGB = new Uint8Array(4096).map((_, i) => {
63
+ const l = i / 4095;
64
+ const s = l <= 0.0031308 ? l * 12.92 : 1.055 * l ** (1 / 2.4) - 0.055;
65
+ return Math.round(s * 255);
66
+ });
67
+
68
+ const byte = (v: number): number => (v <= 0 ? 0 : v >= 1 ? 255 : Math.round(v * 255));
69
+
70
+ /** RGBA8 ORM: R = occlusion, G = roughness, B = metalness, A = 255. */
71
+ export function packOrm(c: Canvas, ao: Float32Array): Uint8Array {
72
+ const out = new Uint8Array(c.w * c.h * 4);
73
+ for (let i = 0, o = 0; i < ao.length; i++, o += 4) {
74
+ out[o] = byte(ao[i] ?? 1);
75
+ out[o + 1] = byte(c.rough[i] ?? 0.8);
76
+ out[o + 2] = byte(c.metal[i] ?? 0);
77
+ out[o + 3] = 255;
78
+ }
79
+ return out;
80
+ }
81
+
82
+ const enc = (t: number): number => SRGB[t <= 0 ? 0 : t >= 1 ? 4095 : Math.round(t * 4095)] ?? 255;
83
+
84
+ /** RGBA8 sRGB albedo: the stamped image if any, else (colour pair by mix or grey) × tone × cavity. */
85
+ export function packAlbedo(c: Canvas, ao: Float32Array): Uint8Array {
86
+ if (c.rgba) return c.rgba;
87
+ const out = new Uint8Array(c.w * c.h * 4);
88
+ const pair = c.colours;
89
+ if (pair) {
90
+ const [a, b] = pair;
91
+ for (let i = 0, o = 0; i < ao.length; i++, o += 4) {
92
+ const t = (c.tone[i] ?? 1) * (0.8 + 0.2 * (ao[i] ?? 1));
93
+ const m = Math.min(1, Math.max(0, c.mix[i] ?? 0));
94
+ for (let k = 0; k < 3; k++) out[o + k] = enc(((a[k] ?? 1) * (1 - m) + (b[k] ?? 1) * m) * t);
95
+ out[o + 3] = 255;
96
+ }
97
+ return out;
98
+ }
99
+ for (let i = 0, o = 0; i < ao.length; i++, o += 4) {
100
+ const t = (c.tone[i] ?? 1) * (0.8 + 0.2 * (ao[i] ?? 1));
101
+ const g = SRGB[t <= 0 ? 0 : t >= 1 ? 4095 : Math.round(t * 4095)] ?? 255;
102
+ out[o] = g;
103
+ out[o + 1] = g;
104
+ out[o + 2] = g;
105
+ out[o + 3] = 255;
106
+ }
107
+ return out;
108
+ }
@@ -0,0 +1,101 @@
1
+ // Timber boards (kind 'planks'): boards run along U, butt joints staggered at random, dark gaps,
2
+ // stretched grain with growth-ring streaks, occasional knots, per-board tone. Species and finish
3
+ // come from params on top: `rings` blends latewood to albedo2, `rays` adds oak's ray flecks,
4
+ // `checks` weathers (eroded soft grain, splits along it, silvering), `paint` lays a film over the
5
+ // grain whose `chips` reveal bare wood (albedo2), mostly along board edges.
6
+ // params: width 0.15 · length 1.0 · gap 0.004 · depth 0.004 · knots 1.5 (per board-metre)
7
+ // rings 0 · rays 0 · checks 0 · paint 0 · paintRough 0.5 · chips 0.3
8
+
9
+ import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
10
+ import { Courses } from './courses.ts';
11
+ import { hash2 } from './noise.ts';
12
+ import type { StripRecipe } from './types.ts';
13
+
14
+ export function paintPlanks(c: Canvas, r: StripRecipe, seed: number): void {
15
+ const p = r.params;
16
+ const width = param(p, 'width', 0.15);
17
+ const length = param(p, 'length', 1);
18
+ const gap = param(p, 'gap', 0.004);
19
+ const depth = param(p, 'depth', 0.004);
20
+ const knots = param(p, 'knots', 1.5);
21
+ const rings = param(p, 'rings', 0);
22
+ const rays = param(p, 'rays', 0);
23
+ const checks = param(p, 'checks', 0);
24
+ const paint = param(p, 'paint', 0);
25
+ const paintRough = param(p, 'paintRough', 0.5);
26
+ const chips = param(p, 'chips', 0.3);
27
+ const boards = new Courses(c, { course: width, length, irregular: 0.6, stagger: 0, seed });
28
+ const pu = c.metresU / c.w;
29
+ const pv = c.metresV / c.h;
30
+ const ringsN = Math.max(1, Math.min(9, Math.floor(width / pv / 4)));
31
+ for (let y = 0; y < c.h; y++) {
32
+ const ym = (y + 0.5) * pv;
33
+ const k = boards.at(ym);
34
+ const dy = Math.min(ym - boards.y0, boards.y1 - ym);
35
+ const vy = (ym - boards.y0) / (boards.y1 - boards.y0);
36
+ for (let x = 0; x < c.w; x++) {
37
+ const i = y * c.w + x;
38
+ const xm = (x + 0.5) * pu;
39
+ const id = boards.id[x] ?? 0;
40
+ const h = hash2(k, id, seed);
41
+ const d = Math.min((boards.dx[x] ?? 0) * 1.5, dy);
42
+ const edge = smooth(gap * 0.5, gap * 1.5, d);
43
+ // Grain: rings warped by a long, thin noise; the board's own seed shifts its figure.
44
+ const warp = noiseAt(c, xm, ym, 0.4, 0.05, seed + id * 31 + k * 7, 3);
45
+ const ring = Math.sin((vy * 9 + warp * 3 + h * 6) * Math.PI * 2);
46
+ const fibre = noiseAt(c, xm, ym, 0.08, 0.002, seed + 17, 2);
47
+ let knot = 0;
48
+ const nk = Math.floor(knots * length * h * 2);
49
+ for (let q = 0; q < nk; q++) {
50
+ const kx = hash2(k * 13 + q, id, seed + 41) * c.metresU;
51
+ const ky =
52
+ boards.y0 + (0.25 + 0.5 * hash2(k, id * 5 + q, seed + 43)) * (boards.y1 - boards.y0);
53
+ let ddx = Math.abs(xm - kx);
54
+ if (c.wrapU) ddx = Math.min(ddx, c.metresU - ddx);
55
+ const rr = Math.sqrt((ddx / 2.2) ** 2 + (ym - ky) ** 2) / (width * 0.12);
56
+ if (rr < 1) knot = Math.max(knot, 1 - rr);
57
+ }
58
+ let height = -depth * (1 - edge) + 0.0004 * ring * edge + 0.0003 * fibre - 0.0008 * knot;
59
+ let tone =
60
+ (0.66 + 0.22 * h) *
61
+ (1 + 0.08 * ring + 0.06 * fibre) *
62
+ (1 - 0.5 * knot) *
63
+ (0.5 + 0.5 * edge);
64
+ let rough = r.roughness + 0.05 * fibre + 0.1 * (1 - edge);
65
+ // Colour rings at a frequency the strip's V texels can hold (≥ ~4 texels per ring).
66
+ const ring2 = Math.sin((vy * ringsN + warp * 2.5 + h * 6) * Math.PI * 2);
67
+ const late = smooth(0.1, 0.8, ring2) * (0.75 + 0.25 * fibre);
68
+ let mix = rings * Math.max(late, knot);
69
+ if (rays > 0) {
70
+ const fleck = smooth(0.55, 0.8, noiseAt(c, xm, ym, 0.03, 0.004, seed + 23, 2));
71
+ tone *= 1 + 0.25 * rays * fleck;
72
+ mix *= 1 - rays * fleck;
73
+ }
74
+ if (checks > 0) {
75
+ // Soft earlywood erodes, leaving latewood ridges; splits open along the grain.
76
+ const split = smooth(0.45, 0.6, noiseAt(c, xm, ym, 0.3, 0.003, seed + 29 + id, 3));
77
+ height += checks * (0.0006 * late - 0.002 * split);
78
+ tone *= 1 - checks * (0.45 * split + 0.08 * (1 - late));
79
+ rough += checks * 0.1 * (1 - late);
80
+ mix = Math.max(mix, checks * split);
81
+ }
82
+ if (paint > 0) {
83
+ // Paint fails first at board edges and ends, then in blotches across the face.
84
+ const n = noiseAt(c, xm, ym, 0.04, 0.03, seed + 31, 4) * 0.5 + 0.5;
85
+ const nearEdge = 1 - smooth(gap, gap * 6, d);
86
+ const chip = smooth(0.5, 0.56, n + chips * 0.5 + nearEdge * 0.15 - 0.45);
87
+ const film = paint * (1 - chip);
88
+ height = height * (1 - 0.4 * film) - chip * paint * 0.0002;
89
+ // The film follows the grain: board tone and rings still read through it, faintly.
90
+ const under = (0.9 + 0.06 * h) * (1 + 0.05 * ring + 0.04 * fibre) * (0.55 + 0.45 * edge);
91
+ tone = tone * (1 - film) + under * film;
92
+ rough = rough * (1 - film) + (paintRough + 0.04 * fibre) * film;
93
+ mix = mix * (1 - film) + paint * chip;
94
+ }
95
+ c.height[i] = height;
96
+ c.tone[i] = tone;
97
+ c.rough[i] = Math.min(1, rough);
98
+ c.mix[i] = mix;
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,72 @@
1
+ // Riveted iron plates (kind 'plates'): lapped plates (each overlaps its left neighbour by a
2
+ // step), domed rivet rows along every seam, hammered surface, rust streaks running down from
3
+ // rivets, chipped paint showing bare metal on edges.
4
+ // params: width 1.0 · height 0.5 · lap 0.003 · rivet 0.014 (radius) · pitch 0.09 · inset 0.05
5
+ // rust 0.4 · chips 0.3
6
+
7
+ import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
8
+ import { Courses } from './courses.ts';
9
+ import { rustStreaks } from './masks.ts';
10
+ import { hash2 } from './noise.ts';
11
+ import type { StripRecipe } from './types.ts';
12
+
13
+ /** Distance (m) from `q` to the nearest multiple of `pitch`. */
14
+ function along(q: number, pitch: number): number {
15
+ const t = q / pitch;
16
+ return Math.abs(t - Math.round(t)) * pitch;
17
+ }
18
+
19
+ export function paintPlates(c: Canvas, r: StripRecipe, seed: number): void {
20
+ const p = r.params;
21
+ const lap = param(p, 'lap', 0.003);
22
+ const rivet = param(p, 'rivet', 0.014);
23
+ const pitch = param(p, 'pitch', 0.09);
24
+ const inset = param(p, 'inset', 0.05);
25
+ const chips = param(p, 'chips', 0.3);
26
+ const metal = r.metalness ?? 0;
27
+ const plates = new Courses(c, {
28
+ course: param(p, 'height', 0.5),
29
+ length: param(p, 'width', 1),
30
+ irregular: 0,
31
+ stagger: 0.5,
32
+ seed,
33
+ });
34
+ const pu = c.metresU / c.w;
35
+ const pv = c.metresV / c.h;
36
+ for (let y = 0; y < c.h; y++) {
37
+ const ym = (y + 0.5) * pv;
38
+ const k = plates.at(ym);
39
+ const dy = Math.min(ym - plates.y0, plates.y1 - ym);
40
+ for (let x = 0; x < c.w; x++) {
41
+ const i = y * c.w + x;
42
+ const xm = (x + 0.5) * pu;
43
+ const ux = plates.ux[x] ?? 0;
44
+ const dx = plates.dx[x] ?? 0;
45
+ const id = plates.id[x] ?? 0;
46
+ const hammer = noiseAt(c, xm, ym, 0.05, 0.05, seed + 3, 3);
47
+ const fine = noiseAt(c, xm, ym, 0.004, 0.004, seed + 9, 2);
48
+ // Lap: the plate sits `lap` proud of the one to its left; a tiny groove on every seam.
49
+ const seam = Math.min(dx, dy);
50
+ let h = lap * smooth(0, 0.004, ux) + 0.0004 * hammer - 0.001 * (1 - smooth(0, 0.0015, seam));
51
+ // Rivet rows: `inset` from each vertical seam and from each course edge, every `pitch`.
52
+ const ry = Math.abs(dy - inset);
53
+ const rx = Math.abs(dx - inset);
54
+ const d1 = Math.sqrt(rx * rx + along(ym - plates.y0, pitch) ** 2);
55
+ const d2 = Math.sqrt(ry * ry + along(xm, pitch) ** 2);
56
+ const d = Math.min(dx > inset * 2 ? 1 : d1, dy > inset * 2 ? 1 : d2);
57
+ const dome = d < rivet ? Math.sqrt(1 - (d / rivet) ** 2) : 0;
58
+ h += dome * rivet * 0.5;
59
+ const chip = smooth(
60
+ 0.35,
61
+ 0.45,
62
+ noiseAt(c, xm, ym, 0.03, 0.03, seed + 21, 4) - (seam * 6 + 0.2) + chips * 0.3,
63
+ );
64
+ c.height[i] = h - chip * 0.0003;
65
+ c.tone[i] =
66
+ (0.8 + 0.06 * hash2(k, id, seed)) * (1 + 0.06 * hammer + 0.03 * fine) * (1 + 0.15 * dome);
67
+ c.rough[i] = r.roughness + 0.08 * fine - 0.25 * chip;
68
+ c.metal[i] = metal + (1 - metal) * chip;
69
+ }
70
+ }
71
+ rustStreaks(c, param(p, 'rust', 0.4), seed + 77);
72
+ }
@@ -0,0 +1,28 @@
1
+ // Tiny SDF-style helpers for cell motifs: distances in metres, no allocation.
2
+
3
+ /** Distance from (px, py) to the segment (ax, ay)–(bx, by). */
4
+ export function segment(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
5
+ const dx = bx - ax;
6
+ const dy = by - ay;
7
+ const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy)));
8
+ return Math.hypot(px - ax - t * dx, py - ay - t * dy);
9
+ }
10
+
11
+ /** Signed distance to an axis-aligned rectangle (negative inside). */
12
+ export function rect(px: number, py: number, x0: number, y0: number, x1: number, y1: number) {
13
+ const qx = Math.max(x0 - px, px - x1);
14
+ const qy = Math.max(y0 - py, py - y1);
15
+ return qx > 0 || qy > 0 ? Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) : Math.max(qx, qy);
16
+ }
17
+
18
+ /** Dome profile in [0, 1] for a round boss of radius `r` at distance `d`. */
19
+ export const dome = (d: number, r: number): number => (d < r ? Math.sqrt(1 - (d / r) ** 2) : 0);
20
+
21
+ /** Distance to the nearest of `n` points evenly spaced on a circle of radius `ring`. */
22
+ export function boltRing(px: number, py: number, ring: number, n: number, phase = 0): number {
23
+ const a = Math.atan2(py, px) - phase;
24
+ const step = (Math.PI * 2) / n;
25
+ const k = Math.round(a / step);
26
+ const ba = k * step + phase;
27
+ return Math.hypot(px - ring * Math.cos(ba), py - ring * Math.sin(ba));
28
+ }
@@ -0,0 +1,52 @@
1
+ // Roof slate (kind 'slate'): overlapping courses — each visible slate rises from where it slides
2
+ // under the course above to a thick, chipped lower edge that casts the shadow line — staggered
3
+ // vertical gaps, riven (laminar) face, per-slate tone and tilt.
4
+ // params: course 0.11 (exposure) · length 0.25 · gap 0.003 · thick 0.007 · variation 0.3
5
+
6
+ import { type Canvas, noiseAt, param, smooth } from './canvas.ts';
7
+ import { Courses } from './courses.ts';
8
+ import { hash2 } from './noise.ts';
9
+ import type { StripRecipe } from './types.ts';
10
+
11
+ export function paintSlate(c: Canvas, r: StripRecipe, seed: number): void {
12
+ const p = r.params;
13
+ const course = param(p, 'course', 0.11);
14
+ const gap = param(p, 'gap', 0.003);
15
+ const thick = param(p, 'thick', 0.007);
16
+ const variation = param(p, 'variation', 0.3);
17
+ const rows = new Courses(c, {
18
+ course,
19
+ length: param(p, 'length', 0.25),
20
+ irregular: 0.15,
21
+ stagger: 0.5,
22
+ seed,
23
+ });
24
+ const pu = c.metresU / c.w;
25
+ const pv = c.metresV / c.h;
26
+ for (let y = 0; y < c.h; y++) {
27
+ const ym = (y + 0.5) * pv;
28
+ const k = rows.at(ym);
29
+ const span = rows.y1 - rows.y0;
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 id = rows.id[x] ?? 0;
34
+ const h = hash2(k, id, seed);
35
+ const chip = (noiseAt(c, xm, ym, 0.015, 0.01, seed + 5, 3) + 0.3) * span * 0.08;
36
+ const uy = Math.max(0, ym - rows.y0 - Math.max(0, chip));
37
+ const t = uy / span;
38
+ const lam = noiseAt(c, xm, ym, 0.12, 0.004, seed + 9, 3);
39
+ const fine = noiseAt(c, xm, ym, 0.005, 0.005, seed + 13, 2);
40
+ const sideGap = smooth(gap * 0.5, gap * 1.2, rows.dx[x] ?? 0);
41
+ const bevel = smooth(0, 0.004, uy);
42
+ const tilt = (hash2(id, k, seed + 1) - 0.5) * 0.002 * ((rows.ux[x] ?? 0) / 0.25);
43
+ c.height[i] =
44
+ (thick * (1 - t) * bevel + 0.0005 * lam + tilt) * sideGap - thick * (1 - sideGap);
45
+ c.tone[i] =
46
+ (0.62 - variation * 0.5 + variation * h) *
47
+ (1 + 0.08 * lam + 0.04 * fine) *
48
+ (0.55 + 0.45 * sideGap);
49
+ c.rough[i] = r.roughness + 0.08 * lam + 0.05 * fine;
50
+ }
51
+ }
52
+ }