@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,199 @@
1
+ // Vegetation kind: `tree(ctx, …)` grows a Weber–Penn skeleton from species params and emits a
2
+ // game tree — `trunk`, `roots` (buttress flare), and a swaying group `crown` holding `branches`
3
+ // (merged) and `leaves` (merged cards) — then fits it onto its real height and crown spread.
4
+ // LOD policy: LOD0 every level and every leaf card; LOD1 one level fewer (trees with ≥ 3 levels),
5
+ // halved path resolution, twigs replaced by cluster cards; LOD2 an impostor under 300 triangles.
6
+
7
+ import {
8
+ BUDGETS,
9
+ type BuildContext,
10
+ defineFamily,
11
+ type Mesh,
12
+ merge,
13
+ type RealDimensions,
14
+ Ts23dError,
15
+ translate,
16
+ triangleCount,
17
+ type Vec3,
18
+ } from '../core/index.ts';
19
+ import { frondLeaflets, fruit } from './vegetation-fronds.ts';
20
+ import { blob, frondCards } from './vegetation-impostor.ts';
21
+ import { clusterCards, crownShade, leafCards } from './vegetation-leaves.ts';
22
+ import {
23
+ barkTint,
24
+ fitFactors,
25
+ radialSegments,
26
+ rootsMesh,
27
+ scaleMesh,
28
+ stemsMesh,
29
+ trunkMesh,
30
+ } from './vegetation-mesh.ts';
31
+ import type { TreeParams } from './vegetation-params.ts';
32
+ import { SPECIES } from './vegetation-presets.ts';
33
+ import { grow } from './vegetation-skeleton.ts';
34
+
35
+ export interface TreeOptions {
36
+ /** A preset name (`oak`, `pine`, `palm`, `birch`, `willow`) or a full parameter set. */
37
+ readonly species?: string | TreeParams;
38
+ /** Overrides on top of the species. */
39
+ readonly params?: Partial<TreeParams>;
40
+ /** Total height, metres (ground to crown top). */
41
+ readonly height: number;
42
+ /** Crown spread (X and Y), metres. Default: the species' typical ratio to height. */
43
+ readonly spread?: number;
44
+ /** Trunk diameter at breast height (1.3 m), metres. Default: the species' typical ratio. */
45
+ readonly dbh?: number;
46
+ readonly seed?: number;
47
+ /** Part-name prefix: `oak_3` → `oak_3_trunk`, `oak_3_crown`, … */
48
+ readonly prefix?: string;
49
+ }
50
+
51
+ export interface TreeInfo {
52
+ readonly parts: { trunk: string; crown: string; branches: string; leaves: string; roots: string };
53
+ readonly height: number;
54
+ readonly spread: number;
55
+ readonly dbh: number;
56
+ }
57
+
58
+ export function treeParams(o: Pick<TreeOptions, 'species' | 'params'>): TreeParams {
59
+ const s = o.species ?? 'oak';
60
+ const base = typeof s === 'string' ? SPECIES[s as keyof typeof SPECIES] : s;
61
+ if (!base) {
62
+ throw new Ts23dError('T3D_RECIPE_INVALID', {
63
+ cause: `unknown tree species '${String(s)}'`,
64
+ fix: `use one of ${Object.keys(SPECIES).join(', ')} or pass a TreeParams object (spread a preset and override)`,
65
+ });
66
+ }
67
+ return { ...base, ...o.params };
68
+ }
69
+
70
+ export function tree(ctx: BuildContext, o: TreeOptions): TreeInfo {
71
+ const p = treeParams(o);
72
+ if (!(o.height > 0) || p.levels.length < 2 || p.levels.length > 4) {
73
+ throw new Ts23dError('T3D_RECIPE_INVALID', {
74
+ cause: `tree height ${o.height} m with ${p.levels.length} branch levels`,
75
+ fix: 'height must be > 0 and levels must hold the trunk plus 1–3 branch levels',
76
+ });
77
+ }
78
+ const k = o.height / p.typical.height;
79
+ const spread = o.spread ?? p.typical.spread * k;
80
+ const dbh = o.dbh ?? p.typical.dbh * k;
81
+ const pre = o.prefix ? `${o.prefix}_` : '';
82
+ const parts = {
83
+ trunk: `${pre}trunk`,
84
+ crown: `${pre}crown`,
85
+ branches: `${pre}branches`,
86
+ leaves: `${pre}leaves`,
87
+ roots: `${pre}roots`,
88
+ };
89
+ const rng = ctx.rng.fork(`tree:${o.prefix ?? ''}:${o.seed ?? 0}`);
90
+ const stems = grow(p, { height: o.height, dbh }, rng.fork('skeleton'));
91
+ const trunkStem = stems[0];
92
+ if (!trunkStem) return { parts, height: o.height, spread, dbh };
93
+ const deepest = p.levels.length - 1;
94
+ const lod = ctx.lod;
95
+ const top = trunkStem.pts[trunkStem.pts.length - 1]?.[2] ?? o.height;
96
+ const shade = crownShade(stems, 1);
97
+ let trunk: (r: number) => Mesh;
98
+ let roots: ((r: number) => Mesh) | null = null;
99
+ let branches: Mesh | null = null;
100
+ let leaves: Mesh;
101
+ if (lod === 2) {
102
+ const imp =
103
+ p.impostor === 'cards' ? frondCards(stems, p, 1) : blob(stems, p, rng.fork('impostor'));
104
+ trunk = () => barkTint(imp.trunk, p, top, rng.fork('bark'));
105
+ leaves = imp.leaves;
106
+ } else {
107
+ const hi = lod === 0 || deepest < 2 ? deepest : deepest - 1;
108
+ trunk = (r) => trunkMesh(trunkStem, p, lod, r, rng.fork('bark'));
109
+ if (p.roots) roots = (r) => rootsMesh(trunkStem, p, lod, r, rng.fork('roots'));
110
+ const wood = [
111
+ barkTint(stemsMesh(stems, 1, hi, lod, p.bark), p, top, rng.fork('bark:branches')),
112
+ ];
113
+ if (p.fruit && lod === 0)
114
+ wood.push(fruit(trunkStem, p.baseSize - 0.01, p.fruit, rng.fork('fruit')));
115
+ branches = merge(wood);
116
+ const lr = rng.fork('leaves');
117
+ leaves =
118
+ p.leaves.shape === 'frond'
119
+ ? frondLeaflets(
120
+ stems,
121
+ deepest,
122
+ p.leaves,
123
+ lod === 0 ? p.leaves.perStem : Math.ceil(p.leaves.perStem / 2),
124
+ shade,
125
+ lr,
126
+ )
127
+ : hi === deepest
128
+ ? leafCards(stems, deepest, radialSegments(deepest, lod), p.leaves, shade, lr)
129
+ : clusterCards(stems, deepest, p.leaves, shade, lr);
130
+ }
131
+ // Fit onto the real envelope; the trunk is rebuilt thinner by 1/sy so DBH (across Y) is exact.
132
+ const s = fitFactors(
133
+ [trunk(1), branches, leaves, roots?.(1) ?? null].filter((m): m is Mesh => m !== null),
134
+ [spread, spread, o.height],
135
+ );
136
+ const fit = (m: Mesh): Mesh => scaleMesh(m, s);
137
+ const base = trunkStem.pts[Math.round(p.baseSize * (trunkStem.pts.length - 1))] ?? [0, 0, 0];
138
+ const pivot: Vec3 = [base[0] * s[0], base[1] * s[1], base[2] * s[2]];
139
+ const back: Vec3 = [-pivot[0], -pivot[1], -pivot[2]];
140
+ ctx.part(parts.trunk, fit(trunk(1 / s[1])));
141
+ if (roots) ctx.part(parts.roots, fit(roots(1 / s[1])));
142
+ ctx.group(parts.crown, {
143
+ at: pivot,
144
+ motion: { kind: 'sway', axis: [1, 0, 0], degrees: 2, hz: 0.4 },
145
+ // The crown grows out of the trunk: they meet by design, and the clash gate's conservative
146
+ // full-turn sway volume would otherwise flag the wood that holds it.
147
+ clearOf: roots ? [parts.trunk, parts.roots] : [parts.trunk],
148
+ });
149
+ if (branches && triangleCount(branches) > 0)
150
+ ctx.part(parts.branches, translate(fit(branches), back), { parent: parts.crown });
151
+ ctx.part(parts.leaves, translate(fit(leaves), back), { parent: parts.crown });
152
+ return { parts, height: o.height, spread, dbh };
153
+ }
154
+
155
+ /** `real` for a tree recipe: overall envelope plus DBH (trunk across Y) and total height. */
156
+ export function treeReal(o: {
157
+ height: number;
158
+ spread: number;
159
+ dbh: number;
160
+ prefix?: string;
161
+ source?: string;
162
+ }): RealDimensions {
163
+ const pre = o.prefix ? `${o.prefix}_` : '';
164
+ const src = o.source ? { source: o.source } : {};
165
+ return {
166
+ length: o.spread,
167
+ width: o.spread,
168
+ height: o.height,
169
+ measures: [
170
+ {
171
+ label: 'trunk diameter at breast height',
172
+ value: o.dbh,
173
+ axis: 'y',
174
+ parts: [`${pre}trunk`],
175
+ ...src,
176
+ },
177
+ {
178
+ label: 'total height',
179
+ value: o.height,
180
+ axis: 'z',
181
+ parts: [`${pre}trunk`, `${pre}crown`, `${pre}branches`, `${pre}leaves`, `${pre}roots`],
182
+ ...src,
183
+ },
184
+ ],
185
+ };
186
+ }
187
+
188
+ export type TreeFamilyParams = Omit<TreeOptions, 'prefix'>;
189
+
190
+ /** One family, many species: `trees.variant({ name: 'oak', params: { species: 'oak', height: 22 } })`. */
191
+ export const trees = defineFamily<TreeFamilyParams>({
192
+ kind: 'vegetation',
193
+ tags: ['vegetation', 'tree'],
194
+ budget: BUDGETS.tree,
195
+ defaults: { species: 'oak', height: 22 },
196
+ build: (ctx, p) => {
197
+ tree(ctx, p);
198
+ },
199
+ });
@@ -0,0 +1,144 @@
1
+ // Vehicle helpers: wheels that spin, tracks, propellers. Named parts follow the engine contract:
2
+ // `axle_*` and `wheel_*` spin about local Y, `prop_*`/`rotor_*` spin about their own axis.
3
+
4
+ import { type BuildContext, place, type Vec3 } from '../core/index.ts';
5
+ import { parseWhyte } from '../grammar/index.ts';
6
+ import { box, cylinder } from '../primitives/index.ts';
7
+
8
+ const segs = (ctx: BuildContext, hi: number): number => [hi, Math.max(8, hi / 2), 6][ctx.lod] ?? 6;
9
+
10
+ /** A pair of wheels on one spinning axle. */
11
+ export function axle(
12
+ ctx: BuildContext,
13
+ o: {
14
+ name: string;
15
+ at: Vec3;
16
+ radius: number;
17
+ gauge: number;
18
+ width: number;
19
+ color: Vec3;
20
+ rps: number;
21
+ },
22
+ ): void {
23
+ ctx.group(o.name, { at: o.at, motion: { kind: 'spin', axis: [0, 1, 0], rps: o.rps } });
24
+ for (const [s, y] of [
25
+ ['l', o.gauge / 2],
26
+ ['r', -o.gauge / 2],
27
+ ] as const) {
28
+ ctx.part(
29
+ `${o.name}_wheel_${s}`,
30
+ cylinder({
31
+ radius: o.radius,
32
+ length: o.width,
33
+ segments: segs(ctx, 24),
34
+ axis: 'y',
35
+ at: [0, y, 0],
36
+ color: o.color,
37
+ strip: 'iron',
38
+ }),
39
+ { parent: o.name },
40
+ );
41
+ }
42
+ }
43
+
44
+ /** Lays out wheel groups from a Whyte arrangement along X; returns axle x-positions per role. */
45
+ export function whyteAxles(
46
+ arrangement: string,
47
+ o: { leading: number; driving: number; trailing: number; spacing: number },
48
+ ): { role: string; x: number; radius: number }[] {
49
+ const groups = parseWhyte(arrangement);
50
+ const out: { role: string; x: number; radius: number }[] = [];
51
+ let x = 0;
52
+ for (const g of groups) {
53
+ const r = g.role === 'driving' ? o.driving : g.role === 'leading' ? o.leading : o.trailing;
54
+ for (let i = 0; i < g.count / 2; i++) {
55
+ out.push({ role: g.role, x: x + r, radius: r });
56
+ x += r * 2 + o.spacing;
57
+ }
58
+ }
59
+ const mid = x / 2;
60
+ // Front (+X) is the leading end.
61
+ return out.map((a) => ({ ...a, x: mid - a.x }));
62
+ }
63
+
64
+ /** One side of a tracked running gear: road wheels (spinning) + a flat track loop. */
65
+ export function trackRun(
66
+ ctx: BuildContext,
67
+ o: {
68
+ side: 'l' | 'r';
69
+ y: number;
70
+ length: number;
71
+ wheels: number;
72
+ radius: number;
73
+ width: number;
74
+ color: Vec3;
75
+ track: Vec3;
76
+ },
77
+ ): void {
78
+ const pitch = o.length / o.wheels;
79
+ for (let i = 0; i < o.wheels; i++) {
80
+ const x = -o.length / 2 + pitch * (i + 0.5);
81
+ ctx.part(
82
+ `road_wheel_${o.side}_${i}`,
83
+ cylinder({
84
+ radius: o.radius,
85
+ length: o.width * 0.8,
86
+ segments: segs(ctx, 20),
87
+ axis: 'y',
88
+ at: [0, 0, 0],
89
+ color: o.color,
90
+ strip: 'iron',
91
+ }),
92
+ { at: [x, o.y, o.radius + 0.06], motion: { kind: 'spin', axis: [0, 1, 0], rps: -0.6 } },
93
+ );
94
+ }
95
+ const t = 0.06;
96
+ ctx.part(
97
+ `track_${o.side}_bottom`,
98
+ box({
99
+ size: [o.length + o.radius, o.width, t],
100
+ at: [0, o.y, 0],
101
+ color: o.track,
102
+ strip: 'iron',
103
+ }),
104
+ );
105
+ ctx.part(
106
+ `track_${o.side}_top`,
107
+ box({
108
+ size: [o.length, o.width, t],
109
+ at: [0, o.y, o.radius * 2 + 0.06],
110
+ color: o.track,
111
+ strip: 'iron',
112
+ }),
113
+ );
114
+ }
115
+
116
+ /** A propeller hub + blades that spin about X (nose direction). */
117
+ export function propeller(
118
+ ctx: BuildContext,
119
+ o: { name: string; at: Vec3; radius: number; blades: number; color: Vec3; parent?: string },
120
+ ): void {
121
+ ctx.group(o.name, {
122
+ at: o.at,
123
+ ...(o.parent ? { parent: o.parent } : {}),
124
+ motion: { kind: 'spin', axis: [1, 0, 0], rps: 6 },
125
+ });
126
+ ctx.part(
127
+ `${o.name}_hub`,
128
+ cylinder({
129
+ radius: o.radius * 0.12,
130
+ length: o.radius * 0.3,
131
+ segments: segs(ctx, 12),
132
+ axis: 'x',
133
+ color: o.color,
134
+ }),
135
+ { parent: o.name },
136
+ );
137
+ // One blade built pointing up (+Z) from the hub, then rolled about the spin axis per blade.
138
+ const blade = box({ size: [0.03, o.radius * 0.12, o.radius], color: o.color });
139
+ for (let i = 0; i < o.blades; i++) {
140
+ ctx.part(`${o.name}_blade_${i}`, place(blade, { roll: (i / o.blades) * Math.PI * 2 }), {
141
+ parent: o.name,
142
+ });
143
+ }
144
+ }
@@ -0,0 +1,41 @@
1
+ // Ready-made CC0 image materials (opt-in, never registered by default, so the repo paints offline).
2
+ // Register with `defineMaterial(CC0_WOOD_PLANKS_GREY)`, fetch once with `bun run textures`, then
3
+ // `provideTextures(await loadTextureCache())` before painting. Hashes pin the exact bytes.
4
+
5
+ import type { MaterialDef } from './material-def.ts';
6
+
7
+ const PH = 'https://dl.polyhaven.org/file/ph-assets/Textures/png/1k/wood_planks_grey';
8
+
9
+ /** Poly Haven "Wood Planks Grey" by Rob Tuytel (CC0), 1.5 m square, 1k PNG maps. */
10
+ export const CC0_WOOD_PLANKS_GREY: MaterialDef = {
11
+ id: 'ph_wood_planks_grey',
12
+ kind: 'flat',
13
+ scaleMetres: 0.6,
14
+ roughness: 0.85,
15
+ description: 'Photo-scanned weathered grey planks (Poly Haven, CC0).',
16
+ sources: ['https://polyhaven.com/a/wood_planks_grey'],
17
+ texture: {
18
+ source: 'polyhaven',
19
+ id: 'wood_planks_grey',
20
+ license: 'CC0-1.0',
21
+ page: 'https://polyhaven.com/a/wood_planks_grey',
22
+ author: 'Rob Tuytel',
23
+ metres: [1.5, 1.5],
24
+ maps: {
25
+ albedo: {
26
+ url: `${PH}/wood_planks_grey_diff_1k.png`,
27
+ sha256: '3e6aeb842edd813759fdc96a6ffe6b695c888280f42501de589442311749ac5d',
28
+ },
29
+ normal: {
30
+ url: `${PH}/wood_planks_grey_nor_gl_1k.png`,
31
+ sha256: 'a9f2ad602eca2d59e84f203b1087e819e62ae517532bb25f7eb02a13c826e08b',
32
+ },
33
+ arm: {
34
+ url: `${PH}/wood_planks_grey_arm_1k.png`,
35
+ sha256: '9b81e9a0b4f9db0a920c4c2ce8ff13e0411b3624d4de455acdabe41e3fafe20e',
36
+ },
37
+ },
38
+ },
39
+ };
40
+
41
+ export const CC0_MATERIALS: readonly MaterialDef[] = [CC0_WOOD_PLANKS_GREY];
@@ -0,0 +1,130 @@
1
+ // Project materials: `defineMaterial` validates a MaterialDef, gives it one of the sheet's custom
2
+ // strip rows (in registration order: define materials in one shared module so every recipe sees
3
+ // the same order) and adds it to the house sheet, so `DEFAULT_SHEET` paints it and primitives map
4
+ // onto it. Redefining an id with the same definition is a no-op; a different one is a conflict.
5
+ // `materialOf` answers what any strip is (built-in or defined) for docs, review and tests;
6
+ // `textureSources` / `stripCredits` serve the CC0 image strips (fetch list, GLB credits).
7
+
8
+ import { Ts23dError } from '../core/index.ts';
9
+ import { MATERIAL_LIBRARY } from './library.ts';
10
+ import { type MaterialDef, toStripRecipe } from './material-def.ts';
11
+ import { HOUSE_STRIPS } from './paint/default-sheet.ts';
12
+ import type { StripRecipe } from './paint/types.ts';
13
+ import { clearCustom, customIds, customIndex, pushCustom } from './slots.ts';
14
+ import { creditOf, type TextureSource } from './texture-source.ts';
15
+ import {
16
+ CLASSIC_STRIPS,
17
+ CUSTOM_STRIP_SLOTS,
18
+ type CustomStripId,
19
+ STRIPS,
20
+ type StripId,
21
+ stripRect,
22
+ } from './trim-sheet.ts';
23
+
24
+ const defined = new Map<string, MaterialDef>();
25
+
26
+ /** The same definition, field by field (painters by identity, colours and params by value). */
27
+ function same(a: MaterialDef, b: MaterialDef): boolean {
28
+ return (
29
+ JSON.stringify({ ...a, paint: 0 }) === JSON.stringify({ ...b, paint: 0 }) && a.paint === b.paint
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Register a project material and return its strip id for `strip:` options.
35
+ * @example
36
+ * const teak = defineMaterial({ id: 'teak_deck', kind: 'planks', roughness: 0.6,
37
+ * albedo: '#8a5a32', albedo2: '#5a3a1e', params: { width: 0.12, rings: 0.5 } });
38
+ * box({ size: [4, 2, 0.05], strip: teak, color: NATURAL });
39
+ */
40
+ export function defineMaterial(def: MaterialDef): CustomStripId {
41
+ const recipe = toStripRecipe(def);
42
+ const id = def.id;
43
+ if ((STRIPS as readonly string[]).includes(id))
44
+ throw new Ts23dError('T3D_MATERIAL_CONFLICT', {
45
+ cause: `material '${id}' is a built-in strip`,
46
+ fix: `pick another id (e.g. '${id}_custom'), or restyle the built-in via paintTrimSheet({ ...DEFAULT_SHEET, strips: { ...DEFAULT_SHEET.strips, ${id}: … } })`,
47
+ });
48
+ const prior = defined.get(id);
49
+ if (prior) {
50
+ if (same(prior, def)) return id as CustomStripId;
51
+ throw new Ts23dError('T3D_MATERIAL_CONFLICT', {
52
+ cause: `material '${id}' is already defined differently`,
53
+ fix: 'define each material once in a shared module and import it, or give the variant its own id',
54
+ });
55
+ }
56
+ if (customIds().length >= CUSTOM_STRIP_SLOTS)
57
+ throw new Ts23dError('T3D_SHEET_FULL', {
58
+ cause: `the trim sheet has ${CUSTOM_STRIP_SLOTS} custom strip rows, all taken (${customIds().join(', ')})`,
59
+ fix: 'reuse a built-in strip with a vertex colour, or restyle an unused built-in strip in your SheetRecipe',
60
+ });
61
+ defined.set(id, def);
62
+ pushCustom(id);
63
+ (HOUSE_STRIPS as Record<string, StripRecipe>)[id] = recipe;
64
+ return id as CustomStripId;
65
+ }
66
+
67
+ /**
68
+ * Forget every defined material (tests; long-lived workers that reload recipes). Built-ins stay.
69
+ * Meshes compiled before the reset keep UVs into rows that may be reassigned.
70
+ */
71
+ export function resetMaterials(): void {
72
+ for (const id of defined.keys()) delete (HOUSE_STRIPS as Record<string, StripRecipe>)[id];
73
+ defined.clear();
74
+ clearCustom();
75
+ }
76
+
77
+ export interface MaterialInfo {
78
+ readonly id: StripId;
79
+ /** classic = greyscale strip tinted by vertex colour; library/defined carry their own colour. */
80
+ readonly origin: 'classic' | 'library' | 'defined';
81
+ readonly recipe: StripRecipe;
82
+ readonly description: string;
83
+ readonly sources: readonly string[];
84
+ }
85
+
86
+ /** What a strip is: its recipe, origin, description and sources. Unknown ids throw. */
87
+ export function materialOf(id: StripId): MaterialInfo {
88
+ const recipe = HOUSE_STRIPS[id];
89
+ const lib = (MATERIAL_LIBRARY as Readonly<Record<string, MaterialDef>>)[id];
90
+ const def = lib ?? defined.get(id);
91
+ if (!recipe || (customIndex(id) < 0 && !(STRIPS as readonly string[]).includes(id)))
92
+ throw new Ts23dError('T3D_MATERIAL_UNKNOWN', {
93
+ cause: `strip '${id}' is neither built in nor defined`,
94
+ fix: `use one of ${STRIPS.join(', ')}, or defineMaterial({ id: '${id}', ... }) first`,
95
+ });
96
+ const classic = (CLASSIC_STRIPS as readonly string[]).includes(id);
97
+ return {
98
+ id,
99
+ origin: classic ? 'classic' : lib ? 'library' : 'defined',
100
+ recipe,
101
+ description:
102
+ def?.description ?? `classic ${recipe.kind} strip (greyscale, vertex colour tints)`,
103
+ sources: def?.sources ?? [],
104
+ };
105
+ }
106
+
107
+ /** Every texture set a defined material references (what `bun run textures` fetches). */
108
+ export function textureSources(): readonly TextureSource[] {
109
+ return [...defined.values()].flatMap((d) => (d.texture ? [d.texture] : []));
110
+ }
111
+
112
+ /**
113
+ * Credit lines for the image strips a mesh samples: any V coordinate inside such a strip's row
114
+ * counts. Empty when no image strip is used (the GLB stays byte-identical).
115
+ */
116
+ export function stripCredits(vs: ArrayLike<number>): string[] {
117
+ const out: string[] = [];
118
+ for (const [id, d] of defined) {
119
+ if (!d.texture) continue;
120
+ const r = stripRect(id as StripId);
121
+ for (let i = 0; i < vs.length; i++) {
122
+ const v = vs[i] ?? -1;
123
+ if (v >= r.v0 && v <= r.v1) {
124
+ out.push(creditOf(d.texture));
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ return out;
130
+ }
@@ -0,0 +1,63 @@
1
+ // Procedural material generator: paints the shared trim sheet (albedo, normal, ORM) from code —
2
+ // brick bonds, plank grain (oak, pine, weathered, painted), riveted plates, bare/cast/painted/
3
+ // galvanised metal, canvas, leather, slate, concrete, glass, decal cells and cutout foliage cards
4
+ // — at a fixed seed, so the texture is as deterministic as the meshes. Supplied PNGs (reference
5
+ // photos, kubeez.com generations) can stamp cells; they are inputs, never the only path.
6
+ //
7
+ // Conventions: V (image row) grows with world up, as box/planar map faces; strips tile seamlessly
8
+ // along U and span `stripRect().metres` there and `scaleMetres` along V; classic strips are
9
+ // near-greyscale sRGB (vertex colour tints them); library/defined material strips, foliage and
10
+ // stamped cells carry colour. Project strips (`defineMaterial`) paint after the built-ins.
11
+
12
+ import { createRng } from '../core/index.ts';
13
+ import { createCanvas } from './paint/canvas.ts';
14
+ import { paintCell } from './paint/cell.ts';
15
+ import { blankSheet, composeRegion } from './paint/compose.ts';
16
+ import { DEFAULT_SHEET } from './paint/default-sheet.ts';
17
+ import { paintStrip } from './paint/strip.ts';
18
+ import type { PaintedSheet, SheetRecipe } from './paint/types.ts';
19
+ import { validateSheet, validateStrip } from './paint/validate.ts';
20
+ import { customIds } from './slots.ts';
21
+ import { CELLS, cellRect, STRIPS, type StripId, stripRect } from './trim-sheet.ts';
22
+
23
+ export { DEFAULT_SHEET } from './paint/default-sheet.ts';
24
+ export type {
25
+ CellImage,
26
+ MaterialPainter,
27
+ MaterialSurface,
28
+ PaintedSheet,
29
+ PaintKit,
30
+ SheetRecipe,
31
+ StripKind,
32
+ StripRecipe,
33
+ } from './paint/types.ts';
34
+
35
+ const FALLBACK = { kind: 'noise', scaleMetres: 1, roughness: 0.6 } as const;
36
+
37
+ export function paintTrimSheet(recipe: SheetRecipe = DEFAULT_SHEET): PaintedSheet {
38
+ validateSheet(recipe);
39
+ const size = recipe.size ?? 2048;
40
+ const sheet = blankSheet(size);
41
+ const rng = createRng(recipe.seed);
42
+ const seedOf = (name: string): number => rng.fork(name).int(0, 0x7fffffff);
43
+ const pad = Math.max(2, size / 256);
44
+ const all = [...STRIPS, ...(customIds() as readonly StripId[])];
45
+ for (const id of all) {
46
+ const r = recipe.strips[id] ?? DEFAULT_SHEET.strips[id] ?? FALLBACK;
47
+ validateStrip(`strips.${id}`, r);
48
+ const rect = stripRect(id);
49
+ const h = Math.round((rect.v1 - rect.v0) * size);
50
+ const c = createCanvas(size, h, rect.metres, r.scaleMetres, true);
51
+ paintStrip(c, r, seedOf(id), `strips.${id}`);
52
+ composeRegion(sheet, c, 0, Math.round(rect.v0 * size), pad);
53
+ }
54
+ for (const id of CELLS) {
55
+ const e = recipe.cells?.[id] ?? DEFAULT_SHEET.cells?.[id] ?? FALLBACK;
56
+ const rect = cellRect(id);
57
+ const w = Math.round((rect.u1 - rect.u0) * size);
58
+ const c = createCanvas(w, w, rect.metres, rect.metres, false);
59
+ paintCell(c, id, e, seedOf(id));
60
+ composeRegion(sheet, c, Math.round(rect.u0 * size), Math.round(rect.v0 * size), pad);
61
+ }
62
+ return sheet;
63
+ }
@@ -0,0 +1,60 @@
1
+ // Public API of @developerz.ai/ts23d/material: the shared trim-sheet layout, the procedural sheet
2
+ // painter, the material library + `defineMaterial` for project materials, palettes and engine
3
+ // shader sources.
4
+
5
+ export { CC0_MATERIALS, CC0_WOOD_PLANKS_GREY } from './cc0.ts';
6
+ export {
7
+ defineMaterial,
8
+ type MaterialInfo,
9
+ materialOf,
10
+ resetMaterials,
11
+ stripCredits,
12
+ textureSources,
13
+ } from './define.ts';
14
+ export {
15
+ type CellImage,
16
+ DEFAULT_SHEET,
17
+ type MaterialPainter,
18
+ type MaterialSurface,
19
+ type PaintedSheet,
20
+ type PaintKit,
21
+ paintTrimSheet,
22
+ type SheetRecipe,
23
+ type StripKind,
24
+ type StripRecipe,
25
+ } from './generator.ts';
26
+ export { MATERIAL_LIBRARY } from './library.ts';
27
+ export type { MaterialDef } from './material-def.ts';
28
+ export { definePalette, hex, NATURAL, type Palette, palettes } from './palette.ts';
29
+ export { godotShaderSource, type ShaderHints, threeMaterialSource } from './shader.ts';
30
+ export {
31
+ creditOf,
32
+ provideTextures,
33
+ type TextureCache,
34
+ type TextureFile,
35
+ type TextureSource,
36
+ } from './texture-source.ts';
37
+ export {
38
+ type BuiltinStripId,
39
+ CELLS,
40
+ type CellId,
41
+ CLASSIC_STRIPS,
42
+ type ClassicStripId,
43
+ CUSTOM_STRIP_SLOTS,
44
+ type CustomStripId,
45
+ cellRect,
46
+ DECAL_CELLS,
47
+ type DecalCellId,
48
+ FOLIAGE_CELLS,
49
+ type FoliageCellId,
50
+ isFoliage,
51
+ MATERIAL_STRIPS,
52
+ type MaterialStripId,
53
+ STRIP_ROWS,
54
+ STRIPS,
55
+ type StripId,
56
+ stripRect,
57
+ stripRow,
58
+ TRIM_SHEET_SIZE,
59
+ type UvRect,
60
+ } from './trim-sheet.ts';