@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,93 @@
1
+ // Shared growth helpers for foliage cells: quadratic stem curves sampled into points, per-leaf
2
+ // colour jitter and the leaf palettes (linear RGB). Allocation is per leaf/stem, never per pixel.
3
+
4
+ import type { Rng } from '../../core/index.ts';
5
+ import type { Foliage, Rgb } from './foliage-raster.ts';
6
+
7
+ /** A quadratic Bézier from p0 via p1 to p2, metres in the cell (anchor usually 0.5, 0.02). */
8
+ export interface Curve {
9
+ readonly x: readonly number[];
10
+ readonly y: readonly number[];
11
+ /** Tangent angle per sample (0 = +V, + toward +U), the convention `Foliage.leaf` takes. */
12
+ readonly a: readonly number[];
13
+ }
14
+
15
+ export function curve(
16
+ x0: number,
17
+ y0: number,
18
+ x1: number,
19
+ y1: number,
20
+ x2: number,
21
+ y2: number,
22
+ n = 16,
23
+ ): Curve {
24
+ const x: number[] = [];
25
+ const y: number[] = [];
26
+ const a: number[] = [];
27
+ for (let i = 0; i <= n; i++) {
28
+ const t = i / n;
29
+ const u = 1 - t;
30
+ x.push(u * u * x0 + 2 * u * t * x1 + t * t * x2);
31
+ y.push(u * u * y0 + 2 * u * t * y1 + t * t * y2);
32
+ const dx = 2 * u * (x1 - x0) + 2 * t * (x2 - x1);
33
+ const dy = 2 * u * (y1 - y0) + 2 * t * (y2 - y1);
34
+ a.push(Math.atan2(dx, dy));
35
+ }
36
+ return { x, y, a };
37
+ }
38
+
39
+ /** Sample index nearest parameter t ∈ [0, 1]. */
40
+ export const at = (c: Curve, t: number): number => Math.round(t * (c.x.length - 1));
41
+
42
+ /** Draw a curve as a tapered stem. */
43
+ export function drawStem(f: Foliage, c: Curve, w0: number, w1: number, col: Rgb): void {
44
+ const n = c.x.length - 1;
45
+ for (let i = 0; i < n; i++) {
46
+ const t0 = i / n;
47
+ const t1 = (i + 1) / n;
48
+ f.stem(
49
+ c.x[i] ?? 0,
50
+ c.y[i] ?? 0,
51
+ c.x[i + 1] ?? 0,
52
+ c.y[i + 1] ?? 0,
53
+ w0 + (w1 - w0) * t0,
54
+ w0 + (w1 - w0) * t1,
55
+ col,
56
+ );
57
+ }
58
+ }
59
+
60
+ /** Jitter a colour per leaf: brightness ±amt and a small hue drift between R and G. */
61
+ export function vary(rng: Rng, col: Rgb, amt: number): Rgb {
62
+ const k = 1 + amt * (rng.next() * 2 - 1);
63
+ const drift = 1 + amt * 0.6 * (rng.next() * 2 - 1);
64
+ return [col[0] * k * drift, col[1] * k, (col[2] * k) / drift];
65
+ }
66
+
67
+ export const dim = (col: Rgb, k: number): Rgb => [col[0] * k, col[1] * k, col[2] * k];
68
+
69
+ /** Leaf palettes, linear RGB (multiply by a near-white vertex colour on the card). */
70
+ export const LEAF = {
71
+ beech: [0.09, 0.2, 0.035] as Rgb,
72
+ maple: [0.1, 0.24, 0.04] as Rgb,
73
+ birch: [0.16, 0.3, 0.05] as Rgb,
74
+ acacia: [0.11, 0.2, 0.06] as Rgb,
75
+ palm: [0.12, 0.22, 0.05] as Rgb,
76
+ dry: [0.3, 0.2, 0.08] as Rgb,
77
+ spruce: [0.035, 0.09, 0.04] as Rgb,
78
+ pine: [0.06, 0.13, 0.04] as Rgb,
79
+ willow: [0.2, 0.3, 0.08] as Rgb,
80
+ reed: [0.2, 0.28, 0.08] as Rgb,
81
+ snow: [0.85, 0.88, 0.92] as Rgb,
82
+ bark: [0.08, 0.055, 0.035] as Rgb,
83
+ twig: [0.12, 0.09, 0.05] as Rgb,
84
+ } as const;
85
+
86
+ /** Autumn maple: each leaf picks one of these, then gets jittered. */
87
+ export const AUTUMN: readonly Rgb[] = [
88
+ [0.6, 0.08, 0.02],
89
+ [0.75, 0.25, 0.02],
90
+ [0.8, 0.45, 0.03],
91
+ [0.45, 0.04, 0.03],
92
+ [0.55, 0.35, 0.05],
93
+ ];
@@ -0,0 +1,135 @@
1
+ // Compound and strand foliage cells, all growing from the bottom-centre anchor toward +V:
2
+ // pinnate acacia (rachis, paired pinnae, tiny leaflets), palm frond (arched rachis, lance
3
+ // leaflets both sides, some dry brown tips), willow curtain (long fine strands of narrow leaves;
4
+ // hang it by mapping the card with V down). A dimmed back layer goes down first.
5
+
6
+ import { createRng } from '../../core/index.ts';
7
+ import type { Canvas } from './canvas.ts';
8
+ import { at, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
9
+ import { Foliage } from './foliage-raster.ts';
10
+
11
+ export function paintAcacia(c: Canvas, seed: number): void {
12
+ const rng = createRng(seed);
13
+ const f = new Foliage(c);
14
+ const rachis = curve(0.5, 0.02, 0.48 + rng.range(-0.05, 0.05), 0.5, 0.5, 0.95);
15
+ for (let layer = 0; layer < 2; layer++) {
16
+ const k = layer === 0 ? 0.5 : 1;
17
+ if (layer === 1) drawStem(f, rachis, 0.01, 0.003, LEAF.twig);
18
+ const pairs = layer === 0 ? 6 : 8;
19
+ for (let p = 0; p < pairs; p++) {
20
+ const t = 0.15 + (p / pairs) * 0.8 + (layer === 0 ? 0.05 : 0);
21
+ const i = at(rachis, t);
22
+ for (const side of [-1, 1]) {
23
+ const a = (rachis.a[i] ?? 0) + side * rng.range(0.9, 1.2);
24
+ const len = 0.22 * (1 - t * 0.5);
25
+ const bx = rachis.x[i] ?? 0.5;
26
+ const by = rachis.y[i] ?? 0.5;
27
+ const pinna = curve(
28
+ bx,
29
+ by,
30
+ bx + Math.sin(a) * len * 0.5,
31
+ by + Math.cos(a) * len * 0.5 + 0.02,
32
+ bx + Math.sin(a) * len,
33
+ by + Math.cos(a) * len,
34
+ 6,
35
+ );
36
+ drawStem(f, pinna, 0.003, 0.0015, dim(LEAF.twig, k));
37
+ for (let q = 1; q <= 12; q++) {
38
+ const j = at(pinna, q / 12.5);
39
+ const col = dim(vary(rng, LEAF.acacia, 0.15), k);
40
+ for (const s2 of [-1, 1])
41
+ f.leaf(
42
+ 'ovate',
43
+ pinna.x[j] ?? 0,
44
+ pinna.y[j] ?? 0,
45
+ (pinna.a[j] ?? 0) + s2 * 1.1,
46
+ 0.03,
47
+ 0.011,
48
+ col,
49
+ 0,
50
+ layer * 0.002,
51
+ );
52
+ }
53
+ }
54
+ }
55
+ }
56
+ f.finish(0.5, 0.25);
57
+ }
58
+
59
+ export function paintPalm(c: Canvas, seed: number): void {
60
+ const rng = createRng(seed);
61
+ const f = new Foliage(c);
62
+ const rachis = curve(0.5, 0.02, 0.3, 0.7, 0.78, 0.96, 24);
63
+ for (let layer = 0; layer < 2; layer++) {
64
+ const k = layer === 0 ? 0.55 : 1;
65
+ const n = 26;
66
+ for (let q = 0; q < n; q++) {
67
+ const t = 0.1 + (q / n) * 0.9 + (layer === 0 ? 0.015 : 0);
68
+ const i = at(rachis, Math.min(1, t));
69
+ const bx = rachis.x[i] ?? 0.5;
70
+ const by = rachis.y[i] ?? 0.5;
71
+ for (const side of [-1, 1]) {
72
+ const a = (rachis.a[i] ?? 0) + side * rng.range(0.75, 1.0);
73
+ const len = 0.3 * Math.sin(Math.PI * (0.2 + 0.75 * t)) * rng.range(0.85, 1.05);
74
+ const wid = 0.03;
75
+ f.leaf('lance', bx, by, a, len, wid, dim(vary(rng, LEAF.palm, 0.15), k), 0, layer * 0.002);
76
+ if (rng.next() < 0.3) {
77
+ const tx = bx + Math.sin(a) * len * 0.72;
78
+ const ty = by + Math.cos(a) * len * 0.72;
79
+ f.leaf(
80
+ 'blade',
81
+ tx,
82
+ ty,
83
+ a,
84
+ len * 0.28,
85
+ wid * 0.55,
86
+ dim(vary(rng, LEAF.dry, 0.2), k),
87
+ 0,
88
+ layer * 0.002 + 0.0002,
89
+ );
90
+ }
91
+ }
92
+ }
93
+ if (layer === 0) drawStem(f, rachis, 0.018, 0.004, dim(LEAF.palm, 0.8));
94
+ }
95
+ f.finish(0.5, 0.2);
96
+ }
97
+
98
+ export function paintWillow(c: Canvas, seed: number): void {
99
+ const rng = createRng(seed);
100
+ const f = new Foliage(c);
101
+ const strands = 9;
102
+ for (let s = 0; s < strands; s++) {
103
+ const back = s < 3;
104
+ const k = back ? 0.55 : 1;
105
+ const ex = 0.5 + rng.range(-0.4, 0.4);
106
+ const strand = curve(
107
+ 0.5 + rng.range(-0.03, 0.03),
108
+ 0.02,
109
+ (0.5 + ex) / 2 + rng.range(-0.1, 0.1),
110
+ 0.5,
111
+ ex,
112
+ rng.range(0.85, 0.97),
113
+ 24,
114
+ );
115
+ drawStem(f, strand, 0.006, 0.0015, dim(LEAF.twig, k));
116
+ for (let q = 2; q < 30; q++) {
117
+ const j = at(strand, q / 30);
118
+ const side = q % 2 === 0 ? 1 : -1;
119
+ const a = (strand.a[j] ?? 0) + side * rng.range(0.2, 0.45);
120
+ const col = dim(vary(rng, LEAF.willow, 0.18), k);
121
+ f.leaf(
122
+ 'lance',
123
+ strand.x[j] ?? 0,
124
+ strand.y[j] ?? 0,
125
+ a,
126
+ rng.range(0.07, 0.1),
127
+ 0.014,
128
+ col,
129
+ 0,
130
+ back ? 0 : 0.002,
131
+ );
132
+ }
133
+ }
134
+ f.finish(0.5, 0.2);
135
+ }
@@ -0,0 +1,143 @@
1
+ // Needle and blade foliage cells, growing from the bottom-centre anchor toward +V: spruce comb
2
+ // (needle tufts off both sides of an axis and its side shoots), snow-loaded spruce (the same with
3
+ // snow lumps riding the upper side of each shoot), conifer needle spray (pine bundles fanning from
4
+ // twig tips) and reeds/grass (tapering blades, some dry).
5
+
6
+ import { createRng, type Rng } from '../../core/index.ts';
7
+ import type { Canvas } from './canvas.ts';
8
+ import { at, type Curve, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
9
+ import { Foliage } from './foliage-raster.ts';
10
+
11
+ /** Needles off both sides of a shoot, angled forward, densest mid-shoot. */
12
+ function comb(f: Foliage, rng: Rng, shoot: Curve, len: number, k: number, lift: number): void {
13
+ const n = shoot.x.length - 1;
14
+ for (let i = 0; i <= n; i++) {
15
+ for (const side of [-1, 1]) {
16
+ const a = (shoot.a[i] ?? 0) + side * rng.range(0.7, 1.0);
17
+ const col = dim(vary(rng, LEAF.spruce, 0.2), k);
18
+ f.leaf(
19
+ 'needle',
20
+ shoot.x[i] ?? 0,
21
+ shoot.y[i] ?? 0,
22
+ a,
23
+ len * rng.range(0.8, 1.1),
24
+ 0.006,
25
+ col,
26
+ 0,
27
+ lift,
28
+ );
29
+ }
30
+ }
31
+ }
32
+
33
+ function spruce(c: Canvas, seed: number, snow: boolean): void {
34
+ const rng = createRng(seed);
35
+ const f = new Foliage(c);
36
+ const axis = curve(0.5, 0.02, 0.5 + rng.range(-0.04, 0.04), 0.5, 0.5, 0.95, 60);
37
+ const shoots: Curve[] = [];
38
+ for (let s = 0; s < 8; s++) {
39
+ const t = 0.15 + (s / 8) * 0.75;
40
+ const i = at(axis, t);
41
+ const side = s % 2 === 0 ? 1 : -1;
42
+ const bx = axis.x[i] ?? 0.5;
43
+ const by = axis.y[i] ?? 0.5;
44
+ const len = 0.32 * (1 - t * 0.6);
45
+ const a = side * rng.range(0.9, 1.2);
46
+ shoots.push(
47
+ curve(
48
+ bx,
49
+ by,
50
+ bx + Math.sin(a) * len * 0.5,
51
+ by + Math.cos(a) * len * 0.5,
52
+ bx + Math.sin(a) * len,
53
+ by + Math.cos(a) * len + 0.03,
54
+ 24,
55
+ ),
56
+ );
57
+ }
58
+ for (const sh of shoots) comb(f, rng, sh, 0.045, 0.55, 0);
59
+ drawStem(f, axis, 0.012, 0.004, LEAF.bark);
60
+ for (const sh of shoots) drawStem(f, sh, 0.005, 0.002, LEAF.bark);
61
+ comb(f, rng, axis, 0.05, 1, 0.002);
62
+ for (const sh of shoots) comb(f, rng, sh, 0.04, 1, 0.002);
63
+ if (snow) {
64
+ for (const sh of [axis, ...shoots]) {
65
+ const n = sh.x.length - 1;
66
+ for (let i = 2; i < n; i += 3) {
67
+ const col = vary(rng, LEAF.snow, 0.05);
68
+ f.leaf(
69
+ 'ovate',
70
+ (sh.x[i] ?? 0) - 0.02,
71
+ (sh.y[i] ?? 0) + 0.008,
72
+ Math.PI / 2 + rng.range(-0.3, 0.3),
73
+ rng.range(0.04, 0.07),
74
+ rng.range(0.025, 0.04),
75
+ col,
76
+ 0,
77
+ 0.006,
78
+ );
79
+ }
80
+ }
81
+ }
82
+ f.finish(0.5, 0.25);
83
+ }
84
+
85
+ export const paintSpruce = (c: Canvas, seed: number): void => spruce(c, seed, false);
86
+ export const paintSpruceSnow = (c: Canvas, seed: number): void => spruce(c, seed, true);
87
+
88
+ export function paintNeedles(c: Canvas, seed: number): void {
89
+ const rng = createRng(seed);
90
+ const f = new Foliage(c);
91
+ const twigs: Curve[] = [];
92
+ for (let s = 0; s < 5; s++) {
93
+ const ex = 0.5 + (s - 2) * 0.13 + rng.range(-0.03, 0.03);
94
+ twigs.push(curve(0.5, 0.02, 0.5 + (ex - 0.5) * 0.4, 0.3, ex, rng.range(0.55, 0.7), 12));
95
+ }
96
+ for (const [s, tw] of twigs.entries()) {
97
+ drawStem(f, tw, 0.012, 0.006, LEAF.bark);
98
+ const k = s % 2 === 0 ? 0.6 : 1;
99
+ // Bundles along the outer third and a full brush at the tip, fanning forward.
100
+ for (let i = 6; i < tw.x.length; i++) {
101
+ const fan = i === tw.x.length - 1 ? 14 : 4;
102
+ for (let q = 0; q < fan; q++) {
103
+ const a = (tw.a[i] ?? 0) + rng.range(-1.1, 1.1) * (fan > 4 ? 0.8 : 1.2);
104
+ const col = dim(vary(rng, LEAF.pine, 0.2), k);
105
+ f.leaf(
106
+ 'needle',
107
+ tw.x[i] ?? 0,
108
+ tw.y[i] ?? 0,
109
+ a,
110
+ rng.range(0.12, 0.2),
111
+ 0.006,
112
+ col,
113
+ 0,
114
+ k * 0.002,
115
+ );
116
+ }
117
+ }
118
+ }
119
+ f.finish(0.5, 0.3);
120
+ }
121
+
122
+ export function paintReeds(c: Canvas, seed: number): void {
123
+ const rng = createRng(seed);
124
+ const f = new Foliage(c);
125
+ for (let b = 0; b < 22; b++) {
126
+ const back = b < 8;
127
+ const x = 0.5 + rng.range(-0.2, 0.2);
128
+ const tilt = (x - 0.5) * 1.2 + rng.range(-0.25, 0.25);
129
+ const col = dim(vary(rng, rng.next() < 0.2 ? LEAF.dry : LEAF.reed, 0.2), back ? 0.55 : 1);
130
+ f.leaf(
131
+ 'blade',
132
+ x,
133
+ 0.0,
134
+ tilt,
135
+ rng.range(0.55, 0.97),
136
+ rng.range(0.018, 0.032),
137
+ col,
138
+ 0,
139
+ back ? 0 : 0.002,
140
+ );
141
+ }
142
+ f.finish(0.5, 0.15);
143
+ }
@@ -0,0 +1,196 @@
1
+ // Rasteriser for foliage cells: tapered stems and leaf shapes composited back-to-front into
2
+ // linear colour + coverage, with leaf-dome and midrib relief. `finish` writes sRGB RGBA (alpha =
3
+ // leaf mask, transparent texels bled with leaf colour so mips don't halo), height, roughness and
4
+ // an occlusion term that darkens toward the twig axis. Shapes iterate only their pixel bounds.
5
+
6
+ import { type Canvas, clamp01, smooth } from './canvas.ts';
7
+ import { SRGB } from './orm.ts';
8
+ import { segment } from './shapes.ts';
9
+
10
+ export type Rgb = readonly [number, number, number];
11
+ /** Width profile of a leaf along its length t ∈ [0, 1]; 1 = full half-width. */
12
+ export type LeafShape = 'ovate' | 'lance' | 'needle' | 'blade' | 'palmate';
13
+
14
+ const total = (a: Float32Array): number => a.reduce((s, v) => s + v, 0);
15
+
16
+ /** Angle between palmate lobe axes. */
17
+ const LOBE = (Math.PI * 2) / 5;
18
+
19
+ export class Foliage {
20
+ private readonly r: Float32Array;
21
+ private readonly g: Float32Array;
22
+ private readonly b: Float32Array;
23
+ private readonly a: Float32Array;
24
+ private readonly c: Canvas;
25
+ private readonly px: number;
26
+
27
+ constructor(c: Canvas) {
28
+ this.c = c;
29
+ const n = c.w * c.h;
30
+ this.r = new Float32Array(n);
31
+ this.g = new Float32Array(n);
32
+ this.b = new Float32Array(n);
33
+ this.a = new Float32Array(n);
34
+ this.px = c.metresU / c.w;
35
+ c.height.fill(0);
36
+ }
37
+
38
+ /** Composite `cov` of colour (cr, cg, cb) × shade over texel i. */
39
+ private put(i: number, cov: number, col: Rgb, shade: number, h: number, rough: number): void {
40
+ if (cov <= 0) return;
41
+ const k = 1 - cov;
42
+ const s = shade * cov;
43
+ this.r[i] = (this.r[i] ?? 0) * k + col[0] * s;
44
+ this.g[i] = (this.g[i] ?? 0) * k + col[1] * s;
45
+ this.b[i] = (this.b[i] ?? 0) * k + col[2] * s;
46
+ this.a[i] = (this.a[i] ?? 0) * k + cov;
47
+ this.c.height[i] = (this.c.height[i] ?? 0) * k + h * cov;
48
+ this.c.rough[i] = (this.c.rough[i] ?? 0.8) * k + rough * cov;
49
+ }
50
+
51
+ /** Pixel bounds of a metre-space box, clipped to the canvas. */
52
+ private span(x0: number, y0: number, x1: number, y1: number): readonly number[] {
53
+ const { w, h } = this.c;
54
+ const p = this.px;
55
+ return [
56
+ Math.max(0, Math.floor(x0 / p) - 1),
57
+ Math.max(0, Math.floor(y0 / p) - 1),
58
+ Math.min(w - 1, Math.ceil(x1 / p) + 1),
59
+ Math.min(h - 1, Math.ceil(y1 / p) + 1),
60
+ ];
61
+ }
62
+
63
+ /** A tapered stem / twig / rachis from (ax, ay) width w0 to (bx, by) width w1, metres. */
64
+ stem(ax: number, ay: number, bx: number, by: number, w0: number, w1: number, col: Rgb): void {
65
+ const m = Math.max(w0, w1);
66
+ const [sx0 = 0, sy0 = 0, sx1 = 0, sy1 = 0] = this.span(
67
+ Math.min(ax, bx) - m,
68
+ Math.min(ay, by) - m,
69
+ Math.max(ax, bx) + m,
70
+ Math.max(ay, by) + m,
71
+ );
72
+ const lx = bx - ax;
73
+ const ly = by - ay;
74
+ const l2 = lx * lx + ly * ly || 1;
75
+ for (let y = sy0; y <= sy1; y++) {
76
+ const ym = (y + 0.5) * this.px;
77
+ for (let x = sx0; x <= sx1; x++) {
78
+ const xm = (x + 0.5) * this.px;
79
+ const t = clamp01(((xm - ax) * lx + (ym - ay) * ly) / l2);
80
+ const half = (w0 + (w1 - w0) * t) / 2;
81
+ const d = segment(xm, ym, ax, ay, bx, by);
82
+ const cov = clamp01(0.5 - (d - half) / this.px);
83
+ const round = half > 0 ? Math.sqrt(Math.max(0, 1 - (d / half) ** 2)) : 0;
84
+ this.put(y * this.c.w + x, cov, col, 1, 0.0015 + half * 0.4 * round, 0.85);
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * A leaf from its base (bx, by) along `angle` (radians, 0 = +V, positive = toward +U), `len`
91
+ * long and `wid` wide at its widest. `serrate` adds teeth; `lift` raises it over earlier layers.
92
+ */
93
+ leaf(
94
+ shape: LeafShape,
95
+ bx: number,
96
+ by: number,
97
+ angle: number,
98
+ len: number,
99
+ wid: number,
100
+ col: Rgb,
101
+ serrate = 0,
102
+ lift = 0,
103
+ ): void {
104
+ const ux = Math.sin(angle);
105
+ const uy = Math.cos(angle);
106
+ // Bounds: the base-to-tip segment padded by the half-width (palmate: the whole blade disc).
107
+ const tx = bx + ux * len;
108
+ const ty = by + uy * len;
109
+ const pad = shape === 'palmate' ? len : wid;
110
+ const [sx0 = 0, sy0 = 0, sx1 = 0, sy1 = 0] = this.span(
111
+ Math.min(bx, tx) - pad,
112
+ Math.min(by, ty) - pad,
113
+ Math.max(bx, tx) + pad,
114
+ Math.max(by, ty) + pad,
115
+ );
116
+ for (let y = sy0; y <= sy1; y++) {
117
+ const ym = (y + 0.5) * this.px - by;
118
+ for (let x = sx0; x <= sx1; x++) {
119
+ const xm = (x + 0.5) * this.px - bx;
120
+ const along = xm * ux + ym * uy;
121
+ const across = xm * uy - ym * ux;
122
+ let sd: number;
123
+ let rib: number;
124
+ if (shape === 'palmate') {
125
+ // Five pointed lobes around the petiole tip; notch at the stem side.
126
+ const rr = Math.hypot(along - len * 0.45, across);
127
+ const th = Math.atan2(across, along - len * 0.45);
128
+ const lobe = 1 - Math.abs(Math.sin(2.5 * th)) ** 0.6;
129
+ const edge =
130
+ len * 0.5 * (0.35 + 0.65 * lobe) * (1 + serrate * Math.abs(Math.sin(18 * th)));
131
+ sd = rr - edge;
132
+ const axis = Math.round(th / LOBE) * LOBE;
133
+ rib = rr * Math.abs(Math.sin(th - axis));
134
+ } else {
135
+ const t = along / len;
136
+ if (t < 0 || t > 1) {
137
+ sd = Math.max(-along, along - len) + Math.abs(across) * 0.5;
138
+ } else {
139
+ const prof =
140
+ shape === 'ovate'
141
+ ? Math.sin(Math.PI * t ** 0.8) ** 0.9
142
+ : shape === 'lance'
143
+ ? Math.sin(Math.PI * t ** 0.6)
144
+ : shape === 'blade'
145
+ ? (1 - t) ** 0.4
146
+ : 1 - 0.6 * t;
147
+ const teeth = 1 - serrate * Math.abs(Math.sin(t * 40));
148
+ sd = Math.abs(across) - (wid / 2) * prof * teeth;
149
+ }
150
+ rib = Math.abs(across);
151
+ }
152
+ const cov = clamp01(0.5 - sd / this.px);
153
+ if (cov <= 0) continue;
154
+ const inside = clamp01(-sd / (wid * 0.5 + 1e-6));
155
+ const vein = 1 - smooth(0, this.px * 1.5, rib);
156
+ const shade = 0.85 + 0.15 * inside;
157
+ this.put(
158
+ y * this.c.w + x,
159
+ cov,
160
+ col,
161
+ shade,
162
+ 0.002 + lift + 0.0015 * inside + 0.0006 * vein,
163
+ 0.6,
164
+ );
165
+ }
166
+ }
167
+ }
168
+
169
+ /** Write the layers to the canvas: sRGB RGBA with bled colour, occlusion toward the twig. */
170
+ finish(axisX = 0.5, reach = 0.35): void {
171
+ const { w, h } = this.c;
172
+ const n = w * h;
173
+ const sa = total(this.a);
174
+ const mean: Rgb =
175
+ sa > 0 ? [total(this.r) / sa, total(this.g) / sa, total(this.b) / sa] : [0.1, 0.2, 0.05];
176
+ const out = new Uint8Array(n * 4);
177
+ const enc = (v: number): number => SRGB[Math.round(clamp01(v) * 4095)] ?? 0;
178
+ for (let y = 0; y < h; y++) {
179
+ for (let x = 0; x < w; x++) {
180
+ const i = y * w + x;
181
+ const a = this.a[i] ?? 0;
182
+ // Un-premultiply; transparent texels take the mean leaf colour (mip-safe bleed).
183
+ const k = a > 0.02 ? 1 / a : 0;
184
+ const mix = clamp01(a * 4);
185
+ out[i * 4] = enc((this.r[i] ?? 0) * k * mix + mean[0] * (1 - mix));
186
+ out[i * 4 + 1] = enc((this.g[i] ?? 0) * k * mix + mean[1] * (1 - mix));
187
+ out[i * 4 + 2] = enc((this.b[i] ?? 0) * k * mix + mean[2] * (1 - mix));
188
+ out[i * 4 + 3] = Math.round(clamp01(a) * 255);
189
+ const d = Math.abs((x + 0.5) * this.px - axisX);
190
+ this.c.occl[i] = 0.55 + 0.45 * smooth(0, reach, d + (y + 0.5) * this.px * 0.4);
191
+ }
192
+ }
193
+ this.c.rgba = out;
194
+ this.c.metal.fill(0);
195
+ }
196
+ }
@@ -0,0 +1,23 @@
1
+ // Foliage cell dispatch: FoliageCellId → painter. Each paints colour + alpha mask (cutout leaf
2
+ // cards), anchor at the cell's bottom-centre, growth toward +V.
3
+
4
+ import type { FoliageCellId } from '../trim-sheet.ts';
5
+ import type { Canvas } from './canvas.ts';
6
+ import { paintBirch, paintBroadleaf, paintMaple, paintMapleAutumn } from './foliage-broad.ts';
7
+ import { paintAcacia, paintPalm, paintWillow } from './foliage-compound.ts';
8
+ import { paintNeedles, paintReeds, paintSpruce, paintSpruceSnow } from './foliage-conifer.ts';
9
+
10
+ export const FOLIAGE_PAINTERS: Readonly<Record<FoliageCellId, (c: Canvas, seed: number) => void>> =
11
+ {
12
+ foliage_broadleaf: paintBroadleaf,
13
+ foliage_maple: paintMaple,
14
+ foliage_maple_autumn: paintMapleAutumn,
15
+ foliage_birch: paintBirch,
16
+ foliage_acacia: paintAcacia,
17
+ foliage_palm: paintPalm,
18
+ foliage_spruce: paintSpruce,
19
+ foliage_spruce_snow: paintSpruceSnow,
20
+ foliage_willow: paintWillow,
21
+ foliage_reeds: paintReeds,
22
+ foliage_needles: paintNeedles,
23
+ };
@@ -0,0 +1,88 @@
1
+ // Paint a strip from a CC0 texture set: decode the cached PNGs, box-filter them onto the strip's
2
+ // texel grid at true scale (the set's `metres`, snapped so a whole number of repeats spans the
3
+ // strip's U length — it still tiles), V flipped so the image's top is world up. Albedo stays sRGB
4
+ // colour (tint with NATURAL); the OpenGL normal's green is flipped to the sheet's +Y = −V; `arm`
5
+ // fills occlusion / roughness / metalness, else the recipe's constants.
6
+
7
+ import type { TextureFile, TextureSource } from '../texture-source.ts';
8
+ import { textureBytes } from '../texture-source.ts';
9
+ import type { Canvas } from './canvas.ts';
10
+ import { decodePng, type Rgba } from './decode-png.ts';
11
+ import type { StripRecipe } from './types.ts';
12
+
13
+ const decoded = new Map<string, Rgba>();
14
+ function image(f: TextureFile, where: string): Rgba {
15
+ const hit = decoded.get(f.sha256);
16
+ if (hit) return hit;
17
+ const img = decodePng(textureBytes(f, where));
18
+ decoded.set(f.sha256, img);
19
+ return img;
20
+ }
21
+
22
+ /** Average of `img` over the canvas texel footprint, per channel, into out[0..3]. */
23
+ function resample(
24
+ c: Canvas,
25
+ t: TextureSource,
26
+ img: Rgba,
27
+ fn: (i: number, px: Float32Array) => void,
28
+ ) {
29
+ const reps = Math.max(1, Math.round(c.metresU / (t.metres[0] ?? 1)));
30
+ const su = c.metresU / reps;
31
+ const sv = t.metres[1] ?? 1;
32
+ const W = img.width;
33
+ const H = img.height;
34
+ const px = new Float32Array(4);
35
+ for (let y = 0; y < c.h; y++) {
36
+ const v0 = ((y * c.metresV) / c.h / sv) * H;
37
+ const v1 = (((y + 1) * c.metresV) / c.h / sv) * H;
38
+ for (let x = 0; x < c.w; x++) {
39
+ const u0 = ((x * c.metresU) / c.w / su) * W;
40
+ const u1 = (((x + 1) * c.metresU) / c.w / su) * W;
41
+ px.fill(0);
42
+ let n = 0;
43
+ for (let sy = Math.floor(v0); sy < Math.max(Math.floor(v0) + 1, Math.ceil(v1)); sy++) {
44
+ const row = H - 1 - (((sy % H) + H) % H);
45
+ for (let sx = Math.floor(u0); sx < Math.max(Math.floor(u0) + 1, Math.ceil(u1)); sx++) {
46
+ const o = (row * W + (((sx % W) + W) % W)) * 4;
47
+ for (let k = 0; k < 4; k++) px[k] = (px[k] ?? 0) + (img.data[o + k] ?? 0);
48
+ n++;
49
+ }
50
+ }
51
+ for (let k = 0; k < 4; k++) px[k] = (px[k] ?? 0) / n;
52
+ fn(y * c.w + x, px);
53
+ }
54
+ }
55
+ }
56
+
57
+ export function paintImageStrip(c: Canvas, r: StripRecipe, t: TextureSource, where: string): void {
58
+ const albedo = new Uint8Array(c.w * c.h * 4);
59
+ resample(c, t, image(t.maps.albedo, where), (i, p) => {
60
+ albedo.set([p[0] ?? 0, p[1] ?? 0, p[2] ?? 0, 255], i * 4);
61
+ });
62
+ c.rgba = albedo;
63
+ c.height.fill(0);
64
+ if (t.maps.normal) {
65
+ const nrm = new Uint8Array(c.w * c.h * 4);
66
+ resample(c, t, image(t.maps.normal, where), (i, p) => {
67
+ const x = (p[0] ?? 128) / 127.5 - 1;
68
+ const y = -((p[1] ?? 128) / 127.5 - 1);
69
+ const z = (p[2] ?? 255) / 127.5 - 1;
70
+ const l = Math.hypot(x, y, z) || 1;
71
+ nrm.set(
72
+ [(x / l) * 127.5 + 127.5, (y / l) * 127.5 + 127.5, (z / l) * 127.5 + 127.5, 255],
73
+ i * 4,
74
+ );
75
+ });
76
+ c.normalRgba = nrm;
77
+ }
78
+ if (t.maps.arm) {
79
+ resample(c, t, image(t.maps.arm, where), (i, p) => {
80
+ c.occl[i] = (p[0] ?? 255) / 255;
81
+ c.rough[i] = (p[1] ?? 204) / 255;
82
+ c.metal[i] = (p[2] ?? 0) / 255;
83
+ });
84
+ } else {
85
+ c.rough.fill(r.roughness);
86
+ c.metal.fill(r.metalness ?? 0);
87
+ }
88
+ }