@developerz.ai/ts23d 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (567) hide show
  1. package/.claude/agents/bug-hunter.md +21 -0
  2. package/.claude/agents/docs-author.md +13 -0
  3. package/.claude/agents/dx-auditor.md +14 -0
  4. package/.claude/agents/primitive-author.md +22 -0
  5. package/.claude/agents/recipe-author.md +36 -0
  6. package/.claude/agents/visual-reviewer.md +23 -0
  7. package/.claude/commands/feature.md +28 -0
  8. package/.claude/commands/new-recipe.md +19 -0
  9. package/.claude/commands/release.md +14 -0
  10. package/.claude/commands/verify.md +9 -0
  11. package/.claude/skills/author-recipe/SKILL.md +93 -0
  12. package/CHANGELOG.md +90 -0
  13. package/CLAUDE.md +85 -0
  14. package/LICENSE +21 -0
  15. package/README.md +385 -2
  16. package/dist/chunk-6b05mnmx.js +19 -0
  17. package/dist/chunk-6b05mnmx.js.map +11 -0
  18. package/dist/chunk-7rtfjh9f.js +45 -0
  19. package/dist/chunk-7rtfjh9f.js.map +10 -0
  20. package/dist/chunk-8j4c28jt.js +4085 -0
  21. package/dist/chunk-8j4c28jt.js.map +46 -0
  22. package/dist/chunk-d1fv6hrm.js +2532 -0
  23. package/dist/chunk-d1fv6hrm.js.map +43 -0
  24. package/dist/chunk-dbq4qf52.js +902 -0
  25. package/dist/chunk-dbq4qf52.js.map +25 -0
  26. package/dist/chunk-drna2rny.js +836 -0
  27. package/dist/chunk-drna2rny.js.map +17 -0
  28. package/dist/chunk-dxvypr7y.js +89 -0
  29. package/dist/chunk-dxvypr7y.js.map +11 -0
  30. package/dist/chunk-f6yme003.js +1589 -0
  31. package/dist/chunk-f6yme003.js.map +30 -0
  32. package/dist/chunk-hg7pf34j.js +465 -0
  33. package/dist/chunk-hg7pf34j.js.map +19 -0
  34. package/dist/chunk-jq4e17tj.js +1031 -0
  35. package/dist/chunk-jq4e17tj.js.map +21 -0
  36. package/dist/chunk-y63c6zcv.js +2668 -0
  37. package/dist/chunk-y63c6zcv.js.map +53 -0
  38. package/dist/cli/args.d.ts +30 -0
  39. package/dist/cli/bin.d.ts +2 -0
  40. package/dist/cli/bin.js +348 -0
  41. package/dist/cli/bin.js.map +15 -0
  42. package/dist/cli/load.d.ts +2 -0
  43. package/dist/cli/manifest.d.ts +53 -0
  44. package/dist/cli/render-cmds.d.ts +12 -0
  45. package/dist/cli/run.d.ts +1 -0
  46. package/dist/cli/texture-cache.d.ts +4 -0
  47. package/dist/core/asset.d.ts +10 -0
  48. package/dist/core/definition.d.ts +155 -0
  49. package/dist/core/errors.d.ts +17 -0
  50. package/dist/core/family.d.ts +23 -0
  51. package/dist/core/index.d.ts +9 -0
  52. package/dist/core/index.js +69 -0
  53. package/dist/core/index.js.map +9 -0
  54. package/dist/core/mesh.d.ts +35 -0
  55. package/dist/core/part.d.ts +114 -0
  56. package/dist/core/png.d.ts +1 -0
  57. package/dist/core/rng.d.ts +12 -0
  58. package/dist/core/transform.d.ts +20 -0
  59. package/dist/core/vec.d.ts +11 -0
  60. package/dist/core/world.d.ts +16 -0
  61. package/dist/export/bin-writer.d.ts +16 -0
  62. package/dist/export/container.d.ts +3 -0
  63. package/dist/export/credits.d.ts +4 -0
  64. package/dist/export/glb-options.d.ts +48 -0
  65. package/dist/export/glb.d.ts +8 -0
  66. package/dist/export/hierarchy.d.ts +23 -0
  67. package/dist/export/index.d.ts +5 -0
  68. package/dist/export/index.js +40 -0
  69. package/dist/export/index.js.map +9 -0
  70. package/dist/export/lights.d.ts +8 -0
  71. package/dist/export/lod-coverage.d.ts +12 -0
  72. package/dist/export/materials.d.ts +23 -0
  73. package/dist/export/merge-pivots.d.ts +6 -0
  74. package/dist/export/metadata.d.ts +21 -0
  75. package/dist/export/motion.d.ts +9 -0
  76. package/dist/export/optimize-parts.d.ts +26 -0
  77. package/dist/export/primitive.d.ts +3 -0
  78. package/dist/export/tangents.d.ts +2 -0
  79. package/dist/export/textures.d.ts +22 -0
  80. package/dist/export/version.d.ts +2 -0
  81. package/dist/grammar/index.d.ts +2 -0
  82. package/dist/grammar/index.js +16 -0
  83. package/dist/grammar/index.js.map +9 -0
  84. package/dist/grammar/split.d.ts +21 -0
  85. package/dist/grammar/whyte.d.ts +5 -0
  86. package/dist/index.d.ts +11 -0
  87. package/dist/index.js +457 -0
  88. package/dist/index.js.map +9 -0
  89. package/dist/kinds/building.d.ts +44 -0
  90. package/dist/kinds/faces.d.ts +27 -0
  91. package/dist/kinds/head-armour.d.ts +5 -0
  92. package/dist/kinds/head-beard.d.ts +6 -0
  93. package/dist/kinds/head-bust.d.ts +5 -0
  94. package/dist/kinds/head-extras.d.ts +15 -0
  95. package/dist/kinds/head-face.d.ts +8 -0
  96. package/dist/kinds/head-garb.d.ts +4 -0
  97. package/dist/kinds/head-geom.d.ts +7 -0
  98. package/dist/kinds/head-hair.d.ts +4 -0
  99. package/dist/kinds/head-layout.d.ts +29 -0
  100. package/dist/kinds/head-mesh.d.ts +31 -0
  101. package/dist/kinds/head-mouth.d.ts +4 -0
  102. package/dist/kinds/head-params.d.ts +116 -0
  103. package/dist/kinds/head-presets.d.ts +19 -0
  104. package/dist/kinds/head-rim.d.ts +10 -0
  105. package/dist/kinds/head-tone.d.ts +10 -0
  106. package/dist/kinds/head-torso.d.ts +35 -0
  107. package/dist/kinds/head.d.ts +39 -0
  108. package/dist/kinds/hull-lines.d.ts +65 -0
  109. package/dist/kinds/hull-skin.d.ts +6 -0
  110. package/dist/kinds/hull.d.ts +14 -0
  111. package/dist/kinds/index.d.ts +16 -0
  112. package/dist/kinds/index.js +78 -0
  113. package/dist/kinds/index.js.map +9 -0
  114. package/dist/kinds/mass.d.ts +15 -0
  115. package/dist/kinds/openings.d.ts +30 -0
  116. package/dist/kinds/planned.d.ts +6 -0
  117. package/dist/kinds/roof-chimney.d.ts +16 -0
  118. package/dist/kinds/roof-shape.d.ts +64 -0
  119. package/dist/kinds/roof.d.ts +31 -0
  120. package/dist/kinds/vegetation-fronds.d.ts +15 -0
  121. package/dist/kinds/vegetation-impostor.d.ts +9 -0
  122. package/dist/kinds/vegetation-leaves.d.ts +15 -0
  123. package/dist/kinds/vegetation-mesh.d.ts +21 -0
  124. package/dist/kinds/vegetation-params.d.ts +90 -0
  125. package/dist/kinds/vegetation-presets.d.ts +20 -0
  126. package/dist/kinds/vegetation-skeleton.d.ts +20 -0
  127. package/dist/kinds/vegetation.d.ts +42 -0
  128. package/dist/kinds/vehicle.d.ts +42 -0
  129. package/dist/material/cc0.d.ts +4 -0
  130. package/dist/material/define.d.ts +34 -0
  131. package/dist/material/generator.d.ts +4 -0
  132. package/dist/material/index.d.ts +9 -0
  133. package/dist/material/index.js +70 -0
  134. package/dist/material/index.js.map +9 -0
  135. package/dist/material/library.d.ts +7 -0
  136. package/dist/material/material-def.d.ts +30 -0
  137. package/dist/material/paint/bond.d.ts +3 -0
  138. package/dist/material/paint/canvas.d.ts +39 -0
  139. package/dist/material/paint/cell.d.ts +4 -0
  140. package/dist/material/paint/cells-round.d.ts +5 -0
  141. package/dist/material/paint/cells.d.ts +7 -0
  142. package/dist/material/paint/compose.d.ts +5 -0
  143. package/dist/material/paint/concrete.d.ts +3 -0
  144. package/dist/material/paint/courses.d.ts +31 -0
  145. package/dist/material/paint/decode-png.d.ts +6 -0
  146. package/dist/material/paint/default-sheet.d.ts +4 -0
  147. package/dist/material/paint/fabric.d.ts +4 -0
  148. package/dist/material/paint/foliage-broad.d.ts +5 -0
  149. package/dist/material/paint/foliage-common.d.ts +35 -0
  150. package/dist/material/paint/foliage-compound.d.ts +4 -0
  151. package/dist/material/paint/foliage-conifer.d.ts +5 -0
  152. package/dist/material/paint/foliage-raster.d.ts +26 -0
  153. package/dist/material/paint/foliage.d.ts +3 -0
  154. package/dist/material/paint/image-strip.d.ts +4 -0
  155. package/dist/material/paint/kit.d.ts +5 -0
  156. package/dist/material/paint/masks.d.ts +3 -0
  157. package/dist/material/paint/metal.d.ts +3 -0
  158. package/dist/material/paint/noise.d.ts +9 -0
  159. package/dist/material/paint/normal.d.ts +8 -0
  160. package/dist/material/paint/orm.d.ts +8 -0
  161. package/dist/material/paint/planks.d.ts +3 -0
  162. package/dist/material/paint/plates.d.ts +3 -0
  163. package/dist/material/paint/shapes.d.ts +8 -0
  164. package/dist/material/paint/slate.d.ts +3 -0
  165. package/dist/material/paint/stamp.d.ts +3 -0
  166. package/dist/material/paint/strip.d.ts +3 -0
  167. package/dist/material/paint/surface.d.ts +5 -0
  168. package/dist/material/paint/types.d.ts +65 -0
  169. package/dist/material/paint/validate.d.ts +3 -0
  170. package/dist/material/palette.d.ts +14 -0
  171. package/dist/material/shader-godot.d.ts +2 -0
  172. package/dist/material/shader-three.d.ts +2 -0
  173. package/dist/material/shader.d.ts +46 -0
  174. package/dist/material/slots.d.ts +5 -0
  175. package/dist/material/texture-source.d.ts +34 -0
  176. package/dist/material/trim-sheet.d.ts +38 -0
  177. package/dist/neural/generator.d.ts +25 -0
  178. package/dist/neural/images.d.ts +17 -0
  179. package/dist/neural/index.d.ts +2 -0
  180. package/dist/neural/index.js +14 -0
  181. package/dist/neural/index.js.map +9 -0
  182. package/dist/passes/bake-ao.d.ts +17 -0
  183. package/dist/passes/grime.d.ts +15 -0
  184. package/dist/passes/index.d.ts +6 -0
  185. package/dist/passes/index.js +22 -0
  186. package/dist/passes/index.js.map +9 -0
  187. package/dist/passes/livery.d.ts +19 -0
  188. package/dist/passes/optimize-cache.d.ts +15 -0
  189. package/dist/passes/optimize-weld.d.ts +15 -0
  190. package/dist/passes/optimize.d.ts +28 -0
  191. package/dist/passes/ray-grid.d.ts +23 -0
  192. package/dist/passes/weighted-normals.d.ts +8 -0
  193. package/dist/primitives/beam.d.ts +17 -0
  194. package/dist/primitives/bevel-box.d.ts +13 -0
  195. package/dist/primitives/box.d.ts +16 -0
  196. package/dist/primitives/cell.d.ts +20 -0
  197. package/dist/primitives/check.d.ts +12 -0
  198. package/dist/primitives/closed.d.ts +11 -0
  199. package/dist/primitives/erosion.d.ts +22 -0
  200. package/dist/primitives/font-5x7.d.ts +3 -0
  201. package/dist/primitives/heightfield.d.ts +16 -0
  202. package/dist/primitives/index.d.ts +23 -0
  203. package/dist/primitives/index.js +81 -0
  204. package/dist/primitives/index.js.map +9 -0
  205. package/dist/primitives/lathe-facets.d.ts +8 -0
  206. package/dist/primitives/lathe.d.ts +26 -0
  207. package/dist/primitives/lattice.d.ts +30 -0
  208. package/dist/primitives/loft.d.ts +15 -0
  209. package/dist/primitives/mc-table.d.ts +2 -0
  210. package/dist/primitives/member.d.ts +19 -0
  211. package/dist/primitives/noise.d.ts +34 -0
  212. package/dist/primitives/planar.d.ts +10 -0
  213. package/dist/primitives/prism.d.ts +13 -0
  214. package/dist/primitives/sculpt-edit.d.ts +56 -0
  215. package/dist/primitives/sculpt-tree.d.ts +23 -0
  216. package/dist/primitives/sculpt.d.ts +27 -0
  217. package/dist/primitives/sdf-grid.d.ts +17 -0
  218. package/dist/primitives/sdf-ops.d.ts +37 -0
  219. package/dist/primitives/sdf.d.ts +21 -0
  220. package/dist/primitives/strip-uv.d.ts +11 -0
  221. package/dist/primitives/surface-nets.d.ts +7 -0
  222. package/dist/primitives/sweep.d.ts +19 -0
  223. package/dist/primitives/terrain-color.d.ts +30 -0
  224. package/dist/primitives/terrain-grid.d.ts +36 -0
  225. package/dist/primitives/text-layout.d.ts +22 -0
  226. package/dist/primitives/text.d.ts +27 -0
  227. package/dist/primitives/tiled.d.ts +22 -0
  228. package/dist/primitives/transport.d.ts +17 -0
  229. package/dist/primitives/triangulate.d.ts +2 -0
  230. package/dist/primitives/value-noise.d.ts +10 -0
  231. package/dist/qa/checks.d.ts +11 -0
  232. package/dist/qa/clash.d.ts +2 -0
  233. package/dist/qa/floating.d.ts +5 -0
  234. package/dist/qa/gates.d.ts +12 -0
  235. package/dist/qa/geometry.d.ts +21 -0
  236. package/dist/qa/index.d.ts +12 -0
  237. package/dist/qa/index.js +78 -0
  238. package/dist/qa/index.js.map +9 -0
  239. package/dist/qa/inside.d.ts +12 -0
  240. package/dist/qa/inspect.d.ts +18 -0
  241. package/dist/qa/lods.d.ts +12 -0
  242. package/dist/qa/measures.d.ts +4 -0
  243. package/dist/qa/occlusion.d.ts +3 -0
  244. package/dist/qa/sanity.d.ts +7 -0
  245. package/dist/qa/types.d.ts +12 -0
  246. package/dist/render/beauty.d.ts +40 -0
  247. package/dist/render/camera.d.ts +33 -0
  248. package/dist/render/depth-map.d.ts +38 -0
  249. package/dist/render/font.d.ts +1 -0
  250. package/dist/render/ground.d.ts +13 -0
  251. package/dist/render/index.d.ts +10 -0
  252. package/dist/render/index.js +47 -0
  253. package/dist/render/index.js.map +9 -0
  254. package/dist/render/lighting.d.ts +27 -0
  255. package/dist/render/lod-strip.d.ts +5 -0
  256. package/dist/render/metrics.d.ts +37 -0
  257. package/dist/render/options.d.ts +28 -0
  258. package/dist/render/photo-match.d.ts +19 -0
  259. package/dist/render/raster.d.ts +25 -0
  260. package/dist/render/resolve.d.ts +11 -0
  261. package/dist/render/review-prompt.d.ts +2 -0
  262. package/dist/render/review-warnings.d.ts +34 -0
  263. package/dist/render/review.d.ts +24 -0
  264. package/dist/render/scene.d.ts +18 -0
  265. package/dist/render/shade.d.ts +22 -0
  266. package/dist/render/sheet.d.ts +15 -0
  267. package/dist/render/texture.d.ts +15 -0
  268. package/dist/render/thumbnail.d.ts +8 -0
  269. package/dist/render/vis-buffer.d.ts +18 -0
  270. package/dist/testing/assert.d.ts +21 -0
  271. package/dist/testing/compiled.d.ts +15 -0
  272. package/dist/testing/hash.d.ts +1 -0
  273. package/dist/testing/index.d.ts +4 -0
  274. package/dist/testing/index.js +134 -0
  275. package/dist/testing/index.js.map +13 -0
  276. package/dist/testing/snapshot.d.ts +13 -0
  277. package/docs/README.md +56 -0
  278. package/docs/architecture/README.md +68 -0
  279. package/docs/architecture/adding-a-kind.md +44 -0
  280. package/docs/architecture/adding-a-primitive.md +49 -0
  281. package/docs/architecture/errors.md +47 -0
  282. package/docs/architecture/glb-production.md +214 -0
  283. package/docs/architecture/gltf-capabilities.md +117 -0
  284. package/docs/architecture/materials.md +151 -0
  285. package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
  286. package/docs/decisions/002-typescript-not-json.md +22 -0
  287. package/docs/decisions/003-shared-trim-sheet.md +21 -0
  288. package/docs/decisions/004-z-up-metres.md +21 -0
  289. package/docs/decisions/005-zero-runtime-deps.md +21 -0
  290. package/docs/decisions/006-single-package-subpath-exports.md +20 -0
  291. package/docs/decisions/007-cpu-contact-sheets.md +21 -0
  292. package/docs/decisions/008-surface-materials.md +21 -0
  293. package/docs/decisions/009-gltf-front-axis.md +21 -0
  294. package/docs/decisions/README.md +15 -0
  295. package/docs/idea/01-vision.md +117 -0
  296. package/docs/idea/02-dsl.md +343 -0
  297. package/docs/idea/03-ir.md +97 -0
  298. package/docs/idea/04-materials.md +114 -0
  299. package/docs/idea/05-agent-loop.md +141 -0
  300. package/docs/idea/06-qa.md +101 -0
  301. package/docs/idea/07-what-fits.md +45 -0
  302. package/docs/idea/08-neural-adapters.md +78 -0
  303. package/docs/idea/09-kinds.md +105 -0
  304. package/docs/idea/10-sculpting.md +166 -0
  305. package/docs/idea/11-materials-authoring.md +132 -0
  306. package/docs/idea/12-blender-bridge.md +136 -0
  307. package/docs/idea/13-asset-pipeline-research.md +89 -0
  308. package/docs/landscape.md +49 -0
  309. package/docs/plans/roadmap.md +139 -0
  310. package/examples/build-with-bun.ts +41 -0
  311. package/llms.txt +47 -0
  312. package/package.json +100 -4
  313. package/recipes/README.md +38 -0
  314. package/recipes/asteroid/README.md +46 -0
  315. package/recipes/asteroid/asteroid.ts +336 -0
  316. package/recipes/boulders/README.md +28 -0
  317. package/recipes/boulders/boulders.ts +117 -0
  318. package/recipes/boxcar/README.md +53 -0
  319. package/recipes/boxcar/boxcar.ts +1000 -0
  320. package/recipes/cargo_container_scifi/README.md +52 -0
  321. package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
  322. package/recipes/dropship/README.md +84 -0
  323. package/recipes/dropship/dropship.ts +1144 -0
  324. package/recipes/eiffel_tower/README.md +110 -0
  325. package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
  326. package/recipes/goblin_head/README.md +75 -0
  327. package/recipes/goblin_head/goblin_head.ts +38 -0
  328. package/recipes/human_head/README.md +68 -0
  329. package/recipes/human_head/human_head.ts +38 -0
  330. package/recipes/loco_440/README.md +32 -0
  331. package/recipes/loco_440/loco_440.ts +1404 -0
  332. package/recipes/material_swatches/README.md +21 -0
  333. package/recipes/material_swatches/material_swatches.ts +189 -0
  334. package/recipes/mountain_range/README.md +45 -0
  335. package/recipes/mountain_range/mountain_range.ts +128 -0
  336. package/recipes/oak/README.md +30 -0
  337. package/recipes/oak/oak.ts +19 -0
  338. package/recipes/orc_head/README.md +78 -0
  339. package/recipes/orc_head/orc_head.ts +41 -0
  340. package/recipes/palm/README.md +30 -0
  341. package/recipes/palm/palm.ts +19 -0
  342. package/recipes/pine/README.md +29 -0
  343. package/recipes/pine/pine.ts +19 -0
  344. package/recipes/signal_gantry/README.md +36 -0
  345. package/recipes/signal_gantry/signal_gantry.ts +522 -0
  346. package/recipes/station_halt/README.md +39 -0
  347. package/recipes/station_halt/station_halt.ts +722 -0
  348. package/recipes/t34/README.md +44 -0
  349. package/recipes/t34/t34.ts +1068 -0
  350. package/recipes/tiger_1/README.md +75 -0
  351. package/recipes/tiger_1/tiger_1.ts +1370 -0
  352. package/recipes/water_tower/README.md +40 -0
  353. package/recipes/water_tower/water_tower.ts +542 -0
  354. package/src/cli/args.ts +125 -0
  355. package/src/cli/bin.ts +7 -0
  356. package/src/cli/load.ts +17 -0
  357. package/src/cli/manifest.ts +95 -0
  358. package/src/cli/render-cmds.ts +66 -0
  359. package/src/cli/run.ts +192 -0
  360. package/src/cli/texture-cache.ts +24 -0
  361. package/src/core/asset.ts +129 -0
  362. package/src/core/definition.ts +156 -0
  363. package/src/core/errors.ts +50 -0
  364. package/src/core/family.ts +50 -0
  365. package/src/core/index.ts +66 -0
  366. package/src/core/mesh.ts +114 -0
  367. package/src/core/part.ts +130 -0
  368. package/src/core/png.ts +49 -0
  369. package/src/core/rng.ts +49 -0
  370. package/src/core/transform.ts +90 -0
  371. package/src/core/vec.ts +26 -0
  372. package/src/core/world.ts +80 -0
  373. package/src/export/bin-writer.ts +79 -0
  374. package/src/export/container.ts +35 -0
  375. package/src/export/credits.ts +25 -0
  376. package/src/export/glb-options.ts +53 -0
  377. package/src/export/glb.ts +187 -0
  378. package/src/export/hierarchy.ts +122 -0
  379. package/src/export/index.ts +34 -0
  380. package/src/export/lights.ts +49 -0
  381. package/src/export/lod-coverage.ts +71 -0
  382. package/src/export/materials.ts +126 -0
  383. package/src/export/merge-pivots.ts +146 -0
  384. package/src/export/metadata.ts +62 -0
  385. package/src/export/motion.ts +70 -0
  386. package/src/export/optimize-parts.ts +80 -0
  387. package/src/export/primitive.ts +35 -0
  388. package/src/export/tangents.ts +75 -0
  389. package/src/export/textures.ts +88 -0
  390. package/src/export/version.ts +6 -0
  391. package/src/grammar/index.ts +4 -0
  392. package/src/grammar/split.ts +87 -0
  393. package/src/grammar/whyte.ts +20 -0
  394. package/src/index.ts +14 -0
  395. package/src/kinds/building.ts +153 -0
  396. package/src/kinds/faces.ts +138 -0
  397. package/src/kinds/head-armour.ts +181 -0
  398. package/src/kinds/head-beard.ts +133 -0
  399. package/src/kinds/head-bust.ts +184 -0
  400. package/src/kinds/head-extras.ts +150 -0
  401. package/src/kinds/head-face.ts +242 -0
  402. package/src/kinds/head-garb.ts +181 -0
  403. package/src/kinds/head-geom.ts +31 -0
  404. package/src/kinds/head-hair.ts +193 -0
  405. package/src/kinds/head-layout.ts +63 -0
  406. package/src/kinds/head-mesh.ts +195 -0
  407. package/src/kinds/head-mouth.ts +111 -0
  408. package/src/kinds/head-params.ts +162 -0
  409. package/src/kinds/head-presets.ts +155 -0
  410. package/src/kinds/head-rim.ts +128 -0
  411. package/src/kinds/head-tone.ts +52 -0
  412. package/src/kinds/head-torso.ts +230 -0
  413. package/src/kinds/head.ts +176 -0
  414. package/src/kinds/hull-lines.ts +160 -0
  415. package/src/kinds/hull-skin.ts +83 -0
  416. package/src/kinds/hull.ts +209 -0
  417. package/src/kinds/index.ts +63 -0
  418. package/src/kinds/mass.ts +110 -0
  419. package/src/kinds/openings.ts +141 -0
  420. package/src/kinds/planned.ts +12 -0
  421. package/src/kinds/roof-chimney.ts +100 -0
  422. package/src/kinds/roof-shape.ts +230 -0
  423. package/src/kinds/roof.ts +250 -0
  424. package/src/kinds/vegetation-fronds.ts +126 -0
  425. package/src/kinds/vegetation-impostor.ts +118 -0
  426. package/src/kinds/vegetation-leaves.ts +168 -0
  427. package/src/kinds/vegetation-mesh.ts +174 -0
  428. package/src/kinds/vegetation-params.ts +147 -0
  429. package/src/kinds/vegetation-presets.ts +184 -0
  430. package/src/kinds/vegetation-skeleton.ts +168 -0
  431. package/src/kinds/vegetation.ts +199 -0
  432. package/src/kinds/vehicle.ts +144 -0
  433. package/src/material/cc0.ts +41 -0
  434. package/src/material/define.ts +130 -0
  435. package/src/material/generator.ts +63 -0
  436. package/src/material/index.ts +60 -0
  437. package/src/material/library.ts +196 -0
  438. package/src/material/material-def.ts +84 -0
  439. package/src/material/paint/bond.ts +60 -0
  440. package/src/material/paint/canvas.ts +101 -0
  441. package/src/material/paint/cell.ts +39 -0
  442. package/src/material/paint/cells-round.ts +119 -0
  443. package/src/material/paint/cells.ts +111 -0
  444. package/src/material/paint/compose.ts +53 -0
  445. package/src/material/paint/concrete.ts +62 -0
  446. package/src/material/paint/courses.ts +85 -0
  447. package/src/material/paint/decode-png.ts +106 -0
  448. package/src/material/paint/default-sheet.ts +73 -0
  449. package/src/material/paint/fabric.ts +63 -0
  450. package/src/material/paint/foliage-broad.ts +126 -0
  451. package/src/material/paint/foliage-common.ts +93 -0
  452. package/src/material/paint/foliage-compound.ts +135 -0
  453. package/src/material/paint/foliage-conifer.ts +143 -0
  454. package/src/material/paint/foliage-raster.ts +196 -0
  455. package/src/material/paint/foliage.ts +23 -0
  456. package/src/material/paint/image-strip.ts +88 -0
  457. package/src/material/paint/kit.ts +37 -0
  458. package/src/material/paint/masks.ts +46 -0
  459. package/src/material/paint/metal.ts +109 -0
  460. package/src/material/paint/noise.ts +52 -0
  461. package/src/material/paint/normal.ts +52 -0
  462. package/src/material/paint/orm.ts +108 -0
  463. package/src/material/paint/planks.ts +101 -0
  464. package/src/material/paint/plates.ts +72 -0
  465. package/src/material/paint/shapes.ts +28 -0
  466. package/src/material/paint/slate.ts +52 -0
  467. package/src/material/paint/stamp.ts +40 -0
  468. package/src/material/paint/strip.ts +48 -0
  469. package/src/material/paint/surface.ts +60 -0
  470. package/src/material/paint/types.ts +94 -0
  471. package/src/material/paint/validate.ts +74 -0
  472. package/src/material/palette.ts +46 -0
  473. package/src/material/shader-godot.ts +77 -0
  474. package/src/material/shader-three.ts +103 -0
  475. package/src/material/shader.ts +100 -0
  476. package/src/material/slots.ts +18 -0
  477. package/src/material/texture-source.ts +86 -0
  478. package/src/material/trim-sheet.ts +166 -0
  479. package/src/neural/generator.ts +37 -0
  480. package/src/neural/images.ts +27 -0
  481. package/src/neural/index.ts +10 -0
  482. package/src/passes/bake-ao.ts +183 -0
  483. package/src/passes/grime.ts +37 -0
  484. package/src/passes/index.ts +8 -0
  485. package/src/passes/livery.ts +66 -0
  486. package/src/passes/optimize-cache.ts +175 -0
  487. package/src/passes/optimize-weld.ts +147 -0
  488. package/src/passes/optimize.ts +85 -0
  489. package/src/passes/ray-grid.ts +237 -0
  490. package/src/passes/weighted-normals.ts +125 -0
  491. package/src/primitives/beam.ts +60 -0
  492. package/src/primitives/bevel-box.ts +140 -0
  493. package/src/primitives/box.ts +90 -0
  494. package/src/primitives/cell.ts +115 -0
  495. package/src/primitives/check.ts +49 -0
  496. package/src/primitives/closed.ts +74 -0
  497. package/src/primitives/erosion.ts +183 -0
  498. package/src/primitives/font-5x7.ts +117 -0
  499. package/src/primitives/heightfield.ts +101 -0
  500. package/src/primitives/index.ts +56 -0
  501. package/src/primitives/lathe-facets.ts +45 -0
  502. package/src/primitives/lathe.ts +174 -0
  503. package/src/primitives/lattice.ts +131 -0
  504. package/src/primitives/loft.ts +174 -0
  505. package/src/primitives/mc-table.ts +41 -0
  506. package/src/primitives/member.ts +130 -0
  507. package/src/primitives/noise.ts +157 -0
  508. package/src/primitives/planar.ts +55 -0
  509. package/src/primitives/prism.ts +85 -0
  510. package/src/primitives/sculpt-edit.ts +212 -0
  511. package/src/primitives/sculpt-tree.ts +226 -0
  512. package/src/primitives/sculpt.ts +80 -0
  513. package/src/primitives/sdf-grid.ts +131 -0
  514. package/src/primitives/sdf-ops.ts +145 -0
  515. package/src/primitives/sdf.ts +83 -0
  516. package/src/primitives/strip-uv.ts +26 -0
  517. package/src/primitives/surface-nets.ts +161 -0
  518. package/src/primitives/sweep.ts +162 -0
  519. package/src/primitives/terrain-color.ts +85 -0
  520. package/src/primitives/terrain-grid.ts +137 -0
  521. package/src/primitives/text-layout.ts +85 -0
  522. package/src/primitives/text.ts +197 -0
  523. package/src/primitives/tiled.ts +93 -0
  524. package/src/primitives/transport.ts +85 -0
  525. package/src/primitives/triangulate.ts +42 -0
  526. package/src/primitives/value-noise.ts +62 -0
  527. package/src/qa/checks.ts +110 -0
  528. package/src/qa/clash.ts +177 -0
  529. package/src/qa/floating.ts +63 -0
  530. package/src/qa/gates.ts +126 -0
  531. package/src/qa/geometry.ts +187 -0
  532. package/src/qa/index.ts +28 -0
  533. package/src/qa/inside.ts +114 -0
  534. package/src/qa/inspect.ts +107 -0
  535. package/src/qa/lods.ts +97 -0
  536. package/src/qa/measures.ts +67 -0
  537. package/src/qa/occlusion.ts +166 -0
  538. package/src/qa/sanity.ts +137 -0
  539. package/src/qa/types.ts +17 -0
  540. package/src/render/beauty.ts +115 -0
  541. package/src/render/camera.ts +142 -0
  542. package/src/render/depth-map.ts +191 -0
  543. package/src/render/font.ts +76 -0
  544. package/src/render/ground.ts +87 -0
  545. package/src/render/index.ts +34 -0
  546. package/src/render/lighting.ts +121 -0
  547. package/src/render/lod-strip.ts +34 -0
  548. package/src/render/metrics.ts +147 -0
  549. package/src/render/options.ts +81 -0
  550. package/src/render/photo-match.ts +28 -0
  551. package/src/render/raster.ts +143 -0
  552. package/src/render/resolve.ts +109 -0
  553. package/src/render/review-prompt.ts +59 -0
  554. package/src/render/review-warnings.ts +92 -0
  555. package/src/render/review.ts +103 -0
  556. package/src/render/scene.ts +119 -0
  557. package/src/render/shade.ts +204 -0
  558. package/src/render/sheet.ts +88 -0
  559. package/src/render/texture.ts +97 -0
  560. package/src/render/thumbnail.ts +75 -0
  561. package/src/render/vis-buffer.ts +119 -0
  562. package/src/testing/assert.ts +115 -0
  563. package/src/testing/compiled.ts +31 -0
  564. package/src/testing/hash.ts +14 -0
  565. package/src/testing/index.ts +14 -0
  566. package/src/testing/snapshot.ts +37 -0
  567. package/src/types/gltf-validator.d.ts +13 -0
