@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,114 @@
1
+ # 04 — Materials
2
+
3
+ **Every asset shares one texture set: a 1024² trim sheet, tinted by vertex colour, under at most four surface materials (opaque, cutout, glass, translucent).** One texture set for everything (with static geometry batched per pivot, draw calls = the parts the engine moves), equal texel density across assets, and a palette swap is a recipe edit, never a new texture. Source: `src/material/`. Decisions: [ADR 003](../decisions/003-shared-trim-sheet.md), superseded by [ADR 008](../decisions/008-surface-materials.md) (surfaces).
4
+
5
+ ## Status (2026-10-25)
6
+
7
+ | Piece | Status |
8
+ |---|---|
9
+ | Trim-sheet layout: 8 strips, 6 decal cells, 11 foliage cells | Shipped |
10
+ | UVs authored against the layout by every primitive | Shipped |
11
+ | Palettes + `hex()` sRGB → linear | Shipped |
12
+ | `grime` pass (vertex-colour wear) | Shipped |
13
+ | ≤ 4 shared materials per GLB (opaque, cutout, glass, translucent) over one sheet | Shipped |
14
+ | `paintTrimSheet`: procedural albedo / normal / ORM | Shipped |
15
+ | Sheet in the GLB: embedded, or shared external PNGs | Shipped |
16
+ | `threeMaterialSource` (TSL) and `godotShaderSource` (.gdshader): wind, window glow, wear | Shipped |
17
+ | KTX2 (`KHR_texture_basisu`) | Planned |
18
+ | `kubeezImages`: image source for decals / texture sources | Planned, M5 |
19
+
20
+ ## The trim sheet
21
+
22
+ | Region | Contents | Mapping |
23
+ |---|---|---|
24
+ | Strips: 8 rows, each 1/16 of V, full U | `plain` `brick` `stone` `slate` `iron` `wood` `glass` `concrete` | Tile along U; one strip rect = 2 m of surface |
25
+ | Cells: 1/8 × 1/8 squares from V = 0.5 | `window_sash` `door_panel` `clock` `smokebox` `sign` `porthole` | Fixed decals; one cell = 1 m |
26
+
27
+ `stripRect(id)` / `cellRect(id)` return `{ u0, v0, u1, v1, metres }`. `metres` is the point: a primitive maps UVs by world size, so a 6 m wall and a 0.4 m crate show bricks at the same size.
28
+
29
+ A primitive takes `strip?: StripId` (default `plain`). Cells are used by the planned `cell()` primitive (M1).
30
+
31
+ ## Colour: palettes and vertex colour
32
+
33
+ The sheet is greyscale detail (mortar lines, grain, rivets); hue comes from `COLOR_0`, multiplied over it.
34
+
35
+ ```ts
36
+ import { definePalette, palettes } from '@developerz.ai/ts23d';
37
+
38
+ const livery = { ...palettes.victorianRail, paint: palettes.victorianRail.red }; // repaint = spread
39
+ const custom = definePalette({ hull: '#203040', deck: '#8a6f4e' }); // sRGB hex in, linear out
40
+ ```
41
+
42
+ | Palette | Keys |
43
+ |---|---|
44
+ | `victorianRail` | `paint` `trim` `iron` `brass` `red` |
45
+ | `artDeco` | `limestone` `steel` `glass` `bronze` |
46
+ | `modernGlass` | `glass` `aluminium` `concrete` |
47
+ | `harbour` | `hull` `deck` `white` `red` |
48
+
49
+ Era/region palettes are data. Add one in `src/material/palette.ts` with the era in the key name.
50
+
51
+ ## Wear: passes
52
+
53
+ `/passes` post-processes a finished mesh, so recipes stay about shape. Shipped: `grime(mesh, { ground?, groundStrength?, zones? })` darkens vertex colour below `ground` metres of the mesh's own z, not world z (mud, dust) and inside spherical `zones` (`{ at, radius, strength }`: soot round a chimney, oil under an engine).
54
+
55
+ ```ts
56
+ ctx.part('hull', grime(box({ size: [6, 3, 1.5], color: green, strip: 'iron' }), { ground: 0.6 }));
57
+ ```
58
+
59
+ ## Shader sources
60
+
61
+ Per-asset effects that keep the shared material. Generated per engine from the same hints:
62
+
63
+ | Function | Output | Hints |
64
+ |---|---|---|
65
+ | `threeMaterialSource(hints)` | three.js TSL node-material source | `wind: { strength }`, `windowGlow: { color, ratio }`, wear |
66
+ | `godotShaderSource(hints)` | Godot `.gdshader` source | same |
67
+
68
+ Wind is crown `sway` motion in the GLB today; per-vertex wind weights are planned.
69
+
70
+ ## Procedural material generator
71
+
72
+ `paintTrimSheet(recipe = DEFAULT_SHEET): PaintedSheet` paints the whole sheet from code at a fixed seed, so textures are as deterministic as meshes.
73
+
74
+ | Output | Format |
75
+ |---|---|
76
+ | `albedo` | RGBA8, sRGB. Greyscale detail, tinted by vertex colour; foliage cells are the exception (coloured albedo, alpha = leaf mask) |
77
+ | `normal` | RGBA8, tangent space |
78
+ | `orm` | RGBA8: R occlusion · G roughness · B metalness |
79
+
80
+ | Size | Paint time (measured) |
81
+ |---|---|
82
+ | 512² | ≈ 0.22 s |
83
+ | 1024² (default) | ≈ 0.8 s |
84
+ | 2048² | ≈ 2.95 s |
85
+
86
+ Strip kinds (`StripRecipe.kind`): `bond` · `planks` · `plates` · `noise` · `flat` · `slate` · `concrete` · `glass`, with `scaleMetres`, `roughness`, `metalness` and per-kind params. Cells: 6 decal cells (window sash, door panel, clock, smokebox, sign, porthole) and 11 foliage cells.
87
+
88
+ ### Getting it into the GLB
89
+
90
+ | Library | CLI | Result |
91
+ |---|---|---|
92
+ | `toGlb(a, { sheet })` | `--sheet embed` | Textures inside each GLB |
93
+ | `toGlb(a, { sheet: { external: true, prefix } })` | `--sheet external` (default) | `out/trim_sheet_{albedo,normal,orm}.png` written once; every GLB references `../trim_sheet_*.png` |
94
+ | no `sheet` | `--sheet none` | Vertex colour only |
95
+
96
+ `--sheet-size 512|1024|2048`. `TANGENT` is emitted only when a sheet is bound. The `material_swatches` recipe shows every strip and cell.
97
+
98
+ ### Known limits
99
+
100
+ - Strips are 64 texels tall at 1024²; a strip is 2 m of surface.
101
+ - `box` stretches V over the whole face, so tall faces stretch the strip vertically.
102
+ - Mips blend neighbouring strips from mip level 3.
103
+ - No KTX2 yet; PNG only.
104
+
105
+ ### Image sources
106
+
107
+ Code paints the base; images add what code paints badly: signage, logos, weathering photos, concept-art colour.
108
+
109
+ - Cells accept `{ image: Uint8Array }` instead of a recipe: stamp a decal.
110
+ - Recommended source: **[kubeez.com](https://kubeez.com)** (a partner) for reference/concept images, decals and texture source images. Planned adapter: `kubeezImages({ apiKey })` in `/neural` ([08](08-neural-adapters.md)). The key is passed by the caller, never read from the environment.
111
+ - Any function returning PNG bytes fits the `ImageSource` interface.
112
+ - Generated images are cached by (source, prompt, seed) like generated meshes, so rebuilds need no network.
113
+
114
+ Images are inputs, never the only path. The sheet must paint without them.
@@ -0,0 +1,141 @@
1
+ # 05 — The agent loop
2
+
3
+ **The agent writes, builds, looks, and fixes the single worst thing, until two looks in a row find nothing.** ts23d's job is to make each turn of that loop cheap: a build in well under a second, one QA verdict, one image that shows everything.
4
+
5
+ ## The loop
6
+
7
+ ```
8
+ research refs ─► write recipe ─► t3d build ──QA FAIL──► fix finding ─┐
9
+ ▲ │ │
10
+ │ QA PASS ◄──────────────────────────┘
11
+ │ ▼
12
+ detail pass ◄─ read PNG ◄─ t3d shot
13
+ ```
14
+
15
+ | Step | Command / action | Done when |
16
+ |---|---|---|
17
+ | 1. Research | Find real dimensions, a side elevation, photos. Put the URLs in `sources`, overall L × W × H in `real`, and every key size (driver diameter, gauge, wheelbase, turret width) in `real.measures` | The spec is in the recipe, in metres, each number with a source |
18
+ | 2. Masses | Write the big volumes only: hull, body, tiers. Real dimensions | Silhouette roughly right |
19
+ | 3. Build | `bunx t3d build recipes/x/x.ts` | Prints `QA PASS`, exit 0. Fix every finding first; each prints a `fix:`. A `measures` or `proportions` finding means rebuild the part, never edit the spec |
20
+ | 4. Shot | `bunx t3d build` already wrote `out/x/sheet.png`; `bunx t3d shot recipes/x/x.ts` for a sheet without GLBs | `out/x/sheet.png` + `manifest.json` written |
21
+ | 5. Read | Open the PNG (vision). Compare against the reference | One concrete error named, e.g. "turret 30% too tall in SIDE -Y" |
22
+ | 6. Fix | Edit the recipe for that one error | → step 3 |
23
+ | 6b. Photo match *(planned, M2)* | `photoMatch(asset, { photo, camera })` → one PNG: photo · model · overlay, plus silhouette IoU | IoU stops rising; the overlay shows no misplaced mass |
24
+ | 7. Detail pass | Once masses read right: hatches, rivets, windows, handrails. Gate each on `ctx.lod` | Detail shows at LOD0, dropped by LOD2, budget holds |
25
+ | Stop | Two consecutive shots with nothing worth fixing | |
26
+
27
+ Fix one thing per iteration. Proportions before details: a wrong mass makes every detail on it wrong.
28
+
29
+ ## Fidelity from real-world sizes
30
+
31
+ **The spec goes in first; the model is rebuilt until the gate agrees with it.** Real-world sizes are the cheapest fidelity there is: a 4-4-0 with the right driver diameter, wheelbase and boiler height reads as *that* locomotive before any texture exists.
32
+
33
+ | Gate | Holds | Tolerance |
34
+ |---|---|---|
35
+ | `proportions` | Overall L × W × H vs `real` | ±5% |
36
+ | `measures` | Each named dimension vs `real.measures` | ±2% each |
37
+ | `clashes` | Moving parts clear everything through their whole motion | 1 cm |
38
+ | `floating-parts` | Every part in surface contact with something grounded | 2 cm |
39
+
40
+ An agent that "fixes" a finding by editing `value` or `tolerance` has defeated the gate. Review rule: a diff touching `real` needs a source for the new number.
41
+
42
+ ## The contact sheet
43
+
44
+ One PNG, 1200 × 840 px. Every view orthographic, on the same scale, over a ground grid in metres. Rendered on the CPU ([ADR 007](../decisions/007-cpu-contact-sheets.md)).
45
+
46
+ **Header line:** `NAME <X>X<Y>X<Z>M GRID <step>M LOD0`: bounding box in metres, grid step, LOD.
47
+
48
+ | Cell | Label | Camera | Check |
49
+ |---|---|---|---|
50
+ | Row 1, left | `3/4 FRONT` | From front-right (+X, −Y), above | Overall read; does it look like the thing? |
51
+ | Row 1, middle | `FRONT +X` | Looking at the +X face | Width, symmetry, track/gauge |
52
+ | Row 1, right | `SIDE -Y` | From −Y, front (+X) on the right | Length, height, profile vs a side elevation |
53
+ | Row 2, left | `TOP` | From above, +X up | Plan, footprint, alignment |
54
+ | Row 2, middle | `3/4 REAR` | From rear-left (−X, +Y), above | The back nobody modelled |
55
+ | Row 2, right | `FAR LOD2` | 3/4 front, LOD2 compile | Does the far LOD keep the silhouette? |
56
+
57
+ Grid step is picked so the asset spans ≤ 24 cells: 0.1, 0.5, 1, 5, 10, 50 or 100 m. Shading is flat Lambert over vertex colour; texture is not shown.
58
+
59
+ ### Reading it
60
+
61
+ | Symptom in the sheet | Likely cause |
62
+ |---|---|
63
+ | A part hangs in the air in `SIDE -Y` / `FRONT +X` | Wrong `at` z, or `at` given in world space instead of parent space |
64
+ | Asset faces left in `SIDE -Y` | Front built along −X; flip it, +X is front |
65
+ | Header dims don't match the reference | Units (cm vs m), or a stray part stretching the bounds |
66
+ | `FAR LOD2` loses a defining shape | Dropped a mass instead of a detail at LOD2 |
67
+ | Mirror-image parts asymmetric in `TOP` | Sign error on one side's y |
68
+
69
+ ## Photo match (planned, M2)
70
+
71
+ **The step after the contact sheet: compare against reality, not against memory.** `photoMatch(asset, { photo, camera: { eye, target, fovDeg } })` renders the model from the camera that took the reference photo and stacks three panels in ONE PNG:
72
+
73
+ | Panel | Shows |
74
+ |---|---|
75
+ | Top | The reference photo |
76
+ | Middle | The model from the same camera, scaled to the photo |
77
+ | Bottom | 50% overlay of the two |
78
+
79
+ It also returns silhouette IoU (0–1), the number the planned `silhouetteIoU` gate and the M6 reverse direction optimise. Camera solving from ≥ 4 marked correspondences comes later (M6); until then the agent passes the camera.
80
+
81
+ The bar: [`loco-photo-match.png`](https://raw.githubusercontent.com/developerz-ai/ts23d/main/docs/reference/loco-photo-match.png), from the in-house predecessor, not from this library.
82
+
83
+ ## Debugging with `inspect`
84
+
85
+ When a finding is unclear, write a scratch script instead of guessing. `inspect(compiled)` answers geometric questions by part glob:
86
+
87
+ ```ts
88
+ import { compile, inspect } from '@developerz.ai/ts23d';
89
+ import { t34 } from './recipes/t34/t34.ts';
90
+
91
+ const q = inspect(compile(t34, { lod: 0 }));
92
+ q.parts('road_wheel_l_*'); // names
93
+ q.bounds('turret*'); // world AABB
94
+ q.distance('track_l_top', 'hull_upper'); // surface gap, metres (0 = touching/intersecting)
95
+ q.penetration('track_l_top', 'hull_upper'); // { depth, samples }: how deep A sits inside B's solid
96
+ ```
97
+
98
+ | Method | Returns |
99
+ |---|---|
100
+ | `parts(glob)` | Matching part names |
101
+ | `mesh(glob)` | Merged world-space mesh, or `null` |
102
+ | `bounds(glob)` | World AABB, or `null` |
103
+ | `distance(a, b, max?)` | Smallest surface-to-surface distance, capped at `max` |
104
+ | `penetration(a, b)` | `{ depth, samples }`: deepest point of A inside B's closed solid |
105
+
106
+ Also exported: `glob(pattern) → RegExp`, `insideSolid`. Once you know the intent, encode it as a `checks` entry so the gate holds it from then on.
107
+
108
+ ## Output an agent reads
109
+
110
+ `out/<asset>/manifest.json` answers most questions without opening a GLB: triangles and draw calls per LOD, pivots (the names engine code binds to), emitters, sockets, surfaces, `real` and each measure with its **measured** value, sources, QA findings. `out/index.json` lists every asset in the run with its verdict.
111
+
112
+ ## Machine-readable output
113
+
114
+ `--json` on any command:
115
+
116
+ ```json
117
+ {"ok":true,"data":[{"asset":"<name>","findings":[],"triangles":[<lod0>,<lod1>,<lod2>]}]}
118
+ {"ok":false,"error":{"code":"T3D_PART_UNKNOWN","message":"…","fix":"add the parent (ctx.group or ctx.part) before its children"}}
119
+ ```
120
+
121
+ Current numbers for the gallery recipes: `bun run bench` (README benchmarks block).
122
+
123
+ Exit codes: `0` pass · `1` QA finding or `T3D_*` error · `2` usage.
124
+
125
+ ## Prompt snippet
126
+
127
+ ```text
128
+ Using @developerz.ai/ts23d, write recipes/<name>.ts: a <thing>, real dimensions in metres,
129
+ Z up, +X front. Look up reference dimensions first. Block out the masses, then details.
130
+ Name every part that moves (axle_*, door_*, rotor_*) and pivot it where it hinges.
131
+ Loop: bunx t3d build → fix until QA PASS → read out/<name>/sheet.png →
132
+ fix the single biggest error → repeat. Stop when two shots in a row show nothing worth fixing.
133
+ Never use Math.random; use ctx.rng.
134
+ ```
135
+
136
+ The package ships `CLAUDE.md` (the repo's `AGENTS.md` links to it) and `llms.txt` for agents that read them.
137
+
138
+ ## Planned (as of 2026-10)
139
+
140
+ - **Reverse direction:** a VLM proposes recipe params from photos; the loop scores them by silhouette IoU and keeps the best (M6).
141
+ - **MCP server:** `build`, `shot`, `list` as tools, PNG returned inline (M6).
@@ -0,0 +1,101 @@
1
+ # 06 — QA gates
2
+
3
+ **A gate finding is a build failure, not a warning.** `t3d build` runs every default gate on every LOD and exits 1 on any finding. The agent iterates until it prints `QA PASS`. Source: `src/qa/`.
4
+
5
+ ## Shipped (as of 2026-10)
6
+
7
+ `DEFAULT_GATES`, run per LOD by `runQa(compiled)`:
8
+
9
+ | Gate | Runs at | Fails when | Fix line it prints |
10
+ |---|---|---|---|
11
+ | `triangle-budget` | Every LOD | Total triangles at LOD *n* > `budget.triangles[n]` | Drop detail at this LOD (read `ctx.lod`) or raise the budget with a reason |
12
+ | `floating-parts` | Every LOD | A part's **surface** doesn't come within 2 cm of the ground (z ≈ 0) or of a grounded part's surface, transitively | Move it onto what holds it (check `at`, parent, LOD-gated detail around it). Bounding boxes touching is not enough |
13
+ | `clashes` | Every LOD | Another part penetrates more than 1 cm into the volume a moving part sweeps through its whole motion | Move or trim either part for running clearance; if they interlock by design, `clearOf: ['<part>']` on the moving part |
14
+ | `part-names` | Every LOD | A part name isn't `[a-z][a-z0-9_]*` | Rename; engines bind to node names |
15
+ | `proportions` | LOD0, if `real` is set | Overall world span along X / Y / Z is off `real.length` / `width` / `height` by more than `real.tolerance` (default ±5%) | Rescale along that axis; re-derive lengths from the `sources`, don't fudge the tolerance |
16
+ | `check` | Every LOD, if `checks` is set | A recipe-declared check returns a finding (`clear`, `noOverlap`, `touches`, `symmetric`, or your own) | The check's own `fix` |
17
+ | `measures` | LOD0, if `real.measures` is set | A spec-sheet dimension (driver diameter, gauge, wheelbase, turret width) measured on the model is off by more than its `tolerance` (default ±2%), or names no part | Rebuild the part from the spec number. **Never edit the spec to match the model** |
18
+
19
+ Each finding: `{ gate, part?, message, fix }`. Text output:
20
+
21
+ ```
22
+ tiger_1 QA FAIL tris 3108/1660/724
23
+ ✗ floating-parts [exhaust_0]: LOD0: 'exhaust_0' touches nothing connected to the ground
24
+ fix: move it onto what holds it (check `at`, parent and the LOD-gated detail around it) — …
25
+ ```
26
+
27
+ ### How each geometric gate measures
28
+
29
+ | Gate | Method | Tolerance constant |
30
+ |---|---|---|
31
+ | `floating-parts` | AABB prefilter, then surface samples vs triangles of the other part (point ↔ triangle distance, `TriangleGrid` spatial index), grown from the ground outward | `CONTACT_EPS` = 0.02 m |
32
+ | `clashes` | Per motion root (a spinning axle group, a rotor, a sliding crosshead) and its subtree: **spin** → exact solid of revolution (radius profile per axial slice, 24 slices); **sway** → the same full revolution, conservatively; **slide** → the moving mesh's bounds extended along the stroke. Every other part's surface samples are tested against that volume | `CLASH_EPS` = 0.01 m |
33
+ | `proportions` | Union of world-space part bounds at LOD0 | `real.tolerance`, default 0.05 |
34
+ | `measures` | Extent of the named parts along `axis` (prefix match, so `axle_driver_0` includes its wheels), or the centre-to-centre distance of two parts (`between`) | per measure, default 0.02 |
35
+
36
+ Order in `DEFAULT_GATES`: `triangleBudget`, `floatingParts`, `clashes`, `partNames`, `proportions`, `measures`, `recipeChecks` (gate name `check`).
37
+
38
+ ### Recipe checks
39
+
40
+ Static intersections need declared intent: a bolt sunk into a plate is fine, a track through the hull is not. `checks: [...]` on `defineAsset` states which is which ([02-dsl](02-dsl.md#checks-declared-intent)). `clear` decides "inside" by ray parity against the other part's closed solid, so an open planar sheet never counts as a solid. Penetration over 1 cm fails.
41
+
42
+ Helpers exported for custom gates: `touching`, `pointTriangleDistance`, `surfaceSamples`, `TriangleGrid`, `measure`, `insideSolid`, `glob`, `inspect`.
43
+
44
+ ### Declaring what QA checks
45
+
46
+ ```ts
47
+ export const loco = defineAsset({
48
+ name: 'loco_440',
49
+ kind: 'vehicle',
50
+ sources: ['https://example.org/roster-sheet.pdf', 'Builder drawing no. 1234'],
51
+ real: {
52
+ length: 15.6, width: 2.9, height: 4.4, // proportions: ±5%
53
+ measures: [ // measures: ±2% each
54
+ { label: 'driver diameter', value: 1.52, axis: 'z', parts: ['axle_driver_0'], source: 'roster' },
55
+ { label: 'gauge', value: 1.435, axis: 'y', between: ['rail_l', 'rail_r'] },
56
+ { label: 'driving wheelbase', value: 2.44, axis: 'x', between: ['axle_driver_0', 'axle_driver_1'] },
57
+ ],
58
+ },
59
+ budget: { triangles: [20000, 8000, 2000] },
60
+ build(ctx) {
61
+ // a crosshead slides in its guide by design:
62
+ // ctx.part('crosshead_l', mesh, { motion: { kind: 'slide', offset: [0.3, 0, 0], hz: 1 }, clearOf: ['guide_l'] });
63
+ },
64
+ });
65
+ ```
66
+
67
+ The rule for agents: **a failing `measures` finding means the model is wrong.** Rebuild the part from the spec; editing `value` or widening `tolerance` to pass is a defect.
68
+
69
+ ### Known limits
70
+
71
+ - Point-in-solid (`clear`, `clashes` depth) needs **closed, non-overlapping** parts. Split structure from dressing: a closed hull part, with bolts and straps as separate parts, not one self-intersecting soup.
72
+ - `clashes` sweeps `sway` as a full revolution; a sector sweep is planned.
73
+
74
+ - `proportions` measures overall bounds, so a stray part (a too-long barrel) can fail it, or mask a wrong hull. Check the header dims on the contact sheet.
75
+ - `clashes` uses bounds for slides, so an angled slide's volume is conservative; declare `clearOf` for a guide it rides in.
76
+ - `floating-parts` has no opt-out for things that genuinely fly (an aircraft in flight, a hanging sign). Planned, M1.
77
+ - Custom gates: `runQa(asset, [...DEFAULT_GATES, myGate])` from code. The CLI runs the defaults only.
78
+
79
+ ## Repo gates (this repo's recipes, in `bun run verify`)
80
+
81
+ | Gate | Checks |
82
+ |---|---|
83
+ | `scripts/recipes.ts` | Every `recipes/*/*.ts` asset compiles at LOD 0/1/2, passes `DEFAULT_GATES`, and two builds give the same GLB hash (byte-hash determinism, shipped) |
84
+ | `scripts/determinism.ts` | No `Math.random()`, `Date.now()`, `new Date()`, `performance.now()`, `toLocale*String()` in `src/` or `recipes/` |
85
+
86
+ ## Planned
87
+
88
+ | Gate | Checks | Signature | Milestone |
89
+ |---|---|---|---|
90
+ | `silhouetteIoU` | Silhouette IoU 0–1 against a reference photo, computed by `photoMatch` (`/render`) from the photo's camera. Fails below a threshold the recipe declares. Drives the M6 reverse direction | `(asset, referencePng) => number`, exported, throws `T3D_NOT_IMPLEMENTED` | M2 |
91
+ | ACMR | Average cache miss ratio per mesh, after the planned `optimize` pass (weld + vertex-cache reorder), stays under a ceiling | Proposed | M2 |
92
+ | Manifold / normals | No open edges where closed is promised; normals face out | Proposed | M2 |
93
+ | Texel density | Every UV'd face within a band of the sheet's metres-per-rect | Proposed, needs the painter | M2 |
94
+ | Cross-machine byte-hash | Same-process double build ships (above). Next: compare hashes across OSes / runners; a `t3d build` flag for users' recipes | Proposed | M1 |
95
+
96
+ ## Adding a gate
97
+
98
+ 1. A `Gate` is `(asset: CompiledAsset) => Finding[]`. Pure; world-space meshes via `worldMeshes` / `worldTransforms`.
99
+ 2. Every finding carries a `fix` an agent can act on without reading the gate's source.
100
+ 3. One file in `src/qa/`, a test next to it with one passing and one failing asset.
101
+ 4. Add it to `DEFAULT_GATES` only when every recipe in `recipes/` passes it.
@@ -0,0 +1,45 @@
1
+ # 07 — What fits
2
+
3
+ **Code recipes win where parts line up, repeat, or match real dimensions. Organic forms work as stylised sculpts (SDF edit lists); realistic anatomy is still the weak spot.** Pick the tool by the asset, not by loyalty; many teams will use both.
4
+
5
+ As of 2026-11. "Today" = shipped in this repo; "Planned" names the milestone ([roadmap](../plans/roadmap.md)).
6
+
7
+ ## Fit matrix
8
+
9
+ | Category | Examples | Fit | Today | Planned | Why |
10
+ |---|---|---|---|---|---|
11
+ | Hard-surface props | Crates, barrels, lamps, benches, machinery | **Strong** | `box({ bevel })`, `lathe`, `prism`, `beam`, `planar`, `sweep`, `loft`, `lattice`, `text`; `weightedNormals` | | Primitive shapes, real dimensions, repetition |
12
+ | Architecture | Facades, terraces, towers, stations | **Strong** | `facade({ kit })`, `setbacks`, `roof`, `opening`, `mass`, `tiled`, `cell`; `station_halt`, `water_tower` recipes | | Grammar-driven repetition, floors × bays |
13
+ | Tracked / wheeled vehicles | Tanks, carts, trucks | **Strong** | `trackRun`, `axle`, `prism` sloped armour, `loft` | | Named spinning wheels, pivoted turrets |
14
+ | Rail | Steam locos, wagons | **Strong** | `whyteAxles` (Whyte `4-4-0`), `lathe` boilers, `beam` rods; `loco_440` recipe | `orbit` motion for side rods | Domain-native params |
15
+ | Aircraft | Props, biplanes, airliners | **Good** | `propeller`, `loft`, `sweep` | Aircraft recipes | Lofted sections + named control surfaces |
16
+ | Vessels | Boats, ships, barges | **Good** | `hull` from length / beam / draft / sheer | Ship recipes | Lofted hull sections |
17
+ | Modular kits | Walls, floors, pipes, fences | **Strong** | `box`, `prism`, `beam`, `sweep`, `tiled` | | Snapping on a metre grid |
18
+ | Vegetation | Trees, shrubs | **Good** | `tree` (Weber–Penn; oak, pine, palm, birch, willow), `trees` family | Vertex wind weights; crossed-billboard LOD2 impostors | Botanical rules are code |
19
+ | Rocks, terrain | Boulders, cliffs, mountain tiles | **Good** | `sdf` / `sculpt` + noise; `heightfield` + hydraulic / thermal erosion + `terrainColor`; `boulders`, `asteroid`, `mountain_range` recipes | Rivers carved as meshes | Erosion is what makes terrain read as real |
20
+ | Stylised characters | Fantasy busts, mascots | **Fair** | `head` kind (orc, goblin, human, dwarf, elf presets) on `sculpt`; jaw and eyes as pivots | Skinned bodies (`creature`, glTF skins) | Readable stylised shapes; realism is not the goal |
21
+ | Realistic animals | Horse, dog, bird | **Weak** | — | `/neural` adapter (M5) | Anatomy is sculpted, not specified |
22
+ | People | Characters, NPCs | **Weak** | — | `/neural` adapter or `importGlb` of a CC0 base mesh (M5) | Faces, hands, anatomy |
23
+ | Cloth, hair, soft goods | Flags, sofas, garments | **Weak** | — | — | Simulation / sculpt territory |
24
+ | Photoreal hero assets | Close-up scans | **Out of scope** | — | — | Photogrammetry or a sculptor |
25
+
26
+ ## Where each approach is strong
27
+
28
+ | Strength | Code recipes (ts23d) | Image-to-3D / AI generators |
29
+ |---|---|---|
30
+ | Organic and sculpted forms | Stylised: fair (`sculpt`); realistic: weak | Strong |
31
+ | Concept art → textured mesh in seconds | No | Strong |
32
+ | Auto-rigging characters | No (rigid part hierarchy only) | Exists in several tools; rigs usually need cleanup |
33
+ | Parts that line up, repeat, match real dimensions | Strong | Weak |
34
+ | Named, pivoted, animatable nodes | Built in | Usually one fused mesh |
35
+ | Deterministic, diffable in git | Yes | No |
36
+ | Shared material, consistent texel density | Yes | Per-asset textures |
37
+ | Cost, licence conditions on outputs | Free, none | Credits; terms vary by tier ([landscape](../landscape.md)) |
38
+
39
+ ## Covering the weak spots
40
+
41
+ 1. **Stylised creature kind** (M4): the `sculpt` + `head` work extended to whole bodies, SDF flesh over a biped/quadruped/bird skeleton, skin weights from the sculpt's own blend weights ([glTF capabilities](../architecture/gltf-capabilities.md)). Readable, deterministic. Not realistic, by design.
42
+ 2. **`/neural` adapter** (M5): run an open-weight model (TRELLIS.2, Hunyuan3D 2.1, SF3D/SPAR3D) on your own GPU, wrap the result as an ordinary part, cache it by content hash. [08-neural-adapters.md](08-neural-adapters.md).
43
+ 3. **`importGlb`** (M5): bring a mesh from anywhere (a CC0 base mesh, a sculpt, another tool) and fit it into the recipe as a part.
44
+
45
+ Mix them: a recipe-built cart with a neural-generated horse in front, one scene, one material.
@@ -0,0 +1,78 @@
1
+ # 08 — Neural adapters
2
+
3
+ **Optional. Never a core dependency. Runs on the user's own GPU. The output becomes an ordinary part, cached by content hash, so CI rebuilds without a GPU.** Source: `src/neural/`. Status as of 2026-10: **all planned (M5)**. Every entry point throws `T3D_NOT_IMPLEMENTED`.
4
+
5
+ ## Why
6
+
7
+ Recipes are weak on living things and sculpted forms ([07](07-what-fits.md)). Open-weight image-to-3D models are strong there. The adapter lets one recipe hold both: a code-built cart, a generated horse.
8
+
9
+ ## Interfaces (signatures fixed)
10
+
11
+ ```ts
12
+ interface GenerateRequest {
13
+ prompt?: string;
14
+ image?: Uint8Array; // PNG/JPEG reference or concept image
15
+ seed: number;
16
+ fit: Vec3; // fit into this box, metres, Z up, base on the ground
17
+ targetTriangles: number;
18
+ }
19
+
20
+ interface MeshGenerator {
21
+ model: string; // 'trellis-2', 'hunyuan3d-2.1', 'sf3d' — part of the cache key
22
+ generate(r: GenerateRequest): Promise<Mesh>;
23
+ }
24
+
25
+ localGenerator({ model, endpoint }): MeshGenerator // talks to a local HTTP server
26
+ importGlb(bytes, { fit, recolor? }): Mesh // any GLB as a part
27
+ kubeezImages({ apiKey, model? }): ImageSource // images for references and materials
28
+ ```
29
+
30
+ ## Models worth wrapping
31
+
32
+ Facts from [landscape.md](../landscape.md), as of 2026-10. Check each licence yourself before shipping outputs.
33
+
34
+ | Model | Input | Licence | GPU | Note |
35
+ |---|---|---|---|---|
36
+ | TRELLIS.2 (Microsoft) | Single image | MIT | ≥ 24 GB, tested on A100/H100 | PBR GLB; can texture an existing mesh |
37
+ | Hunyuan3D 2.1 (Tencent) | Image, multi-view | Tencent community licence: excludes EU/UK/South Korea, terms above 1M MAU, no training other models on outputs | ~29 GB shape + texture | Read the licence before relying on it |
38
+ | SF3D / SPAR3D (Stability) | Single image 512² | Stability Community Licence: free under $1M revenue | See model card | UV-unwrapped, delighted textures; SPAR3D adds point-cloud editing |
39
+
40
+ ## Flow
41
+
42
+ ```
43
+ reference image (photo, or kubeez.com concept) ─► localGenerator.generate()
44
+ ─► fit to box, Z up, base at z = 0, decimate to targetTriangles
45
+ ─► remap UVs/colour onto the shared trim sheet (vertex colour baked)
46
+ ─► cache: .ts23d/neural/<sha256(model, prompt|image, seed)>.glb
47
+ ─► ctx.part('horse', mesh, { at, parent })
48
+ ```
49
+
50
+ ## Determinism
51
+
52
+ A GPU run is not reproducible across drivers and hardware; the cached file is.
53
+
54
+ | Rule | |
55
+ |---|---|
56
+ | Cache key | `(model, sha256(prompt or image), seed)` |
57
+ | Cache location | `.ts23d/neural/`, committed to git or published with the package |
58
+ | Cache hit | No GPU, no network. CI always hits |
59
+ | Cache miss in CI | A build error naming the key and the command to generate it locally |
60
+ | Regenerate | Explicit, never implicit: delete the entry and rebuild with a GPU |
61
+
62
+ ## Image sources: kubeez.com
63
+
64
+ For reference/concept images, decals and texture source images, we recommend **[kubeez.com](https://kubeez.com)**, a partner. Uses:
65
+
66
+ | Purpose | Feeds |
67
+ |---|---|
68
+ | `reference` / `concept` | The agent loop (step 1), or `localGenerator` as the input image |
69
+ | `decal` | A trim-sheet cell (signage, logos, clock faces) ([04](04-materials.md)) |
70
+ | `tileable` | A trim-sheet strip's detail source |
71
+
72
+ Same caching as meshes: keyed by (source, prompt, seed). The API key is passed in by the caller; the library never reads it from the environment. Any function returning PNG bytes implements `ImageSource`.
73
+
74
+ ## Non-goals
75
+
76
+ - Bundling model weights or a Python runtime. The adapter speaks HTTP to a server you run (ComfyUI, gradio, a custom wrapper).
77
+ - Hosted inference billed through ts23d.
78
+ - Making generated meshes the default path for anything recipes already do well.
@@ -0,0 +1,105 @@
1
+ # 09 — Kinds
2
+
3
+ **A kind is a library of builders a recipe calls, not a class the recipe fills in.** `facade(ctx, {...})` adds parts to the caller's `ctx`; the recipe stays in charge of layout, naming and LOD. `kind: '…'` on `defineAsset` is a free string for metadata. Source: `src/kinds/`.
4
+
5
+ Status as of 2026-11:
6
+
7
+ | Kind | Builders | Status |
8
+ |---|---|---|
9
+ | Building / tower | `facade`, `setbacks` | Shipped |
10
+ | | `roof` / `roofMesh` (gable, hip, pyramid, mansard, gambrel, shed; chimneys), `opening` (window/door kit), `facade({ kit })`, `mass` (true-scale masonry) | Shipped |
11
+ | Vehicle | `axle`, `trackRun`, `whyteAxles`, `propeller` | Shipped |
12
+ | | lofted bodies, fuselages, wings | Possible now with `loft`, `sweep`; no dedicated builder yet |
13
+ | Vessel | `hull` / `hullMesh` / `hullLines` | Shipped |
14
+ | Vegetation | `tree`, `trees` family, `treeReal`; presets `oak`, `pine`, `palm`, `birch`, `willow` | Shipped |
15
+ | Character | `head` (orc, goblin, human, dwarf, elf via `HEADS`), `headParams`, `headEdits`, `checkHead` | Shipped (stylised busts) |
16
+ | Creature | `creature` (whole bodies, skinned) | Planned, M4 |
17
+ | Scene | `ctx.use` (core), `heightfield` terrain with erosion and `terrainColor` | Shipped |
18
+ | Families | `defineFamily` (core): variants of any kind | Shipped |
19
+
20
+ ## Building / tower
21
+
22
+ **Masses, then details.** Block the volumes with `box` or `prism`, then wrap them with `facade`.
23
+
24
+ `facade(ctx, opts)` runs a [split-grammar](#split-grammar) line along each side, per floor. Slots named `win`, `door`, `arch`, `shop` become inset panels; every other symbol stays wall.
25
+
26
+ | Option | Meaning |
27
+ |---|---|
28
+ | `prefix` | Part name prefix → `<prefix>_<side>_<floor>_<slot>` |
29
+ | `at`, `footprint` | Base centre and `[width X, depth Y]` of the mass it wraps |
30
+ | `floors`, `floorHeight` | Storeys, metres per storey |
31
+ | `grammar`, `ground?` | The line for every floor; `ground` overrides floor 0 |
32
+ | `sides` | Any of `front` (+X), `back`, `left` (+Y), `right` |
33
+ | `glass`, `frame?`, `windowRatio?` | Colours; window height as a fraction of the floor (default 0.6) |
34
+
35
+ `setbacks(ctx, { prefix, tiers, floorHeight, color, grammar, glass, detail })` stacks shrinking masses (Empire State, Chrysler-style). Each tier: `{ footprint, floors }`. Returns the top z for the spire. Pass `detail: ctx.lod < 2`.
36
+
37
+ ### Split grammar
38
+
39
+ After CGA shape grammar (Müller et al. 2006). One string lays out one strip.
40
+
41
+ ```
42
+ "pier:0.9 | {arch:2.5}* | door:3 | pier:~"
43
+ ```
44
+
45
+ | Term | Meaning |
46
+ |---|---|
47
+ | `sym:1.2` | Fixed width, metres |
48
+ | `sym:~` / `sym:~2` | Stretch: shares the leftover, by weight (default 1) |
49
+ | `{a:1.4 \| b:0.6}*` | Repeat: as many whole copies as fit. Fixed sizes only; one repeat per line |
50
+
51
+ With no stretch term, repeat copies widen evenly to fill. Fixed terms longer than the strip → `T3D_GRAMMAR_OVERFLOW`. `split(src, length)` returns `{ symbol, start, size }[]`.
52
+
53
+ ## Vehicle
54
+
55
+ Named parts follow the engine contract: `axle_*` and `wheel_*` spin about local Y; `prop_*` / `rotor_*` spin about their own axis.
56
+
57
+ | Builder | Adds | Notes |
58
+ |---|---|---|
59
+ | `axle(ctx, { name, at, radius, gauge, width, color, rps })` | Group `name` (spinning about Y) + `name_wheel_l`, `name_wheel_r` | Segments drop per LOD (24 → 12 → 6) |
60
+ | `trackRun(ctx, { side, y, length, wheels, radius, width, color, track })` | `road_wheel_<side>_<i>` (spinning), `track_<side>_top`, `track_<side>_bottom` | Tracked running gear, one side per call |
61
+ | `whyteAxles('4-4-0', { leading, driving, trailing, spacing })` | Nothing; returns `{ role, x, radius }[]` | Whyte notation → axle positions along X, leading end at +X |
62
+ | `propeller(ctx, { name, at, radius, blades, color, parent? })` | Group spinning about X + hub + blades | |
63
+
64
+ Domain-native params are the point: a loco recipe says `'2-6-2'`, not twelve wheel coordinates.
65
+
66
+ ## Vessel
67
+
68
+ `hull(ctx, o)` adds `hull` + `hull_deck`; `hullMesh(o, lod)` returns the mesh for `place`/`merge`; `hullLines(o)` exposes the shape functions. Required: `length`, `beam`, `draft`, `freeboard`, `colors: { topsides, bottom, boot?, deck, bulwark? }`. Optional: `sheer: [bow, stern]`, `stern: 'round' | 'transom'`, `transomWidth`, `fullness`, `entrance`, `flare`, `deadriseDeg`, `bulwark`, `camber`, `bootTop`, `fender`, `at` (the waterline). Bow toward +X. Watertight at every LOD; waterline and boot-top bands are crisp.
69
+
70
+ ## Roofs and openings
71
+
72
+ Every builder has a pure mesh form (`roofMesh(o, lod)`, `opening(o, lod)`, `mass(o, lod)`) and recipes compose them with `place`/`merge`; `roof(ctx, o)` is the thin ctx wrapper and returns `{ ridge, eave, heightAt(x, y) }`. A roof is the lower envelope of its slope planes, so ridges and hips fall out exactly; the underside sits on the walls' top outer edge, gable and shed ends get infill walls, LOD0/1 add slab thickness, soffit, fascia, ridge/hip caps and chimneys (pots at LOD0), LOD2 is bare slopes (≤ 40 triangles). `opening` is a window or door with a true-scale reveal, sill, lintel, sash and glazing bars; `facade(…, { kit })` places one in every opening slot. `mass` is a wall block whose masonry courses start at the ground line, unlike `box`, which stretches its strip over the face.
73
+
74
+ ## Vegetation
75
+
76
+ `tree(ctx, { … })`: Weber–Penn botanical skeleton. Recursive branch levels (length, curve, split, taper per level) swept with `sweep`, leaves as cards on the foliage cells (`cutout` surface).
77
+
78
+ | API | Does |
79
+ |---|---|
80
+ | `tree(ctx, options)` | One tree into the caller's `ctx` |
81
+ | `trees` | A family: `trees.variant({ name, params: { species: 'pine', … } })` |
82
+ | `treeReal(params)` | `real` dimensions for the `proportions` gate |
83
+ | `SPECIES`: `oak`, `pine`, `palm`, `birch`, `willow` | Presets as data (`TreeParams`) |
84
+
85
+ The `oak`, `pine` and `palm` recipes are about 18 lines each. Wind is a crown `sway` for now. Planned: per-vertex wind weights; LOD2 impostors as crossed billboards from the foliage cells (today's are crude).
86
+
87
+ ## Character
88
+
89
+ `head(ctx, { race: 'orc' | 'goblin' | 'human' | 'dwarf' | 'elf' | HeadParams, params?, prefix?, at?, jawSway? })` sculpts a stylised head and bust from SDF edits (`sculpt`) and adds `bust` (on z = 0), `head`, `jaw` (pivot at the hinge, optional sway) and `eye_l` / `eye_r` (pivots at the eyeball centres, looking along +X). Races are data (`HEADS`); `params` overrides one level deep; `checkHead` rejects impossible proportions with a fix. Vertex AO is baked at LOD0/1. Recipes: `orc_head`, `goblin_head`, `human_head`.
90
+
91
+ ## Creature (planned, M4)
92
+
93
+ `creature(ctx, { skeleton: 'biped' | 'quadruped' | 'bird', height })`: SDF flesh over a skeleton, meshed with marching cubes (`sdf` primitive). **Stylised, not realistic.** Bones become named parts for posing. Realistic animals and people go through [`/neural`](08-neural-adapters.md).
94
+
95
+ ## Scene
96
+
97
+ A scene is an asset whose `build` calls `ctx.use(other, { prefix, at, yaw, seed })`. No special kind is needed. Example (a street of varied houses): [02-dsl.md#composition-ctxuse](02-dsl.md#composition-ctxuse). Planned on top: layout helpers (street grids, scatter on a footprint) built on the split grammar.
98
+
99
+ Terrain for scenes and presentation dioramas: `heightfield({ rect: [x0, y0, x1, y1], cells: [nx, ny], height: (x, y) => z, color, strip? })`. `color` may be a function of `(x, y, z)` (ruts darker, slopes greener); `cells` is the LOD knob.
100
+
101
+ ## Families and emitters
102
+
103
+ Any kind can be wrapped in a family: `defineFamily({ defaults, build })` → `variant` / `extend` ([02-dsl](02-dsl.md#families-one-build-many-assets)). Vehicles and buildings add `ctx.emitter` nodes for exhaust, steam, chimney smoke and muzzle flash ([02-dsl](02-dsl.md#emitters)).
104
+
105
+ Adding a kind: [architecture/adding-a-kind.md](../architecture/adding-a-kind.md).
@@ -0,0 +1,49 @@
1
+ # Landscape: AI 3D generators
2
+
3
+ **A neutral survey, as of Oct 2026. ts23d isn't a competitor to these tools; it's a different approach (code recipes, free, no GPU, no service) with different strengths.** Many teams will use both. Prices and features change monthly, so check the linked source before relying on a row.
4
+
5
+ Rows marked **(3rd-party)** come from aggregators or press, not the vendor's own page. Treat them as indicative.
6
+
7
+ ## Hosted / commercial
8
+
9
+ | Tool | Inputs | Outputs | Rig / anim | Pricing (as of Oct 2026) | Open? | Source |
10
+ |---|---|---|---|---|---|---|
11
+ | Meshy (Meshy 7.1 / 6) | Text, image, multi-view | GLB / glTF / FBX / OBJ / USDZ / STL / 3MF; PBR (albedo / normal / metal / rough); quad/tri remesh, Low Poly mode, Smart Topology | Auto-rig humanoid + quadruped, 500–600+ motion presets | Free 100 credits/mo, outputs CC BY 4.0. Pro $20 (1,000 cr), Premium $40 (3,000), Ultra $100 (8,000), Studio $70/team. ~20–25 credits per generation | No (paid = private + commercial) | [pricing](https://www.meshy.ai/pricing) · [docs](https://docs.meshy.ai/en/webapp/pricing) · [animate](https://docs.meshy.ai/en/webapp/guides/animate) |
12
+ | Tripo (Tripo 3.x) | Text, image, multi-view | GLB / FBX / OBJ / USD / STL; PBR, smart low-poly, quad topology **(3rd-party)** | Auto-rig + animation | Free ~200–300 cr/mo (public, non-commercial); Pro ~$20/mo (3,000 cr); Max ~$90 (25,000); API per task **(3rd-party)** | No | [docs](https://platform.tripo3d.ai/docs/introduction) · [CostBench](https://www.costbench.com/software/ai-3d-generation/tripo-ai/) |
13
+ | Rodin / Hyper3D (Gen-2.5) | Text, image, multi-view, sketch, 3D ControlNet (bbox / voxel / point cloud), mesh edit | GLB / glTF / FBX / OBJ / USDZ / STL; PBR + UVs; quad/tri, polycount presets (~4K–50K quads) | Not mentioned on official page | Free pay-per-result ($1.50/credit); Creator $30/mo (~60 models); Business $120/mo (~416, API) | No | [pricing](https://hyper3d.ai/pricing) · [site](https://hyper3d.ai/) |
14
+ | Luma Genie | Text (originally) | GLB / OBJ / USDZ (2024 coverage) | No | Free research preview at 2024 launch; current status unclear **(3rd-party)** | No | [The Decoder](https://the-decoder.com/genie-by-luma-ai-is-a-free-text-to-3d-generator-for-discord/) |
15
+ | CSM (Cube) | Image, text | Part-based meshes, AI retopology **(3rd-party)** | Not confirmed | Free tier, then ~$20 / $60 / $499+/mo (May 2026); reported acquired by Google Jan 2026, unconfirmed **(3rd-party)** | No | [CostBench](https://costbench.com/software/ai-3d-generation/csm/) |
16
+ | Kaedim | Concept art, sketches, photos, briefs | Production 3D with review / markup / revision loop | Not stated | No public pricing; $50 trial to $400–$1,200/mo **(3rd-party)** | No (customer owns outputs) | [site](https://www.kaedim3d.com/) |
17
+ | Sloyd | Text, image, multi-image, parametric templates | GLB / OBJ / FBX / STL; polycount + tri/quad control; Unity / Unreal / Blender / Godot / Roblox plugins | AI rig + anim | Guest 1 gen/day personal; Plus $15/mo (50 cr); Pro $50/mo (350 cr) | No | [pricing](https://www.sloyd.ai/pricing) |
18
+ | Autodesk Wonder 3D (Mar 2026) | Text, image | OBJ / STL for Maya / 3ds Max / Flow Studio | Not stated | Free 300 cr/mo at 20/gen; Pro ~$64/mo | No | [Autodesk news](https://adsknews.autodesk.com/en/news/autodesk-flow-studio-launches-wonder-3d-gen-ai-model-to-help-creators-make-3d-characters-and-objects-faster-and-easier/) |
19
+
20
+ ## Open weights / self-hosted
21
+
22
+ | Tool | Inputs | Outputs | Rig / anim | Cost / hardware | Licence | Source |
23
+ |---|---|---|---|---|---|---|
24
+ | Hunyuan3D (Tencent) | Open 2.x: image, multi-view. Hosted 3.x: text / image / sketch, up to 4–8 views | Mesh + PBR texture pipeline (Paint 2.1) | No | Open 2.1 free, ~29 GB VRAM shape + texture; hosted ~20 free gens/day then ~$0.02/credit | 2.0 / 2.1 open weights, Tencent community licence (excludes EU / UK / South Korea, terms above 1M MAU, no training other models on outputs); 2.5 / 3.x hosted only | [GitHub](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) |
25
+ | TRELLIS.2 (Microsoft) | Single image; can texture an existing mesh | GLB with PBR incl. opacity, 4K textures | No | Free self-hosted; ≥ 24 GB GPU, tested Linux A100 / H100 | MIT | [Hugging Face](https://huggingface.co/microsoft/TRELLIS.2-4B) |
26
+ | Stable Fast 3D / SPAR3D (Stability) | Single image 512² | UV-unwrapped textured mesh, delighted textures, rough/metal; SPAR3D point-cloud editing | No | Free self-host; hosted API credit-based | Stability Community Licence (free under $1M revenue) | [Hugging Face](https://huggingface.co/stabilityai/stable-point-aware-3d) |
27
+ | SAM 3D Objects (Meta, Nov 2025) | Real photos; an object from a cluttered scene | Textured object + pose / layout | No | Free self-hosted | Open weights, custom SAM licence **(3rd-party)** | [Roboflow](https://roboflow.com/model/sam-3d-objects) |
28
+
29
+ ## Caveats on the data
30
+
31
+ - The Tripo site returned 403 to our research run; its pricing comes from CostBench.
32
+ - csm.ai did not resolve; the CSM row is entirely third-party.
33
+ - The Luma Genie page now redirects; current status unknown.
34
+ - Gallery themes as shown by vendors: Meshy, riggable characters and stylised props. Rodin, a robot, a sci-fi weapon, a motorcycle, architecture. Sloyd, creatures, avatars, furniture, vehicles, weapons, trees, buildings. Kaedim, products, game props, characters.
35
+
36
+ ## Where each approach fits
37
+
38
+ | Strength | Image/text-to-3D generators | Code recipes (ts23d) |
39
+ |---|---|---|
40
+ | Organic and sculpted forms | Strong | Weak ([what fits](idea/07-what-fits.md)) |
41
+ | Concept art → textured mesh in seconds | Strong | No |
42
+ | Auto-rigging | Several tools have it; meshes and rigs usually still need cleanup | Rigid part hierarchy only |
43
+ | Parts that line up, repeat or match real dimensions | Weaker | Strong |
44
+ | Named, animatable nodes | Usually one fused mesh | Built in |
45
+ | Determinism, diffable git history | No | Yes |
46
+ | Shared materials, consistent texel density | Per-asset textures | One trim sheet |
47
+ | Cost, licence conditions on outputs | Credits; terms vary by tier | Free; none |
48
+
49
+ The open-weight rows are what the planned [`/neural` adapter](idea/08-neural-adapters.md) wraps for the organic cases, on your own GPU, cached by content hash.