@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,119 @@
1
+ // Flatten a compiled asset into one packed, world-space triangle soup for the beauty renderer:
2
+ // positions, normals, UVs, colours per vertex; surface code + tangent frame per triangle.
3
+ // Packed typed arrays keep the per-pixel loops monomorphic and allocation-free.
4
+
5
+ import { type CompiledAsset, type Surface, worldMeshes } from '../core/index.ts';
6
+
7
+ export const SURFACE_CODE: Readonly<Record<Surface, number>> = {
8
+ opaque: 0,
9
+ cutout: 1,
10
+ glass: 2,
11
+ translucent: 3,
12
+ };
13
+
14
+ export interface Scene {
15
+ readonly positions: Float32Array;
16
+ readonly normals: Float32Array;
17
+ readonly uvs: Float32Array;
18
+ readonly colors: Float32Array;
19
+ /** Three global vertex indices per triangle. */
20
+ readonly indices: Uint32Array;
21
+ /** SURFACE_CODE per triangle. */
22
+ readonly surface: Uint8Array;
23
+ /** Per-triangle tangent frame for the normal map, 6 floats: T = dP/dU, B = −dP/dV (glTF +Y). */
24
+ readonly tangents: Float32Array;
25
+ readonly min: readonly [number, number, number];
26
+ readonly max: readonly [number, number, number];
27
+ readonly triangles: number;
28
+ }
29
+
30
+ export function sceneOf(asset: CompiledAsset): Scene {
31
+ const meshes = worldMeshes(asset.parts);
32
+ const surfOf = new Map(asset.parts.map((p) => [p.name, SURFACE_CODE[p.surface ?? 'opaque']]));
33
+ let nv = 0;
34
+ let nt = 0;
35
+ for (const m of meshes.values()) {
36
+ nv += m.positions.length / 3;
37
+ nt += m.indices.length / 3;
38
+ }
39
+ const positions = new Float32Array(nv * 3);
40
+ const normals = new Float32Array(nv * 3);
41
+ const uvs = new Float32Array(nv * 2);
42
+ const colors = new Float32Array(nv * 3);
43
+ const indices = new Uint32Array(nt * 3);
44
+ const surface = new Uint8Array(nt);
45
+ let vo = 0;
46
+ let to = 0;
47
+ for (const [name, m] of meshes) {
48
+ positions.set(m.positions, vo * 3);
49
+ normals.set(m.normals, vo * 3);
50
+ uvs.set(m.uvs, vo * 2);
51
+ colors.set(m.colors, vo * 3);
52
+ for (let i = 0; i < m.indices.length; i++) indices[to * 3 + i] = (m.indices[i] ?? 0) + vo;
53
+ surface.fill(surfOf.get(name) ?? 0, to, to + m.indices.length / 3);
54
+ vo += m.positions.length / 3;
55
+ to += m.indices.length / 3;
56
+ }
57
+ const min: [number, number, number] = [Infinity, Infinity, Infinity];
58
+ const max: [number, number, number] = [-Infinity, -Infinity, -Infinity];
59
+ for (let i = 0; i < positions.length; i++) {
60
+ const v = positions[i] ?? 0;
61
+ const k = i % 3;
62
+ if (v < (min[k] ?? 0)) min[k] = v;
63
+ if (v > (max[k] ?? 0)) max[k] = v;
64
+ }
65
+ if (nv === 0) {
66
+ min.fill(-0.5);
67
+ max.fill(0.5);
68
+ }
69
+ return {
70
+ positions,
71
+ normals,
72
+ uvs,
73
+ colors,
74
+ indices,
75
+ surface,
76
+ tangents: tangentsOf(positions, uvs, indices),
77
+ min,
78
+ max,
79
+ triangles: nt,
80
+ };
81
+ }
82
+
83
+ /** Per-triangle dP/dU and −dP/dV from the UV parameterisation; zero frame when UVs degenerate. */
84
+ function tangentsOf(p: Float32Array, uv: Float32Array, idx: Uint32Array): Float32Array {
85
+ const out = new Float32Array(idx.length * 2);
86
+ for (let t = 0; t < idx.length; t += 3) {
87
+ const a = idx[t] ?? 0;
88
+ const b = idx[t + 1] ?? 0;
89
+ const c = idx[t + 2] ?? 0;
90
+ const e1x = (p[b * 3] ?? 0) - (p[a * 3] ?? 0);
91
+ const e1y = (p[b * 3 + 1] ?? 0) - (p[a * 3 + 1] ?? 0);
92
+ const e1z = (p[b * 3 + 2] ?? 0) - (p[a * 3 + 2] ?? 0);
93
+ const e2x = (p[c * 3] ?? 0) - (p[a * 3] ?? 0);
94
+ const e2y = (p[c * 3 + 1] ?? 0) - (p[a * 3 + 1] ?? 0);
95
+ const e2z = (p[c * 3 + 2] ?? 0) - (p[a * 3 + 2] ?? 0);
96
+ const du1 = (uv[b * 2] ?? 0) - (uv[a * 2] ?? 0);
97
+ const dv1 = (uv[b * 2 + 1] ?? 0) - (uv[a * 2 + 1] ?? 0);
98
+ const du2 = (uv[c * 2] ?? 0) - (uv[a * 2] ?? 0);
99
+ const dv2 = (uv[c * 2 + 1] ?? 0) - (uv[a * 2 + 1] ?? 0);
100
+ const det = du1 * dv2 - du2 * dv1;
101
+ if (Math.abs(det) < 1e-12) continue;
102
+ const tx = (e1x * dv2 - e2x * dv1) / det;
103
+ const ty = (e1y * dv2 - e2y * dv1) / det;
104
+ const tz = (e1z * dv2 - e2z * dv1) / det;
105
+ const bx = (e2x * du1 - e1x * du2) / det;
106
+ const by = (e2y * du1 - e1y * du2) / det;
107
+ const bz = (e2z * du1 - e1z * du2) / det;
108
+ const lt = Math.hypot(tx, ty, tz) || 1;
109
+ const lb = -(Math.hypot(bx, by, bz) || 1);
110
+ const o = t * 2;
111
+ out[o] = tx / lt;
112
+ out[o + 1] = ty / lt;
113
+ out[o + 2] = tz / lt;
114
+ out[o + 3] = bx / lb;
115
+ out[o + 4] = by / lb;
116
+ out[o + 5] = bz / lb;
117
+ }
118
+ return out;
119
+ }
@@ -0,0 +1,204 @@
1
+ // Per-pixel shading of the visibility buffer: reconstruct position, normal, UV and vertex colour
2
+ // from (triangle, barycentrics), perturb by the trim sheet's normal map, then light it — key sun
3
+ // with a PCF shadow, sky/bounce hemisphere × multi-direction AO × the sheet's baked occlusion,
4
+ // a roughness-driven sun specular (normalised Blinn–Phong + Schlick Fresnel) and a studio
5
+ // environment reflection (envRadiance) per texel from the sheet's ORM, so metal reads as metal.
6
+
7
+ import type { PerspectiveCamera } from './camera.ts';
8
+ import { type DepthMap, openness, visibility } from './depth-map.ts';
9
+ import { envRadiance, type Rig } from './lighting.ts';
10
+ import type { Scene } from './scene.ts';
11
+ import { type SheetTextures, sample } from './texture.ts';
12
+
13
+ export interface ShadeContext {
14
+ readonly scene: Scene;
15
+ readonly cam: PerspectiveCamera;
16
+ readonly tex: SheetTextures;
17
+ readonly rig: Rig;
18
+ readonly sunMap: DepthMap;
19
+ readonly aoMaps: readonly DepthMap[];
20
+ readonly pcf: readonly Float32Array[];
21
+ /** AO falloff distance, metres: occluders farther than this along a sky direction don't count. */
22
+ readonly reach: number;
23
+ }
24
+
25
+ const ONE_TAP = new Float32Array([0, 0]);
26
+ const hyp = (x: number, y: number, z: number): number => Math.sqrt(x * x + y * y + z * z);
27
+ const texel = new Float32Array(4);
28
+
29
+ /** Sky openness at p for a surface facing n: cosine-weighted share of unoccluded sky directions. */
30
+ export function skyOpen(c: ShadeContext, p: readonly number[], n: readonly number[]): number {
31
+ let open = 0;
32
+ let total = 0;
33
+ const dirs = c.rig.skyDirs;
34
+ for (let k = 0; k < dirs.length; k++) {
35
+ const d = dirs[k];
36
+ const m = c.aoMaps[k];
37
+ if (!d || !m) continue;
38
+ const w = 0.5 + 0.5 * ((n[0] ?? 0) * d[0] + (n[1] ?? 0) * d[1] + (n[2] ?? 0) * d[2]);
39
+ if (w <= 0) continue;
40
+ const off = 1.5 / m.k;
41
+ const v = openness(
42
+ m,
43
+ (p[0] ?? 0) + (n[0] ?? 0) * off,
44
+ (p[1] ?? 0) + (n[1] ?? 0) * off,
45
+ (p[2] ?? 0) + (n[2] ?? 0) * off,
46
+ 2 / m.k,
47
+ c.reach,
48
+ );
49
+ open += w * v;
50
+ total += w;
51
+ }
52
+ return total > 0 ? open / total : 1;
53
+ }
54
+
55
+ /** Sun visibility at p (geometric normal n), PCF with one of four rotated tap sets. */
56
+ export function sunVisible(
57
+ c: ShadeContext,
58
+ p: readonly number[],
59
+ n: readonly number[],
60
+ set: number,
61
+ ): number {
62
+ const m = c.sunMap;
63
+ const off = 1.2 / m.k;
64
+ return visibility(
65
+ m,
66
+ (p[0] ?? 0) + (n[0] ?? 0) * off,
67
+ (p[1] ?? 0) + (n[1] ?? 0) * off,
68
+ (p[2] ?? 0) + (n[2] ?? 0) * off,
69
+ 1.5 / m.k,
70
+ c.pcf[set & 3] ?? ONE_TAP,
71
+ );
72
+ }
73
+
74
+ const pos = [0, 0, 0];
75
+ const nrm = [0, 0, 0];
76
+ const geo = [0, 0, 0];
77
+
78
+ /** Linear radiance of triangle `t` at barycentrics (b1, b2) into out[0..2]. */
79
+ export function shadeSample(
80
+ c: ShadeContext,
81
+ t: number,
82
+ b1: number,
83
+ b2: number,
84
+ mip: number,
85
+ set: number,
86
+ out: Float32Array,
87
+ ): void {
88
+ const s = c.scene;
89
+ const ia = (s.indices[t * 3] ?? 0) * 3;
90
+ const ib = (s.indices[t * 3 + 1] ?? 0) * 3;
91
+ const ic = (s.indices[t * 3 + 2] ?? 0) * 3;
92
+ const b0 = 1 - b1 - b2;
93
+ const P = s.positions;
94
+ const N = s.normals;
95
+ const C = s.colors;
96
+ for (let k = 0; k < 3; k++) {
97
+ pos[k] = (P[ia + k] ?? 0) * b0 + (P[ib + k] ?? 0) * b1 + (P[ic + k] ?? 0) * b2;
98
+ nrm[k] = (N[ia + k] ?? 0) * b0 + (N[ib + k] ?? 0) * b1 + (N[ic + k] ?? 0) * b2;
99
+ }
100
+ const ua = (ia / 3) * 2;
101
+ const ub = (ib / 3) * 2;
102
+ const uc = (ic / 3) * 2;
103
+ const U = s.uvs;
104
+ const u = (U[ua] ?? 0) * b0 + (U[ub] ?? 0) * b1 + (U[uc] ?? 0) * b2;
105
+ const v = (U[ua + 1] ?? 0) * b0 + (U[ub + 1] ?? 0) * b1 + (U[uc + 1] ?? 0) * b2;
106
+ const e = c.cam.eye;
107
+ let vx = e[0] - (pos[0] ?? 0);
108
+ let vy = e[1] - (pos[1] ?? 0);
109
+ let vz = e[2] - (pos[2] ?? 0);
110
+ const vl = hyp(vx, vy, vz) || 1;
111
+ vx /= vl;
112
+ vy /= vl;
113
+ vz /= vl;
114
+ let nl = hyp(nrm[0] ?? 0, nrm[1] ?? 0, nrm[2] ?? 1) || 1;
115
+ // Double-sided view: a normal facing away from the eye is lit as its back face.
116
+ if ((nrm[0] ?? 0) * vx + (nrm[1] ?? 0) * vy + (nrm[2] ?? 0) * vz < 0) nl = -nl;
117
+ for (let k = 0; k < 3; k++) {
118
+ nrm[k] = (nrm[k] ?? 0) / nl;
119
+ geo[k] = nrm[k] ?? 0;
120
+ }
121
+ // Normal map through the triangle's tangent frame (skipped where UVs are degenerate).
122
+ const T = s.tangents;
123
+ const to = t * 6;
124
+ const tx = T[to] ?? 0;
125
+ const ty = T[to + 1] ?? 0;
126
+ const tz = T[to + 2] ?? 0;
127
+ if (tx !== 0 || ty !== 0 || tz !== 0) {
128
+ sample(c.tex.normal, mip, u, v, texel);
129
+ const mx = texel[0] ?? 0;
130
+ const my = texel[1] ?? 0;
131
+ const mz = texel[2] ?? 1;
132
+ const n0 = nrm[0] ?? 0;
133
+ const n1 = nrm[1] ?? 0;
134
+ const n2 = nrm[2] ?? 0;
135
+ const bx = T[to + 3] ?? 0;
136
+ const by = T[to + 4] ?? 0;
137
+ const bz = T[to + 5] ?? 0;
138
+ const px = tx * mx + bx * my + n0 * mz;
139
+ const py = ty * mx + by * my + n1 * mz;
140
+ const pz = tz * mx + bz * my + n2 * mz;
141
+ const pl = hyp(px, py, pz) || 1;
142
+ nrm[0] = px / pl;
143
+ nrm[1] = py / pl;
144
+ nrm[2] = pz / pl;
145
+ }
146
+ sample(c.tex.albedo, mip, u, v, texel);
147
+ const surf = s.surface[t] ?? 0;
148
+ const glass = surf === 2;
149
+ let ar = (texel[0] ?? 1) * ((C[ia] ?? 1) * b0 + (C[ib] ?? 1) * b1 + (C[ic] ?? 1) * b2);
150
+ let ag =
151
+ (texel[1] ?? 1) * ((C[ia + 1] ?? 1) * b0 + (C[ib + 1] ?? 1) * b1 + (C[ic + 1] ?? 1) * b2);
152
+ let ab =
153
+ (texel[2] ?? 1) * ((C[ia + 2] ?? 1) * b0 + (C[ib + 2] ?? 1) * b1 + (C[ic + 2] ?? 1) * b2);
154
+ sample(c.tex.orm, mip, u, v, texel);
155
+ const occ = texel[0] ?? 1;
156
+ const rough = glass ? 0.06 : Math.max(0.08, texel[1] ?? 0.8);
157
+ const metal = glass ? 0 : (texel[2] ?? 0);
158
+ if (glass) {
159
+ ar *= 0.12;
160
+ ag *= 0.14;
161
+ ab *= 0.16;
162
+ }
163
+ const r = c.rig;
164
+ const n0 = nrm[0] ?? 0;
165
+ const n1 = nrm[1] ?? 0;
166
+ const n2 = nrm[2] ?? 1;
167
+ const ndl = n0 * r.sun[0] + n1 * r.sun[1] + n2 * r.sun[2];
168
+ const gdl = (geo[0] ?? 0) * r.sun[0] + (geo[1] ?? 0) * r.sun[1] + (geo[2] ?? 0) * r.sun[2];
169
+ const lit = ndl > 0 && gdl > -0.05 ? sunVisible(c, pos, geo, set) * ndl : 0;
170
+ const open = skyOpen(c, pos, geo);
171
+ const hemi = 0.5 + 0.5 * n2;
172
+ const amb = open * (0.35 + 0.65 * occ);
173
+ const dk = 1 - metal;
174
+ // Specular: half vector, normalised Blinn–Phong lobe, Schlick Fresnel against F0.
175
+ let hx = r.sun[0] + vx;
176
+ let hy = r.sun[1] + vy;
177
+ let hz = r.sun[2] + vz;
178
+ const hl = hyp(hx, hy, hz) || 1;
179
+ hx /= hl;
180
+ hy /= hl;
181
+ hz /= hl;
182
+ const ndh = Math.max(0, n0 * hx + n1 * hy + n2 * hz);
183
+ const vdh = Math.max(0, vx * hx + vy * hy + vz * hz);
184
+ const ndv = Math.max(0, n0 * vx + n1 * vy + n2 * vz);
185
+ const shin = Math.min(2048, 2 / (rough * rough * rough * rough) - 2);
186
+ const lobe = ((shin + 8) / 25.13) * ndh ** shin;
187
+ const fr = (1 - vdh) ** 5;
188
+ const fv = (1 - ndv) ** 5;
189
+ // Image-based term: the studio seen along the reflection, occluded like the sky (+ cavity AO).
190
+ const rz = 2 * ndv * n2 - vz;
191
+ const specOcc = open * (0.4 + 0.6 * occ);
192
+ const ch = [ar, ag, ab];
193
+ for (let k = 0; k < 3; k++) {
194
+ const al = ch[k] ?? 0;
195
+ const f0 = 0.04 * dk + al * metal;
196
+ const sky = (r.bounce[k] ?? 0) * (1 - hemi) + (r.sky[k] ?? 0) * hemi;
197
+ const diffuse = al * dk * ((r.sunColor[k] ?? 0) * lit + sky * amb);
198
+ const spec = (f0 + (1 - f0) * fr) * lobe * lit * (r.sunColor[k] ?? 0) * 0.8;
199
+ const env = glass
200
+ ? (f0 + (1 - f0) * fv * (1 - rough) * (1 - rough)) * (r.sky[k] ?? 0) * open * 2.5
201
+ : (f0 + (Math.max(1 - rough, f0) - f0) * fv) * envRadiance(rz, rough, k) * specOcc;
202
+ out[k] = (diffuse + spec + env) * r.exposure;
203
+ }
204
+ }
@@ -0,0 +1,88 @@
1
+ // Contact sheet: ONE PNG, six labelled orthographic views on a metre grid. Built for an agent's
2
+ // eyes — one image read shows proportions from every side, so it fixes the right thing first.
3
+ // row 1: 3/4 FRONT · FRONT (+X) · SIDE (-Y)
4
+ // row 2: TOP · 3/4 REAR · FAR LOD (LOD2, 3/4 front)
5
+
6
+ import { type CompiledAsset, encodePng, type Vec3 } from '../core/index.ts';
7
+ import { drawText } from './font.ts';
8
+ import { drawAsset, line, orthoCamera, type Target, type View, worldParts } from './raster.ts';
9
+
10
+ export const VIEWS: readonly View[] = [
11
+ { label: '3/4 FRONT', dir: [-1, 0.8, -0.6], up: [0, 0, 1] },
12
+ { label: 'FRONT +X', dir: [-1, 0, 0], up: [0, 0, 1] },
13
+ { label: 'SIDE -Y', dir: [0, 1, 0], up: [0, 0, 1] },
14
+ { label: 'TOP', dir: [0, 0, -1], up: [1, 0, 0] },
15
+ { label: '3/4 REAR', dir: [1, -0.8, -0.6], up: [0, 0, 1] },
16
+ { label: 'FAR LOD2', dir: [-1, 0.8, -0.6], up: [0, 0, 1] },
17
+ ];
18
+
19
+ export function bounds(asset: CompiledAsset): {
20
+ centre: Vec3;
21
+ extent: number;
22
+ min: Vec3;
23
+ max: Vec3;
24
+ } {
25
+ const min = [Infinity, Infinity, Infinity];
26
+ const max = [-Infinity, -Infinity, -Infinity];
27
+ for (const m of worldParts(asset)) {
28
+ for (let i = 0; i < m.positions.length; i += 3) {
29
+ for (let k = 0; k < 3; k++) {
30
+ const v = m.positions[i + k] ?? 0;
31
+ min[k] = Math.min(min[k] ?? 0, v);
32
+ max[k] = Math.max(max[k] ?? 0, v);
33
+ }
34
+ }
35
+ }
36
+ const lo: Vec3 = [min[0] ?? 0, min[1] ?? 0, min[2] ?? 0];
37
+ const hi: Vec3 = [max[0] ?? 0, max[1] ?? 0, max[2] ?? 0];
38
+ const extent = Math.max(hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2], 0.1);
39
+ return {
40
+ centre: [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2],
41
+ extent,
42
+ min: lo,
43
+ max: hi,
44
+ };
45
+ }
46
+
47
+ const gridStep = (extent: number): number =>
48
+ [0.1, 0.5, 1, 5, 10, 50, 100].find((s) => extent / s <= 24) ?? 100;
49
+
50
+ export interface SheetOptions {
51
+ readonly cell?: number;
52
+ /** The far view's asset — compile the same recipe at LOD 2 and pass it here. */
53
+ readonly far?: CompiledAsset;
54
+ }
55
+
56
+ export function contactSheet(asset: CompiledAsset, o: SheetOptions = {}): Uint8Array {
57
+ const cell = o.cell ?? 400;
58
+ const width = cell * 3;
59
+ const height = cell * 2 + 40;
60
+ const px = new Uint8Array(width * height * 4).fill(255);
61
+ const b = bounds(asset);
62
+ const step = gridStep(b.extent);
63
+ VIEWS.forEach((view, i) => {
64
+ const t: Target = {
65
+ px: new Uint8Array(cell * cell * 4),
66
+ depth: new Float32Array(cell * cell).fill(Infinity),
67
+ width: cell,
68
+ height: cell,
69
+ };
70
+ for (let k = 0; k < cell * cell; k++) t.px.set([238, 240, 243, 255], k * 4);
71
+ const cam = orthoCamera(view, b.centre, b.extent, cell);
72
+ drawAsset(t, cam, i === 5 && o.far ? o.far : asset);
73
+ const g0 = Math.floor(Math.min(b.min[0], b.min[1]) / step - 1) * step;
74
+ const g1 = Math.ceil(Math.max(b.max[0], b.max[1]) / step + 1) * step;
75
+ for (let g = g0; g <= g1 + 1e-9; g += step) {
76
+ line(t, cam.project([g, g0, 0]), cam.project([g, g1, 0]), [190, 196, 204]);
77
+ line(t, cam.project([g0, g, 0]), cam.project([g1, g, 0]), [190, 196, 204]);
78
+ }
79
+ const ox = (i % 3) * cell;
80
+ const oy = 40 + Math.floor(i / 3) * cell;
81
+ for (let y = 0; y < cell; y++)
82
+ px.set(t.px.subarray(y * cell * 4, (y + 1) * cell * 4), ((oy + y) * width + ox) * 4);
83
+ drawText(px, width, ox + 8, oy + 8, view.label, 3, [40, 44, 52]);
84
+ });
85
+ const dims = `${asset.name} ${(b.max[0] - b.min[0]).toFixed(1)}X${(b.max[1] - b.min[1]).toFixed(1)}X${(b.max[2] - b.min[2]).toFixed(1)}M GRID ${step}M LOD${asset.lod}`;
86
+ drawText(px, width, 10, 10, dims, 4, [20, 22, 26]);
87
+ return encodePng(width, height, px);
88
+ }
@@ -0,0 +1,97 @@
1
+ // The trim sheet as the beauty renderer samples it: albedo (linear, alpha), tangent-space normal
2
+ // and ORM, each as a box-filtered mip chain, sampled bilinearly — U repeats (strips tile), V clamps
3
+ // (rows never bleed). The mip level is chosen per triangle by the caller (texel ÷ pixel footprint).
4
+
5
+ import type { PaintedSheet } from '../material/index.ts';
6
+
7
+ export interface Mip {
8
+ readonly size: number;
9
+ /** 4 floats per texel. */
10
+ readonly data: Float32Array;
11
+ }
12
+
13
+ export interface SheetTextures {
14
+ readonly size: number;
15
+ readonly albedo: readonly Mip[];
16
+ readonly normal: readonly Mip[];
17
+ readonly orm: readonly Mip[];
18
+ }
19
+
20
+ const SRGB = new Float32Array(256).map((_, i) => {
21
+ const v = i / 255;
22
+ return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
23
+ });
24
+
25
+ function chain(src: Uint8Array, size: number, decode: (c: number, v: number) => number): Mip[] {
26
+ const top = new Float32Array(size * size * 4);
27
+ for (let i = 0; i < top.length; i++) top[i] = decode(i & 3, src[i] ?? 0);
28
+ const out: Mip[] = [{ size, data: top }];
29
+ let cur = out[0];
30
+ while (cur && cur.size > 4) {
31
+ const s = cur.size / 2;
32
+ const d = new Float32Array(s * s * 4);
33
+ const p = cur.data;
34
+ const w = cur.size;
35
+ for (let y = 0; y < s; y++)
36
+ for (let x = 0; x < s; x++)
37
+ for (let c = 0; c < 4; c++) {
38
+ const a = ((2 * y * w + 2 * x) << 2) + c;
39
+ const b = a + w * 4;
40
+ d[((y * s + x) << 2) + c] =
41
+ ((p[a] ?? 0) + (p[a + 4] ?? 0) + (p[b] ?? 0) + (p[b + 4] ?? 0)) * 0.25;
42
+ }
43
+ cur = { size: s, data: d };
44
+ out.push(cur);
45
+ }
46
+ return out;
47
+ }
48
+
49
+ const cache = new WeakMap<PaintedSheet, SheetTextures>();
50
+
51
+ export function sheetTextures(sheet: PaintedSheet): SheetTextures {
52
+ const hit = cache.get(sheet);
53
+ if (hit) return hit;
54
+ const t: SheetTextures = {
55
+ size: sheet.size,
56
+ albedo: chain(sheet.albedo, sheet.size, (c, v) => (c === 3 ? v / 255 : (SRGB[v] ?? 0))),
57
+ normal: chain(sheet.normal, sheet.size, (c, v) => (c === 3 ? 1 : v / 127.5 - 1)),
58
+ orm: chain(sheet.orm, sheet.size, (_c, v) => v / 255),
59
+ };
60
+ cache.set(sheet, t);
61
+ return t;
62
+ }
63
+
64
+ /** Bilinear sample of mip `level` at (u, v) into out[0..3]. */
65
+ export function sample(
66
+ mips: readonly Mip[],
67
+ level: number,
68
+ u: number,
69
+ v: number,
70
+ out: Float32Array,
71
+ ): void {
72
+ const m = mips[Math.min(mips.length - 1, Math.max(0, level))] ?? mips[0];
73
+ if (!m) return;
74
+ const s = m.size;
75
+ const x = u * s - 0.5;
76
+ const y = Math.min(s - 1, Math.max(0, v * s - 0.5));
77
+ const xf = Math.floor(x);
78
+ const yf = Math.floor(y);
79
+ const fx = x - xf;
80
+ const fy = y - yf;
81
+ const x0 = ((xf % s) + s) % s;
82
+ const x1 = (x0 + 1) % s;
83
+ const y0 = yf;
84
+ const y1 = Math.min(s - 1, yf + 1);
85
+ const d = m.data;
86
+ const a = (y0 * s + x0) << 2;
87
+ const b = (y0 * s + x1) << 2;
88
+ const c = (y1 * s + x0) << 2;
89
+ const e = (y1 * s + x1) << 2;
90
+ const w0 = (1 - fx) * (1 - fy);
91
+ const w1 = fx * (1 - fy);
92
+ const w2 = (1 - fx) * fy;
93
+ const w3 = fx * fy;
94
+ for (let k = 0; k < 4; k++)
95
+ out[k] =
96
+ (d[a + k] ?? 0) * w0 + (d[b + k] ?? 0) * w1 + (d[c + k] ?? 0) * w2 + (d[e + k] ?? 0) * w3;
97
+ }
@@ -0,0 +1,75 @@
1
+ // Thumbnail: ONE hero view (3/4 front, slightly from above) on a transparent background, for
2
+ // gallery cards and asset browsers. Rendered at 3×, cropped to the asset, filtered down: smooth edges.
3
+ // Same zero-GPU rasteriser as the contact sheet — deterministic bytes, runs in CI.
4
+
5
+ import { type CompiledAsset, encodePng } from '../core/index.ts';
6
+ import { drawAsset, orthoCamera, type Target, type View } from './raster.ts';
7
+ import { bounds } from './sheet.ts';
8
+
9
+ export interface ThumbnailOptions {
10
+ /** Output edge in pixels (square). Default 512. */
11
+ readonly size?: number;
12
+ readonly view?: View;
13
+ }
14
+
15
+ const HERO: View = { label: 'HERO', dir: [-1, 0.75, -0.45], up: [0, 0, 1] };
16
+
17
+ export function thumbnail(asset: CompiledAsset, o: ThumbnailOptions = {}): Uint8Array {
18
+ const size = o.size ?? 512;
19
+ const big = size * 3;
20
+ const t: Target = {
21
+ px: new Uint8Array(big * big * 4),
22
+ depth: new Float32Array(big * big).fill(Infinity),
23
+ width: big,
24
+ height: big,
25
+ };
26
+ const b = bounds(asset);
27
+ drawAsset(t, orthoCamera(o.view ?? HERO, b.centre, b.extent * 0.92, big), asset);
28
+ // Crop to the drawn pixels (square, 6% margin) so every card is filled the same way.
29
+ let x0 = big;
30
+ let y0 = big;
31
+ let x1 = -1;
32
+ let y1 = -1;
33
+ for (let y = 0; y < big; y++)
34
+ for (let x = 0; x < big; x++)
35
+ if ((t.px[(y * big + x) * 4 + 3] ?? 0) > 0) {
36
+ x0 = Math.min(x0, x);
37
+ y0 = Math.min(y0, y);
38
+ x1 = Math.max(x1, x);
39
+ y1 = Math.max(y1, y);
40
+ }
41
+ const side = x1 < 0 ? big : Math.max(x1 - x0, y1 - y0) * 1.12 + 1;
42
+ const ox = x1 < 0 ? 0 : (x0 + x1) / 2 - side / 2;
43
+ const oy = x1 < 0 ? 0 : (y0 + y1) / 2 - side / 2;
44
+ const step = side / size;
45
+ // Supersampled box filter over each output pixel's footprint; alpha-weighted colour so
46
+ // silhouettes stay clean against any page background.
47
+ const out = new Uint8Array(size * size * 4);
48
+ const taps = [0.25, 0.75];
49
+ for (let y = 0; y < size; y++) {
50
+ for (let x = 0; x < size; x++) {
51
+ let r = 0;
52
+ let g = 0;
53
+ let bl = 0;
54
+ let a = 0;
55
+ for (const fy of taps)
56
+ for (const fx of taps) {
57
+ const sx = Math.floor(ox + (x + fx) * step);
58
+ const sy = Math.floor(oy + (y + fy) * step);
59
+ if (sx < 0 || sy < 0 || sx >= big || sy >= big) continue;
60
+ const i = (sy * big + sx) * 4;
61
+ const w = t.px[i + 3] ?? 0;
62
+ r += (t.px[i] ?? 0) * w;
63
+ g += (t.px[i + 1] ?? 0) * w;
64
+ bl += (t.px[i + 2] ?? 0) * w;
65
+ a += w;
66
+ }
67
+ if (a > 0)
68
+ out.set(
69
+ [Math.round(r / a), Math.round(g / a), Math.round(bl / a), Math.round(a / 4)],
70
+ (y * size + x) * 4,
71
+ );
72
+ }
73
+ }
74
+ return encodePng(size, size, out);
75
+ }