@@ -0,0 +1,81 @@
1
+ // Validate beauty-render options up front: a bad size or angle fails with a code and the exact
2
+ // fix instead of a blank or giant image. The CLI's --size/--az/--el/--fov flags land here too.
3
+
4
+ import { Ts23dError } from '../core/index.ts';
5
+
6
+ export interface CheckedBeauty {
7
+ readonly width: number;
8
+ readonly height: number;
9
+ readonly azDeg: number;
10
+ readonly elDeg: number;
11
+ readonly fovDeg: number;
12
+ readonly supersample: number;
13
+ }
14
+
15
+ interface Raw {
16
+ readonly width?: number;
17
+ readonly height?: number;
18
+ readonly azDeg?: number;
19
+ readonly elDeg?: number;
20
+ readonly fovDeg?: number;
21
+ readonly supersample?: number;
22
+ readonly window?: { readonly x: number; readonly y: number; readonly zoom: number };
23
+ }
24
+
25
+ function need(ok: boolean, cause: string, fix: string): void {
26
+ if (!ok) throw new Ts23dError('T3D_RENDER_OPTIONS', { cause, fix });
27
+ }
28
+
29
+ export function checkBeautyOptions(o: Raw): CheckedBeauty {
30
+ const v = {
31
+ width: o.width ?? 1600,
32
+ height: o.height ?? 900,
33
+ azDeg: o.azDeg ?? -35,
34
+ elDeg: o.elDeg ?? 18,
35
+ fovDeg: o.fovDeg ?? 30,
36
+ supersample: o.supersample ?? 2,
37
+ };
38
+ for (const k of ['width', 'height'] as const)
39
+ need(
40
+ Number.isInteger(v[k]) && v[k] >= 16 && v[k] <= 8192,
41
+ `${k} ${v[k]} is not an integer in 16…8192 pixels`,
42
+ 'pass a size like 1600x900 (--size WxH), each side 16–8192',
43
+ );
44
+ need(
45
+ Number.isFinite(v.azDeg),
46
+ `azimuth ${v.azDeg} is not a number`,
47
+ 'pass degrees, e.g. --az -35',
48
+ );
49
+ need(
50
+ Number.isFinite(v.elDeg) && v.elDeg >= -10 && v.elDeg <= 89,
51
+ `elevation ${v.elDeg} is outside −10…89°`,
52
+ 'pass degrees above the horizon, e.g. --el 18 (89 = straight down)',
53
+ );
54
+ need(
55
+ Number.isFinite(v.fovDeg) && v.fovDeg >= 5 && v.fovDeg <= 120,
56
+ `field of view ${v.fovDeg} is outside 5…120°`,
57
+ 'pass a vertical FOV in degrees, e.g. --fov 30',
58
+ );
59
+ need(
60
+ Number.isInteger(v.supersample) && v.supersample >= 1 && v.supersample <= 4,
61
+ `supersample ${v.supersample} is not 1, 2, 3 or 4`,
62
+ 'use supersample: 2 (4 samples per pixel)',
63
+ );
64
+ if (o.window)
65
+ need(
66
+ Number.isFinite(o.window.zoom) && o.window.zoom >= 1 && o.window.zoom <= 64,
67
+ `window zoom ${o.window.zoom} is outside 1…64`,
68
+ 'zoom into the shot with window: { x, y, zoom } and 1 ≤ zoom ≤ 64',
69
+ );
70
+ return v;
71
+ }
72
+
73
+ /** Parse a `WxH` size flag ("1600x900"). */
74
+ export function parseSize(text: string): { width: number; height: number } {
75
+ const m = /^(\d+)[x×](\d+)$/i.exec(text.trim());
76
+ need(m !== null, `size "${text}" is not WIDTHxHEIGHT`, 'pass --size 1600x900');
77
+ const width = Number(m?.[1]);
78
+ const height = Number(m?.[2]);
79
+ checkBeautyOptions({ width, height });
80
+ return { width, height };
81
+ }
@@ -0,0 +1,28 @@
1
+ // Photo match: render the asset from the camera that took a reference photo and stack
2
+ // photo · model · 50% overlay in ONE image. The strongest check an agent has — silhouette,
3
+ // proportions and colour against reality at a glance. Planned: M2 (docs/plans/roadmap.md).
4
+
5
+ import { type CompiledAsset, notImplemented, type Vec3 } from '../core/index.ts';
6
+
7
+ export interface PhotoCamera {
8
+ /** Camera position and look-at target in asset space (metres, Z up). */
9
+ readonly eye: Vec3;
10
+ readonly target: Vec3;
11
+ /** Vertical field of view, degrees. */
12
+ readonly fovDeg: number;
13
+ }
14
+
15
+ export interface PhotoMatchOptions {
16
+ readonly photo: Uint8Array;
17
+ /** Solve the camera from ≥ 4 marked correspondences instead of passing it (planned with it). */
18
+ readonly camera: PhotoCamera;
19
+ readonly width?: number;
20
+ }
21
+
22
+ /** Returns a PNG: photo on top, model in the middle, overlay at the bottom; plus silhouette IoU. */
23
+ export function photoMatch(
24
+ _asset: CompiledAsset,
25
+ _o: PhotoMatchOptions,
26
+ ): { png: Uint8Array; iou: number } {
27
+ return notImplemented('photoMatch (photo · model · overlay sheet + silhouette IoU)');
28
+ }
@@ -0,0 +1,143 @@
1
+ // CPU rasteriser: orthographic, z-buffered, Lambert-shaded triangles plus depth-tested lines.
2
+ // Slow by GPU standards and fast enough for 6 × 400² views of a game asset in well under a second.
3
+
4
+ import type { CompiledAsset, Vec3 } from '../core/index.ts';
5
+ import { cross, dot, normalize, rotateZ, sub, toWorld, worldTransforms } from '../core/index.ts';
6
+
7
+ export interface View {
8
+ readonly label: string;
9
+ /** Direction the camera looks along (world, Z up). */
10
+ readonly dir: Vec3;
11
+ readonly up: Vec3;
12
+ }
13
+
14
+ export interface Target {
15
+ readonly px: Uint8Array;
16
+ readonly depth: Float32Array;
17
+ readonly width: number;
18
+ readonly height: number;
19
+ }
20
+
21
+ export interface Camera {
22
+ project(p: Vec3): Vec3;
23
+ }
24
+
25
+ export function orthoCamera(view: View, centre: Vec3, extent: number, size: number): Camera {
26
+ const f = normalize(view.dir);
27
+ const r = normalize(cross(f, view.up));
28
+ const u = cross(r, f);
29
+ const k = size / (extent * 1.15);
30
+ return {
31
+ project: (p) => {
32
+ const d = sub(p, centre);
33
+ return [size / 2 + dot(d, r) * k, size / 2 - dot(d, u) * k, dot(d, f)];
34
+ },
35
+ };
36
+ }
37
+
38
+ const LIGHT = normalize([0.4, -0.6, 0.8]);
39
+
40
+ export function worldParts(asset: CompiledAsset): {
41
+ positions: Float32Array;
42
+ normals: Float32Array;
43
+ colors: Float32Array;
44
+ indices: Uint32Array;
45
+ }[] {
46
+ const xf = worldTransforms(asset.parts);
47
+ const out = [];
48
+ for (const p of asset.parts) {
49
+ const t = xf.get(p.name);
50
+ if (!p.mesh || !t) continue;
51
+ const positions = new Float32Array(p.mesh.positions.length);
52
+ const normals = new Float32Array(p.mesh.normals.length);
53
+ for (let i = 0; i < positions.length; i += 3) {
54
+ positions.set(
55
+ toWorld(t, [
56
+ p.mesh.positions[i] ?? 0,
57
+ p.mesh.positions[i + 1] ?? 0,
58
+ p.mesh.positions[i + 2] ?? 0,
59
+ ]),
60
+ i,
61
+ );
62
+ normals.set(
63
+ rotateZ(
64
+ [p.mesh.normals[i] ?? 0, p.mesh.normals[i + 1] ?? 0, p.mesh.normals[i + 2] ?? 0],
65
+ t.yaw,
66
+ ),
67
+ i,
68
+ );
69
+ }
70
+ out.push({ positions, normals, colors: p.mesh.colors, indices: p.mesh.indices });
71
+ }
72
+ return out;
73
+ }
74
+
75
+ function tri(t: Target, a: Vec3, b: Vec3, c: Vec3, rgb: Vec3): void {
76
+ const minX = Math.max(0, Math.floor(Math.min(a[0], b[0], c[0])));
77
+ const maxX = Math.min(t.width - 1, Math.ceil(Math.max(a[0], b[0], c[0])));
78
+ const minY = Math.max(0, Math.floor(Math.min(a[1], b[1], c[1])));
79
+ const maxY = Math.min(t.height - 1, Math.ceil(Math.max(a[1], b[1], c[1])));
80
+ const area = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
81
+ if (Math.abs(area) < 1e-9) return;
82
+ for (let y = minY; y <= maxY; y++) {
83
+ for (let x = minX; x <= maxX; x++) {
84
+ const px = x + 0.5;
85
+ const py = y + 0.5;
86
+ const w0 = ((b[0] - px) * (c[1] - py) - (b[1] - py) * (c[0] - px)) / area;
87
+ const w1 = ((c[0] - px) * (a[1] - py) - (c[1] - py) * (a[0] - px)) / area;
88
+ const w2 = 1 - w0 - w1;
89
+ if (w0 < 0 || w1 < 0 || w2 < 0) continue;
90
+ const z = w0 * a[2] + w1 * b[2] + w2 * c[2];
91
+ const i = y * t.width + x;
92
+ if (z >= (t.depth[i] ?? Infinity)) continue;
93
+ t.depth[i] = z;
94
+ t.px.set([rgb[0], rgb[1], rgb[2], 255], i * 4);
95
+ }
96
+ }
97
+ }
98
+
99
+ const toSrgb = (v: number): number =>
100
+ Math.round(255 * Math.min(1, v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055));
101
+
102
+ export function drawAsset(t: Target, cam: Camera, asset: CompiledAsset): void {
103
+ for (const m of worldParts(asset)) {
104
+ for (let i = 0; i < m.indices.length; i += 3) {
105
+ const ids = [m.indices[i] ?? 0, m.indices[i + 1] ?? 0, m.indices[i + 2] ?? 0];
106
+ const pts = ids.map((v) =>
107
+ cam.project([
108
+ m.positions[v * 3] ?? 0,
109
+ m.positions[v * 3 + 1] ?? 0,
110
+ m.positions[v * 3 + 2] ?? 0,
111
+ ]),
112
+ );
113
+ const v0 = ids[0] ?? 0;
114
+ const n: Vec3 = [
115
+ m.normals[v0 * 3] ?? 0,
116
+ m.normals[v0 * 3 + 1] ?? 0,
117
+ m.normals[v0 * 3 + 2] ?? 1,
118
+ ];
119
+ const shade = 0.35 + 0.65 * Math.max(0, dot(n, LIGHT));
120
+ const rgb: Vec3 = [
121
+ toSrgb((m.colors[v0 * 3] ?? 1) * shade),
122
+ toSrgb((m.colors[v0 * 3 + 1] ?? 1) * shade),
123
+ toSrgb((m.colors[v0 * 3 + 2] ?? 1) * shade),
124
+ ];
125
+ const [a, b, c] = pts;
126
+ if (a && b && c) tri(t, a, b, c, rgb);
127
+ }
128
+ }
129
+ }
130
+
131
+ export function line(t: Target, a: Vec3, b: Vec3, rgb: Vec3): void {
132
+ const steps = Math.ceil(Math.max(Math.abs(b[0] - a[0]), Math.abs(b[1] - a[1]), 1));
133
+ for (let s = 0; s <= steps; s++) {
134
+ const k = s / steps;
135
+ const x = Math.round(a[0] + (b[0] - a[0]) * k);
136
+ const y = Math.round(a[1] + (b[1] - a[1]) * k);
137
+ if (x < 0 || y < 0 || x >= t.width || y >= t.height) continue;
138
+ const z = a[2] + (b[2] - a[2]) * k;
139
+ const i = y * t.width + x;
140
+ if (z > (t.depth[i] ?? Infinity) + 1e-3) continue;
141
+ t.px.set([rgb[0], rgb[1], rgb[2], 255], i * 4);
142
+ }
143
+ }
@@ -0,0 +1,109 @@
1
+ // Resolve the supersampled visibility buffer to output pixels, MSAA-style: samples in a pixel
2
+ // that hit the same triangle are shaded ONCE at their mean barycentrics and weighted by count, so
3
+ // edges get ss² coverage samples while interiors cost one shade. Background samples show the
4
+ // studio backdrop × the floor's shadow light. Also yields the coverage mask and a detail grid.
5
+
6
+ import type { FloorGrid } from './ground.ts';
7
+ import { floorLight } from './ground.ts';
8
+ import { fromSrgb, tone, toSrgb8 } from './lighting.ts';
9
+ import { type ShadeContext, shadeSample } from './shade.ts';
10
+ import type { VisBuffer } from './vis-buffer.ts';
11
+
12
+ export interface Resolved {
13
+ readonly width: number;
14
+ readonly height: number;
15
+ readonly rgba: Uint8Array;
16
+ readonly mask: Uint8Array;
17
+ readonly detail: Uint32Array;
18
+ }
19
+
20
+ /** Backdrop colour (linear) at normalised screen (nx, ny): soft radial falloff below centre. */
21
+ function backdrop(nx: number, ny: number, out: Float32Array): void {
22
+ const dx = (nx - 0.5) * 1.1;
23
+ const dy = ny - 0.62;
24
+ const fall = Math.min(1, dx * dx + dy * dy * 1.6);
25
+ out[0] = fromSrgb(0.93 - 0.17 * fall);
26
+ out[1] = fromSrgb(0.935 - 0.15 * fall);
27
+ out[2] = fromSrgb(0.94 - 0.12 * fall);
28
+ }
29
+
30
+ export function resolve(
31
+ ctx: ShadeContext,
32
+ vis: VisBuffer,
33
+ floor: FloorGrid,
34
+ width: number,
35
+ height: number,
36
+ ss: number,
37
+ ): Resolved {
38
+ const rgba = new Uint8Array(width * height * 4);
39
+ const mask = new Uint8Array(width * height);
40
+ // Last detail cell each triangle was counted in: ≈ distinct triangles per cell, no hash set.
41
+ const counted = new Int32Array(ctx.scene.triangles).fill(-1);
42
+ const detail = new Uint32Array(16 * 9);
43
+ const lin = new Float32Array(3);
44
+ const bg = new Float32Array(3);
45
+ const n = ss * ss;
46
+ const ids = new Int32Array(n);
47
+ const cnt = new Int32Array(n);
48
+ const sb1 = new Float64Array(n);
49
+ const sb2 = new Float64Array(n);
50
+ const W = vis.width;
51
+ const inv = 1 / n;
52
+ for (let y = 0; y < height; y++) {
53
+ for (let x = 0; x < width; x++) {
54
+ backdrop(x / width, y / height, bg);
55
+ let r = 0;
56
+ let g = 0;
57
+ let b = 0;
58
+ let groups = 0;
59
+ let hits = 0;
60
+ for (let sy = 0; sy < ss; sy++)
61
+ for (let sx = 0; sx < ss; sx++) {
62
+ const X = x * ss + sx;
63
+ const Y = y * ss + sy;
64
+ const i = Y * W + X;
65
+ const t = vis.tri[i] ?? -1;
66
+ if (t < 0) {
67
+ const l = floorLight(floor, ctx.cam, X + 0.5, Y + 0.5);
68
+ r += (bg[0] ?? 0) * l;
69
+ g += (bg[1] ?? 0) * l;
70
+ b += (bg[2] ?? 0) * l;
71
+ continue;
72
+ }
73
+ hits++;
74
+ let k = 0;
75
+ while (k < groups && ids[k] !== t) k++;
76
+ if (k === groups) {
77
+ ids[k] = t;
78
+ cnt[k] = 0;
79
+ sb1[k] = 0;
80
+ sb2[k] = 0;
81
+ groups++;
82
+ }
83
+ cnt[k] = (cnt[k] ?? 0) + 1;
84
+ sb1[k] = (sb1[k] ?? 0) + (vis.b1[i] ?? 0);
85
+ sb2[k] = (sb2[k] ?? 0) + (vis.b2[i] ?? 0);
86
+ }
87
+ const cell = Math.floor((y * 9) / height) * 16 + Math.floor((x * 16) / width);
88
+ for (let k = 0; k < groups; k++) {
89
+ const t = ids[k] ?? 0;
90
+ const c = cnt[k] ?? 1;
91
+ if (counted[t] !== cell) {
92
+ counted[t] = cell;
93
+ detail[cell] = (detail[cell] ?? 0) + 1;
94
+ }
95
+ shadeSample(ctx, t, (sb1[k] ?? 0) / c, (sb2[k] ?? 0) / c, vis.mip[t] ?? 0, x + y * 2, lin);
96
+ r += tone(lin[0] ?? 0) * c;
97
+ g += tone(lin[1] ?? 0) * c;
98
+ b += tone(lin[2] ?? 0) * c;
99
+ }
100
+ const o = (y * width + x) * 4;
101
+ rgba[o] = toSrgb8(r * inv);
102
+ rgba[o + 1] = toSrgb8(g * inv);
103
+ rgba[o + 2] = toSrgb8(b * inv);
104
+ rgba[o + 3] = 255;
105
+ mask[y * width + x] = Math.round(hits * inv * 255);
106
+ }
107
+ }
108
+ return { width, height, rgba, mask, detail };
109
+ }
@@ -0,0 +1,59 @@
1
+ // review.md: the checklist prompt a vision-capable agent follows over the review pack. Mined from
2
+ // years of rejected assets (railroads procgen AGENT_BRIEF "review checklist"), plus the metrics
3
+ // and warnings this pack computed, so the reviewer starts from evidence, not from scratch.
4
+
5
+ import type { ReviewMetrics } from './review-warnings.ts';
6
+
7
+ const CHECKLIST = [
8
+ '**Floating bits** — every bar, bracket, lamp, rod, handle touches what holds it. Look at the close-ups and the TOP view.',
9
+ '**Clipping** — wheels through frames or fenders, rods through guides, brackets through roofs, branches through trunks.',
10
+ '**Proportions vs reality** — compare overall length/width/height ratios and signature features with the reference dossier and its photos.',
11
+ '**Open stacks and throats** — chimneys, funnels, exhausts, muzzles and pipes are OPEN (rim + dark inner throat), never capped.',
12
+ '**Hidden detail** — detail buried inside another mass (a clock behind a roof, a hatch under a turret) is wasted triangles.',
13
+ '**Silhouette at LOD2** — in lods.png, LOD2 keeps the outline and the big colour blocks; nothing pops, vanishes or shifts colour.',
14
+ '**Texture stretching** — trim-sheet strips run along faces at a sensible scale: no smeared bricks, giant planks or streaked rivets.',
15
+ '**Colour / value contrast** — materials separate (paint vs rubber vs metal vs glass); not one flat tone; no pure black or white.',
16
+ '**Lettering and decals** — numbers and signs are legible in the close-ups and read the right way round.',
17
+ '**Organic forms** — foliage, snow and terrain read as volume from the 3/4 hero, not as blotches, slabs or cards edge-on.',
18
+ '**Grounding** — the asset stands on the floor (contact shadow under every wheel, foot or base), not sunk or hovering.',
19
+ ];
20
+
21
+ export function reviewPrompt(m: ReviewMetrics, files: readonly string[], refs?: string): string {
22
+ const pct = (v: number): string => `${(v * 100).toFixed(1)}%`;
23
+ const views = m.views
24
+ .map(
25
+ (v) =>
26
+ `| ${v.view} | ${pct(v.lod0.coverage)} | ${pct(v.lod0.bboxFill)} | ${v.lodIoU.toFixed(3)} | ${v.lodIoUExact.toFixed(3)} |`,
27
+ )
28
+ .join('\n');
29
+ const warnings = m.warnings.length
30
+ ? m.warnings.map((w) => `- **${w.code}**: ${w.message}\n fix: ${w.fix}`).join('\n')
31
+ : '- none — the metrics are proxies, so still run the checklist.';
32
+ return `# Visual review: ${m.asset}
33
+
34
+ You are reviewing a game asset generated from code. READ every image in this folder before
35
+ answering: ${files.filter((f) => f.endsWith('.png')).join(', ')}.
36
+ ${refs ? `Reference dossier (specs, measures, photo links): \`${refs}\` — read it first.\n` : 'No reference dossier was found (expected recipes/<name>/README.md): judge against well-known photos of the real thing.\n'}
37
+ - \`hero.png\` — 3/4 product shot, LOD0. · \`sheet.png\` — six orthographic views on a metre grid.
38
+ - \`detail_*.png\` — 3× close-ups where the triangles are. · \`lods.png\` — LOD0 · LOD1 · LOD2, same camera.
39
+
40
+ Triangles per LOD: ${m.triangles.join(' / ')}.
41
+
42
+ ## Checklist — answer each with PASS or FAIL + where + the fix
43
+ ${CHECKLIST.map((c, i) => `${i + 1}. ${c}`).join('\n')}
44
+
45
+ ## Automatic metrics (review.json)
46
+ Hero: coverage ${pct(m.hero.coverage)}, span ${pct(m.hero.span)}, mean luminance ${m.hero.mean.toFixed(3)}, contrast ${m.hero.contrast.toFixed(3)}, near-black ${pct(m.hero.nearBlack)}, near-white ${pct(m.hero.nearWhite)}.
47
+
48
+ | view | LOD0 coverage | bbox fill | LOD0↔LOD2 IoU (far) | IoU (exact) |
49
+ |---|---|---|---|---|
50
+ ${views}
51
+
52
+ ## Warnings
53
+ ${warnings}
54
+
55
+ ## Output
56
+ A list of findings, worst first: \`[FAIL] <check> — <where, which view/image> — <fix in the recipe>\`,
57
+ then one line: \`VERDICT: SHIP\` or \`VERDICT: ITERATE\`.
58
+ `;
59
+ }
@@ -0,0 +1,92 @@
1
+ // Threshold checks over the review metrics: each warning names the metric, the value, the limit
2
+ // and the fix. Warnings, not failures — the image metrics are proxies; the vision pass decides.
3
+
4
+ import type { ExposureStats, MaskStats } from './metrics.ts';
5
+
6
+ export interface ViewMetrics {
7
+ readonly view: string;
8
+ readonly lod0: MaskStats;
9
+ readonly lod2: MaskStats;
10
+ /** Silhouette IoU LOD0 ↔ LOD2 as seen from afar (64² max-pooled masks, same frame). Low = popping. */
11
+ readonly lodIoU: number;
12
+ /** The same at full mask resolution (256²): lattices and foliage score low here by nature. */
13
+ readonly lodIoUExact: number;
14
+ }
15
+
16
+ export interface ReviewWarning {
17
+ readonly code: string;
18
+ readonly message: string;
19
+ readonly fix: string;
20
+ }
21
+
22
+ export interface ReviewMetrics {
23
+ readonly asset: string;
24
+ readonly triangles: readonly number[];
25
+ readonly hero: ExposureStats;
26
+ readonly views: readonly ViewMetrics[];
27
+ readonly warnings: readonly ReviewWarning[];
28
+ }
29
+
30
+ export const REVIEW_THRESHOLDS = {
31
+ /** LOD0 ↔ LOD2 silhouette IoU below this in any view = visible popping. */
32
+ lodIoU: 0.8,
33
+ heroMeanMin: 0.18,
34
+ heroMeanMax: 0.85,
35
+ heroContrastMin: 0.06,
36
+ nearBlackMax: 0.2,
37
+ nearWhiteMax: 0.1,
38
+ /** Asset spanning less of the hero frame than this: something stretches the bounds. */
39
+ heroSpanMin: 0.5,
40
+ } as const;
41
+
42
+ const f = (v: number): string => v.toFixed(3);
43
+
44
+ export function warningsOf(m: Omit<ReviewMetrics, 'warnings'>): ReviewWarning[] {
45
+ const t = REVIEW_THRESHOLDS;
46
+ const out: ReviewWarning[] = [];
47
+ for (const v of m.views)
48
+ if (v.lodIoU < t.lodIoU)
49
+ out.push({
50
+ code: 'lod-popping',
51
+ message: `${v.view}: LOD0↔LOD2 silhouette IoU ${f(v.lodIoU)} < ${t.lodIoU}`,
52
+ fix: 'keep the LOD2 massing on the LOD0 silhouette — same outer envelope, fewer cuts (see lods.png)',
53
+ });
54
+ const h = m.hero;
55
+ if (h.span < t.heroSpanMin)
56
+ out.push({
57
+ code: 'tiny-in-frame',
58
+ message: `asset spans ${f(h.span)} of the hero frame (< ${t.heroSpanMin})`,
59
+ fix: 'a stray or far-off part is stretching the bounds — find it in sheet.png and attach or remove it',
60
+ });
61
+ if (h.mean < t.heroMeanMin)
62
+ out.push({
63
+ code: 'too-dark',
64
+ message: `hero mean luminance ${f(h.mean)} < ${t.heroMeanMin}`,
65
+ fix: 'raise vertex colours (dark paint ≈ 0.05–0.1 linear, never 0) or check for inward-facing normals',
66
+ });
67
+ if (h.mean > t.heroMeanMax)
68
+ out.push({
69
+ code: 'washed-out',
70
+ message: `hero mean luminance ${f(h.mean)} > ${t.heroMeanMax}`,
71
+ fix: 'lower vertex colours: real paint and stone rarely exceed ~0.7 linear albedo',
72
+ });
73
+ if (h.contrast < t.heroContrastMin)
74
+ out.push({
75
+ code: 'low-contrast',
76
+ message: `hero luminance contrast ${f(h.contrast)} < ${t.heroContrastMin}`,
77
+ fix: 'add value contrast between materials (trim, rubber, metal, glass) and grime in crevices',
78
+ });
79
+ if (h.nearBlack > t.nearBlackMax)
80
+ out.push({
81
+ code: 'crushed-blacks',
82
+ message: `${f(h.nearBlack)} of asset pixels are near black (> ${t.nearBlackMax})`,
83
+ fix: 'lift near-black colours to ≥ 0.03 linear and check normals point outward',
84
+ });
85
+ if (h.nearWhite > t.nearWhiteMax)
86
+ out.push({
87
+ code: 'blown-whites',
88
+ message: `${f(h.nearWhite)} of asset pixels are near white (> ${t.nearWhiteMax})`,
89
+ fix: 'bring white paint down to ~0.8 linear so texture and shading stay readable',
90
+ });
91
+ return out;
92
+ }
@@ -0,0 +1,103 @@
1
+ // AI review pack: everything a vision-capable agent needs to judge an asset in one folder — hero,
2
+ // contact sheet, two detail close-ups (where the triangles are), the LOD strip, review.md (the
3
+ // checklist prompt) and review.json (model-free image metrics with threshold warnings).
4
+
5
+ import { type Asset, type CompiledAsset, compile, encodePng, type Lod } from '../core/index.ts';
6
+ import type { PaintedSheet } from '../material/index.ts';
7
+ import { type BeautyOptions, renderBeauty } from './beauty.ts';
8
+ import { lodStrip } from './lod-strip.ts';
9
+ import { exposure, maskIoU, maskStats, poolMask, silhouette, viewFrame } from './metrics.ts';
10
+ import { reviewPrompt } from './review-prompt.ts';
11
+ import { type ReviewMetrics, warningsOf } from './review-warnings.ts';
12
+ import { contactSheet, VIEWS } from './sheet.ts';
13
+
14
+ export interface ReviewOptions {
15
+ readonly sheet?: PaintedSheet;
16
+ readonly width?: number;
17
+ readonly height?: number;
18
+ readonly azDeg?: number;
19
+ readonly elDeg?: number;
20
+ readonly fovDeg?: number;
21
+ /** Path of the reference dossier (recipes/<name>/README.md) the prompt points the reviewer at. */
22
+ readonly refs?: string;
23
+ }
24
+
25
+ export interface ReviewPack {
26
+ /** File name (inside the review folder) → contents. */
27
+ readonly files: ReadonlyMap<string, Uint8Array | string>;
28
+ readonly metrics: ReviewMetrics;
29
+ }
30
+
31
+ const tris = (a: CompiledAsset): number =>
32
+ a.parts.reduce((s, p) => s + (p.mesh ? p.mesh.indices.length / 3 : 0), 0);
33
+
34
+ /** Two zoom windows centred on the densest cells of the hero's 16 × 9 detail grid, ≥ 4 cells apart. */
35
+ export function detailWindows(detail: Uint32Array): { x: number; y: number }[] {
36
+ const order = [...detail.keys()].sort((a, b) => (detail[b] ?? 0) - (detail[a] ?? 0) || a - b);
37
+ const picked: number[] = [];
38
+ for (const c of order) {
39
+ if ((detail[c] ?? 0) === 0 || picked.length === 2) break;
40
+ if (
41
+ picked.every(
42
+ (p) => Math.hypot((p % 16) - (c % 16), Math.floor(p / 16) - Math.floor(c / 16)) >= 4,
43
+ )
44
+ )
45
+ picked.push(c);
46
+ }
47
+ const clamp = (v: number): number => Math.min(5 / 6, Math.max(1 / 6, v));
48
+ return picked.map((c) => ({
49
+ x: clamp(((c % 16) + 0.5) / 16),
50
+ y: clamp((Math.floor(c / 16) + 0.5) / 9),
51
+ }));
52
+ }
53
+
54
+ export function reviewPack(asset: Asset, o: ReviewOptions = {}): ReviewPack {
55
+ const lods = ([0, 1, 2] as Lod[]).map((lod) => compile(asset, { lod }));
56
+ const lod0 = lods[0] ?? compile(asset);
57
+ const lod2 = lods[2] ?? lod0;
58
+ const base: BeautyOptions = {
59
+ ...(o.sheet ? { sheet: o.sheet } : {}),
60
+ ...(o.width ? { width: o.width } : {}),
61
+ ...(o.height ? { height: o.height } : {}),
62
+ ...(o.azDeg !== undefined ? { azDeg: o.azDeg } : {}),
63
+ ...(o.elDeg !== undefined ? { elDeg: o.elDeg } : {}),
64
+ ...(o.fovDeg !== undefined ? { fovDeg: o.fovDeg } : {}),
65
+ };
66
+ const files = new Map<string, Uint8Array | string>();
67
+ const hero = renderBeauty(lod0, base);
68
+ files.set('hero.png', encodePng(hero.width, hero.height, hero.rgba));
69
+ files.set('sheet.png', contactSheet(lod0, { far: lod2 }));
70
+ detailWindows(hero.detail).forEach((w, i) => {
71
+ const r = renderBeauty(lod0, { ...base, window: { ...w, zoom: 3 } });
72
+ files.set(`detail_${i + 1}.png`, encodePng(r.width, r.height, r.rgba));
73
+ });
74
+ // Each LOD panel at most 640 × 480 (smaller when the pack itself is rendered small).
75
+ const k = Math.min(1, (o.width ?? 1600) / 640);
76
+ const panel = {
77
+ width: Math.max(16, Math.round(640 * k)),
78
+ height: Math.max(16, Math.round(480 * k)),
79
+ };
80
+ files.set('lods.png', lodStrip(lods, { ...base, ...panel }));
81
+ const views = VIEWS.slice(0, 5).map((view) => {
82
+ const frame = viewFrame(lod0, view);
83
+ const a = silhouette(lod0, view, 256, frame);
84
+ const b = silhouette(lod2, view, 256, frame);
85
+ return {
86
+ view: view.label,
87
+ lod0: maskStats(a),
88
+ lod2: maskStats(b),
89
+ lodIoU: maskIoU(poolMask(a, 4).mask, poolMask(b, 4).mask),
90
+ lodIoUExact: maskIoU(a.mask, b.mask),
91
+ };
92
+ });
93
+ const partial = {
94
+ asset: asset.name,
95
+ triangles: lods.map(tris),
96
+ hero: exposure(hero.rgba, hero.mask, hero.width),
97
+ views,
98
+ };
99
+ const metrics: ReviewMetrics = { ...partial, warnings: warningsOf(partial) };
100
+ files.set('review.json', `${JSON.stringify(metrics, null, 2)}\n`);
101
+ files.set('review.md', reviewPrompt(metrics, [...files.keys()], o.refs));
102
+ return { files, metrics };
103
+ }