@developerz.ai/ts23d 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (524) 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 +79 -0
  13. package/CLAUDE.md +85 -0
  14. package/LICENSE +21 -0
  15. package/README.md +380 -2
  16. package/dist/chunk-11v28977.js +1575 -0
  17. package/dist/chunk-11v28977.js.map +30 -0
  18. package/dist/chunk-1ypfs7ga.js +3571 -0
  19. package/dist/chunk-1ypfs7ga.js.map +42 -0
  20. package/dist/chunk-4ddzsqtc.js +464 -0
  21. package/dist/chunk-4ddzsqtc.js.map +19 -0
  22. package/dist/chunk-a8fxakcj.js +19 -0
  23. package/dist/chunk-a8fxakcj.js.map +11 -0
  24. package/dist/chunk-gcyp9b4d.js +755 -0
  25. package/dist/chunk-gcyp9b4d.js.map +22 -0
  26. package/dist/chunk-kgvbpqqw.js +44 -0
  27. package/dist/chunk-kgvbpqqw.js.map +10 -0
  28. package/dist/chunk-kvcep7ft.js +1919 -0
  29. package/dist/chunk-kvcep7ft.js.map +43 -0
  30. package/dist/chunk-ravbb06q.js +89 -0
  31. package/dist/chunk-ravbb06q.js.map +11 -0
  32. package/dist/chunk-w1zyhj40.js +1031 -0
  33. package/dist/chunk-w1zyhj40.js.map +21 -0
  34. package/dist/chunk-wtqhq529.js +2532 -0
  35. package/dist/chunk-wtqhq529.js.map +43 -0
  36. package/dist/chunk-xnt2sdhf.js +836 -0
  37. package/dist/chunk-xnt2sdhf.js.map +17 -0
  38. package/dist/cli/args.d.ts +24 -0
  39. package/dist/cli/bin.d.ts +2 -0
  40. package/dist/cli/bin.js +307 -0
  41. package/dist/cli/bin.js.map +14 -0
  42. package/dist/cli/load.d.ts +2 -0
  43. package/dist/cli/manifest.d.ts +49 -0
  44. package/dist/cli/render-cmds.d.ts +12 -0
  45. package/dist/cli/run.d.ts +1 -0
  46. package/dist/core/asset.d.ts +10 -0
  47. package/dist/core/definition.d.ts +148 -0
  48. package/dist/core/errors.d.ts +17 -0
  49. package/dist/core/family.d.ts +23 -0
  50. package/dist/core/index.d.ts +9 -0
  51. package/dist/core/index.js +69 -0
  52. package/dist/core/index.js.map +9 -0
  53. package/dist/core/mesh.d.ts +35 -0
  54. package/dist/core/part.d.ts +114 -0
  55. package/dist/core/png.d.ts +1 -0
  56. package/dist/core/rng.d.ts +12 -0
  57. package/dist/core/transform.d.ts +20 -0
  58. package/dist/core/vec.d.ts +11 -0
  59. package/dist/core/world.d.ts +16 -0
  60. package/dist/export/bin-writer.d.ts +16 -0
  61. package/dist/export/container.d.ts +3 -0
  62. package/dist/export/glb.d.ts +38 -0
  63. package/dist/export/index.d.ts +4 -0
  64. package/dist/export/index.js +26 -0
  65. package/dist/export/index.js.map +9 -0
  66. package/dist/export/lights.d.ts +8 -0
  67. package/dist/export/materials.d.ts +23 -0
  68. package/dist/export/merge-pivots.d.ts +6 -0
  69. package/dist/export/metadata.d.ts +19 -0
  70. package/dist/export/motion.d.ts +9 -0
  71. package/dist/export/optimize-parts.d.ts +26 -0
  72. package/dist/export/primitive.d.ts +3 -0
  73. package/dist/export/tangents.d.ts +2 -0
  74. package/dist/export/textures.d.ts +22 -0
  75. package/dist/export/version.d.ts +2 -0
  76. package/dist/grammar/index.d.ts +2 -0
  77. package/dist/grammar/index.js +16 -0
  78. package/dist/grammar/index.js.map +9 -0
  79. package/dist/grammar/split.d.ts +21 -0
  80. package/dist/grammar/whyte.d.ts +5 -0
  81. package/dist/index.d.ts +11 -0
  82. package/dist/index.js +411 -0
  83. package/dist/index.js.map +9 -0
  84. package/dist/kinds/building.d.ts +44 -0
  85. package/dist/kinds/faces.d.ts +27 -0
  86. package/dist/kinds/head-armour.d.ts +9 -0
  87. package/dist/kinds/head-beard.d.ts +6 -0
  88. package/dist/kinds/head-bust.d.ts +4 -0
  89. package/dist/kinds/head-extras.d.ts +15 -0
  90. package/dist/kinds/head-face.d.ts +6 -0
  91. package/dist/kinds/head-geom.d.ts +7 -0
  92. package/dist/kinds/head-hair.d.ts +4 -0
  93. package/dist/kinds/head-layout.d.ts +29 -0
  94. package/dist/kinds/head-mesh.d.ts +27 -0
  95. package/dist/kinds/head-params.d.ts +108 -0
  96. package/dist/kinds/head-presets.d.ts +19 -0
  97. package/dist/kinds/head-rim.d.ts +10 -0
  98. package/dist/kinds/head.d.ts +39 -0
  99. package/dist/kinds/hull-lines.d.ts +65 -0
  100. package/dist/kinds/hull-skin.d.ts +6 -0
  101. package/dist/kinds/hull.d.ts +14 -0
  102. package/dist/kinds/index.d.ts +16 -0
  103. package/dist/kinds/index.js +78 -0
  104. package/dist/kinds/index.js.map +9 -0
  105. package/dist/kinds/mass.d.ts +15 -0
  106. package/dist/kinds/openings.d.ts +30 -0
  107. package/dist/kinds/planned.d.ts +6 -0
  108. package/dist/kinds/roof-chimney.d.ts +16 -0
  109. package/dist/kinds/roof-shape.d.ts +64 -0
  110. package/dist/kinds/roof.d.ts +31 -0
  111. package/dist/kinds/vegetation-fronds.d.ts +15 -0
  112. package/dist/kinds/vegetation-impostor.d.ts +9 -0
  113. package/dist/kinds/vegetation-leaves.d.ts +15 -0
  114. package/dist/kinds/vegetation-mesh.d.ts +21 -0
  115. package/dist/kinds/vegetation-params.d.ts +90 -0
  116. package/dist/kinds/vegetation-presets.d.ts +20 -0
  117. package/dist/kinds/vegetation-skeleton.d.ts +20 -0
  118. package/dist/kinds/vegetation.d.ts +42 -0
  119. package/dist/kinds/vehicle.d.ts +42 -0
  120. package/dist/material/generator.d.ts +4 -0
  121. package/dist/material/index.d.ts +4 -0
  122. package/dist/material/index.js +38 -0
  123. package/dist/material/index.js.map +9 -0
  124. package/dist/material/paint/bond.d.ts +3 -0
  125. package/dist/material/paint/canvas.d.ts +32 -0
  126. package/dist/material/paint/cell.d.ts +4 -0
  127. package/dist/material/paint/cells-round.d.ts +5 -0
  128. package/dist/material/paint/cells.d.ts +7 -0
  129. package/dist/material/paint/compose.d.ts +5 -0
  130. package/dist/material/paint/concrete.d.ts +3 -0
  131. package/dist/material/paint/courses.d.ts +31 -0
  132. package/dist/material/paint/decode-png.d.ts +6 -0
  133. package/dist/material/paint/default-sheet.d.ts +2 -0
  134. package/dist/material/paint/foliage-broad.d.ts +5 -0
  135. package/dist/material/paint/foliage-common.d.ts +35 -0
  136. package/dist/material/paint/foliage-compound.d.ts +4 -0
  137. package/dist/material/paint/foliage-conifer.d.ts +5 -0
  138. package/dist/material/paint/foliage-raster.d.ts +26 -0
  139. package/dist/material/paint/foliage.d.ts +3 -0
  140. package/dist/material/paint/masks.d.ts +3 -0
  141. package/dist/material/paint/noise.d.ts +9 -0
  142. package/dist/material/paint/normal.d.ts +8 -0
  143. package/dist/material/paint/orm.d.ts +8 -0
  144. package/dist/material/paint/planks.d.ts +3 -0
  145. package/dist/material/paint/plates.d.ts +3 -0
  146. package/dist/material/paint/shapes.d.ts +8 -0
  147. package/dist/material/paint/slate.d.ts +3 -0
  148. package/dist/material/paint/stamp.d.ts +3 -0
  149. package/dist/material/paint/strip.d.ts +3 -0
  150. package/dist/material/paint/surface.d.ts +5 -0
  151. package/dist/material/paint/types.d.ts +35 -0
  152. package/dist/material/paint/validate.d.ts +3 -0
  153. package/dist/material/palette.d.ts +12 -0
  154. package/dist/material/shader-godot.d.ts +2 -0
  155. package/dist/material/shader-three.d.ts +2 -0
  156. package/dist/material/shader.d.ts +46 -0
  157. package/dist/material/trim-sheet.d.ts +22 -0
  158. package/dist/neural/generator.d.ts +25 -0
  159. package/dist/neural/images.d.ts +17 -0
  160. package/dist/neural/index.d.ts +2 -0
  161. package/dist/neural/index.js +14 -0
  162. package/dist/neural/index.js.map +9 -0
  163. package/dist/passes/bake-ao.d.ts +17 -0
  164. package/dist/passes/grime.d.ts +15 -0
  165. package/dist/passes/index.d.ts +6 -0
  166. package/dist/passes/index.js +22 -0
  167. package/dist/passes/index.js.map +9 -0
  168. package/dist/passes/livery.d.ts +19 -0
  169. package/dist/passes/optimize-cache.d.ts +15 -0
  170. package/dist/passes/optimize-weld.d.ts +15 -0
  171. package/dist/passes/optimize.d.ts +28 -0
  172. package/dist/passes/ray-grid.d.ts +23 -0
  173. package/dist/passes/weighted-normals.d.ts +8 -0
  174. package/dist/primitives/beam.d.ts +17 -0
  175. package/dist/primitives/bevel-box.d.ts +13 -0
  176. package/dist/primitives/box.d.ts +16 -0
  177. package/dist/primitives/cell.d.ts +20 -0
  178. package/dist/primitives/check.d.ts +12 -0
  179. package/dist/primitives/closed.d.ts +11 -0
  180. package/dist/primitives/erosion.d.ts +22 -0
  181. package/dist/primitives/font-5x7.d.ts +3 -0
  182. package/dist/primitives/heightfield.d.ts +16 -0
  183. package/dist/primitives/index.d.ts +23 -0
  184. package/dist/primitives/index.js +81 -0
  185. package/dist/primitives/index.js.map +9 -0
  186. package/dist/primitives/lathe-facets.d.ts +8 -0
  187. package/dist/primitives/lathe.d.ts +26 -0
  188. package/dist/primitives/lattice.d.ts +30 -0
  189. package/dist/primitives/loft.d.ts +15 -0
  190. package/dist/primitives/mc-table.d.ts +2 -0
  191. package/dist/primitives/member.d.ts +19 -0
  192. package/dist/primitives/noise.d.ts +34 -0
  193. package/dist/primitives/planar.d.ts +10 -0
  194. package/dist/primitives/prism.d.ts +13 -0
  195. package/dist/primitives/sculpt-edit.d.ts +56 -0
  196. package/dist/primitives/sculpt-tree.d.ts +23 -0
  197. package/dist/primitives/sculpt.d.ts +27 -0
  198. package/dist/primitives/sdf-grid.d.ts +17 -0
  199. package/dist/primitives/sdf-ops.d.ts +37 -0
  200. package/dist/primitives/sdf.d.ts +21 -0
  201. package/dist/primitives/strip-uv.d.ts +11 -0
  202. package/dist/primitives/surface-nets.d.ts +7 -0
  203. package/dist/primitives/sweep.d.ts +19 -0
  204. package/dist/primitives/terrain-color.d.ts +30 -0
  205. package/dist/primitives/terrain-grid.d.ts +36 -0
  206. package/dist/primitives/text-layout.d.ts +22 -0
  207. package/dist/primitives/text.d.ts +27 -0
  208. package/dist/primitives/tiled.d.ts +22 -0
  209. package/dist/primitives/transport.d.ts +17 -0
  210. package/dist/primitives/triangulate.d.ts +2 -0
  211. package/dist/primitives/value-noise.d.ts +10 -0
  212. package/dist/qa/checks.d.ts +11 -0
  213. package/dist/qa/clash.d.ts +2 -0
  214. package/dist/qa/floating.d.ts +5 -0
  215. package/dist/qa/gates.d.ts +12 -0
  216. package/dist/qa/geometry.d.ts +21 -0
  217. package/dist/qa/index.d.ts +12 -0
  218. package/dist/qa/index.js +78 -0
  219. package/dist/qa/index.js.map +9 -0
  220. package/dist/qa/inside.d.ts +12 -0
  221. package/dist/qa/inspect.d.ts +18 -0
  222. package/dist/qa/lods.d.ts +12 -0
  223. package/dist/qa/measures.d.ts +4 -0
  224. package/dist/qa/occlusion.d.ts +3 -0
  225. package/dist/qa/sanity.d.ts +7 -0
  226. package/dist/qa/types.d.ts +12 -0
  227. package/dist/render/beauty.d.ts +40 -0
  228. package/dist/render/camera.d.ts +33 -0
  229. package/dist/render/depth-map.d.ts +38 -0
  230. package/dist/render/font.d.ts +1 -0
  231. package/dist/render/ground.d.ts +13 -0
  232. package/dist/render/index.d.ts +10 -0
  233. package/dist/render/index.js +47 -0
  234. package/dist/render/index.js.map +9 -0
  235. package/dist/render/lighting.d.ts +20 -0
  236. package/dist/render/lod-strip.d.ts +5 -0
  237. package/dist/render/metrics.d.ts +37 -0
  238. package/dist/render/options.d.ts +28 -0
  239. package/dist/render/photo-match.d.ts +19 -0
  240. package/dist/render/raster.d.ts +25 -0
  241. package/dist/render/resolve.d.ts +11 -0
  242. package/dist/render/review-prompt.d.ts +2 -0
  243. package/dist/render/review-warnings.d.ts +34 -0
  244. package/dist/render/review.d.ts +24 -0
  245. package/dist/render/scene.d.ts +18 -0
  246. package/dist/render/shade.d.ts +22 -0
  247. package/dist/render/sheet.d.ts +15 -0
  248. package/dist/render/texture.d.ts +15 -0
  249. package/dist/render/thumbnail.d.ts +8 -0
  250. package/dist/render/vis-buffer.d.ts +18 -0
  251. package/dist/testing/assert.d.ts +21 -0
  252. package/dist/testing/compiled.d.ts +15 -0
  253. package/dist/testing/hash.d.ts +1 -0
  254. package/dist/testing/index.d.ts +4 -0
  255. package/dist/testing/index.js +134 -0
  256. package/dist/testing/index.js.map +13 -0
  257. package/dist/testing/snapshot.d.ts +13 -0
  258. package/docs/README.md +52 -0
  259. package/docs/architecture/README.md +68 -0
  260. package/docs/architecture/adding-a-kind.md +44 -0
  261. package/docs/architecture/adding-a-primitive.md +49 -0
  262. package/docs/architecture/errors.md +47 -0
  263. package/docs/architecture/glb-production.md +214 -0
  264. package/docs/architecture/gltf-capabilities.md +80 -0
  265. package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
  266. package/docs/decisions/002-typescript-not-json.md +22 -0
  267. package/docs/decisions/003-shared-trim-sheet.md +21 -0
  268. package/docs/decisions/004-z-up-metres.md +21 -0
  269. package/docs/decisions/005-zero-runtime-deps.md +21 -0
  270. package/docs/decisions/006-single-package-subpath-exports.md +20 -0
  271. package/docs/decisions/007-cpu-contact-sheets.md +21 -0
  272. package/docs/decisions/008-surface-materials.md +21 -0
  273. package/docs/decisions/009-gltf-front-axis.md +21 -0
  274. package/docs/decisions/README.md +15 -0
  275. package/docs/idea/01-vision.md +117 -0
  276. package/docs/idea/02-dsl.md +343 -0
  277. package/docs/idea/03-ir.md +97 -0
  278. package/docs/idea/04-materials.md +114 -0
  279. package/docs/idea/05-agent-loop.md +141 -0
  280. package/docs/idea/06-qa.md +101 -0
  281. package/docs/idea/07-what-fits.md +45 -0
  282. package/docs/idea/08-neural-adapters.md +78 -0
  283. package/docs/idea/09-kinds.md +105 -0
  284. package/docs/landscape.md +49 -0
  285. package/docs/plans/roadmap.md +127 -0
  286. package/examples/build-with-bun.ts +41 -0
  287. package/llms.txt +47 -0
  288. package/package.json +99 -4
  289. package/recipes/README.md +38 -0
  290. package/recipes/asteroid/README.md +46 -0
  291. package/recipes/asteroid/asteroid.ts +336 -0
  292. package/recipes/boulders/README.md +28 -0
  293. package/recipes/boulders/boulders.ts +117 -0
  294. package/recipes/boxcar/README.md +46 -0
  295. package/recipes/boxcar/boxcar.ts +916 -0
  296. package/recipes/cargo_container_scifi/README.md +52 -0
  297. package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
  298. package/recipes/dropship/README.md +84 -0
  299. package/recipes/dropship/dropship.ts +1144 -0
  300. package/recipes/eiffel_tower/README.md +110 -0
  301. package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
  302. package/recipes/goblin_head/README.md +62 -0
  303. package/recipes/goblin_head/goblin_head.ts +36 -0
  304. package/recipes/human_head/README.md +64 -0
  305. package/recipes/human_head/human_head.ts +37 -0
  306. package/recipes/loco_440/README.md +32 -0
  307. package/recipes/loco_440/loco_440.ts +1404 -0
  308. package/recipes/material_swatches/README.md +21 -0
  309. package/recipes/material_swatches/material_swatches.ts +183 -0
  310. package/recipes/mountain_range/README.md +45 -0
  311. package/recipes/mountain_range/mountain_range.ts +128 -0
  312. package/recipes/oak/README.md +30 -0
  313. package/recipes/oak/oak.ts +19 -0
  314. package/recipes/orc_head/README.md +66 -0
  315. package/recipes/orc_head/orc_head.ts +39 -0
  316. package/recipes/palm/README.md +30 -0
  317. package/recipes/palm/palm.ts +19 -0
  318. package/recipes/pine/README.md +29 -0
  319. package/recipes/pine/pine.ts +19 -0
  320. package/recipes/signal_gantry/README.md +36 -0
  321. package/recipes/signal_gantry/signal_gantry.ts +522 -0
  322. package/recipes/station_halt/README.md +39 -0
  323. package/recipes/station_halt/station_halt.ts +722 -0
  324. package/recipes/t34/README.md +44 -0
  325. package/recipes/t34/t34.ts +1068 -0
  326. package/recipes/tiger_1/README.md +75 -0
  327. package/recipes/tiger_1/tiger_1.ts +1370 -0
  328. package/recipes/water_tower/README.md +40 -0
  329. package/recipes/water_tower/water_tower.ts +542 -0
  330. package/src/cli/args.ts +112 -0
  331. package/src/cli/bin.ts +7 -0
  332. package/src/cli/load.ts +17 -0
  333. package/src/cli/manifest.ts +89 -0
  334. package/src/cli/render-cmds.ts +66 -0
  335. package/src/cli/run.ts +168 -0
  336. package/src/core/asset.ts +128 -0
  337. package/src/core/definition.ts +149 -0
  338. package/src/core/errors.ts +45 -0
  339. package/src/core/family.ts +50 -0
  340. package/src/core/index.ts +66 -0
  341. package/src/core/mesh.ts +114 -0
  342. package/src/core/part.ts +130 -0
  343. package/src/core/png.ts +49 -0
  344. package/src/core/rng.ts +49 -0
  345. package/src/core/transform.ts +90 -0
  346. package/src/core/vec.ts +26 -0
  347. package/src/core/world.ts +80 -0
  348. package/src/export/bin-writer.ts +79 -0
  349. package/src/export/container.ts +35 -0
  350. package/src/export/glb.ts +200 -0
  351. package/src/export/index.ts +18 -0
  352. package/src/export/lights.ts +49 -0
  353. package/src/export/materials.ts +126 -0
  354. package/src/export/merge-pivots.ts +146 -0
  355. package/src/export/metadata.ts +59 -0
  356. package/src/export/motion.ts +70 -0
  357. package/src/export/optimize-parts.ts +80 -0
  358. package/src/export/primitive.ts +35 -0
  359. package/src/export/tangents.ts +75 -0
  360. package/src/export/textures.ts +88 -0
  361. package/src/export/version.ts +6 -0
  362. package/src/grammar/index.ts +4 -0
  363. package/src/grammar/split.ts +87 -0
  364. package/src/grammar/whyte.ts +20 -0
  365. package/src/index.ts +14 -0
  366. package/src/kinds/building.ts +153 -0
  367. package/src/kinds/faces.ts +138 -0
  368. package/src/kinds/head-armour.ts +151 -0
  369. package/src/kinds/head-beard.ts +133 -0
  370. package/src/kinds/head-bust.ts +221 -0
  371. package/src/kinds/head-extras.ts +150 -0
  372. package/src/kinds/head-face.ts +222 -0
  373. package/src/kinds/head-geom.ts +31 -0
  374. package/src/kinds/head-hair.ts +152 -0
  375. package/src/kinds/head-layout.ts +63 -0
  376. package/src/kinds/head-mesh.ts +157 -0
  377. package/src/kinds/head-params.ts +153 -0
  378. package/src/kinds/head-presets.ts +152 -0
  379. package/src/kinds/head-rim.ts +128 -0
  380. package/src/kinds/head.ts +171 -0
  381. package/src/kinds/hull-lines.ts +160 -0
  382. package/src/kinds/hull-skin.ts +83 -0
  383. package/src/kinds/hull.ts +209 -0
  384. package/src/kinds/index.ts +63 -0
  385. package/src/kinds/mass.ts +110 -0
  386. package/src/kinds/openings.ts +141 -0
  387. package/src/kinds/planned.ts +12 -0
  388. package/src/kinds/roof-chimney.ts +100 -0
  389. package/src/kinds/roof-shape.ts +230 -0
  390. package/src/kinds/roof.ts +250 -0
  391. package/src/kinds/vegetation-fronds.ts +126 -0
  392. package/src/kinds/vegetation-impostor.ts +118 -0
  393. package/src/kinds/vegetation-leaves.ts +168 -0
  394. package/src/kinds/vegetation-mesh.ts +174 -0
  395. package/src/kinds/vegetation-params.ts +147 -0
  396. package/src/kinds/vegetation-presets.ts +184 -0
  397. package/src/kinds/vegetation-skeleton.ts +168 -0
  398. package/src/kinds/vegetation.ts +199 -0
  399. package/src/kinds/vehicle.ts +144 -0
  400. package/src/material/generator.ts +56 -0
  401. package/src/material/index.ts +29 -0
  402. package/src/material/paint/bond.ts +60 -0
  403. package/src/material/paint/canvas.ts +91 -0
  404. package/src/material/paint/cell.ts +39 -0
  405. package/src/material/paint/cells-round.ts +119 -0
  406. package/src/material/paint/cells.ts +111 -0
  407. package/src/material/paint/compose.ts +53 -0
  408. package/src/material/paint/concrete.ts +62 -0
  409. package/src/material/paint/courses.ts +85 -0
  410. package/src/material/paint/decode-png.ts +100 -0
  411. package/src/material/paint/default-sheet.ts +61 -0
  412. package/src/material/paint/foliage-broad.ts +126 -0
  413. package/src/material/paint/foliage-common.ts +93 -0
  414. package/src/material/paint/foliage-compound.ts +135 -0
  415. package/src/material/paint/foliage-conifer.ts +143 -0
  416. package/src/material/paint/foliage-raster.ts +196 -0
  417. package/src/material/paint/foliage.ts +23 -0
  418. package/src/material/paint/masks.ts +44 -0
  419. package/src/material/paint/noise.ts +52 -0
  420. package/src/material/paint/normal.ts +52 -0
  421. package/src/material/paint/orm.ts +95 -0
  422. package/src/material/paint/planks.ts +56 -0
  423. package/src/material/paint/plates.ts +72 -0
  424. package/src/material/paint/shapes.ts +28 -0
  425. package/src/material/paint/slate.ts +52 -0
  426. package/src/material/paint/stamp.ts +40 -0
  427. package/src/material/paint/strip.ts +42 -0
  428. package/src/material/paint/surface.ts +60 -0
  429. package/src/material/paint/types.ts +51 -0
  430. package/src/material/paint/validate.ts +58 -0
  431. package/src/material/palette.ts +43 -0
  432. package/src/material/shader-godot.ts +77 -0
  433. package/src/material/shader-three.ts +103 -0
  434. package/src/material/shader.ts +100 -0
  435. package/src/material/trim-sheet.ts +100 -0
  436. package/src/neural/generator.ts +37 -0
  437. package/src/neural/images.ts +27 -0
  438. package/src/neural/index.ts +10 -0
  439. package/src/passes/bake-ao.ts +183 -0
  440. package/src/passes/grime.ts +37 -0
  441. package/src/passes/index.ts +8 -0
  442. package/src/passes/livery.ts +66 -0
  443. package/src/passes/optimize-cache.ts +175 -0
  444. package/src/passes/optimize-weld.ts +147 -0
  445. package/src/passes/optimize.ts +85 -0
  446. package/src/passes/ray-grid.ts +237 -0
  447. package/src/passes/weighted-normals.ts +125 -0
  448. package/src/primitives/beam.ts +60 -0
  449. package/src/primitives/bevel-box.ts +140 -0
  450. package/src/primitives/box.ts +90 -0
  451. package/src/primitives/cell.ts +115 -0
  452. package/src/primitives/check.ts +49 -0
  453. package/src/primitives/closed.ts +74 -0
  454. package/src/primitives/erosion.ts +183 -0
  455. package/src/primitives/font-5x7.ts +117 -0
  456. package/src/primitives/heightfield.ts +101 -0
  457. package/src/primitives/index.ts +56 -0
  458. package/src/primitives/lathe-facets.ts +45 -0
  459. package/src/primitives/lathe.ts +174 -0
  460. package/src/primitives/lattice.ts +131 -0
  461. package/src/primitives/loft.ts +174 -0
  462. package/src/primitives/mc-table.ts +41 -0
  463. package/src/primitives/member.ts +130 -0
  464. package/src/primitives/noise.ts +157 -0
  465. package/src/primitives/planar.ts +55 -0
  466. package/src/primitives/prism.ts +85 -0
  467. package/src/primitives/sculpt-edit.ts +212 -0
  468. package/src/primitives/sculpt-tree.ts +226 -0
  469. package/src/primitives/sculpt.ts +80 -0
  470. package/src/primitives/sdf-grid.ts +131 -0
  471. package/src/primitives/sdf-ops.ts +145 -0
  472. package/src/primitives/sdf.ts +83 -0
  473. package/src/primitives/strip-uv.ts +26 -0
  474. package/src/primitives/surface-nets.ts +161 -0
  475. package/src/primitives/sweep.ts +162 -0
  476. package/src/primitives/terrain-color.ts +85 -0
  477. package/src/primitives/terrain-grid.ts +137 -0
  478. package/src/primitives/text-layout.ts +85 -0
  479. package/src/primitives/text.ts +197 -0
  480. package/src/primitives/tiled.ts +93 -0
  481. package/src/primitives/transport.ts +85 -0
  482. package/src/primitives/triangulate.ts +42 -0
  483. package/src/primitives/value-noise.ts +62 -0
  484. package/src/qa/checks.ts +110 -0
  485. package/src/qa/clash.ts +177 -0
  486. package/src/qa/floating.ts +63 -0
  487. package/src/qa/gates.ts +126 -0
  488. package/src/qa/geometry.ts +187 -0
  489. package/src/qa/index.ts +28 -0
  490. package/src/qa/inside.ts +114 -0
  491. package/src/qa/inspect.ts +107 -0
  492. package/src/qa/lods.ts +97 -0
  493. package/src/qa/measures.ts +67 -0
  494. package/src/qa/occlusion.ts +166 -0
  495. package/src/qa/sanity.ts +137 -0
  496. package/src/qa/types.ts +17 -0
  497. package/src/render/beauty.ts +115 -0
  498. package/src/render/camera.ts +142 -0
  499. package/src/render/depth-map.ts +191 -0
  500. package/src/render/font.ts +76 -0
  501. package/src/render/ground.ts +87 -0
  502. package/src/render/index.ts +34 -0
  503. package/src/render/lighting.ts +95 -0
  504. package/src/render/lod-strip.ts +34 -0
  505. package/src/render/metrics.ts +147 -0
  506. package/src/render/options.ts +81 -0
  507. package/src/render/photo-match.ts +28 -0
  508. package/src/render/raster.ts +143 -0
  509. package/src/render/resolve.ts +109 -0
  510. package/src/render/review-prompt.ts +59 -0
  511. package/src/render/review-warnings.ts +92 -0
  512. package/src/render/review.ts +103 -0
  513. package/src/render/scene.ts +119 -0
  514. package/src/render/shade.ts +199 -0
  515. package/src/render/sheet.ts +88 -0
  516. package/src/render/texture.ts +97 -0
  517. package/src/render/thumbnail.ts +75 -0
  518. package/src/render/vis-buffer.ts +119 -0
  519. package/src/testing/assert.ts +115 -0
  520. package/src/testing/compiled.ts +31 -0
  521. package/src/testing/hash.ts +14 -0
  522. package/src/testing/index.ts +14 -0
  523. package/src/testing/snapshot.ts +37 -0
  524. package/src/types/gltf-validator.d.ts +13 -0
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/passes/ray-grid.ts", "../src/passes/bake-ao.ts", "../src/passes/grime.ts", "../src/passes/livery.ts", "../src/passes/optimize-cache.ts", "../src/passes/optimize-weld.ts", "../src/passes/optimize.ts", "../src/passes/weighted-normals.ts"],
4
+ "sourcesContent": [
5
+ "// Ray casting for bakes: triangles bucketed in a uniform 3D grid (CSR, typed arrays), walked\n// with Amanatides–Woo DDA, tested with Möller–Trumbore. Answers \"does this ray hit anything\n// within `tMax`?\" — all an occlusion bake needs. Allocation-free per ray, deterministic.\n\nimport { boundsOf, type Mesh, type Vec3 } from '../core/index.ts';\n\nconst CELL_SCALE = 0.3;\n\nexport class RayGrid {\n private readonly tri: Float64Array;\n /** Per triangle: v0, e1 = v1 − v0, e2 = v2 − v0 (Möller–Trumbore inputs, computed once). */\n private readonly edges: Float64Array;\n /** Per triangle: unnormalised plane normal e1 × e2 and its offset n·v0. */\n private readonly planes: Float64Array;\n private readonly start: Int32Array;\n private readonly list: Int32Array;\n private readonly min: Vec3;\n private readonly res: [number, number, number];\n private readonly cell: number;\n private readonly stamp: Int32Array;\n private ray = 0;\n\n constructor(mesh: Mesh) {\n const n = mesh.indices.length / 3;\n this.tri = new Float64Array(n * 9);\n for (let t = 0; t < n; t++)\n for (let v = 0; v < 3; v++) {\n const i = (mesh.indices[t * 3 + v] ?? 0) * 3;\n for (let k = 0; k < 3; k++) this.tri[t * 9 + v * 3 + k] = mesh.positions[i + k] ?? 0;\n }\n this.edges = new Float64Array(n * 9);\n for (let t = 0; t < n; t++) {\n const o = t * 9;\n for (let k = 0; k < 3; k++) {\n const v0 = this.tri[o + k] ?? 0;\n this.edges[o + k] = v0;\n this.edges[o + 3 + k] = (this.tri[o + 3 + k] ?? 0) - v0;\n this.edges[o + 6 + k] = (this.tri[o + 6 + k] ?? 0) - v0;\n }\n }\n this.planes = new Float64Array(n * 4);\n for (let t = 0; t < n; t++) {\n const E = this.edges;\n const o = t * 9;\n const [ax, ay, az] = [E[o + 3] ?? 0, E[o + 4] ?? 0, E[o + 5] ?? 0];\n const [bx, by, bz] = [E[o + 6] ?? 0, E[o + 7] ?? 0, E[o + 8] ?? 0];\n const nx = ay * bz - az * by;\n const ny = az * bx - ax * bz;\n const nz = ax * by - ay * bx;\n this.planes.set(\n [nx, ny, nz, nx * (E[o] ?? 0) + ny * (E[o + 1] ?? 0) + nz * (E[o + 2] ?? 0)],\n t * 4,\n );\n }\n const b = boundsOf(mesh);\n const span = [0, 1, 2].map((k) => Math.max(1e-6, (b.max[k] ?? 0) - (b.min[k] ?? 0)));\n // Cells ~0.3× the \"two triangles per cell if they filled the volume\" size: triangles live\n // on surfaces, so that size leaves a dozen per occupied cell. Measured on tiger_1 and a\n // sculpted head (0.2–0.5 is flat; larger cells cost 2–4×). Bounded to 256 per axis and\n // ~4M cells overall. Any cell size gives the same answers; only the speed changes.\n const vol = (span[0] ?? 1) * (span[1] ?? 1) * (span[2] ?? 1);\n this.cell = Math.max(\n CELL_SCALE * Math.cbrt(vol / Math.max(1, n / 2)),\n Math.max(...span) / 256,\n Math.cbrt(vol / 4e6),\n 1e-4,\n );\n this.min = [b.min[0] - 1e-6, b.min[1] - 1e-6, b.min[2] - 1e-6];\n this.res = [0, 1, 2].map((k) => Math.max(1, Math.ceil((span[k] ?? 0) / this.cell) + 1)) as [\n number,\n number,\n number,\n ];\n const [rx, ry] = this.res;\n const cells = rx * ry * this.res[2];\n // Each triangle's cell box once, then a counting pass and a fill pass (CSR, t ascending).\n const box = new Int32Array(n * 6);\n const T = this.tri;\n for (let t = 0; t < n; t++)\n for (let k = 0; k < 3; k++) {\n const a = T[t * 9 + k] ?? 0;\n const b2 = T[t * 9 + 3 + k] ?? 0;\n const c = T[t * 9 + 6 + k] ?? 0;\n box[t * 6 + k] = this.coord(Math.min(a, b2, c), k);\n box[t * 6 + 3 + k] = this.coord(Math.max(a, b2, c), k);\n }\n const count = new Int32Array(cells + 1);\n const visit = (t: number, f: (c: number) => void): void => {\n const o = t * 6;\n for (let x = box[o] ?? 0; x <= (box[o + 3] ?? 0); x++)\n for (let y = box[o + 1] ?? 0; y <= (box[o + 4] ?? 0); y++)\n for (let z = box[o + 2] ?? 0; z <= (box[o + 5] ?? 0); z++) f((z * ry + y) * rx + x);\n };\n const bump = (c: number): void => {\n count[c + 1] = (count[c + 1] ?? 0) + 1;\n };\n for (let t = 0; t < n; t++) visit(t, bump);\n for (let c = 0; c < cells; c++) count[c + 1] = (count[c + 1] ?? 0) + (count[c] ?? 0);\n this.start = count;\n const list = new Int32Array(count[cells] ?? 0);\n const fill = new Int32Array(cells);\n for (let t = 0; t < n; t++)\n visit(t, (c) => {\n list[(count[c] ?? 0) + (fill[c] ?? 0)] = t;\n fill[c] = (fill[c] ?? 0) + 1;\n });\n this.list = list;\n this.stamp = new Int32Array(n);\n }\n\n private coord(v: number, k: number): number {\n const r = this.res[k as 0 | 1 | 2];\n return Math.min(\n r - 1,\n Math.max(0, Math.floor((v - (this.min[k as 0 | 1 | 2] ?? 0)) / this.cell)),\n );\n }\n\n /** Any hit of the ray o + t·d (d unit) for 0 < t ≤ tMax. Scalar arguments: no allocation. */\n hit(\n ox: number,\n oy: number,\n oz: number,\n dx: number,\n dy: number,\n dz: number,\n tMax: number,\n ): boolean {\n const q = ++this.ray;\n const start = this.start;\n const list = this.list;\n const stamp = this.stamp;\n const size = this.cell;\n const [rx, ry, rz] = this.res;\n const [mx, my, mz] = this.min;\n let cx = this.coord(ox, 0);\n let cy = this.coord(oy, 1);\n let cz = this.coord(oz, 2);\n const sx = dx > 0 ? 1 : -1;\n const sy = dy > 0 ? 1 : -1;\n const sz = dz > 0 ? 1 : -1;\n let nx = Math.abs(dx) < 1e-12 ? Infinity : (mx + (cx + (dx > 0 ? 1 : 0)) * size - ox) / dx;\n let ny = Math.abs(dy) < 1e-12 ? Infinity : (my + (cy + (dy > 0 ? 1 : 0)) * size - oy) / dy;\n let nz = Math.abs(dz) < 1e-12 ? Infinity : (mz + (cz + (dz > 0 ? 1 : 0)) * size - oz) / dz;\n const ddx = Math.abs(dx) < 1e-12 ? Infinity : size / Math.abs(dx);\n const ddy = Math.abs(dy) < 1e-12 ? Infinity : size / Math.abs(dy);\n const ddz = Math.abs(dz) < 1e-12 ? Infinity : size / Math.abs(dz);\n let t = 0;\n while (t <= tMax) {\n const c = (cz * ry + cy) * rx + cx;\n const end = start[c + 1] ?? 0;\n for (let i = start[c] ?? 0; i < end; i++) {\n const tri = list[i] ?? 0;\n if (stamp[tri] === q) continue;\n stamp[tri] = q;\n if (this.test(tri, ox, oy, oz, dx, dy, dz, tMax)) return true;\n }\n if (nx < ny) {\n if (nx < nz) {\n t = nx;\n cx += sx;\n if (cx < 0 || cx >= rx) return false;\n nx = t + ddx;\n } else {\n t = nz;\n cz += sz;\n if (cz < 0 || cz >= rz) return false;\n nz = t + ddz;\n }\n } else if (ny < nz) {\n t = ny;\n cy += sy;\n if (cy < 0 || cy >= ry) return false;\n ny = t + ddy;\n } else {\n t = nz;\n cz += sz;\n if (cz < 0 || cz >= rz) return false;\n nz = t + ddz;\n }\n }\n return false;\n }\n\n /** Möller–Trumbore on cached v0, e1, e2 (the same arithmetic as deriving them per ray). */\n private test(\n tri: number,\n ox: number,\n oy: number,\n oz: number,\n dx: number,\n dy: number,\n dz: number,\n tMax: number,\n ): boolean {\n // Plane pre-reject: the hit distance along the ray, from two dot products. Only clear misses\n // (behind the origin, or past tMax, with margin) are skipped; Möller–Trumbore decides the rest.\n const P = this.planes;\n const f = tri * 4;\n const nx = P[f] ?? 0;\n const ny = P[f + 1] ?? 0;\n const nz = P[f + 2] ?? 0;\n const th0 = ((P[f + 3] ?? 0) - (nx * ox + ny * oy + nz * oz)) / (nx * dx + ny * dy + nz * dz);\n if (!(th0 > -1e-9 && th0 <= tMax * (1 + 1e-9) + 1e-9)) return false;\n const E = this.edges;\n const b = tri * 9;\n const e1x = E[b + 3] ?? 0;\n const e1y = E[b + 4] ?? 0;\n const e1z = E[b + 5] ?? 0;\n const e2x = E[b + 6] ?? 0;\n const e2y = E[b + 7] ?? 0;\n const e2z = E[b + 8] ?? 0;\n const px = dy * e2z - dz * e2y;\n const py = dz * e2x - dx * e2z;\n const pz = dx * e2y - dy * e2x;\n const det = e1x * px + e1y * py + e1z * pz;\n if (Math.abs(det) < 1e-14) return false;\n const inv = 1 / det;\n const tx = ox - (E[b] ?? 0);\n const ty = oy - (E[b + 1] ?? 0);\n const tz = oz - (E[b + 2] ?? 0);\n const u = (tx * px + ty * py + tz * pz) * inv;\n if (u < 0 || u > 1) return false;\n const qx = ty * e1z - tz * e1y;\n const qy = tz * e1x - tx * e1z;\n const qz = tx * e1y - ty * e1x;\n const v = (dx * qx + dy * qy + dz * qz) * inv;\n if (v < 0 || u + v > 1) return false;\n const th = (e2x * qx + e2y * qy + e2z * qz) * inv;\n return th > 0 && th <= tMax;\n }\n\n /** True if the ray o + t·d (d unit) hits any triangle for 0 < t ≤ tMax. */\n occluded(o: Vec3, d: Vec3, tMax: number): boolean {\n return this.hit(o[0], o[1], o[2], d[0], d[1], d[2], tMax);\n }\n}\n",
6
+ "// Ambient occlusion baked into vertex colour: the cheapest big look upgrade for a vertex-coloured\n// asset — creases, wheel wells and the gap under a turret darken, as a lit render would. Each\n// vertex fires a fixed cosine-weighted hemisphere of rays (Fibonacci spiral, no randomness) at the\n// asset's own triangles (and optionally the ground plane); the hit fraction darkens its colour.\n\nimport { type CompiledAsset, type Mesh, merge, type Vec3, worldMeshes } from '../core/index.ts';\nimport { RayGrid } from './ray-grid.ts';\n\nexport interface AoOptions {\n /** Rays per vertex. Default 24. */\n readonly samples?: number;\n /** Occluders farther than this (metres) don't count. Default 1. */\n readonly radius?: number;\n /** 0 = no change, 1 = fully occluded vertices go black. Default 0.6. */\n readonly strength?: number;\n /** Treat z = 0 as a ground plane (contact shadow under the asset). Default true. */\n readonly ground?: boolean;\n}\n\nconst GOLDEN = Math.PI * (3 - Math.sqrt(5));\n\n/** Fixed cosine-weighted directions around +Z. */\nfunction hemisphere(n: number): Vec3[] {\n return Array.from({ length: n }, (_, i) => {\n const r = Math.sqrt((i + 0.5) / n);\n const phi = i * GOLDEN;\n return [r * Math.cos(phi), r * Math.sin(phi), Math.sqrt(Math.max(0, 1 - r * r))] as Vec3;\n });\n}\n\n/** Orthonormal frame with `n` as its Z (Frisvad / Duff et al. branchless basis). */\nfunction frame(n: Vec3): [Vec3, Vec3] {\n const s = n[2] >= 0 ? 1 : -1;\n const a = -1 / (s + n[2]);\n const b = n[0] * n[1] * a;\n return [\n [1 + s * n[0] * n[0] * a, s * b, -s * n[0]],\n [b, s + n[1] * n[1] * a, -n[1]],\n ];\n}\n\n/**\n * Vertices sharing a position (0.1 mm) and normal (0.001) share one result: welded seams and\n * duplicated soup vertices cost one hemisphere, not many. Open addressing on the six integers.\n */\nclass Memo {\n private readonly keys: Float64Array;\n private readonly vals: Float64Array;\n private readonly used: Uint8Array;\n private readonly mask: number;\n\n constructor(n: number) {\n let cap = 16;\n while (cap < n * 2) cap *= 2;\n this.mask = cap - 1;\n this.keys = new Float64Array(cap * 6);\n this.vals = new Float64Array(cap);\n this.used = new Uint8Array(cap);\n }\n\n /** Slot for the key; `found` tells whether it already holds a value. */\n slot(k: Float64Array): { at: number; found: boolean } {\n let h = 2166136261;\n for (let i = 0; i < 6; i++) h = Math.imul(h ^ ((k[i] ?? 0) | 0), 16777619);\n let at = (h >>> 0) & this.mask;\n for (;;) {\n if (!this.used[at]) return { at, found: false };\n let same = true;\n for (let i = 0; i < 6 && same; i++) same = this.keys[at * 6 + i] === k[i];\n if (same) return { at, found: true };\n at = (at + 1) & this.mask;\n }\n }\n\n get(at: number): number {\n return this.vals[at] ?? 0;\n }\n\n set(at: number, k: Float64Array, v: number): void {\n this.used[at] = 1;\n this.keys.set(k, at * 6);\n this.vals[at] = v;\n }\n}\n\n/** Per-vertex visibility (1 = open sky, 0 = buried) of `mesh` against `grid`. */\nfunction visibility(mesh: Mesh, grid: RayGrid, o: AoOptions): Float32Array {\n const hemi = hemisphere(o.samples ?? 24);\n const D = Float64Array.from(hemi.flat());\n const count = hemi.length;\n const radius = o.radius ?? 1;\n const ground = o.ground ?? true;\n const P = mesh.positions;\n const N = mesh.normals;\n const out = new Float32Array(P.length / 3);\n const memo = new Memo(out.length);\n const key = new Float64Array(6);\n // Lift off the surface so a ray never hits the face it starts on.\n const bias = 2e-3;\n for (let v = 0; v < out.length; v++) {\n const px = P[v * 3] ?? 0;\n const py = P[v * 3 + 1] ?? 0;\n const pz = P[v * 3 + 2] ?? 0;\n const rx = N[v * 3] ?? 0;\n const ry = N[v * 3 + 1] ?? 0;\n const rz = N[v * 3 + 2] ?? 1;\n const len = Math.hypot(rx, ry, rz) || 1;\n const n: Vec3 = [rx / len, ry / len, rz / len];\n key[0] = Math.round(px * 1e4);\n key[1] = Math.round(py * 1e4);\n key[2] = Math.round(pz * 1e4);\n key[3] = Math.round(n[0] * 1e3);\n key[4] = Math.round(n[1] * 1e3);\n key[5] = Math.round(n[2] * 1e3);\n const s = memo.slot(key);\n if (s.found) {\n out[v] = memo.get(s.at);\n continue;\n }\n const [t, b] = frame(n);\n const ox = px + n[0] * bias;\n const oy = py + n[1] * bias;\n const oz = pz + n[2] * bias;\n let blocked = 0;\n for (let i = 0; i < count; i++) {\n const d0 = D[i * 3] ?? 0;\n const d1 = D[i * 3 + 1] ?? 0;\n const d2 = D[i * 3 + 2] ?? 0;\n const wx = t[0] * d0 + b[0] * d1 + n[0] * d2;\n const wy = t[1] * d0 + b[1] * d1 + n[1] * d2;\n const wz = t[2] * d0 + b[2] * d1 + n[2] * d2;\n if (ground && wz < -1e-6 && oz >= 0 && -oz / wz <= radius) blocked++;\n else if (grid.hit(ox, oy, oz, wx, wy, wz, radius)) blocked++;\n }\n const vis = 1 - blocked / count;\n memo.set(s.at, key, vis);\n out[v] = vis;\n }\n return out;\n}\n\nconst darken = (mesh: Mesh, vis: Float32Array, strength: number): Mesh => {\n const colors = new Float32Array(mesh.colors);\n for (let v = 0; v < vis.length; v++) {\n const f = 1 - strength * (1 - (vis[v] ?? 1));\n for (let k = 0; k < 3; k++) colors[v * 3 + k] = (colors[v * 3 + k] ?? 0) * f;\n }\n return { ...mesh, colors };\n};\n\n/** Bake AO of one mesh against itself (or `occluder`, in the same space). */\n// Callers bake several parts against one occluder (a head's skin, jaw and eyes against the\n// whole head): index each occluder once. Meshes are immutable, so the grid stays valid.\nconst grids = new WeakMap<Mesh, RayGrid>();\nconst gridOf = (m: Mesh): RayGrid => {\n const hit = grids.get(m);\n if (hit) return hit;\n const g = new RayGrid(m);\n grids.set(m, g);\n return g;\n};\n\nexport function bakeAo(mesh: Mesh, o: AoOptions = {}, occluder: Mesh = mesh): Mesh {\n return darken(mesh, visibility(mesh, gridOf(occluder), o), o.strength ?? 0.6);\n}\n\n/**\n * Bake AO across a whole compiled asset: every part is occluded by every other (a wheel darkens\n * the hull behind it). Colours only change, so parts keep their local meshes and pivots.\n */\nexport function bakeAoAsset(asset: CompiledAsset, o: AoOptions = {}): CompiledAsset {\n const world = worldMeshes(asset.parts);\n if (world.size === 0) return asset;\n const grid = new RayGrid(merge([...world.values()]));\n const strength = o.strength ?? 0.6;\n return {\n ...asset,\n parts: asset.parts.map((p) => {\n const w = world.get(p.name);\n return p.mesh && w ? { ...p, mesh: darken(p.mesh, visibility(w, grid, o), strength) } : p;\n }),\n };\n}\n",
7
+ "// Grime: darken vertex colour near the ground and in named zones (soot, mud, dust). A pass over a\n// finished mesh, so recipes stay about shape; wear is applied once, the same way everywhere.\n\nimport type { Mesh, Vec3 } from '../core/index.ts';\n\nexport interface GrimeZone {\n readonly at: Vec3;\n readonly radius: number;\n /** 0..1 darkening at the centre. */\n readonly strength: number;\n}\n\nexport interface GrimeOptions {\n /** Height (m) below which mud/dust builds up toward the ground. */\n readonly ground?: number;\n /** Darkening at z = 0. */\n readonly groundStrength?: number;\n readonly zones?: readonly GrimeZone[];\n}\n\nexport function grime(mesh: Mesh, o: GrimeOptions): Mesh {\n const colors = new Float32Array(mesh.colors);\n const g = o.ground ?? 0;\n const gs = o.groundStrength ?? 0.3;\n for (let i = 0; i < mesh.positions.length / 3; i++) {\n const x = mesh.positions[i * 3] ?? 0;\n const y = mesh.positions[i * 3 + 1] ?? 0;\n const z = mesh.positions[i * 3 + 2] ?? 0;\n let f = g > 0 && z < g ? 1 - gs * (1 - Math.max(0, z) / g) : 1;\n for (const zone of o.zones ?? []) {\n const d = Math.hypot(x - zone.at[0], y - zone.at[1], z - zone.at[2]) / zone.radius;\n if (d < 1) f *= 1 - zone.strength * (1 - d);\n }\n for (let k = 0; k < 3; k++) colors[i * 3 + k] = (colors[i * 3 + k] ?? 1) * f;\n }\n return { ...mesh, colors };\n}\n",
8
+ "// Livery: paint a finished mesh by rules instead of by hand. Base colour, then ordered rules —\n// the last matching rule wins — keyed on height bands, an along-length range, a side-elevation\n// polygon (stripes, wings, numbers' backgrounds), and surface orientation (roof, front, back,\n// sides, underside). One call turns a grey loco into \"Daylight\", a tank into its camo scheme.\n\nimport type { Mesh, Vec2, Vec3 } from '../core/index.ts';\n\nexport interface LiveryRule {\n readonly color: Vec3;\n /** Height band [z0, z1] (metres). */\n readonly z?: readonly [number, number];\n /** Along-length band [x0, x1] (metres, +X forward). */\n readonly x?: readonly [number, number];\n /** Polygon in the side elevation (x, z): the region a stripe or panel covers. */\n readonly poly?: readonly Vec2[];\n /** Only faces whose normal points this way (n·axis > 0.5; roof: n.z > 0.55). */\n readonly facing?: 'roof' | 'under' | 'front' | 'back' | 'side';\n /** Multiply into the existing colour instead of replacing it (weathering, shading). */\n readonly multiply?: boolean;\n}\n\nexport interface Livery {\n readonly base?: Vec3;\n readonly rules: readonly LiveryRule[];\n}\n\nfunction inPoly(poly: readonly Vec2[], x: number, z: number): boolean {\n let inside = false;\n for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {\n const [xi, zi] = poly[i] ?? [0, 0];\n const [xj, zj] = poly[j] ?? [0, 0];\n if (zi > z !== zj > z && x < ((xj - xi) * (z - zi)) / (zj - zi) + xi) inside = !inside;\n }\n return inside;\n}\n\nfunction faces(f: NonNullable<LiveryRule['facing']>, n: Vec3): boolean {\n if (f === 'roof') return n[2] > 0.55;\n if (f === 'under') return n[2] < -0.55;\n if (f === 'front') return n[0] > 0.5;\n if (f === 'back') return n[0] < -0.5;\n return Math.abs(n[1]) > 0.5;\n}\n\nexport function livery(mesh: Mesh, l: Livery): Mesh {\n const colors = new Float32Array(mesh.colors);\n for (let i = 0; i < mesh.positions.length / 3; i++) {\n const x = mesh.positions[i * 3] ?? 0;\n const z = mesh.positions[i * 3 + 2] ?? 0;\n const n: Vec3 = [\n mesh.normals[i * 3] ?? 0,\n mesh.normals[i * 3 + 1] ?? 0,\n mesh.normals[i * 3 + 2] ?? 0,\n ];\n let c: Vec3 = l.base ?? [colors[i * 3] ?? 1, colors[i * 3 + 1] ?? 1, colors[i * 3 + 2] ?? 1];\n for (const r of l.rules) {\n if (r.z && (z < r.z[0] || z > r.z[1])) continue;\n if (r.x && (x < r.x[0] || x > r.x[1])) continue;\n if (r.poly && !inPoly(r.poly, x, z)) continue;\n if (r.facing && !faces(r.facing, n)) continue;\n c = r.multiply ? [c[0] * r.color[0], c[1] * r.color[1], c[2] * r.color[2]] : r.color;\n }\n colors.set(c, i * 3);\n }\n return { ...mesh, colors };\n}\n",
9
+ "// Post-transform vertex cache: Tom Forsyth's \"Linear-Speed Vertex Cache Optimisation\" (2006)\n// triangle reorder, the vertex-fetch reorder that follows it, and the ACMR/ATVR analysis that\n// measures both. All integer bookkeeping and fixed tie-breaks (lowest triangle index wins), so\n// the output is byte-deterministic.\n\nconst CACHE = 32;\nconst DECAY = 1.5;\nconst LAST_TRI = 0.75;\nconst VALENCE_SCALE = 2;\nconst VALENCE_POWER = 0.5;\nconst MAX_VALENCE = 64;\n\nconst CACHE_SCORE = Float64Array.from({ length: CACHE }, (_, i) =>\n i < 3 ? LAST_TRI : (1 - (i - 3) / (CACHE - 3)) ** DECAY,\n);\nconst VALENCE_SCORE = Float64Array.from({ length: MAX_VALENCE + 1 }, (_, n) =>\n n === 0 ? 0 : VALENCE_SCALE * n ** -VALENCE_POWER,\n);\n\nconst vertexScore = (pos: number, live: number): number =>\n live === 0\n ? -1\n : (pos >= 0 ? (CACHE_SCORE[pos] ?? 0) : 0) + (VALENCE_SCORE[Math.min(live, MAX_VALENCE)] ?? 0);\n\n/** Reorder triangles for an LRU cache of 32 (Forsyth). Returns a new index buffer. */\nexport function forsyth(indices: Uint32Array, vertexCount: number): Uint32Array {\n const triCount = indices.length / 3;\n if (triCount === 0) return new Uint32Array(0);\n // CSR adjacency: vertex → triangles; `live[v]` counts the not-yet-emitted prefix.\n const live = new Int32Array(vertexCount);\n for (const v of indices) live[v] = (live[v] ?? 0) + 1;\n const offset = new Int32Array(vertexCount + 1);\n for (let v = 0; v < vertexCount; v++) offset[v + 1] = (offset[v] ?? 0) + (live[v] ?? 0);\n const adj = new Int32Array(indices.length);\n const fill = new Int32Array(vertexCount);\n for (let t = 0; t < triCount; t++)\n for (let k = 0; k < 3; k++) {\n const v = indices[t * 3 + k] ?? 0;\n adj[(offset[v] ?? 0) + (fill[v] ?? 0)] = t;\n fill[v] = (fill[v] ?? 0) + 1;\n }\n const cachePos = new Int32Array(vertexCount).fill(-1);\n const score = new Float64Array(vertexCount);\n for (let v = 0; v < vertexCount; v++) score[v] = vertexScore(-1, live[v] ?? 0);\n const emitted = new Uint8Array(triCount);\n const triScoreOf = (t: number): number =>\n (score[indices[t * 3] ?? 0] ?? 0) +\n (score[indices[t * 3 + 1] ?? 0] ?? 0) +\n (score[indices[t * 3 + 2] ?? 0] ?? 0);\n let best = 0;\n let top = triScoreOf(0);\n for (let t = 1; t < triCount; t++) {\n const s = triScoreOf(t);\n if (s > top) {\n top = s;\n best = t;\n }\n }\n const out = new Uint32Array(indices.length);\n let cache = new Int32Array(CACHE + 3);\n let spare = new Int32Array(CACHE + 3);\n let size = 0;\n let cursor = 0;\n for (let n = 0; n < triCount; n++) {\n if (best < 0) {\n while (emitted[cursor]) cursor++;\n best = cursor;\n }\n const t = best;\n emitted[t] = 1;\n const a = indices[t * 3] ?? 0;\n const b = indices[t * 3 + 1] ?? 0;\n const c = indices[t * 3 + 2] ?? 0;\n out[n * 3] = a;\n out[n * 3 + 1] = b;\n out[n * 3 + 2] = c;\n for (const v of [a, b, c]) {\n // Remove t from v's live adjacency (swap with the last live entry).\n const o = offset[v] ?? 0;\n const l = live[v] ?? 0;\n for (let i = o; i < o + l; i++)\n if (adj[i] === t) {\n adj[i] = adj[o + l - 1] ?? 0;\n adj[o + l - 1] = t;\n break;\n }\n live[v] = l - 1;\n }\n // New LRU: the triangle's vertices first, then the old cache minus them.\n spare[0] = a;\n spare[1] = b;\n spare[2] = c;\n let m = 3;\n for (let i = 0; i < size; i++) {\n const v = cache[i] ?? 0;\n if (v !== a && v !== b && v !== c) spare[m++] = v;\n }\n for (let i = CACHE; i < m; i++) {\n const v = spare[i] ?? 0;\n cachePos[v] = -1;\n score[v] = vertexScore(-1, live[v] ?? 0);\n }\n [cache, spare] = [spare, cache];\n size = Math.min(m, CACHE);\n for (let i = 0; i < size; i++) {\n const v = cache[i] ?? 0;\n cachePos[v] = i;\n score[v] = vertexScore(i, live[v] ?? 0);\n }\n best = -1;\n let bestScore = -Infinity;\n for (let j = 0; j < size; j++) {\n const v = cache[j] ?? 0;\n const o = offset[v] ?? 0;\n for (let i = o; i < o + (live[v] ?? 0); i++) {\n const u = adj[i] ?? 0;\n const s = triScoreOf(u);\n if (s > bestScore || (s === bestScore && u < best)) {\n bestScore = s;\n best = u;\n }\n }\n }\n }\n return out;\n}\n\n/** Renumber vertices in order of first use; unreferenced vertices are dropped. */\nexport function fetchOrder(\n indices: Uint32Array,\n vertexCount: number,\n): { indices: Uint32Array; order: Uint32Array } {\n const map = new Int32Array(vertexCount).fill(-1);\n const order: number[] = [];\n const out = new Uint32Array(indices.length);\n for (let i = 0; i < indices.length; i++) {\n const v = indices[i] ?? 0;\n if ((map[v] ?? -1) < 0) {\n map[v] = order.length;\n order.push(v);\n }\n out[i] = map[v] ?? 0;\n }\n return { indices: out, order: Uint32Array.from(order) };\n}\n\nexport interface CacheStats {\n /** Average cache misses per triangle (0.5 ideal on a regular grid, 3 worst). */\n readonly acmr: number;\n /** Misses per unique vertex (1.0 ideal). */\n readonly atvr: number;\n}\n\n/** Simulate a FIFO post-transform cache (16 entries, the meshoptimizer/NVIDIA convention). */\nexport function cacheStats(indices: Uint32Array, vertexCount: number, size = 16): CacheStats {\n const tris = indices.length / 3;\n if (tris === 0) return { acmr: 0, atvr: 0 };\n const stamp = new Int32Array(vertexCount).fill(-1_000_000_000);\n let misses = 0;\n for (const v of indices) {\n // A vertex is cached if it was loaded within the last `size` misses.\n if (misses - (stamp[v] ?? 0) >= size) {\n stamp[v] = misses;\n misses++;\n }\n }\n const seen = new Uint8Array(vertexCount);\n let used = 0;\n for (const v of indices)\n if (!seen[v]) {\n seen[v] = 1;\n used++;\n }\n return { acmr: misses / tris, atvr: used ? misses / used : 0 };\n}\n",
10
+ "// Vertex weld + degenerate/duplicate triangle removal: the \"index\" step of mesh optimisation.\n// Two vertices merge when position, normal, UV and colour all quantise to the same cell, so hard\n// edges (split normals) and UV seams survive. First occurrence wins; output order is the order of\n// first appearance — deterministic, no hashing of floats into iteration order.\n\nimport type { Mesh } from '../core/index.ts';\n\nexport interface WeldTolerance {\n /** Position grid, metres. */\n readonly position: number;\n readonly normal: number;\n readonly uv: number;\n readonly color: number;\n}\n\nexport const DEFAULT_WELD: WeldTolerance = {\n position: 1e-5,\n normal: 1e-3,\n uv: 1e-5,\n // Below the GLB's unorm16 colour step: welding never changes exported colour.\n color: 1 / 131072,\n};\n\nexport interface Welded {\n readonly mesh: Mesh;\n readonly degenerate: number;\n readonly duplicate: number;\n}\n\n/** The quantised attribute tuple of vertex i (12 ints). */\nfunction cellOf(m: Mesh, i: number, t: WeldTolerance, out: Int32Array): void {\n for (let k = 0; k < 3; k++) {\n out[k] = Math.round((m.positions[i * 3 + k] ?? 0) / t.position);\n out[3 + k] = Math.round((m.normals[i * 3 + k] ?? 0) / t.normal);\n out[8 + k] = Math.round((m.colors[i * 3 + k] ?? 0) / t.color);\n }\n out[6] = Math.round((m.uvs[i * 2] ?? 0) / t.uv);\n out[7] = Math.round((m.uvs[i * 2 + 1] ?? 0) / t.uv);\n out[11] = 0;\n}\n\nconst hashCell = (c: Int32Array): number => {\n let h = 2166136261;\n for (let k = 0; k < 11; k++) h = Math.imul(h ^ (c[k] ?? 0), 16777619);\n return h >>> 0;\n};\n\n/** Map each input vertex to its welded representative (open-addressing table, linear probe). */\nfunction weldMap(m: Mesh, t: WeldTolerance): { remap: Uint32Array; keep: number[] } {\n const n = m.positions.length / 3;\n const cells = new Int32Array(n * 12);\n const remap = new Uint32Array(n);\n const keep: number[] = [];\n let cap = 16;\n while (cap < n * 2) cap *= 2;\n const table = new Int32Array(cap).fill(-1);\n const cur = new Int32Array(12);\n for (let i = 0; i < n; i++) {\n cellOf(m, i, t, cur);\n cells.set(cur, i * 12);\n let slot = hashCell(cur) & (cap - 1);\n let found = -1;\n for (;;) {\n const j = table[slot] ?? -1;\n if (j < 0) break;\n const rep = (keep[j] ?? 0) * 12;\n let same = true;\n for (let k = 0; k < 11 && same; k++) same = cells[rep + k] === cur[k];\n if (same) {\n found = j;\n break;\n }\n slot = (slot + 1) & (cap - 1);\n }\n if (found < 0) {\n found = keep.length;\n keep.push(i);\n table[slot] = found;\n }\n remap[i] = found;\n }\n return { remap, keep };\n}\n\n/** Twice the triangle's area, from welded positions. */\nfunction area2(p: Float32Array, a: number, b: number, c: number): number {\n const ax = p[a * 3] ?? 0;\n const ay = p[a * 3 + 1] ?? 0;\n const az = p[a * 3 + 2] ?? 0;\n const ux = (p[b * 3] ?? 0) - ax;\n const uy = (p[b * 3 + 1] ?? 0) - ay;\n const uz = (p[b * 3 + 2] ?? 0) - az;\n const vx = (p[c * 3] ?? 0) - ax;\n const vy = (p[c * 3 + 1] ?? 0) - ay;\n const vz = (p[c * 3 + 2] ?? 0) - az;\n return Math.hypot(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);\n}\n\nexport function weld(m: Mesh, t: WeldTolerance = DEFAULT_WELD): Welded {\n const { remap, keep } = weldMap(m, t);\n const pick = (src: Float32Array, w: number): Float32Array => {\n const out = new Float32Array(keep.length * w);\n keep.forEach((i, j) => {\n for (let k = 0; k < w; k++) out[j * w + k] = src[i * w + k] ?? 0;\n });\n return out;\n };\n const positions = pick(m.positions, 3);\n const idx: number[] = [];\n // Duplicate detection: canonical rotation (lowest index first) hashed to a numeric key.\n const seen = new Map<number, number[]>();\n let degenerate = 0;\n let duplicate = 0;\n for (let f = 0; f < m.indices.length; f += 3) {\n const a = remap[m.indices[f] ?? 0] ?? 0;\n const b = remap[m.indices[f + 1] ?? 0] ?? 0;\n const c = remap[m.indices[f + 2] ?? 0] ?? 0;\n // Zero area covers no pixel: dropping it cannot open a crack.\n if (a === b || b === c || a === c || area2(positions, a, b, c) < 1e-12) {\n degenerate++;\n continue;\n }\n // Same triangle, same winding (any rotation) is pure overdraw; the reverse winding is a\n // deliberate double-sided face and stays.\n const [x, y, z] = a < b && a < c ? [a, b, c] : b < c ? [b, c, a] : [c, a, b];\n const h = (Math.imul(x, 73856093) ^ Math.imul(y, 19349663) ^ Math.imul(z, 83492791)) >>> 0;\n const list = seen.get(h);\n if (list?.some((at) => idx[at] === x && idx[at + 1] === y && idx[at + 2] === z)) {\n duplicate++;\n continue;\n }\n if (list) list.push(idx.length);\n else seen.set(h, [idx.length]);\n idx.push(x, y, z);\n }\n return {\n mesh: {\n positions,\n normals: pick(m.normals, 3),\n uvs: pick(m.uvs, 2),\n colors: pick(m.colors, 3),\n indices: new Uint32Array(idx),\n },\n degenerate,\n duplicate,\n };\n}\n",
11
+ "// Game-ready optimisation, run on every LOD before export, in meshoptimizer's order: weld\n// duplicate vertices (index) → reorder triangles for the post-transform vertex cache (Forsyth)\n// → reorder vertices for fetch locality. Same geometry, fewer vertices, fewer cache misses.\n// Zero dependencies, byte-deterministic. Reports ACMR/ATVR before and after.\n\nimport type { Mesh } from '../core/index.ts';\nimport { cacheStats, fetchOrder, forsyth } from './optimize-cache.ts';\nimport { DEFAULT_WELD, type WeldTolerance, weld } from './optimize-weld.ts';\n\nexport interface OptimizeOptions {\n /** Merge vertices closer than this (metres) with matching normal/uv/colour. Default 1e-5. */\n readonly weldEpsilon?: number;\n /** Reorder for the vertex cache (Forsyth). Default true. */\n readonly vertexCache?: boolean;\n}\n\nexport interface OptimizeReport {\n readonly verticesBefore: number;\n readonly verticesAfter: number;\n readonly trianglesBefore: number;\n readonly trianglesAfter: number;\n /** Zero-area / collapsed triangles dropped. */\n readonly degenerate: number;\n /** Exact duplicate triangles (same winding) dropped. */\n readonly duplicate: number;\n /** Average cache miss ratio per triangle before (FIFO 16). */\n readonly acmrBefore: number;\n /** Average cache miss ratio per triangle after reordering (lower is better, ~0.6–0.8 is good). */\n readonly acmr: number;\n /** Cache misses per vertex before / after (1.0 is ideal). */\n readonly atvrBefore: number;\n readonly atvr: number;\n}\n\nfunction reorderVertices(m: Mesh, order: Uint32Array, indices: Uint32Array): Mesh {\n const pick = (src: Float32Array, w: number): Float32Array => {\n const out = new Float32Array(order.length * w);\n for (let j = 0; j < order.length; j++)\n for (let k = 0; k < w; k++) out[j * w + k] = src[(order[j] ?? 0) * w + k] ?? 0;\n return out;\n };\n return {\n positions: pick(m.positions, 3),\n normals: pick(m.normals, 3),\n uvs: pick(m.uvs, 2),\n colors: pick(m.colors, 3),\n indices,\n };\n}\n\nexport function optimize(\n mesh: Mesh,\n o: OptimizeOptions = {},\n): { mesh: Mesh; report: OptimizeReport } {\n const vBefore = mesh.positions.length / 3;\n const before = cacheStats(mesh.indices, vBefore);\n const tol: WeldTolerance = { ...DEFAULT_WELD, position: o.weldEpsilon ?? DEFAULT_WELD.position };\n const w = weld(mesh, tol);\n const vWelded = w.mesh.positions.length / 3;\n // Forsyth targets an LRU-32 cache; on already-good input (flat strips) it can lose a hair on\n // the FIFO-16 measure. Keep whichever order measures better — never make a mesh worse.\n const reordered = o.vertexCache === false ? null : forsyth(w.mesh.indices, vWelded);\n const tris =\n reordered && cacheStats(reordered, vWelded).acmr <= cacheStats(w.mesh.indices, vWelded).acmr\n ? reordered\n : w.mesh.indices;\n const fetched = fetchOrder(tris, vWelded);\n const out = reorderVertices(w.mesh, fetched.order, fetched.indices);\n const after = cacheStats(out.indices, out.positions.length / 3);\n return {\n mesh: out,\n report: {\n verticesBefore: vBefore,\n verticesAfter: out.positions.length / 3,\n trianglesBefore: mesh.indices.length / 3,\n trianglesAfter: out.indices.length / 3,\n degenerate: w.degenerate,\n duplicate: w.duplicate,\n acmrBefore: before.acmr,\n acmr: after.acmr,\n atvrBefore: before.atvr,\n atvr: after.atvr,\n },\n };\n}\n",
12
+ "// Face-weighted vertex normals with a hard-edge split: the mid-poly hard-surface look. Each corner\n// averages the face normals around its position, weighted by face area × corner angle, but only\n// over faces within `hardAngle` of its own face. Big flat faces dominate, so a bevelled box keeps\n// dead-flat sides while its bevels bend the light into soft highlights — no normal map needed.\n\nimport type { Mesh, Vec3 } from '../core/index.ts';\n\nexport interface WeightedNormalsOptions {\n /** Faces meeting at more than this many degrees keep a hard edge. Default 40. */\n readonly hardAngle?: number;\n /** Positions closer than this (metres) count as one point. Default 1e-5. */\n readonly epsilon?: number;\n}\n\ninterface Corner {\n readonly n: Vec3;\n readonly w: number;\n}\n\nfunction faceData(m: Mesh, t: number): { n: Vec3; area: number; angles: Vec3 } {\n const p = (k: number): Vec3 => {\n const i = (m.indices[t * 3 + k] ?? 0) * 3;\n return [m.positions[i] ?? 0, m.positions[i + 1] ?? 0, m.positions[i + 2] ?? 0];\n };\n const v = [p(0), p(1), p(2)] as const;\n const e = (a: Vec3, b: Vec3): Vec3 => [b[0] - a[0], b[1] - a[1], b[2] - a[2]];\n const u = e(v[0], v[1]);\n const w = e(v[0], v[2]);\n const g: Vec3 = [u[1] * w[2] - u[2] * w[1], u[2] * w[0] - u[0] * w[2], u[0] * w[1] - u[1] * w[0]];\n const len = Math.hypot(...g);\n const angle = (k: number): number => {\n const a = e(v[k] ?? v[0], v[(k + 1) % 3] ?? v[0]);\n const b = e(v[k] ?? v[0], v[(k + 2) % 3] ?? v[0]);\n const d = Math.hypot(...a) * Math.hypot(...b);\n return d\n ? Math.acos(Math.max(-1, Math.min(1, (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) / d)))\n : 0;\n };\n return {\n n: len ? [g[0] / len, g[1] / len, g[2] / len] : [0, 0, 0],\n area: len / 2,\n angles: [angle(0), angle(1), angle(2)],\n };\n}\n\nexport function weightedNormals(m: Mesh, o: WeightedNormalsOptions = {}): Mesh {\n const cosHard = Math.cos(((o.hardAngle ?? 40) * Math.PI) / 180);\n const eps = o.epsilon ?? 1e-5;\n const tris = m.indices.length / 3;\n const corners: Corner[] = [];\n for (let t = 0; t < tris; t++) {\n const f = faceData(m, t);\n for (let k = 0; k < 3; k++) corners.push({ n: f.n, w: f.area * (f.angles[k] ?? 0) });\n }\n // Group corners by quantised position (insertion order — deterministic).\n const groups = new Map<string, number[]>();\n const keyOf = (i: number): string =>\n [0, 1, 2].map((k) => Math.round((m.positions[i * 3 + k] ?? 0) / eps)).join(',');\n for (let c = 0; c < corners.length; c++) {\n const key = keyOf(m.indices[c] ?? 0);\n const g = groups.get(key);\n if (g) g.push(c);\n else groups.set(key, [c]);\n }\n const normal = new Float32Array(corners.length * 3);\n for (const g of groups.values())\n for (const c of g) {\n const me = corners[c];\n if (!me) continue;\n let x = 0;\n let y = 0;\n let z = 0;\n for (const d of g) {\n const o2 = corners[d];\n if (!o2 || me.n[0] * o2.n[0] + me.n[1] * o2.n[1] + me.n[2] * o2.n[2] < cosHard) continue;\n x += o2.n[0] * o2.w;\n y += o2.n[1] * o2.w;\n z += o2.n[2] * o2.w;\n }\n const len = Math.hypot(x, y, z);\n // A degenerate face keeps its original normal.\n const src = (m.indices[c] ?? 0) * 3;\n normal.set(\n len > 1e-12\n ? [x / len, y / len, z / len]\n : [m.normals[src] ?? 0, m.normals[src + 1] ?? 0, m.normals[src + 2] ?? 0],\n c * 3,\n );\n }\n return splitByNormal(m, normal);\n}\n\n/** One output vertex per (input vertex, distinct new normal); first appearance order. */\nfunction splitByNormal(m: Mesh, cornerNormals: Float32Array): Mesh {\n const seen = new Map<string, number>();\n const p: number[] = [];\n const n: number[] = [];\n const uv: number[] = [];\n const col: number[] = [];\n const idx = new Uint32Array(m.indices.length);\n for (let c = 0; c < m.indices.length; c++) {\n const v = m.indices[c] ?? 0;\n const nx = cornerNormals[c * 3] ?? 0;\n const ny = cornerNormals[c * 3 + 1] ?? 0;\n const nz = cornerNormals[c * 3 + 2] ?? 0;\n const key = `${v}:${Math.round(nx * 1e4)},${Math.round(ny * 1e4)},${Math.round(nz * 1e4)}`;\n let out = seen.get(key);\n if (out === undefined) {\n out = p.length / 3;\n seen.set(key, out);\n p.push(m.positions[v * 3] ?? 0, m.positions[v * 3 + 1] ?? 0, m.positions[v * 3 + 2] ?? 0);\n n.push(nx, ny, nz);\n uv.push(m.uvs[v * 2] ?? 0, m.uvs[v * 2 + 1] ?? 0);\n col.push(m.colors[v * 3] ?? 0, m.colors[v * 3 + 1] ?? 0, m.colors[v * 3 + 2] ?? 0);\n }\n idx[c] = out;\n }\n return {\n positions: new Float32Array(p),\n normals: new Float32Array(n),\n uvs: new Float32Array(uv),\n colors: new Float32Array(col),\n indices: idx,\n };\n}\n"
13
+ ],
14
+ "mappings": "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",
15
+ "debugId": "BEEEAE9C1B221C7564756E2164756E21",
16
+ "names": []
17
+ }
@@ -0,0 +1,24 @@
1
+ export interface Args {
2
+ readonly cmd: string;
3
+ readonly files: string[];
4
+ readonly out: string;
5
+ readonly json: boolean;
6
+ readonly animations: boolean;
7
+ /** `build` also renders the contact sheet unless --no-shot. */
8
+ readonly shot: boolean;
9
+ /** Trim-sheet textures: shared files in <out>/ (default), embedded per GLB, or none. */
10
+ readonly sheet: 'external' | 'embed' | 'none';
11
+ readonly sheetSize: 512 | 1024 | 2048;
12
+ /** Which detail levels to build: all three by default, or e.g. only the hero mesh. */
13
+ readonly lods: readonly (0 | 1 | 2)[];
14
+ /** Beauty-render camera and size (hero, review, and build's hero.png); unset = defaults. */
15
+ readonly render: {
16
+ readonly width?: number;
17
+ readonly height?: number;
18
+ readonly azDeg?: number;
19
+ readonly elDeg?: number;
20
+ readonly fovDeg?: number;
21
+ };
22
+ }
23
+ export declare const HELP = "usage: t3d <command> <recipe.ts\u2026> [flags]\n build GLB \u00D7 3 LODs + hero + contact sheet + manifest.json per asset; exit 1 on any QA finding\n shot contact sheet + manifest.json only (fast look)\n hero beauty render only \u2192 <out>/<asset>/hero.png (3/4 product shot, textured, shadowed)\n review AI review pack \u2192 <out>/<asset>/review/: hero, sheet, close-ups, LOD strip, review.md\n (checklist prompt for a vision agent) and review.json (image metrics + warnings)\n list the assets each module exports\nflags:\n --out <dir> output root (default: out) \u2192 <dir>/<asset>/{lod0,lod1,lod2}.glb hero.png sheet.png manifest.json\n --no-animations skip the baked 'run' clip (default: baked, so viewers can play it)\n --no-shot build without rendering hero.png and the contact sheet\n --size WxH beauty render size (default 1600x900) \u00B7 --az <deg> camera azimuth from the\n front, default -35 (front-left 3/4) \u00B7 --el <deg> elevation, default 18 \u00B7 --fov <deg> 30\n --sheet <mode> external (default): one shared trim_sheet_{albedo,normal,orm}.png in <out>/,\n referenced by every GLB \u00B7 embed: self-contained GLBs \u00B7 none: vertex colour only\n --sheet-size n 512 | 1024 (default) | 2048\n --lods <list> which detail levels to build: 0,1,2 (default) \u00B7 0 = hero only \u00B7 0,2 = near + far\n --json machine-readable output";
24
+ export declare function parseArgs(argv: readonly string[]): Args;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ Ts23dError2,
4
+ isAsset2,
5
+ compile2
6
+ } from "../chunk-4ddzsqtc.js";
7
+ import {
8
+ DEFAULT_SHEET2,
9
+ paintTrimSheet2
10
+ } from "../chunk-kvcep7ft.js";
11
+ import"../chunk-xnt2sdhf.js";
12
+ import {
13
+ SHEET_MAPS2,
14
+ sheetFileName2,
15
+ sheetPngs2,
16
+ toGlb2
17
+ } from "../chunk-gcyp9b4d.js";
18
+ import {
19
+ runQa2
20
+ } from "../chunk-w1zyhj40.js";
21
+ import {
22
+ checkBeautyOptions2,
23
+ parseSize2,
24
+ beauty2,
25
+ contactSheet2,
26
+ reviewPack2,
27
+ thumbnail2
28
+ } from "../chunk-11v28977.js";
29
+ import {
30
+ manifestOf2
31
+ } from "../chunk-kgvbpqqw.js";
32
+
33
+ // src/cli/run.ts
34
+ import { mkdir as mkdir2 } from "node:fs/promises";
35
+ import { join as join2 } from "node:path";
36
+
37
+ // src/cli/args.ts
38
+ var HELP = `usage: t3d <command> <recipe.ts…> [flags]
39
+ build GLB × 3 LODs + hero + contact sheet + manifest.json per asset; exit 1 on any QA finding
40
+ shot contact sheet + manifest.json only (fast look)
41
+ hero beauty render only → <out>/<asset>/hero.png (3/4 product shot, textured, shadowed)
42
+ review AI review pack → <out>/<asset>/review/: hero, sheet, close-ups, LOD strip, review.md
43
+ (checklist prompt for a vision agent) and review.json (image metrics + warnings)
44
+ list the assets each module exports
45
+ flags:
46
+ --out <dir> output root (default: out) → <dir>/<asset>/{lod0,lod1,lod2}.glb hero.png sheet.png manifest.json
47
+ --no-animations skip the baked 'run' clip (default: baked, so viewers can play it)
48
+ --no-shot build without rendering hero.png and the contact sheet
49
+ --size WxH beauty render size (default 1600x900) · --az <deg> camera azimuth from the
50
+ front, default -35 (front-left 3/4) · --el <deg> elevation, default 18 · --fov <deg> 30
51
+ --sheet <mode> external (default): one shared trim_sheet_{albedo,normal,orm}.png in <out>/,
52
+ referenced by every GLB · embed: self-contained GLBs · none: vertex colour only
53
+ --sheet-size n 512 | 1024 (default) | 2048
54
+ --lods <list> which detail levels to build: 0,1,2 (default) · 0 = hero only · 0,2 = near + far
55
+ --json machine-readable output`;
56
+ function parseArgs(argv) {
57
+ const files = [];
58
+ let out = "out";
59
+ let json = false;
60
+ let animations = true;
61
+ let shot = true;
62
+ let sheet = "external";
63
+ let sheetSize = 1024;
64
+ let lods = [0, 1, 2];
65
+ const render = {};
66
+ const num = (flag, v) => {
67
+ const n = Number(v);
68
+ if (v === undefined || v.trim() === "" || !Number.isFinite(n))
69
+ throw new Ts23dError2("T3D_RENDER_OPTIONS", {
70
+ cause: `${flag} needs a number of degrees, got ${v === undefined ? "nothing" : `"${v}"`}`,
71
+ fix: `pass e.g. ${flag} ${flag === "--fov" ? 30 : flag === "--el" ? 18 : -35}`
72
+ });
73
+ return n;
74
+ };
75
+ for (let i = 1;i < argv.length; i++) {
76
+ const a = argv[i] ?? "";
77
+ if (a === "--json")
78
+ json = true;
79
+ else if (a === "--no-animations")
80
+ animations = false;
81
+ else if (a === "--animations")
82
+ animations = true;
83
+ else if (a === "--no-shot")
84
+ shot = false;
85
+ else if (a === "--out")
86
+ out = argv[++i] ?? out;
87
+ else if (a === "--sheet") {
88
+ const m = argv[++i];
89
+ sheet = m === "embed" || m === "none" ? m : "external";
90
+ } else if (a === "--lods") {
91
+ const picked = (argv[++i] ?? "").split(",").map(Number).filter((n) => n === 0 || n === 1 || n === 2);
92
+ lods = picked.length ? [...new Set(picked)].sort() : lods;
93
+ } else if (a === "--sheet-size") {
94
+ const n = Number(argv[++i]);
95
+ sheetSize = n === 512 || n === 2048 ? n : 1024;
96
+ } else if (a === "--size")
97
+ Object.assign(render, parseSize2(argv[++i] ?? ""));
98
+ else if (a === "--az")
99
+ render.azDeg = num(a, argv[++i]);
100
+ else if (a === "--el")
101
+ render.elDeg = num(a, argv[++i]);
102
+ else if (a === "--fov")
103
+ render.fovDeg = num(a, argv[++i]);
104
+ else
105
+ files.push(a);
106
+ }
107
+ checkBeautyOptions2(render);
108
+ return {
109
+ cmd: argv[0] ?? "help",
110
+ files,
111
+ out,
112
+ json,
113
+ animations,
114
+ shot,
115
+ sheet,
116
+ sheetSize,
117
+ lods,
118
+ render
119
+ };
120
+ }
121
+
122
+ // src/cli/load.ts
123
+ import { resolve } from "node:path";
124
+ async function loadAssets(file) {
125
+ const mod = await import(resolve(file));
126
+ const assets = Object.values(mod).filter(isAsset2);
127
+ if (assets.length === 0) {
128
+ throw new Ts23dError2("T3D_NO_ASSET_EXPORT", {
129
+ cause: `${file} exports no asset`,
130
+ fix: "export const my_thing = defineAsset({ … }) — named export, no default"
131
+ });
132
+ }
133
+ return assets;
134
+ }
135
+
136
+ // src/cli/render-cmds.ts
137
+ import { mkdir } from "node:fs/promises";
138
+ import { dirname, join, relative, resolve as resolve2 } from "node:path";
139
+ async function heroOne(asset, args, sheet) {
140
+ const dir = join(args.out, asset.name);
141
+ await mkdir(dir, { recursive: true });
142
+ const path = join(dir, "hero.png");
143
+ await Bun.write(path, beauty2(compile2(asset, { lod: 0 }), { ...args.render, sheet }));
144
+ return { name: asset.name, files: [path], warnings: [] };
145
+ }
146
+ async function refsFor(name, recipe) {
147
+ for (const p of [
148
+ join(dirname(resolve2(recipe)), "README.md"),
149
+ join(dirname(resolve2(recipe)), "..", "refs", `${name}.md`),
150
+ resolve2("refs", `${name}.md`)
151
+ ])
152
+ if (await Bun.file(p).exists())
153
+ return relative(process.cwd(), p) || p;
154
+ return;
155
+ }
156
+ async function reviewOne(asset, recipe, args, sheet) {
157
+ const dir = join(args.out, asset.name, "review");
158
+ await mkdir(dir, { recursive: true });
159
+ const refs = await refsFor(asset.name, recipe);
160
+ const pack = reviewPack2(asset, { ...args.render, sheet, ...refs ? { refs } : {} });
161
+ const files = [];
162
+ for (const [name, bytes] of pack.files) {
163
+ const path = join(dir, name);
164
+ await Bun.write(path, bytes);
165
+ files.push(path);
166
+ }
167
+ return { name: asset.name, files, warnings: pack.metrics.warnings };
168
+ }
169
+ function printRendered(r) {
170
+ console.log(`${r.name} ${r.warnings.length ? `${r.warnings.length} warning(s)` : "no warnings"}`);
171
+ for (const f of r.files)
172
+ console.log(` → ${f}`);
173
+ for (const w of r.warnings)
174
+ console.log(` ! ${w.code}: ${w.message}
175
+ fix: ${w.fix}`);
176
+ }
177
+
178
+ // src/cli/run.ts
179
+ async function buildOne(asset, args, texture, painted) {
180
+ const dir = join2(args.out, asset.name);
181
+ await mkdir2(dir, { recursive: true });
182
+ const lods = args.lods.map((lod) => compile2(asset, { lod }));
183
+ const findings = lods.flatMap((a) => runQa2(a).map((f) => ({ ...f, message: `LOD${a.lod}: ${f.message}` })));
184
+ const glbs = [];
185
+ if (args.cmd === "build") {
186
+ for (const a of lods) {
187
+ const path = join2(dir, `lod${a.lod}.glb`);
188
+ await Bun.write(path, toGlb2(a, { animations: args.animations, ...texture ? { sheet: texture } : {} }));
189
+ glbs.push(path);
190
+ }
191
+ }
192
+ let sheet;
193
+ let thumb;
194
+ let hero;
195
+ if (args.cmd === "build" && args.shot) {
196
+ hero = join2(dir, "hero.png");
197
+ const top = lods[0] ?? compile2(asset);
198
+ await Bun.write(hero, beauty2(top, { ...args.render, ...painted ? { sheet: painted } : {} }));
199
+ }
200
+ if (args.cmd === "shot" || args.shot) {
201
+ thumb = join2(dir, "thumb.png");
202
+ await Bun.write(thumb, thumbnail2(lods[0] ?? compile2(asset)));
203
+ sheet = join2(dir, "sheet.png");
204
+ await Bun.write(sheet, contactSheet2(lods[0] ?? compile2(asset), { far: lods.at(-1) ?? compile2(asset, { lod: 2 }) }));
205
+ }
206
+ const manifest = manifestOf2(asset, lods, {
207
+ lods: glbs,
208
+ ...hero ? { hero } : {},
209
+ ...sheet ? { sheet } : {},
210
+ ...thumb ? { thumb } : {}
211
+ }, findings);
212
+ await Bun.write(join2(dir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
213
+ `);
214
+ return manifest;
215
+ }
216
+ function print(m) {
217
+ console.log(`${m.name} QA ${m.qa.pass ? "PASS" : "FAIL"} tris ${m.triangles.join("/")} draws ${m.drawCalls.join("/")}`);
218
+ for (const f of [
219
+ ...m.files.lods,
220
+ ...m.files.hero ? [m.files.hero] : [],
221
+ ...m.files.sheet ? [m.files.sheet] : []
222
+ ])
223
+ console.log(` → ${f}`);
224
+ for (const f of m.qa.findings)
225
+ console.log(` ✗ ${f.gate}${f.part ? ` [${f.part}]` : ""}: ${f.message}
226
+ fix: ${f.fix}`);
227
+ }
228
+ var COMMANDS = ["build", "shot", "list", "hero", "review"];
229
+ function fail(e, json) {
230
+ if (!(e instanceof Ts23dError2))
231
+ throw e;
232
+ console.error(json ? JSON.stringify({ ok: false, error: e.toJSON() }) : `${e.message}
233
+ fix: ${e.fix}`);
234
+ return 1;
235
+ }
236
+ async function run(argv) {
237
+ let args;
238
+ try {
239
+ args = parseArgs(argv);
240
+ } catch (e) {
241
+ return fail(e, argv.includes("--json"));
242
+ }
243
+ if (!COMMANDS.includes(args.cmd) || args.files.length === 0) {
244
+ console.log(HELP);
245
+ return args.cmd === "help" ? 0 : 2;
246
+ }
247
+ const built = [];
248
+ const rendered = [];
249
+ const renders = args.cmd === "hero" || args.cmd === "review";
250
+ const wantsSheet = args.cmd === "build" && args.sheet !== "none" || renders;
251
+ const painted = wantsSheet ? paintTrimSheet2({ ...DEFAULT_SHEET2, size: args.sheetSize }) : undefined;
252
+ let texture;
253
+ if (args.cmd === "build" && args.sheet !== "none" && painted) {
254
+ if (args.sheet === "embed")
255
+ texture = painted;
256
+ else {
257
+ const pngs = sheetPngs2(painted);
258
+ await mkdir2(args.out, { recursive: true });
259
+ for (const map of SHEET_MAPS2)
260
+ await Bun.write(join2(args.out, sheetFileName2(map)), pngs[map]);
261
+ texture = { external: true, prefix: "../trim_sheet" };
262
+ }
263
+ }
264
+ try {
265
+ for (const file of args.files) {
266
+ for (const asset of await loadAssets(file)) {
267
+ if (args.cmd === "list")
268
+ console.log(args.json ? JSON.stringify({ asset: asset.name, file }) : `${asset.name} ${file}`);
269
+ else if (renders && painted)
270
+ rendered.push(args.cmd === "hero" ? await heroOne(asset, args, painted) : await reviewOne(asset, file, args, painted));
271
+ else
272
+ built.push(await buildOne(asset, args, texture, painted));
273
+ }
274
+ }
275
+ } catch (e) {
276
+ return fail(e, args.json);
277
+ }
278
+ if (args.cmd === "list")
279
+ return 0;
280
+ if (renders) {
281
+ if (args.json)
282
+ console.log(JSON.stringify({ ok: true, data: rendered }));
283
+ else
284
+ rendered.forEach(printRendered);
285
+ return 0;
286
+ }
287
+ const ok = built.every((m) => m.qa.pass);
288
+ const index = built.map((m) => ({
289
+ name: m.name,
290
+ qa: m.qa.pass ? "PASS" : "FAIL",
291
+ triangles: m.triangles,
292
+ manifest: join2(m.name, "manifest.json")
293
+ }));
294
+ await Bun.write(join2(args.out, "index.json"), `${JSON.stringify({ ok, assets: index }, null, 2)}
295
+ `);
296
+ if (args.json)
297
+ console.log(JSON.stringify({ ok, data: built }));
298
+ else
299
+ built.forEach(print);
300
+ return ok ? 0 : 1;
301
+ }
302
+
303
+ // src/cli/bin.ts
304
+ process.exitCode = await run(process.argv.slice(2));
305
+
306
+ //# debugId=0183AEABD246D46664756E2164756E21
307
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/run.ts", "../src/cli/args.ts", "../src/cli/load.ts", "../src/cli/render-cmds.ts", "../src/cli/bin.ts"],
4
+ "sourcesContent": [
5
+ "// Command dispatch for `t3d`. Pure function of argv → exit code, so it is testable in-process.\n// Output layout, one folder per asset (stable paths agents and pipelines can rely on):\n// <out>/<asset>/lod0.glb lod1.glb lod2.glb hero.png thumb.png sheet.png manifest.json\n// <out>/<asset>/review/… (t3d review) · <out>/index.json — every asset built, with its QA verdict\n\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { type Asset, compile, type Lod, Ts23dError } from '../core/index.ts';\nimport { SHEET_MAPS, type SheetBinding, sheetFileName, sheetPngs, toGlb } from '../export/index.ts';\nimport { DEFAULT_SHEET, type PaintedSheet, paintTrimSheet } from '../material/index.ts';\nimport { type Finding, runQa } from '../qa/index.ts';\nimport { beauty, contactSheet, thumbnail } from '../render/index.ts';\nimport { type Args, HELP, parseArgs } from './args.ts';\nimport { loadAssets } from './load.ts';\nimport { type AssetManifest, manifestOf } from './manifest.ts';\nimport { heroOne, printRendered, type RenderResult, reviewOne } from './render-cmds.ts';\n\nasync function buildOne(\n asset: Asset,\n args: Args,\n texture: SheetBinding | undefined,\n painted: PaintedSheet | undefined,\n): Promise<AssetManifest> {\n const dir = join(args.out, asset.name);\n await mkdir(dir, { recursive: true });\n // Only the LODs the user asked for: QA, GLBs and the manifest cover exactly those.\n const lods = args.lods.map((lod: Lod) => compile(asset, { lod }));\n const findings: Finding[] = lods.flatMap((a) =>\n runQa(a).map((f) => ({ ...f, message: `LOD${a.lod}: ${f.message}` })),\n );\n const glbs: string[] = [];\n if (args.cmd === 'build') {\n for (const a of lods) {\n const path = join(dir, `lod${a.lod}.glb`);\n await Bun.write(\n path,\n toGlb(a, { animations: args.animations, ...(texture ? { sheet: texture } : {}) }),\n );\n glbs.push(path);\n }\n }\n let sheet: string | undefined;\n let thumb: string | undefined;\n let hero: string | undefined;\n if (args.cmd === 'build' && args.shot) {\n hero = join(dir, 'hero.png');\n const top = lods[0] ?? compile(asset);\n await Bun.write(hero, beauty(top, { ...args.render, ...(painted ? { sheet: painted } : {}) }));\n }\n if (args.cmd === 'shot' || args.shot) {\n thumb = join(dir, 'thumb.png');\n await Bun.write(thumb, thumbnail(lods[0] ?? compile(asset)));\n sheet = join(dir, 'sheet.png');\n await Bun.write(\n sheet,\n contactSheet(lods[0] ?? compile(asset), { far: lods.at(-1) ?? compile(asset, { lod: 2 }) }),\n );\n }\n const manifest = manifestOf(\n asset,\n lods,\n {\n lods: glbs,\n ...(hero ? { hero } : {}),\n ...(sheet ? { sheet } : {}),\n ...(thumb ? { thumb } : {}),\n },\n findings,\n );\n await Bun.write(join(dir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\\n`);\n return manifest;\n}\n\nfunction print(m: AssetManifest): void {\n console.log(\n `${m.name} QA ${m.qa.pass ? 'PASS' : 'FAIL'} tris ${m.triangles.join('/')} draws ${m.drawCalls.join('/')}`,\n );\n for (const f of [\n ...m.files.lods,\n ...(m.files.hero ? [m.files.hero] : []),\n ...(m.files.sheet ? [m.files.sheet] : []),\n ])\n console.log(` → ${f}`);\n for (const f of m.qa.findings)\n console.log(` ✗ ${f.gate}${f.part ? ` [${f.part}]` : ''}: ${f.message}\\n fix: ${f.fix}`);\n}\n\nconst COMMANDS = ['build', 'shot', 'list', 'hero', 'review'];\n\nfunction fail(e: unknown, json: boolean): number {\n if (!(e instanceof Ts23dError)) throw e;\n console.error(\n json ? JSON.stringify({ ok: false, error: e.toJSON() }) : `${e.message}\\n fix: ${e.fix}`,\n );\n return 1;\n}\n\nexport async function run(argv: readonly string[]): Promise<number> {\n let args: Args;\n try {\n args = parseArgs(argv);\n } catch (e) {\n return fail(e, argv.includes('--json'));\n }\n if (!COMMANDS.includes(args.cmd) || args.files.length === 0) {\n console.log(HELP);\n return args.cmd === 'help' ? 0 : 2;\n }\n const built: AssetManifest[] = [];\n const rendered: RenderResult[] = [];\n // Paint the trim sheet once per run (~1 s at 1024²). External mode writes it once into <out>/\n // and every GLB in <out>/<asset>/ references ../trim_sheet_*.png — one texture set for all.\n // The beauty renders sample the same painted sheet.\n const renders = args.cmd === 'hero' || args.cmd === 'review';\n const wantsSheet = (args.cmd === 'build' && args.sheet !== 'none') || renders;\n const painted = wantsSheet\n ? paintTrimSheet({ ...DEFAULT_SHEET, size: args.sheetSize })\n : undefined;\n let texture: SheetBinding | undefined;\n if (args.cmd === 'build' && args.sheet !== 'none' && painted) {\n if (args.sheet === 'embed') texture = painted;\n else {\n const pngs = sheetPngs(painted);\n await mkdir(args.out, { recursive: true });\n for (const map of SHEET_MAPS) await Bun.write(join(args.out, sheetFileName(map)), pngs[map]);\n texture = { external: true, prefix: '../trim_sheet' };\n }\n }\n try {\n for (const file of args.files) {\n for (const asset of await loadAssets(file)) {\n if (args.cmd === 'list')\n console.log(\n args.json ? JSON.stringify({ asset: asset.name, file }) : `${asset.name} ${file}`,\n );\n else if (renders && painted)\n rendered.push(\n args.cmd === 'hero'\n ? await heroOne(asset, args, painted)\n : await reviewOne(asset, file, args, painted),\n );\n else built.push(await buildOne(asset, args, texture, painted));\n }\n }\n } catch (e) {\n return fail(e, args.json);\n }\n if (args.cmd === 'list') return 0;\n if (renders) {\n if (args.json) console.log(JSON.stringify({ ok: true, data: rendered }));\n else rendered.forEach(printRendered);\n return 0;\n }\n const ok = built.every((m) => m.qa.pass);\n const index = built.map((m) => ({\n name: m.name,\n qa: m.qa.pass ? 'PASS' : 'FAIL',\n triangles: m.triangles,\n manifest: join(m.name, 'manifest.json'),\n }));\n await Bun.write(\n join(args.out, 'index.json'),\n `${JSON.stringify({ ok, assets: index }, null, 2)}\\n`,\n );\n if (args.json) console.log(JSON.stringify({ ok, data: built }));\n else built.forEach(print);\n return ok ? 0 : 1;\n}\n",
6
+ "// argv → Args for `t3d`. No dependency, no magic: flags are few and documented in HELP.\n// Bad render flags (--size/--az/--el/--fov) throw T3D_RENDER_OPTIONS with the fix.\n\nimport { Ts23dError } from '../core/index.ts';\nimport { checkBeautyOptions, parseSize } from '../render/index.ts';\n\nexport interface Args {\n readonly cmd: string;\n readonly files: string[];\n readonly out: string;\n readonly json: boolean;\n readonly animations: boolean;\n /** `build` also renders the contact sheet unless --no-shot. */\n readonly shot: boolean;\n /** Trim-sheet textures: shared files in <out>/ (default), embedded per GLB, or none. */\n readonly sheet: 'external' | 'embed' | 'none';\n readonly sheetSize: 512 | 1024 | 2048;\n /** Which detail levels to build: all three by default, or e.g. only the hero mesh. */\n readonly lods: readonly (0 | 1 | 2)[];\n /** Beauty-render camera and size (hero, review, and build's hero.png); unset = defaults. */\n readonly render: {\n readonly width?: number;\n readonly height?: number;\n readonly azDeg?: number;\n readonly elDeg?: number;\n readonly fovDeg?: number;\n };\n}\n\nexport const HELP = `usage: t3d <command> <recipe.ts…> [flags]\n build GLB × 3 LODs + hero + contact sheet + manifest.json per asset; exit 1 on any QA finding\n shot contact sheet + manifest.json only (fast look)\n hero beauty render only → <out>/<asset>/hero.png (3/4 product shot, textured, shadowed)\n review AI review pack → <out>/<asset>/review/: hero, sheet, close-ups, LOD strip, review.md\n (checklist prompt for a vision agent) and review.json (image metrics + warnings)\n list the assets each module exports\nflags:\n --out <dir> output root (default: out) → <dir>/<asset>/{lod0,lod1,lod2}.glb hero.png sheet.png manifest.json\n --no-animations skip the baked 'run' clip (default: baked, so viewers can play it)\n --no-shot build without rendering hero.png and the contact sheet\n --size WxH beauty render size (default 1600x900) · --az <deg> camera azimuth from the\n front, default -35 (front-left 3/4) · --el <deg> elevation, default 18 · --fov <deg> 30\n --sheet <mode> external (default): one shared trim_sheet_{albedo,normal,orm}.png in <out>/,\n referenced by every GLB · embed: self-contained GLBs · none: vertex colour only\n --sheet-size n 512 | 1024 (default) | 2048\n --lods <list> which detail levels to build: 0,1,2 (default) · 0 = hero only · 0,2 = near + far\n --json machine-readable output`;\n\nexport function parseArgs(argv: readonly string[]): Args {\n const files: string[] = [];\n let out = 'out';\n let json = false;\n let animations = true;\n let shot = true;\n let sheet: Args['sheet'] = 'external';\n let sheetSize: Args['sheetSize'] = 1024;\n let lods: Args['lods'] = [0, 1, 2];\n const render: {\n width?: number;\n height?: number;\n azDeg?: number;\n elDeg?: number;\n fovDeg?: number;\n } = {};\n const num = (flag: string, v: string | undefined): number => {\n const n = Number(v);\n if (v === undefined || v.trim() === '' || !Number.isFinite(n))\n throw new Ts23dError('T3D_RENDER_OPTIONS', {\n cause: `${flag} needs a number of degrees, got ${v === undefined ? 'nothing' : `\"${v}\"`}`,\n fix: `pass e.g. ${flag} ${flag === '--fov' ? 30 : flag === '--el' ? 18 : -35}`,\n });\n return n;\n };\n for (let i = 1; i < argv.length; i++) {\n const a = argv[i] ?? '';\n if (a === '--json') json = true;\n else if (a === '--no-animations') animations = false;\n else if (a === '--animations') animations = true;\n else if (a === '--no-shot') shot = false;\n else if (a === '--out') out = argv[++i] ?? out;\n else if (a === '--sheet') {\n const m = argv[++i];\n sheet = m === 'embed' || m === 'none' ? m : 'external';\n } else if (a === '--lods') {\n const picked = (argv[++i] ?? '')\n .split(',')\n .map(Number)\n .filter((n): n is 0 | 1 | 2 => n === 0 || n === 1 || n === 2);\n lods = picked.length ? [...new Set(picked)].sort() : lods;\n } else if (a === '--sheet-size') {\n const n = Number(argv[++i]);\n sheetSize = n === 512 || n === 2048 ? n : 1024;\n } else if (a === '--size') Object.assign(render, parseSize(argv[++i] ?? ''));\n else if (a === '--az') render.azDeg = num(a, argv[++i]);\n else if (a === '--el') render.elDeg = num(a, argv[++i]);\n else if (a === '--fov') render.fovDeg = num(a, argv[++i]);\n else files.push(a);\n }\n checkBeautyOptions(render);\n return {\n cmd: argv[0] ?? 'help',\n files,\n out,\n json,\n animations,\n shot,\n sheet,\n sheetSize,\n lods,\n render,\n };\n}\n",
7
+ "// Load a recipe module and collect every exported Asset. Recipes are plain TS modules: the CLI,\n// the site and tests all import them the same way.\n\nimport { resolve } from 'node:path';\nimport { type Asset, isAsset, Ts23dError } from '../core/index.ts';\n\nexport async function loadAssets(file: string): Promise<Asset[]> {\n const mod: Record<string, unknown> = await import(resolve(file));\n const assets = Object.values(mod).filter(isAsset);\n if (assets.length === 0) {\n throw new Ts23dError('T3D_NO_ASSET_EXPORT', {\n cause: `${file} exports no asset`,\n fix: 'export const my_thing = defineAsset({ … }) — named export, no default',\n });\n }\n return assets;\n}\n",
8
+ "// `t3d hero` and `t3d review`: beauty renders and the AI review pack, one folder per asset.\n// <out>/<asset>/hero.png\n// <out>/<asset>/review/{hero,sheet,detail_1,detail_2,lods}.png review.md review.json\n\nimport { mkdir } from 'node:fs/promises';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { type Asset, compile } from '../core/index.ts';\nimport type { PaintedSheet } from '../material/index.ts';\nimport { beauty, type ReviewWarning, reviewPack } from '../render/index.ts';\nimport type { Args } from './args.ts';\n\nexport interface RenderResult {\n readonly name: string;\n readonly files: readonly string[];\n readonly warnings: readonly ReviewWarning[];\n}\n\nexport async function heroOne(\n asset: Asset,\n args: Args,\n sheet: PaintedSheet,\n): Promise<RenderResult> {\n const dir = join(args.out, asset.name);\n await mkdir(dir, { recursive: true });\n const path = join(dir, 'hero.png');\n await Bun.write(path, beauty(compile(asset, { lod: 0 }), { ...args.render, sheet }));\n return { name: asset.name, files: [path], warnings: [] };\n}\n\n/** The dossier: README.md next to the recipe (recipes/<name>/), else a legacy refs/<name>.md. */\nasync function refsFor(name: string, recipe: string): Promise<string | undefined> {\n for (const p of [\n join(dirname(resolve(recipe)), 'README.md'),\n join(dirname(resolve(recipe)), '..', 'refs', `${name}.md`),\n resolve('refs', `${name}.md`),\n ])\n if (await Bun.file(p).exists()) return relative(process.cwd(), p) || p;\n return undefined;\n}\n\nexport async function reviewOne(\n asset: Asset,\n recipe: string,\n args: Args,\n sheet: PaintedSheet,\n): Promise<RenderResult> {\n const dir = join(args.out, asset.name, 'review');\n await mkdir(dir, { recursive: true });\n const refs = await refsFor(asset.name, recipe);\n const pack = reviewPack(asset, { ...args.render, sheet, ...(refs ? { refs } : {}) });\n const files: string[] = [];\n for (const [name, bytes] of pack.files) {\n const path = join(dir, name);\n await Bun.write(path, bytes);\n files.push(path);\n }\n return { name: asset.name, files, warnings: pack.metrics.warnings };\n}\n\nexport function printRendered(r: RenderResult): void {\n console.log(\n `${r.name} ${r.warnings.length ? `${r.warnings.length} warning(s)` : 'no warnings'}`,\n );\n for (const f of r.files) console.log(` → ${f}`);\n for (const w of r.warnings) console.log(` ! ${w.code}: ${w.message}\\n fix: ${w.fix}`);\n}\n",
9
+ "#!/usr/bin/env bun\n// `t3d` — compile TypeScript recipes to GLB, gate them, and shoot contact sheets. `t3d help`\n// prints the commands; src/cli/args.ts holds the flags, src/cli/run.ts the output layout.\n\nimport { run } from './run.ts';\n\nprocess.exitCode = await run(process.argv.slice(2));\n"
10
+ ],
11
+ "mappings": "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",
12
+ "debugId": "0183AEABD246D46664756E2164756E21",
13
+ "names": []
14
+ }
@@ -0,0 +1,2 @@
1
+ import { type Asset } from '../core/index.js';
2
+ export declare function loadAssets(file: string): Promise<Asset[]>;
@@ -0,0 +1,49 @@
1
+ import type { Asset, CompiledAsset } from '../core/index.js';
2
+ import { type Finding } from '../qa/index.js';
3
+ export interface AssetManifest {
4
+ readonly name: string;
5
+ readonly kind: string;
6
+ readonly description?: string;
7
+ readonly tags: readonly string[];
8
+ readonly files: {
9
+ readonly lods: readonly string[];
10
+ readonly hero?: string;
11
+ readonly sheet?: string;
12
+ readonly thumb?: string;
13
+ };
14
+ readonly triangles: readonly number[];
15
+ readonly drawCalls: readonly number[];
16
+ readonly pivots: readonly {
17
+ name: string;
18
+ parent: string | null;
19
+ motion?: string;
20
+ }[];
21
+ readonly emitters: readonly {
22
+ name: string;
23
+ kind: string;
24
+ when: string;
25
+ }[];
26
+ readonly sockets: readonly {
27
+ name: string;
28
+ kind: string;
29
+ parent: string | null;
30
+ }[];
31
+ readonly surfaces: readonly string[];
32
+ readonly real?: {
33
+ length: number;
34
+ width: number;
35
+ height: number;
36
+ };
37
+ readonly measures: readonly {
38
+ label: string;
39
+ spec: number;
40
+ measured: number | null;
41
+ source?: string;
42
+ }[];
43
+ readonly sources: readonly string[];
44
+ readonly qa: {
45
+ readonly pass: boolean;
46
+ readonly findings: readonly Finding[];
47
+ };
48
+ }
49
+ export declare function manifestOf(asset: Asset, lods: readonly CompiledAsset[], files: AssetManifest['files'], findings: readonly Finding[]): AssetManifest;
@@ -0,0 +1,12 @@
1
+ import { type Asset } from '../core/index.js';
2
+ import type { PaintedSheet } from '../material/index.js';
3
+ import { type ReviewWarning } from '../render/index.js';
4
+ import type { Args } from './args.js';
5
+ export interface RenderResult {
6
+ readonly name: string;
7
+ readonly files: readonly string[];
8
+ readonly warnings: readonly ReviewWarning[];
9
+ }
10
+ export declare function heroOne(asset: Asset, args: Args, sheet: PaintedSheet): Promise<RenderResult>;
11
+ export declare function reviewOne(asset: Asset, recipe: string, args: Args, sheet: PaintedSheet): Promise<RenderResult>;
12
+ export declare function printRendered(r: RenderResult): void;