@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,175 @@
1
+ // Post-transform vertex cache: Tom Forsyth's "Linear-Speed Vertex Cache Optimisation" (2006)
2
+ // triangle reorder, the vertex-fetch reorder that follows it, and the ACMR/ATVR analysis that
3
+ // measures both. All integer bookkeeping and fixed tie-breaks (lowest triangle index wins), so
4
+ // the output is byte-deterministic.
5
+
6
+ const CACHE = 32;
7
+ const DECAY = 1.5;
8
+ const LAST_TRI = 0.75;
9
+ const VALENCE_SCALE = 2;
10
+ const VALENCE_POWER = 0.5;
11
+ const MAX_VALENCE = 64;
12
+
13
+ const CACHE_SCORE = Float64Array.from({ length: CACHE }, (_, i) =>
14
+ i < 3 ? LAST_TRI : (1 - (i - 3) / (CACHE - 3)) ** DECAY,
15
+ );
16
+ const VALENCE_SCORE = Float64Array.from({ length: MAX_VALENCE + 1 }, (_, n) =>
17
+ n === 0 ? 0 : VALENCE_SCALE * n ** -VALENCE_POWER,
18
+ );
19
+
20
+ const vertexScore = (pos: number, live: number): number =>
21
+ live === 0
22
+ ? -1
23
+ : (pos >= 0 ? (CACHE_SCORE[pos] ?? 0) : 0) + (VALENCE_SCORE[Math.min(live, MAX_VALENCE)] ?? 0);
24
+
25
+ /** Reorder triangles for an LRU cache of 32 (Forsyth). Returns a new index buffer. */
26
+ export function forsyth(indices: Uint32Array, vertexCount: number): Uint32Array {
27
+ const triCount = indices.length / 3;
28
+ if (triCount === 0) return new Uint32Array(0);
29
+ // CSR adjacency: vertex → triangles; `live[v]` counts the not-yet-emitted prefix.
30
+ const live = new Int32Array(vertexCount);
31
+ for (const v of indices) live[v] = (live[v] ?? 0) + 1;
32
+ const offset = new Int32Array(vertexCount + 1);
33
+ for (let v = 0; v < vertexCount; v++) offset[v + 1] = (offset[v] ?? 0) + (live[v] ?? 0);
34
+ const adj = new Int32Array(indices.length);
35
+ const fill = new Int32Array(vertexCount);
36
+ for (let t = 0; t < triCount; t++)
37
+ for (let k = 0; k < 3; k++) {
38
+ const v = indices[t * 3 + k] ?? 0;
39
+ adj[(offset[v] ?? 0) + (fill[v] ?? 0)] = t;
40
+ fill[v] = (fill[v] ?? 0) + 1;
41
+ }
42
+ const cachePos = new Int32Array(vertexCount).fill(-1);
43
+ const score = new Float64Array(vertexCount);
44
+ for (let v = 0; v < vertexCount; v++) score[v] = vertexScore(-1, live[v] ?? 0);
45
+ const emitted = new Uint8Array(triCount);
46
+ const triScoreOf = (t: number): number =>
47
+ (score[indices[t * 3] ?? 0] ?? 0) +
48
+ (score[indices[t * 3 + 1] ?? 0] ?? 0) +
49
+ (score[indices[t * 3 + 2] ?? 0] ?? 0);
50
+ let best = 0;
51
+ let top = triScoreOf(0);
52
+ for (let t = 1; t < triCount; t++) {
53
+ const s = triScoreOf(t);
54
+ if (s > top) {
55
+ top = s;
56
+ best = t;
57
+ }
58
+ }
59
+ const out = new Uint32Array(indices.length);
60
+ let cache = new Int32Array(CACHE + 3);
61
+ let spare = new Int32Array(CACHE + 3);
62
+ let size = 0;
63
+ let cursor = 0;
64
+ for (let n = 0; n < triCount; n++) {
65
+ if (best < 0) {
66
+ while (emitted[cursor]) cursor++;
67
+ best = cursor;
68
+ }
69
+ const t = best;
70
+ emitted[t] = 1;
71
+ const a = indices[t * 3] ?? 0;
72
+ const b = indices[t * 3 + 1] ?? 0;
73
+ const c = indices[t * 3 + 2] ?? 0;
74
+ out[n * 3] = a;
75
+ out[n * 3 + 1] = b;
76
+ out[n * 3 + 2] = c;
77
+ for (const v of [a, b, c]) {
78
+ // Remove t from v's live adjacency (swap with the last live entry).
79
+ const o = offset[v] ?? 0;
80
+ const l = live[v] ?? 0;
81
+ for (let i = o; i < o + l; i++)
82
+ if (adj[i] === t) {
83
+ adj[i] = adj[o + l - 1] ?? 0;
84
+ adj[o + l - 1] = t;
85
+ break;
86
+ }
87
+ live[v] = l - 1;
88
+ }
89
+ // New LRU: the triangle's vertices first, then the old cache minus them.
90
+ spare[0] = a;
91
+ spare[1] = b;
92
+ spare[2] = c;
93
+ let m = 3;
94
+ for (let i = 0; i < size; i++) {
95
+ const v = cache[i] ?? 0;
96
+ if (v !== a && v !== b && v !== c) spare[m++] = v;
97
+ }
98
+ for (let i = CACHE; i < m; i++) {
99
+ const v = spare[i] ?? 0;
100
+ cachePos[v] = -1;
101
+ score[v] = vertexScore(-1, live[v] ?? 0);
102
+ }
103
+ [cache, spare] = [spare, cache];
104
+ size = Math.min(m, CACHE);
105
+ for (let i = 0; i < size; i++) {
106
+ const v = cache[i] ?? 0;
107
+ cachePos[v] = i;
108
+ score[v] = vertexScore(i, live[v] ?? 0);
109
+ }
110
+ best = -1;
111
+ let bestScore = -Infinity;
112
+ for (let j = 0; j < size; j++) {
113
+ const v = cache[j] ?? 0;
114
+ const o = offset[v] ?? 0;
115
+ for (let i = o; i < o + (live[v] ?? 0); i++) {
116
+ const u = adj[i] ?? 0;
117
+ const s = triScoreOf(u);
118
+ if (s > bestScore || (s === bestScore && u < best)) {
119
+ bestScore = s;
120
+ best = u;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ return out;
126
+ }
127
+
128
+ /** Renumber vertices in order of first use; unreferenced vertices are dropped. */
129
+ export function fetchOrder(
130
+ indices: Uint32Array,
131
+ vertexCount: number,
132
+ ): { indices: Uint32Array; order: Uint32Array } {
133
+ const map = new Int32Array(vertexCount).fill(-1);
134
+ const order: number[] = [];
135
+ const out = new Uint32Array(indices.length);
136
+ for (let i = 0; i < indices.length; i++) {
137
+ const v = indices[i] ?? 0;
138
+ if ((map[v] ?? -1) < 0) {
139
+ map[v] = order.length;
140
+ order.push(v);
141
+ }
142
+ out[i] = map[v] ?? 0;
143
+ }
144
+ return { indices: out, order: Uint32Array.from(order) };
145
+ }
146
+
147
+ export interface CacheStats {
148
+ /** Average cache misses per triangle (0.5 ideal on a regular grid, 3 worst). */
149
+ readonly acmr: number;
150
+ /** Misses per unique vertex (1.0 ideal). */
151
+ readonly atvr: number;
152
+ }
153
+
154
+ /** Simulate a FIFO post-transform cache (16 entries, the meshoptimizer/NVIDIA convention). */
155
+ export function cacheStats(indices: Uint32Array, vertexCount: number, size = 16): CacheStats {
156
+ const tris = indices.length / 3;
157
+ if (tris === 0) return { acmr: 0, atvr: 0 };
158
+ const stamp = new Int32Array(vertexCount).fill(-1_000_000_000);
159
+ let misses = 0;
160
+ for (const v of indices) {
161
+ // A vertex is cached if it was loaded within the last `size` misses.
162
+ if (misses - (stamp[v] ?? 0) >= size) {
163
+ stamp[v] = misses;
164
+ misses++;
165
+ }
166
+ }
167
+ const seen = new Uint8Array(vertexCount);
168
+ let used = 0;
169
+ for (const v of indices)
170
+ if (!seen[v]) {
171
+ seen[v] = 1;
172
+ used++;
173
+ }
174
+ return { acmr: misses / tris, atvr: used ? misses / used : 0 };
175
+ }
@@ -0,0 +1,147 @@
1
+ // Vertex weld + degenerate/duplicate triangle removal: the "index" step of mesh optimisation.
2
+ // Two vertices merge when position, normal, UV and colour all quantise to the same cell, so hard
3
+ // edges (split normals) and UV seams survive. First occurrence wins; output order is the order of
4
+ // first appearance — deterministic, no hashing of floats into iteration order.
5
+
6
+ import type { Mesh } from '../core/index.ts';
7
+
8
+ export interface WeldTolerance {
9
+ /** Position grid, metres. */
10
+ readonly position: number;
11
+ readonly normal: number;
12
+ readonly uv: number;
13
+ readonly color: number;
14
+ }
15
+
16
+ export const DEFAULT_WELD: WeldTolerance = {
17
+ position: 1e-5,
18
+ normal: 1e-3,
19
+ uv: 1e-5,
20
+ // Below the GLB's unorm16 colour step: welding never changes exported colour.
21
+ color: 1 / 131072,
22
+ };
23
+
24
+ export interface Welded {
25
+ readonly mesh: Mesh;
26
+ readonly degenerate: number;
27
+ readonly duplicate: number;
28
+ }
29
+
30
+ /** The quantised attribute tuple of vertex i (12 ints). */
31
+ function cellOf(m: Mesh, i: number, t: WeldTolerance, out: Int32Array): void {
32
+ for (let k = 0; k < 3; k++) {
33
+ out[k] = Math.round((m.positions[i * 3 + k] ?? 0) / t.position);
34
+ out[3 + k] = Math.round((m.normals[i * 3 + k] ?? 0) / t.normal);
35
+ out[8 + k] = Math.round((m.colors[i * 3 + k] ?? 0) / t.color);
36
+ }
37
+ out[6] = Math.round((m.uvs[i * 2] ?? 0) / t.uv);
38
+ out[7] = Math.round((m.uvs[i * 2 + 1] ?? 0) / t.uv);
39
+ out[11] = 0;
40
+ }
41
+
42
+ const hashCell = (c: Int32Array): number => {
43
+ let h = 2166136261;
44
+ for (let k = 0; k < 11; k++) h = Math.imul(h ^ (c[k] ?? 0), 16777619);
45
+ return h >>> 0;
46
+ };
47
+
48
+ /** Map each input vertex to its welded representative (open-addressing table, linear probe). */
49
+ function weldMap(m: Mesh, t: WeldTolerance): { remap: Uint32Array; keep: number[] } {
50
+ const n = m.positions.length / 3;
51
+ const cells = new Int32Array(n * 12);
52
+ const remap = new Uint32Array(n);
53
+ const keep: number[] = [];
54
+ let cap = 16;
55
+ while (cap < n * 2) cap *= 2;
56
+ const table = new Int32Array(cap).fill(-1);
57
+ const cur = new Int32Array(12);
58
+ for (let i = 0; i < n; i++) {
59
+ cellOf(m, i, t, cur);
60
+ cells.set(cur, i * 12);
61
+ let slot = hashCell(cur) & (cap - 1);
62
+ let found = -1;
63
+ for (;;) {
64
+ const j = table[slot] ?? -1;
65
+ if (j < 0) break;
66
+ const rep = (keep[j] ?? 0) * 12;
67
+ let same = true;
68
+ for (let k = 0; k < 11 && same; k++) same = cells[rep + k] === cur[k];
69
+ if (same) {
70
+ found = j;
71
+ break;
72
+ }
73
+ slot = (slot + 1) & (cap - 1);
74
+ }
75
+ if (found < 0) {
76
+ found = keep.length;
77
+ keep.push(i);
78
+ table[slot] = found;
79
+ }
80
+ remap[i] = found;
81
+ }
82
+ return { remap, keep };
83
+ }
84
+
85
+ /** Twice the triangle's area, from welded positions. */
86
+ function area2(p: Float32Array, a: number, b: number, c: number): number {
87
+ const ax = p[a * 3] ?? 0;
88
+ const ay = p[a * 3 + 1] ?? 0;
89
+ const az = p[a * 3 + 2] ?? 0;
90
+ const ux = (p[b * 3] ?? 0) - ax;
91
+ const uy = (p[b * 3 + 1] ?? 0) - ay;
92
+ const uz = (p[b * 3 + 2] ?? 0) - az;
93
+ const vx = (p[c * 3] ?? 0) - ax;
94
+ const vy = (p[c * 3 + 1] ?? 0) - ay;
95
+ const vz = (p[c * 3 + 2] ?? 0) - az;
96
+ return Math.hypot(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
97
+ }
98
+
99
+ export function weld(m: Mesh, t: WeldTolerance = DEFAULT_WELD): Welded {
100
+ const { remap, keep } = weldMap(m, t);
101
+ const pick = (src: Float32Array, w: number): Float32Array => {
102
+ const out = new Float32Array(keep.length * w);
103
+ keep.forEach((i, j) => {
104
+ for (let k = 0; k < w; k++) out[j * w + k] = src[i * w + k] ?? 0;
105
+ });
106
+ return out;
107
+ };
108
+ const positions = pick(m.positions, 3);
109
+ const idx: number[] = [];
110
+ // Duplicate detection: canonical rotation (lowest index first) hashed to a numeric key.
111
+ const seen = new Map<number, number[]>();
112
+ let degenerate = 0;
113
+ let duplicate = 0;
114
+ for (let f = 0; f < m.indices.length; f += 3) {
115
+ const a = remap[m.indices[f] ?? 0] ?? 0;
116
+ const b = remap[m.indices[f + 1] ?? 0] ?? 0;
117
+ const c = remap[m.indices[f + 2] ?? 0] ?? 0;
118
+ // Zero area covers no pixel: dropping it cannot open a crack.
119
+ if (a === b || b === c || a === c || area2(positions, a, b, c) < 1e-12) {
120
+ degenerate++;
121
+ continue;
122
+ }
123
+ // Same triangle, same winding (any rotation) is pure overdraw; the reverse winding is a
124
+ // deliberate double-sided face and stays.
125
+ const [x, y, z] = a < b && a < c ? [a, b, c] : b < c ? [b, c, a] : [c, a, b];
126
+ const h = (Math.imul(x, 73856093) ^ Math.imul(y, 19349663) ^ Math.imul(z, 83492791)) >>> 0;
127
+ const list = seen.get(h);
128
+ if (list?.some((at) => idx[at] === x && idx[at + 1] === y && idx[at + 2] === z)) {
129
+ duplicate++;
130
+ continue;
131
+ }
132
+ if (list) list.push(idx.length);
133
+ else seen.set(h, [idx.length]);
134
+ idx.push(x, y, z);
135
+ }
136
+ return {
137
+ mesh: {
138
+ positions,
139
+ normals: pick(m.normals, 3),
140
+ uvs: pick(m.uvs, 2),
141
+ colors: pick(m.colors, 3),
142
+ indices: new Uint32Array(idx),
143
+ },
144
+ degenerate,
145
+ duplicate,
146
+ };
147
+ }
@@ -0,0 +1,85 @@
1
+ // Game-ready optimisation, run on every LOD before export, in meshoptimizer's order: weld
2
+ // duplicate vertices (index) → reorder triangles for the post-transform vertex cache (Forsyth)
3
+ // → reorder vertices for fetch locality. Same geometry, fewer vertices, fewer cache misses.
4
+ // Zero dependencies, byte-deterministic. Reports ACMR/ATVR before and after.
5
+
6
+ import type { Mesh } from '../core/index.ts';
7
+ import { cacheStats, fetchOrder, forsyth } from './optimize-cache.ts';
8
+ import { DEFAULT_WELD, type WeldTolerance, weld } from './optimize-weld.ts';
9
+
10
+ export interface OptimizeOptions {
11
+ /** Merge vertices closer than this (metres) with matching normal/uv/colour. Default 1e-5. */
12
+ readonly weldEpsilon?: number;
13
+ /** Reorder for the vertex cache (Forsyth). Default true. */
14
+ readonly vertexCache?: boolean;
15
+ }
16
+
17
+ export interface OptimizeReport {
18
+ readonly verticesBefore: number;
19
+ readonly verticesAfter: number;
20
+ readonly trianglesBefore: number;
21
+ readonly trianglesAfter: number;
22
+ /** Zero-area / collapsed triangles dropped. */
23
+ readonly degenerate: number;
24
+ /** Exact duplicate triangles (same winding) dropped. */
25
+ readonly duplicate: number;
26
+ /** Average cache miss ratio per triangle before (FIFO 16). */
27
+ readonly acmrBefore: number;
28
+ /** Average cache miss ratio per triangle after reordering (lower is better, ~0.6–0.8 is good). */
29
+ readonly acmr: number;
30
+ /** Cache misses per vertex before / after (1.0 is ideal). */
31
+ readonly atvrBefore: number;
32
+ readonly atvr: number;
33
+ }
34
+
35
+ function reorderVertices(m: Mesh, order: Uint32Array, indices: Uint32Array): Mesh {
36
+ const pick = (src: Float32Array, w: number): Float32Array => {
37
+ const out = new Float32Array(order.length * w);
38
+ for (let j = 0; j < order.length; j++)
39
+ for (let k = 0; k < w; k++) out[j * w + k] = src[(order[j] ?? 0) * w + k] ?? 0;
40
+ return out;
41
+ };
42
+ return {
43
+ positions: pick(m.positions, 3),
44
+ normals: pick(m.normals, 3),
45
+ uvs: pick(m.uvs, 2),
46
+ colors: pick(m.colors, 3),
47
+ indices,
48
+ };
49
+ }
50
+
51
+ export function optimize(
52
+ mesh: Mesh,
53
+ o: OptimizeOptions = {},
54
+ ): { mesh: Mesh; report: OptimizeReport } {
55
+ const vBefore = mesh.positions.length / 3;
56
+ const before = cacheStats(mesh.indices, vBefore);
57
+ const tol: WeldTolerance = { ...DEFAULT_WELD, position: o.weldEpsilon ?? DEFAULT_WELD.position };
58
+ const w = weld(mesh, tol);
59
+ const vWelded = w.mesh.positions.length / 3;
60
+ // Forsyth targets an LRU-32 cache; on already-good input (flat strips) it can lose a hair on
61
+ // the FIFO-16 measure. Keep whichever order measures better — never make a mesh worse.
62
+ const reordered = o.vertexCache === false ? null : forsyth(w.mesh.indices, vWelded);
63
+ const tris =
64
+ reordered && cacheStats(reordered, vWelded).acmr <= cacheStats(w.mesh.indices, vWelded).acmr
65
+ ? reordered
66
+ : w.mesh.indices;
67
+ const fetched = fetchOrder(tris, vWelded);
68
+ const out = reorderVertices(w.mesh, fetched.order, fetched.indices);
69
+ const after = cacheStats(out.indices, out.positions.length / 3);
70
+ return {
71
+ mesh: out,
72
+ report: {
73
+ verticesBefore: vBefore,
74
+ verticesAfter: out.positions.length / 3,
75
+ trianglesBefore: mesh.indices.length / 3,
76
+ trianglesAfter: out.indices.length / 3,
77
+ degenerate: w.degenerate,
78
+ duplicate: w.duplicate,
79
+ acmrBefore: before.acmr,
80
+ acmr: after.acmr,
81
+ atvrBefore: before.atvr,
82
+ atvr: after.atvr,
83
+ },
84
+ };
85
+ }
@@ -0,0 +1,237 @@
1
+ // Ray casting for bakes: triangles bucketed in a uniform 3D grid (CSR, typed arrays), walked
2
+ // with Amanatides–Woo DDA, tested with Möller–Trumbore. Answers "does this ray hit anything
3
+ // within `tMax`?" — all an occlusion bake needs. Allocation-free per ray, deterministic.
4
+
5
+ import { boundsOf, type Mesh, type Vec3 } from '../core/index.ts';
6
+
7
+ const CELL_SCALE = 0.3;
8
+
9
+ export class RayGrid {
10
+ private readonly tri: Float64Array;
11
+ /** Per triangle: v0, e1 = v1 − v0, e2 = v2 − v0 (Möller–Trumbore inputs, computed once). */
12
+ private readonly edges: Float64Array;
13
+ /** Per triangle: unnormalised plane normal e1 × e2 and its offset n·v0. */
14
+ private readonly planes: Float64Array;
15
+ private readonly start: Int32Array;
16
+ private readonly list: Int32Array;
17
+ private readonly min: Vec3;
18
+ private readonly res: [number, number, number];
19
+ private readonly cell: number;
20
+ private readonly stamp: Int32Array;
21
+ private ray = 0;
22
+
23
+ constructor(mesh: Mesh) {
24
+ const n = mesh.indices.length / 3;
25
+ this.tri = new Float64Array(n * 9);
26
+ for (let t = 0; t < n; t++)
27
+ for (let v = 0; v < 3; v++) {
28
+ const i = (mesh.indices[t * 3 + v] ?? 0) * 3;
29
+ for (let k = 0; k < 3; k++) this.tri[t * 9 + v * 3 + k] = mesh.positions[i + k] ?? 0;
30
+ }
31
+ this.edges = new Float64Array(n * 9);
32
+ for (let t = 0; t < n; t++) {
33
+ const o = t * 9;
34
+ for (let k = 0; k < 3; k++) {
35
+ const v0 = this.tri[o + k] ?? 0;
36
+ this.edges[o + k] = v0;
37
+ this.edges[o + 3 + k] = (this.tri[o + 3 + k] ?? 0) - v0;
38
+ this.edges[o + 6 + k] = (this.tri[o + 6 + k] ?? 0) - v0;
39
+ }
40
+ }
41
+ this.planes = new Float64Array(n * 4);
42
+ for (let t = 0; t < n; t++) {
43
+ const E = this.edges;
44
+ const o = t * 9;
45
+ const [ax, ay, az] = [E[o + 3] ?? 0, E[o + 4] ?? 0, E[o + 5] ?? 0];
46
+ const [bx, by, bz] = [E[o + 6] ?? 0, E[o + 7] ?? 0, E[o + 8] ?? 0];
47
+ const nx = ay * bz - az * by;
48
+ const ny = az * bx - ax * bz;
49
+ const nz = ax * by - ay * bx;
50
+ this.planes.set(
51
+ [nx, ny, nz, nx * (E[o] ?? 0) + ny * (E[o + 1] ?? 0) + nz * (E[o + 2] ?? 0)],
52
+ t * 4,
53
+ );
54
+ }
55
+ const b = boundsOf(mesh);
56
+ const span = [0, 1, 2].map((k) => Math.max(1e-6, (b.max[k] ?? 0) - (b.min[k] ?? 0)));
57
+ // Cells ~0.3× the "two triangles per cell if they filled the volume" size: triangles live
58
+ // on surfaces, so that size leaves a dozen per occupied cell. Measured on tiger_1 and a
59
+ // sculpted head (0.2–0.5 is flat; larger cells cost 2–4×). Bounded to 256 per axis and
60
+ // ~4M cells overall. Any cell size gives the same answers; only the speed changes.
61
+ const vol = (span[0] ?? 1) * (span[1] ?? 1) * (span[2] ?? 1);
62
+ this.cell = Math.max(
63
+ CELL_SCALE * Math.cbrt(vol / Math.max(1, n / 2)),
64
+ Math.max(...span) / 256,
65
+ Math.cbrt(vol / 4e6),
66
+ 1e-4,
67
+ );
68
+ this.min = [b.min[0] - 1e-6, b.min[1] - 1e-6, b.min[2] - 1e-6];
69
+ this.res = [0, 1, 2].map((k) => Math.max(1, Math.ceil((span[k] ?? 0) / this.cell) + 1)) as [
70
+ number,
71
+ number,
72
+ number,
73
+ ];
74
+ const [rx, ry] = this.res;
75
+ const cells = rx * ry * this.res[2];
76
+ // Each triangle's cell box once, then a counting pass and a fill pass (CSR, t ascending).
77
+ const box = new Int32Array(n * 6);
78
+ const T = this.tri;
79
+ for (let t = 0; t < n; t++)
80
+ for (let k = 0; k < 3; k++) {
81
+ const a = T[t * 9 + k] ?? 0;
82
+ const b2 = T[t * 9 + 3 + k] ?? 0;
83
+ const c = T[t * 9 + 6 + k] ?? 0;
84
+ box[t * 6 + k] = this.coord(Math.min(a, b2, c), k);
85
+ box[t * 6 + 3 + k] = this.coord(Math.max(a, b2, c), k);
86
+ }
87
+ const count = new Int32Array(cells + 1);
88
+ const visit = (t: number, f: (c: number) => void): void => {
89
+ const o = t * 6;
90
+ for (let x = box[o] ?? 0; x <= (box[o + 3] ?? 0); x++)
91
+ for (let y = box[o + 1] ?? 0; y <= (box[o + 4] ?? 0); y++)
92
+ for (let z = box[o + 2] ?? 0; z <= (box[o + 5] ?? 0); z++) f((z * ry + y) * rx + x);
93
+ };
94
+ const bump = (c: number): void => {
95
+ count[c + 1] = (count[c + 1] ?? 0) + 1;
96
+ };
97
+ for (let t = 0; t < n; t++) visit(t, bump);
98
+ for (let c = 0; c < cells; c++) count[c + 1] = (count[c + 1] ?? 0) + (count[c] ?? 0);
99
+ this.start = count;
100
+ const list = new Int32Array(count[cells] ?? 0);
101
+ const fill = new Int32Array(cells);
102
+ for (let t = 0; t < n; t++)
103
+ visit(t, (c) => {
104
+ list[(count[c] ?? 0) + (fill[c] ?? 0)] = t;
105
+ fill[c] = (fill[c] ?? 0) + 1;
106
+ });
107
+ this.list = list;
108
+ this.stamp = new Int32Array(n);
109
+ }
110
+
111
+ private coord(v: number, k: number): number {
112
+ const r = this.res[k as 0 | 1 | 2];
113
+ return Math.min(
114
+ r - 1,
115
+ Math.max(0, Math.floor((v - (this.min[k as 0 | 1 | 2] ?? 0)) / this.cell)),
116
+ );
117
+ }
118
+
119
+ /** Any hit of the ray o + t·d (d unit) for 0 < t ≤ tMax. Scalar arguments: no allocation. */
120
+ hit(
121
+ ox: number,
122
+ oy: number,
123
+ oz: number,
124
+ dx: number,
125
+ dy: number,
126
+ dz: number,
127
+ tMax: number,
128
+ ): boolean {
129
+ const q = ++this.ray;
130
+ const start = this.start;
131
+ const list = this.list;
132
+ const stamp = this.stamp;
133
+ const size = this.cell;
134
+ const [rx, ry, rz] = this.res;
135
+ const [mx, my, mz] = this.min;
136
+ let cx = this.coord(ox, 0);
137
+ let cy = this.coord(oy, 1);
138
+ let cz = this.coord(oz, 2);
139
+ const sx = dx > 0 ? 1 : -1;
140
+ const sy = dy > 0 ? 1 : -1;
141
+ const sz = dz > 0 ? 1 : -1;
142
+ let nx = Math.abs(dx) < 1e-12 ? Infinity : (mx + (cx + (dx > 0 ? 1 : 0)) * size - ox) / dx;
143
+ let ny = Math.abs(dy) < 1e-12 ? Infinity : (my + (cy + (dy > 0 ? 1 : 0)) * size - oy) / dy;
144
+ let nz = Math.abs(dz) < 1e-12 ? Infinity : (mz + (cz + (dz > 0 ? 1 : 0)) * size - oz) / dz;
145
+ const ddx = Math.abs(dx) < 1e-12 ? Infinity : size / Math.abs(dx);
146
+ const ddy = Math.abs(dy) < 1e-12 ? Infinity : size / Math.abs(dy);
147
+ const ddz = Math.abs(dz) < 1e-12 ? Infinity : size / Math.abs(dz);
148
+ let t = 0;
149
+ while (t <= tMax) {
150
+ const c = (cz * ry + cy) * rx + cx;
151
+ const end = start[c + 1] ?? 0;
152
+ for (let i = start[c] ?? 0; i < end; i++) {
153
+ const tri = list[i] ?? 0;
154
+ if (stamp[tri] === q) continue;
155
+ stamp[tri] = q;
156
+ if (this.test(tri, ox, oy, oz, dx, dy, dz, tMax)) return true;
157
+ }
158
+ if (nx < ny) {
159
+ if (nx < nz) {
160
+ t = nx;
161
+ cx += sx;
162
+ if (cx < 0 || cx >= rx) return false;
163
+ nx = t + ddx;
164
+ } else {
165
+ t = nz;
166
+ cz += sz;
167
+ if (cz < 0 || cz >= rz) return false;
168
+ nz = t + ddz;
169
+ }
170
+ } else if (ny < nz) {
171
+ t = ny;
172
+ cy += sy;
173
+ if (cy < 0 || cy >= ry) return false;
174
+ ny = t + ddy;
175
+ } else {
176
+ t = nz;
177
+ cz += sz;
178
+ if (cz < 0 || cz >= rz) return false;
179
+ nz = t + ddz;
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+
185
+ /** Möller–Trumbore on cached v0, e1, e2 (the same arithmetic as deriving them per ray). */
186
+ private test(
187
+ tri: number,
188
+ ox: number,
189
+ oy: number,
190
+ oz: number,
191
+ dx: number,
192
+ dy: number,
193
+ dz: number,
194
+ tMax: number,
195
+ ): boolean {
196
+ // Plane pre-reject: the hit distance along the ray, from two dot products. Only clear misses
197
+ // (behind the origin, or past tMax, with margin) are skipped; Möller–Trumbore decides the rest.
198
+ const P = this.planes;
199
+ const f = tri * 4;
200
+ const nx = P[f] ?? 0;
201
+ const ny = P[f + 1] ?? 0;
202
+ const nz = P[f + 2] ?? 0;
203
+ const th0 = ((P[f + 3] ?? 0) - (nx * ox + ny * oy + nz * oz)) / (nx * dx + ny * dy + nz * dz);
204
+ if (!(th0 > -1e-9 && th0 <= tMax * (1 + 1e-9) + 1e-9)) return false;
205
+ const E = this.edges;
206
+ const b = tri * 9;
207
+ const e1x = E[b + 3] ?? 0;
208
+ const e1y = E[b + 4] ?? 0;
209
+ const e1z = E[b + 5] ?? 0;
210
+ const e2x = E[b + 6] ?? 0;
211
+ const e2y = E[b + 7] ?? 0;
212
+ const e2z = E[b + 8] ?? 0;
213
+ const px = dy * e2z - dz * e2y;
214
+ const py = dz * e2x - dx * e2z;
215
+ const pz = dx * e2y - dy * e2x;
216
+ const det = e1x * px + e1y * py + e1z * pz;
217
+ if (Math.abs(det) < 1e-14) return false;
218
+ const inv = 1 / det;
219
+ const tx = ox - (E[b] ?? 0);
220
+ const ty = oy - (E[b + 1] ?? 0);
221
+ const tz = oz - (E[b + 2] ?? 0);
222
+ const u = (tx * px + ty * py + tz * pz) * inv;
223
+ if (u < 0 || u > 1) return false;
224
+ const qx = ty * e1z - tz * e1y;
225
+ const qy = tz * e1x - tx * e1z;
226
+ const qz = tx * e1y - ty * e1x;
227
+ const v = (dx * qx + dy * qy + dz * qz) * inv;
228
+ if (v < 0 || u + v > 1) return false;
229
+ const th = (e2x * qx + e2y * qy + e2z * qz) * inv;
230
+ return th > 0 && th <= tMax;
231
+ }
232
+
233
+ /** True if the ray o + t·d (d unit) hits any triangle for 0 < t ≤ tMax. */
234
+ occluded(o: Vec3, d: Vec3, tMax: number): boolean {
235
+ return this.hit(o[0], o[1], o[2], d[0], d[1], d[2], tMax);
236
+ }
237
+ }