@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,174 @@
1
+ // Skeleton → wood meshes: stems as tapered sweeps (LOD sets radial and path resolution), the
2
+ // trunk with a bark gradient and marks, the buttressed root flare as a lobed `loft`. Plus the
3
+ // fit that scales the finished tree onto its real height and crown spread.
4
+
5
+ import { boundsOf, type Mesh, merge, type Rng, type Vec3 } from '../core/index.ts';
6
+ import { loft, ringXY, sweep } from '../primitives/index.ts';
7
+ import type { TreeParams } from './vegetation-params.ts';
8
+ import type { Stem } from './vegetation-skeleton.ts';
9
+
10
+ /** Radial segments per [level][lod]. The trunk's 8 keeps ring vertices on ±Y (exact DBH). */
11
+ const RADIAL = [
12
+ [8, 6, 5],
13
+ [6, 4, 4],
14
+ [4, 3, 3],
15
+ [3, 3, 3],
16
+ ];
17
+ export const radialSegments = (level: number, lod: number): number =>
18
+ RADIAL[Math.min(3, level)]?.[lod] ?? 3;
19
+
20
+ /** Every `step`-th path point (always keeping both ends): LOD1 halves path resolution. */
21
+ export function coarsen(st: Stem, step: number): { pts: Vec3[]; radii: number[] } {
22
+ const pts: Vec3[] = [];
23
+ const radii: number[] = [];
24
+ const last = st.pts.length - 1;
25
+ for (let i = 0; i <= last; i += step) {
26
+ pts.push(st.pts[i] ?? [0, 0, 0]);
27
+ radii.push(st.radii[i] ?? 0);
28
+ }
29
+ if (last % step !== 0) {
30
+ pts.push(st.pts[last] ?? [0, 0, 0]);
31
+ radii.push(st.radii[last] ?? 0);
32
+ }
33
+ return { pts, radii };
34
+ }
35
+
36
+ /** All stems with level in [lo, hi], merged. */
37
+ export function stemsMesh(
38
+ stems: readonly Stem[],
39
+ lo: number,
40
+ hi: number,
41
+ lod: number,
42
+ color: Vec3,
43
+ ): Mesh {
44
+ const out: Mesh[] = [];
45
+ for (const st of stems) {
46
+ if (st.level < lo || st.level > hi) continue;
47
+ const { pts, radii } = coarsen(st, lod === 0 ? 1 : 2);
48
+ out.push(
49
+ sweep({
50
+ section: { radius: 1, segments: radialSegments(st.level, lod) },
51
+ path: pts,
52
+ scale: radii,
53
+ color,
54
+ strip: 'wood',
55
+ }),
56
+ );
57
+ }
58
+ return merge(out);
59
+ }
60
+
61
+ const mix = (a: Vec3, b: Vec3, t: number): [number, number, number] => [
62
+ a[0] + (b[0] - a[0]) * t,
63
+ a[1] + (b[1] - a[1]) * t,
64
+ a[2] + (b[2] - a[2]) * t,
65
+ ];
66
+
67
+ /** Bark colour by height (bark → barkTop) with seeded marks; rewrites the mesh's colours. */
68
+ export function barkTint(m: Mesh, p: TreeParams, top: number, rng: Rng): Mesh {
69
+ const hi = p.barkTop ?? p.bark;
70
+ for (let i = 0; i < m.colors.length / 3; i++) {
71
+ const z = m.positions[i * 3 + 2] ?? 0;
72
+ const t = Math.min(1, Math.max(0, (z / Math.max(0.1, top) - 0.3) / 0.4));
73
+ let c = mix(p.bark, hi, t * t * (3 - 2 * t));
74
+ if (p.barkMarks && rng.next() < p.barkMarks.amount) c = mix(c, p.barkMarks.color, 0.85);
75
+ const k = 1 + rng.range(-0.06, 0.06);
76
+ m.colors.set([c[0] * k, c[1] * k, c[2] * k], i * 3);
77
+ }
78
+ return m;
79
+ }
80
+
81
+ /** The trunk, radius scaled by `radiusScale` (the fit's 1/sy keeps DBH exact after scaling). */
82
+ export function trunkMesh(
83
+ trunk: Stem,
84
+ p: TreeParams,
85
+ lod: number,
86
+ radiusScale: number,
87
+ rng: Rng,
88
+ ): Mesh {
89
+ const { pts, radii } = coarsen(trunk, lod === 0 ? 1 : 2);
90
+ const m = sweep({
91
+ section: { radius: 1, segments: radialSegments(0, lod) },
92
+ path: pts,
93
+ scale: radii.map((r) => r * radiusScale),
94
+ color: p.bark,
95
+ strip: 'wood',
96
+ caps: p.impostor === 'cards' ? 'end' : false,
97
+ });
98
+ return barkTint(m, p, pts[pts.length - 1]?.[2] ?? 1, rng);
99
+ }
100
+
101
+ /** Trunk axis point at height z (linear between path points). */
102
+ function axisAt(trunk: Stem, z: number): Vec3 {
103
+ for (let i = 1; i < trunk.pts.length; i++) {
104
+ const a = trunk.pts[i - 1] ?? [0, 0, 0];
105
+ const b = trunk.pts[i] ?? a;
106
+ if (b[2] >= z || i === trunk.pts.length - 1) {
107
+ const f = Math.min(1, Math.max(0, (z - a[2]) / (b[2] - a[2] || 1)));
108
+ return [a[0] + (b[0] - a[0]) * f, a[1] + (b[1] - a[1]) * f, z];
109
+ }
110
+ }
111
+ return [0, 0, z];
112
+ }
113
+
114
+ /** Buttressed flare: `p.roots` lobes, wide at the ground, melting into the trunk by ~2·DBH. */
115
+ export function rootsMesh(
116
+ trunk: Stem,
117
+ p: TreeParams,
118
+ lod: number,
119
+ radiusScale: number,
120
+ rng: Rng,
121
+ ): Mesh {
122
+ const r0 = (trunk.radii[0] ?? 0.2) * radiusScale;
123
+ const lobes = p.roots ?? 0;
124
+ const phase = rng.range(0, Math.PI * 2);
125
+ const levels = [
126
+ { z: -0.1, k: 1.7, amp: 0.7 },
127
+ { z: 0.5 * r0, k: 1.3, amp: 0.4 },
128
+ { z: 1.3 * r0, k: 1.04, amp: 0.15 },
129
+ { z: 2.4 * r0, k: 0.9, amp: 0 },
130
+ ];
131
+ const segs = lod === 0 ? 16 : 10;
132
+ const rings = levels.map(({ z, k, amp }) => {
133
+ const c = axisAt(trunk, z);
134
+ const lobe = (a: number): number => 1 + amp * Math.max(0, Math.cos(lobes * (a - phase))) ** 2;
135
+ return ringXY(c, r0 * k, r0 * k, segs, lobe);
136
+ });
137
+ const m = loft({ rings, color: p.bark, strip: 'wood' });
138
+ return barkTint(m, p, trunk.pts[trunk.pts.length - 1]?.[2] ?? 1, rng);
139
+ }
140
+
141
+ /** Per-axis factors that bring the union of `meshes` to `target` = [X, Y, Z] spans. */
142
+ export function fitFactors(meshes: readonly Mesh[], target: Vec3): Vec3 {
143
+ const lo = [Infinity, Infinity, Infinity];
144
+ const hi = [-Infinity, -Infinity, -Infinity];
145
+ for (const m of meshes) {
146
+ if (m.indices.length === 0) continue;
147
+ const b = boundsOf(m);
148
+ for (let k = 0; k < 3; k++) {
149
+ lo[k] = Math.min(lo[k] ?? 0, b.min[k] ?? 0);
150
+ hi[k] = Math.max(hi[k] ?? 0, b.max[k] ?? 0);
151
+ }
152
+ }
153
+ const f = (k: 0 | 1 | 2): number => target[k] / Math.max(1e-6, (hi[k] ?? 1) - (lo[k] ?? 0));
154
+ return [f(0), f(1), f(2)];
155
+ }
156
+
157
+ /** Non-uniform scale about the origin; normals by the inverse transpose, renormalised. */
158
+ export function scaleMesh(m: Mesh, s: Vec3): Mesh {
159
+ const positions = new Float32Array(m.positions.length);
160
+ const normals = new Float32Array(m.normals.length);
161
+ for (let i = 0; i < positions.length; i += 3) {
162
+ positions[i] = (m.positions[i] ?? 0) * s[0];
163
+ positions[i + 1] = (m.positions[i + 1] ?? 0) * s[1];
164
+ positions[i + 2] = (m.positions[i + 2] ?? 0) * s[2];
165
+ const nx = (m.normals[i] ?? 0) / s[0];
166
+ const ny = (m.normals[i + 1] ?? 0) / s[1];
167
+ const nz = (m.normals[i + 2] ?? 0) / s[2];
168
+ const l = Math.hypot(nx, ny, nz) || 1;
169
+ normals[i] = nx / l;
170
+ normals[i + 1] = ny / l;
171
+ normals[i + 2] = nz / l;
172
+ }
173
+ return { ...m, positions, normals };
174
+ }
@@ -0,0 +1,147 @@
1
+ // Tree vocabulary: Weber & Penn 1995 ("Creation and Rendering of Realistic Trees", SIGGRAPH)
2
+ // parameters, trimmed to what a game tree needs. Plain data, so presets are objects a recipe
3
+ // spreads and overrides.
4
+
5
+ import type { Vec2, Vec3 } from '../core/index.ts';
6
+
7
+ /** Crown envelope: how level-1 branch length varies up the trunk (Weber–Penn `Shape`). */
8
+ export type CrownShape =
9
+ | 'conical'
10
+ | 'spherical'
11
+ | 'hemispherical'
12
+ | 'cylindrical'
13
+ | 'tapered'
14
+ | 'flame';
15
+
16
+ /** One recursion level. Level 0 is the trunk; levels 1–3 are branches, sub-branches, twigs. */
17
+ export interface BranchLevel {
18
+ /** Children per parent (level 1: along the trunk). Ignored for level 0. */
19
+ readonly branches: number;
20
+ /** Level 0: fraction of tree height. Level ≥ 1: fraction of the parent's length. */
21
+ readonly length: number;
22
+ /** ± relative random length variation. */
23
+ readonly lengthVar?: number;
24
+ /** Degrees between child and parent axis at the attachment. */
25
+ readonly downAngle: number;
26
+ /** Added to downAngle from the first child (bottom) to the last (top) of the parent's zone. */
27
+ readonly downAngleV?: number;
28
+ /** Phyllotaxis step about the parent, degrees (golden angle 137.5° by default). */
29
+ readonly rotate?: number;
30
+ /** Total bend over the stem, degrees: + towards the sky, − towards the ground. Trunk: + bends
31
+ * back against `lean` (towards −X). */
32
+ readonly curve?: number;
33
+ /** If set, the second half bends by this instead (S-curves, weeping tips). */
34
+ readonly curveBack?: number;
35
+ /** Seeded wobble per path segment, ± degrees. */
36
+ readonly curveVar?: number;
37
+ /** Where children start along this stem's parent, as a fraction (level 1 uses `baseSize`). */
38
+ readonly start?: number;
39
+ /** Path segments at LOD0 (LOD1 halves them). */
40
+ readonly segments: number;
41
+ }
42
+
43
+ export type LeafShape = 'oval' | 'lobed' | 'tuft' | 'strand' | 'frond';
44
+
45
+ /** Card outlines: u along the card (0 = stem end), v across (±0.5). */
46
+ export const OUTLINES: Record<Exclude<LeafShape, 'frond'>, readonly Vec2[]> = {
47
+ oval: [
48
+ [0, 0],
49
+ [0.25, -0.4],
50
+ [0.65, -0.5],
51
+ [1, 0],
52
+ [0.65, 0.5],
53
+ [0.25, 0.4],
54
+ ],
55
+ lobed: [
56
+ [0, 0],
57
+ [0.3, -0.45],
58
+ [0.5, -0.3],
59
+ [0.75, -0.5],
60
+ [1, 0],
61
+ [0.75, 0.5],
62
+ [0.5, 0.3],
63
+ [0.3, 0.45],
64
+ ],
65
+ tuft: [
66
+ [0, 0],
67
+ [0.35, -0.5],
68
+ [0.45, -0.15],
69
+ [0.75, -0.4],
70
+ [1, 0],
71
+ [0.75, 0.4],
72
+ [0.45, 0.15],
73
+ [0.35, 0.5],
74
+ ],
75
+ strand: [
76
+ [0, 0],
77
+ [0.1, -0.5],
78
+ [1, 0],
79
+ [0.1, 0.5],
80
+ ],
81
+ };
82
+
83
+ export interface LeafParams {
84
+ readonly shape: LeafShape;
85
+ /** Card length at LOD0, metres. Cards stand for a cluster of leaves, not one leaf. */
86
+ readonly size: number;
87
+ /** Card width as a fraction of its length (fronds: leaflet length as a fraction of frond). */
88
+ readonly width: number;
89
+ /** Cards per terminal stem (fronds: leaflets per side). */
90
+ readonly perStem: number;
91
+ /** Where cards start along a terminal stem, 0–1 (default 0.3; tufts sit near the tip). */
92
+ readonly from?: number;
93
+ readonly color: Vec3;
94
+ /** ± relative colour variation per card. */
95
+ readonly colorVar: number;
96
+ /** 0–1: lay cards level, like the horizontal needle pads of a pine. */
97
+ readonly flat?: number;
98
+ /** 0 = cards splay outward, 1 = they hang straight down (willow strands). */
99
+ readonly hang?: number;
100
+ }
101
+
102
+ export interface TreeParams {
103
+ readonly shape: CrownShape;
104
+ /** Fraction of the trunk free of branches (clear bole). */
105
+ readonly baseSize: number;
106
+ /** [trunk, level 1, level 2?, level 3?]. */
107
+ readonly levels: readonly BranchLevel[];
108
+ /** Child radius = parent radius · (child length / parent length)^ratioPower. */
109
+ readonly ratioPower: number;
110
+ /** Trunk radius at the top as a fraction of its radius at breast height. */
111
+ readonly taper: number;
112
+ /** Trunk lean from vertical towards +X, degrees. The trunk stays in the XZ plane. */
113
+ readonly lean?: number;
114
+ readonly bark: Vec3;
115
+ /** Bark colour at the top of the trunk (Scots pine's orange upper bark); default `bark`. */
116
+ readonly barkTop?: Vec3;
117
+ /** Dark bark marks scattered per vertex (birch lenticels): colour + fraction of vertices. */
118
+ readonly barkMarks?: { readonly color: Vec3; readonly amount: number };
119
+ readonly leaves: LeafParams;
120
+ /** Buttress lobes in the root flare at the base (LOD0/1); 0 or absent = no `roots` part. */
121
+ readonly roots?: number;
122
+ /** Fruit hung under the crown (coconuts), merged into `branches`. LOD0 only. */
123
+ readonly fruit?: { readonly count: number; readonly radius: number; readonly color: Vec3 };
124
+ /** LOD2 stand-in: a lathe of the crown envelope, or a few coarse frond cards. */
125
+ readonly impostor: 'blob' | 'cards';
126
+ /** Typical mature size this preset was tuned on (used when the caller omits spread/dbh). */
127
+ readonly typical: { readonly height: number; readonly spread: number; readonly dbh: number };
128
+ }
129
+
130
+ /** Weber–Penn ShapeRatio; `r` is 1 at the crown base and 0 at the trunk tip. */
131
+ export function shapeRatio(shape: CrownShape, r: number): number {
132
+ const x = Math.min(1, Math.max(0, r));
133
+ switch (shape) {
134
+ case 'conical':
135
+ return 0.2 + 0.8 * x;
136
+ case 'spherical':
137
+ return 0.2 + 0.8 * Math.sin(Math.PI * x);
138
+ case 'hemispherical':
139
+ return 0.2 + 0.8 * Math.sin((Math.PI / 2) * x);
140
+ case 'cylindrical':
141
+ return 1;
142
+ case 'tapered':
143
+ return 0.5 + 0.5 * x;
144
+ case 'flame':
145
+ return x <= 0.7 ? 0.15 + (0.85 * x) / 0.7 : 0.15 + (0.85 * (1 - x)) / 0.3;
146
+ }
147
+ }
@@ -0,0 +1,184 @@
1
+ // Species presets: plain `TreeParams` objects — spread one and override what you need
2
+ // (`{ ...oak, leaves: { ...oak.leaves, color: hex('#7a8a2a') } }` is an autumn oak).
3
+ // `typical` sizes are mature open-grown trees; sources in each comment and in recipes/<name>/README.md.
4
+
5
+ import { hex } from '../material/index.ts';
6
+ import type { TreeParams } from './vegetation-params.ts';
7
+
8
+ /** Quercus robur (English oak): 20–25 m, crown about as wide; girth 4–12 m. Spreading, crooked limbs. */
9
+ export const oak: TreeParams = {
10
+ shape: 'hemispherical',
11
+ baseSize: 0.3,
12
+ ratioPower: 1.15,
13
+ taper: 0.3,
14
+ levels: [
15
+ { branches: 0, length: 0.55, downAngle: 0, curveVar: 5, segments: 8 },
16
+ {
17
+ branches: 8,
18
+ length: 0.95,
19
+ downAngle: 58,
20
+ downAngleV: -18,
21
+ curve: -8,
22
+ curveVar: 16,
23
+ segments: 7,
24
+ },
25
+ { branches: 6, length: 0.55, downAngle: 50, curve: 12, curveVar: 18, start: 0.2, segments: 4 },
26
+ { branches: 5, length: 0.5, downAngle: 45, curveVar: 14, start: 0.15, segments: 2 },
27
+ ],
28
+ bark: hex('#4a4036'),
29
+ barkTop: hex('#5a5045'),
30
+ leaves: {
31
+ shape: 'lobed',
32
+ size: 1.7,
33
+ width: 0.8,
34
+ perStem: 5,
35
+ color: hex('#3d6022'),
36
+ colorVar: 0.16,
37
+ },
38
+ roots: 5,
39
+ impostor: 'blob',
40
+ typical: { height: 22, spread: 22, dbh: 1.3 },
41
+ };
42
+
43
+ /** Pinus sylvestris (Scots pine): 25–35 m, DBH up to 1 m; long clean bole, orange upper bark,
44
+ * flat-topped crown in the top third, dark blue-green needle tufts on upturned tips. */
45
+ export const pine: TreeParams = {
46
+ shape: 'cylindrical',
47
+ baseSize: 0.6,
48
+ ratioPower: 1.2,
49
+ taper: 0.12,
50
+ levels: [
51
+ { branches: 0, length: 0.94, downAngle: 0, curveVar: 3, segments: 10 },
52
+ {
53
+ branches: 13,
54
+ length: 0.34,
55
+ downAngle: 94,
56
+ downAngleV: -14,
57
+ curve: 16,
58
+ curveVar: 14,
59
+ segments: 5,
60
+ },
61
+ { branches: 5, length: 0.42, downAngle: 60, curve: 18, curveVar: 14, start: 0.3, segments: 3 },
62
+ ],
63
+ bark: hex('#4a3a30'),
64
+ barkTop: hex('#c0652c'),
65
+ leaves: {
66
+ shape: 'tuft',
67
+ size: 2.1,
68
+ width: 0.95,
69
+ perStem: 5,
70
+ from: 0.5,
71
+ flat: 0.8,
72
+ color: hex('#24422e'),
73
+ colorVar: 0.1,
74
+ },
75
+ roots: 4,
76
+ impostor: 'blob',
77
+ typical: { height: 25, spread: 9, dbh: 0.6 },
78
+ };
79
+
80
+ /** Cocos nucifera (coconut palm): up to 30 m; single ringed stem 30–40 cm; 4–6 m pinnate fronds. */
81
+ export const palm: TreeParams = {
82
+ shape: 'cylindrical',
83
+ baseSize: 0.95,
84
+ ratioPower: 1.1,
85
+ taper: 0.7,
86
+ lean: 12,
87
+ levels: [
88
+ { branches: 0, length: 1, downAngle: 0, curve: 24, curveVar: 1.5, segments: 12 },
89
+ {
90
+ branches: 22,
91
+ length: 0.26,
92
+ downAngle: 115,
93
+ downAngleV: -85,
94
+ rotate: 137.5,
95
+ curve: -55,
96
+ curveVar: 4,
97
+ segments: 8,
98
+ },
99
+ ],
100
+ bark: hex('#6b5d4a'),
101
+ barkTop: hex('#8a7a62'),
102
+ leaves: {
103
+ shape: 'frond',
104
+ size: 0,
105
+ width: 0.2,
106
+ perStem: 26,
107
+ color: hex('#4f7a2a'),
108
+ colorVar: 0.12,
109
+ },
110
+ roots: 9,
111
+ fruit: { count: 7, radius: 0.13, color: hex('#5f6a22') },
112
+ impostor: 'cards',
113
+ typical: { height: 20, spread: 9, dbh: 0.35 },
114
+ };
115
+
116
+ /** Betula pendula (silver birch): 15–25 m, narrow crown, white bark with black marks, weeping twigs. */
117
+ export const birch: TreeParams = {
118
+ shape: 'flame',
119
+ baseSize: 0.3,
120
+ ratioPower: 1.4,
121
+ taper: 0.1,
122
+ levels: [
123
+ { branches: 0, length: 1, downAngle: 0, curveVar: 3, segments: 10 },
124
+ {
125
+ branches: 22,
126
+ length: 0.32,
127
+ downAngle: 45,
128
+ downAngleV: 10,
129
+ curve: -25,
130
+ curveVar: 8,
131
+ segments: 4,
132
+ },
133
+ { branches: 5, length: 0.55, downAngle: 30, curve: -45, curveVar: 10, start: 0.2, segments: 3 },
134
+ ],
135
+ bark: hex('#d9d6cc'),
136
+ barkMarks: { color: hex('#24221f'), amount: 0.12 },
137
+ leaves: {
138
+ shape: 'oval',
139
+ size: 0.8,
140
+ width: 0.7,
141
+ perStem: 4,
142
+ color: hex('#6f9a32'),
143
+ colorVar: 0.15,
144
+ },
145
+ impostor: 'blob',
146
+ typical: { height: 18, spread: 8, dbh: 0.4 },
147
+ };
148
+
149
+ /** Salix babylonica (weeping willow): 10–15 m, wider than tall; long hanging leafy strands. */
150
+ export const willow: TreeParams = {
151
+ shape: 'hemispherical',
152
+ baseSize: 0.25,
153
+ ratioPower: 1.7,
154
+ taper: 0.35,
155
+ levels: [
156
+ { branches: 0, length: 0.55, downAngle: 0, curveVar: 5, segments: 6 },
157
+ {
158
+ branches: 8,
159
+ length: 0.95,
160
+ downAngle: 42,
161
+ curve: 40,
162
+ curveBack: -70,
163
+ curveVar: 8,
164
+ segments: 6,
165
+ },
166
+ { branches: 7, length: 0.55, downAngle: 35, curve: -80, curveVar: 8, start: 0.3, segments: 4 },
167
+ ],
168
+ bark: hex('#4f463a'),
169
+ leaves: {
170
+ shape: 'strand',
171
+ size: 2.6,
172
+ width: 0.22,
173
+ perStem: 4,
174
+ color: hex('#8aa83e'),
175
+ colorVar: 0.12,
176
+ hang: 0.92,
177
+ },
178
+ roots: 6,
179
+ impostor: 'blob',
180
+ typical: { height: 12, spread: 14, dbh: 0.8 },
181
+ };
182
+
183
+ export const SPECIES = { oak, pine, palm, birch, willow } as const;
184
+ export type SpeciesName = keyof typeof SPECIES;
@@ -0,0 +1,168 @@
1
+ // The botanical skeleton: Weber–Penn recursive stems as plain polylines + radii. Always grown at
2
+ // full depth and LOD0 resolution from one seeded stream, so every LOD is the same tree; meshing
3
+ // decides what to emit. Children start inside their parent (centre offset r_parent − r_child along
4
+ // the attachment normal), so each one physically grows out of the bark.
5
+
6
+ import type { Rng, Vec3 } from '../core/index.ts';
7
+ import { arcLengths, transportFrames } from '../primitives/index.ts';
8
+ import { type BranchLevel, shapeRatio, type TreeParams } from './vegetation-params.ts';
9
+
10
+ export interface Stem {
11
+ readonly level: number;
12
+ readonly pts: readonly Vec3[];
13
+ readonly radii: readonly number[];
14
+ readonly length: number;
15
+ /** Index of the parent stem, −1 for the trunk. */
16
+ readonly parent: number;
17
+ /** Attachment point on the parent's bark (cluster cards root here at LOD1). */
18
+ readonly surface: Vec3;
19
+ /** Nominal length before shape/variation: drives the child count. */
20
+ readonly maxLength: number;
21
+ }
22
+
23
+ const deg = Math.PI / 180;
24
+ const unit = (a: Vec3): Vec3 => {
25
+ const l = Math.hypot(a[0], a[1], a[2]) || 1;
26
+ return [a[0] / l, a[1] / l, a[2] / l];
27
+ };
28
+
29
+ /** Rodrigues rotation of v about unit axis k by angle a (radians). */
30
+ export function rotate(v: Vec3, k: Vec3, a: number): Vec3 {
31
+ const c = Math.cos(a);
32
+ const s = Math.sin(a);
33
+ const d = (k[0] * v[0] + k[1] * v[1] + k[2] * v[2]) * (1 - c);
34
+ return [
35
+ v[0] * c + (k[1] * v[2] - k[2] * v[1]) * s + k[0] * d,
36
+ v[1] * c + (k[2] * v[0] - k[0] * v[2]) * s + k[1] * d,
37
+ v[2] * c + (k[0] * v[1] - k[1] * v[0]) * s + k[2] * d,
38
+ ];
39
+ }
40
+
41
+ /** Axis that turns `dir` towards +Z when rotated by a positive angle. */
42
+ function upAxis(dir: Vec3): Vec3 {
43
+ const k: Vec3 = [dir[1], -dir[0], 0]; // dir × Z
44
+ return Math.hypot(k[0], k[1]) < 1e-4 ? [0, 1, 0] : unit(k);
45
+ }
46
+
47
+ /** A stem's polyline: `res` segments, bending by curve/curveBack plus seeded wobble. */
48
+ function path(start: Vec3, dir0: Vec3, len: number, lv: BranchLevel, trunk: boolean, rng: Rng) {
49
+ const res = Math.max(1, lv.segments);
50
+ const step = len / res;
51
+ const pts: Vec3[] = [start];
52
+ let dir = dir0;
53
+ let p = start;
54
+ for (let i = 0; i < res; i++) {
55
+ const half = lv.curveBack !== undefined && i >= res / 2;
56
+ const bend = half
57
+ ? (lv.curveBack ?? 0) / Math.ceil(res / 2)
58
+ : (lv.curve ?? 0) / (lv.curveBack !== undefined ? Math.max(1, Math.floor(res / 2)) : res);
59
+ const wob = lv.curveVar ?? 0;
60
+ // The trunk stays in the XZ plane and bends about Y: + curve swings it back against the lean
61
+ // (−X), so a leaning palm bows and its crown comes back over its base.
62
+ if (trunk) dir = rotate(dir, [0, 1, 0], -(bend + rng.range(-wob, wob)) * deg);
63
+ else {
64
+ dir = rotate(dir, upAxis(dir), (bend + rng.range(-wob, wob)) * deg);
65
+ dir = rotate(dir, [0, 0, 1], rng.range(-wob, wob) * deg);
66
+ }
67
+ dir = unit(dir);
68
+ p = [p[0] + dir[0] * step, p[1] + dir[1] * step, p[2] + dir[2] * step];
69
+ pts.push(p);
70
+ }
71
+ return pts;
72
+ }
73
+
74
+ export function grow(p: TreeParams, size: { height: number; dbh: number }, rng: Rng): Stem[] {
75
+ const t0 = p.levels[0];
76
+ if (!t0) return [];
77
+ const L0 = size.height * t0.length;
78
+ const r0 = size.dbh / 2;
79
+ const lean = (p.lean ?? 0) * deg;
80
+ const trunkPts = path([0, 0, -0.05], [Math.sin(lean), 0, Math.cos(lean)], L0, t0, true, rng);
81
+ const arc0 = arcLengths(trunkPts);
82
+ const bh = 1.3; // breast height, metres (forestry DBH convention)
83
+ const trunkR = arc0.map((s) => {
84
+ const f = Math.max(0, (s - bh) / Math.max(0.1, L0 - bh));
85
+ return r0 * (1 - (1 - p.taper) * f);
86
+ });
87
+ const stems: Stem[] = [
88
+ {
89
+ level: 0,
90
+ pts: trunkPts,
91
+ radii: trunkR,
92
+ length: L0,
93
+ parent: -1,
94
+ surface: [0, 0, 0],
95
+ maxLength: L0,
96
+ },
97
+ ];
98
+ for (let si = 0; si < stems.length; si++) {
99
+ const par = stems[si];
100
+ const lv = par ? p.levels[par.level + 1] : undefined;
101
+ if (!par || !lv) continue;
102
+ const arc = arcLengths(par.pts);
103
+ const frames = transportFrames(par.pts);
104
+ const s0 = (par.level === 0 ? p.baseSize : (lv.start ?? 0.1)) * par.length;
105
+ const s1 = par.length * 0.97;
106
+ const fill = par.level === 0 ? 1 : Math.min(1, par.length / par.maxLength);
107
+ const n = par.level === 0 ? lv.branches : Math.round(lv.branches * (0.25 + 0.75 * fill));
108
+ let phi = rng.range(0, 360);
109
+ for (let i = 0; i < n; i++) {
110
+ const s = s0 + ((s1 - s0) * (i + rng.range(0.15, 0.85))) / Math.max(1, n);
111
+ let j = 0;
112
+ while (j < arc.length - 2 && (arc[j + 1] ?? 0) < s) j++;
113
+ const a = par.pts[j] ?? [0, 0, 0];
114
+ const b = par.pts[j + 1] ?? a;
115
+ const f = (s - (arc[j] ?? 0)) / ((arc[j + 1] ?? 1) - (arc[j] ?? 0) || 1);
116
+ const c: Vec3 = [
117
+ a[0] + (b[0] - a[0]) * f,
118
+ a[1] + (b[1] - a[1]) * f,
119
+ a[2] + (b[2] - a[2]) * f,
120
+ ];
121
+ const rp = (par.radii[j] ?? 0) * (1 - f) + (par.radii[j + 1] ?? 0) * f;
122
+ const T = unit([b[0] - a[0], b[1] - a[1], b[2] - a[2]]);
123
+ const fr = frames[j] ?? { n: [1, 0, 0], b: [0, 1, 0] };
124
+ phi += (lv.rotate ?? 137.5) + rng.range(-10, 10);
125
+ const cp = Math.cos(phi * deg);
126
+ const sp = Math.sin(phi * deg);
127
+ const raw: Vec3 = [
128
+ cp * fr.n[0] + sp * fr.b[0],
129
+ cp * fr.n[1] + sp * fr.b[1],
130
+ cp * fr.n[2] + sp * fr.b[2],
131
+ ];
132
+ const tdot = raw[0] * T[0] + raw[1] * T[1] + raw[2] * T[2];
133
+ const np = unit([raw[0] - tdot * T[0], raw[1] - tdot * T[1], raw[2] - tdot * T[2]]);
134
+ const zone = (s - s0) / Math.max(1e-6, s1 - s0);
135
+ const down = (lv.downAngle + (lv.downAngleV ?? 0) * zone + rng.range(-5, 5)) * deg;
136
+ const dir = unit([
137
+ T[0] * Math.cos(down) + np[0] * Math.sin(down),
138
+ T[1] * Math.cos(down) + np[1] * Math.sin(down),
139
+ T[2] * Math.cos(down) + np[2] * Math.sin(down),
140
+ ]);
141
+ const vary = 1 + rng.range(-(lv.lengthVar ?? 0.15), lv.lengthVar ?? 0.15);
142
+ const maxLength = lv.length * par.length;
143
+ const base =
144
+ par.level === 0
145
+ ? maxLength * shapeRatio(p.shape, (par.length - s) / Math.max(1e-6, par.length - s0))
146
+ : lv.length * (par.length - 0.6 * s);
147
+ const len = Math.max(0.15, base * vary);
148
+ const r0c = par.radii[0] ?? rp;
149
+ const rc = Math.max(0.006, Math.min(0.8 * rp, r0c * (len / par.length) ** p.ratioPower));
150
+ const start: Vec3 = [
151
+ c[0] + np[0] * (rp - rc),
152
+ c[1] + np[1] * (rp - rc),
153
+ c[2] + np[2] * (rp - rc),
154
+ ];
155
+ const pts = path(start, dir, len, lv, false, rng);
156
+ stems.push({
157
+ level: par.level + 1,
158
+ pts,
159
+ radii: pts.map((_, k) => rc * (1 - (0.75 * k) / Math.max(1, pts.length - 1))),
160
+ length: len,
161
+ parent: si,
162
+ surface: [c[0] + np[0] * rp, c[1] + np[1] * rp, c[2] + np[2] * rp],
163
+ maxLength,
164
+ });
165
+ }
166
+ }
167
+ return stems;
168
+ }