@volter/game-editor 0.5.65

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 (653) hide show
  1. package/BUNDLED_NOTICES +11228 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-BLENDER +1409 -0
  4. package/LICENSE-MCP +21 -0
  5. package/NOTICE +21 -0
  6. package/catalog/entries/audio.json +14 -0
  7. package/catalog/entries/bake.json +36 -0
  8. package/catalog/entries/blender.json +17 -0
  9. package/catalog/entries/react-root.json +24 -0
  10. package/catalog/entries/reflections.json +29 -0
  11. package/catalog/entries/static-batch.json +25 -0
  12. package/catalog/project-source/LICENSE +37 -0
  13. package/catalog/project-source/src/contributions/bake.kind.ts +40 -0
  14. package/catalog/project-source/src/contributions/builder-document.tsx +65 -0
  15. package/catalog/project-source/src/contributions/builder-state.ts +27 -0
  16. package/catalog/project-source/src/contributions/builder-ui.tsx +61 -0
  17. package/catalog/project-source/src/contributions/module-source.ts +502 -0
  18. package/catalog/project-source/src/contributions/pages.finder.ts +61 -0
  19. package/catalog/project-source/src/lib/audio/sfx.ts +255 -0
  20. package/catalog/project-source/src/lib/bake/asset-seed.ts +128 -0
  21. package/catalog/project-source/src/lib/bake/bake-schema.ts +65 -0
  22. package/catalog/project-source/src/lib/bake/clip-layering.ts +203 -0
  23. package/catalog/project-source/src/lib/bake/gltf-bake.ts +657 -0
  24. package/catalog/project-source/src/lib/bake/mesh-slice.ts +157 -0
  25. package/catalog/project-source/src/lib/bake/node-image.ts +463 -0
  26. package/catalog/project-source/src/lib/bake/object3d-bake.ts +121 -0
  27. package/catalog/project-source/src/lib/bake/object3d-lifecycle.ts +15 -0
  28. package/catalog/project-source/src/lib/bake/object3d-source.ts +8 -0
  29. package/catalog/project-source/src/lib/bake/params.ts +75 -0
  30. package/catalog/project-source/src/lib/bake/rest-relative-animation.ts +217 -0
  31. package/catalog/project-source/src/lib/bake/use-model.ts +95 -0
  32. package/catalog/project-source/src/lib/react-root/index.ts +20 -0
  33. package/catalog/project-source/src/lib/react-root/react-root-adapter.tsx +57 -0
  34. package/catalog/project-source/src/lib/reflections/ReflectionProbe.tsx +201 -0
  35. package/catalog/project-source/src/lib/reflections/index.ts +19 -0
  36. package/catalog/project-source/src/lib/reflections/probe-controller.ts +75 -0
  37. package/catalog/project-source/src/lib/reflections/reflection-probe-registry.ts +402 -0
  38. package/catalog/project-source/src/lib/reflections/volume-reflection-material.ts +416 -0
  39. package/catalog/project-source/src/lib/static-batch/Frozen.tsx +91 -0
  40. package/catalog/project-source/src/lib/static-batch/freeze.ts +579 -0
  41. package/catalog/project-source/src/lib/static-batch/index.ts +44 -0
  42. package/catalog/project-source/src/lib/static-batch/mutation-watch.ts +149 -0
  43. package/catalog/project-source/src/lib/static-batch/shadow-cache.ts +171 -0
  44. package/catalog/project-source/src/tools/bake-module.tool.ts +139 -0
  45. package/catalog/project-source/src/tools/bake-preview.tool.ts +475 -0
  46. package/dist/.vite/manifest.json +8304 -0
  47. package/dist/assets/AssetBudgetPanel-J9--TZPU.js +23 -0
  48. package/dist/assets/AudioViewer-CR7333fe.js +1 -0
  49. package/dist/assets/BVHLoader-6bQIXhMX.js +1 -0
  50. package/dist/assets/BufferGeometryUtils-Nz37EVXf.js +1 -0
  51. package/dist/assets/CCDIKSolver-CD7vLpsK.js +1 -0
  52. package/dist/assets/CSS3DRenderer-CL7ABLVK.js +1 -0
  53. package/dist/assets/CanvasSceneViewport-Bpi6gVTC.js +1 -0
  54. package/dist/assets/ColladaLoader-aVOPIsCh.js +3 -0
  55. package/dist/assets/ColorInput-DQBNrW-K.js +1 -0
  56. package/dist/assets/ConvexGeometry-CAzXlPVG.js +1 -0
  57. package/dist/assets/ConvexHull-BINCESFp.js +1 -0
  58. package/dist/assets/CopyShader-BzTUYzf6.js +24 -0
  59. package/dist/assets/DecalGeometry-160KRdfg.js +1 -0
  60. package/dist/assets/EXRLoader-Cdans_oJ.js +1 -0
  61. package/dist/assets/EffectComposer-DQxfsXBZ.js +1 -0
  62. package/dist/assets/EntityModelDocument-Ccvw66N9.js +1 -0
  63. package/dist/assets/EnvironmentAssetDocument-Dg-Qq6O6.js +1 -0
  64. package/dist/assets/FBXLoader-B4MoUtAn.js +1 -0
  65. package/dist/assets/Fbo-Dz-Soul3.js +1 -0
  66. package/dist/assets/FontLoader-CvGoVI5a.js +2 -0
  67. package/dist/assets/GLTFExporter-HgrCle4L.js +1 -0
  68. package/dist/assets/GameplaySessionTimeline-B52W3FVZ.js +1 -0
  69. package/dist/assets/Gltf-UHVWKz65.js +2 -0
  70. package/dist/assets/HDRLoader-THU88oVD.js +5 -0
  71. package/dist/assets/ImageViewer-Tn7s_MnW.js +1 -0
  72. package/dist/assets/InspectorCanvasPreview-CEr_Kxna.js +1 -0
  73. package/dist/assets/InspectorObjectPreview-CVejvLRg.js +1 -0
  74. package/dist/assets/JsonAssetDocument-C7VGcLhu.js +3 -0
  75. package/dist/assets/LineSegments2-CsOpKSiM.js +370 -0
  76. package/dist/assets/MTLLoader-_Xzd4_0p.js +2 -0
  77. package/dist/assets/MarchingCubes-Bsp4wrzd.js +1 -0
  78. package/dist/assets/MeshBVH-BiDHDcu-.js +1 -0
  79. package/dist/assets/ModelAssetDocument-F5GACirk.js +1 -0
  80. package/dist/assets/NonThreeAuthoringBootstrap-C3woOMxX.js +1 -0
  81. package/dist/assets/OBJLoader-BhIC6Hwo.js +5 -0
  82. package/dist/assets/Object3DDocumentViewport-DB7T0NTl.js +2 -0
  83. package/dist/assets/Object3DPreview-DL8oi7Xs.js +2 -0
  84. package/dist/assets/OnlineAssetDetail-C6z9RvYK.js +2 -0
  85. package/dist/assets/OrbitControls-CM3bPray.js +1 -0
  86. package/dist/assets/OrbitControls-DLpayAaK.js +1 -0
  87. package/dist/assets/OrthographicCamera-bQozawrh.js +1 -0
  88. package/dist/assets/PLYLoader-DYQqs5Ik.js +2 -0
  89. package/dist/assets/PerspectiveCamera-CQ2lh01o.js +1 -0
  90. package/dist/assets/PlayBar-BR-Vg3Jb.js +2 -0
  91. package/dist/assets/RGBELoader-BgBcQv8j.js +1 -0
  92. package/dist/assets/RectAreaLightUniformsLib-CMMoWfll.js +1 -0
  93. package/dist/assets/RenderPass-DPrn-Uky.js +1 -0
  94. package/dist/assets/ResolutionPicker-C67vs1F3.js +1 -0
  95. package/dist/assets/RootSelectionOverlay-BZPlxmXU.js +1 -0
  96. package/dist/assets/RoundedBox-BDGBg6QZ.js +1 -0
  97. package/dist/assets/RoundedBoxGeometry-CyUuYPfW.js +1 -0
  98. package/dist/assets/STLLoader-Dyv3lsxY.js +1 -0
  99. package/dist/assets/SelectionBox-D_75KrNW.js +1 -0
  100. package/dist/assets/ShaderPass-QYRpPuFJ.js +1 -0
  101. package/dist/assets/ShadowEditor-DkImwu1Y.js +1 -0
  102. package/dist/assets/SimplifyModifier-C8ayvRzd.js +1 -0
  103. package/dist/assets/SkeletonUtils-Cps8GOC6.js +1 -0
  104. package/dist/assets/Sky-CQKCgYDs.js +154 -0
  105. package/dist/assets/SourceAssetViewer-2SWQoUut.js +12 -0
  106. package/dist/assets/StageHost-BN-IoTef.js +2 -0
  107. package/dist/assets/StoryPreviewMount-DjyxRCXu.js +1 -0
  108. package/dist/assets/TDSLoader-D_T8XJ81.js +1 -0
  109. package/dist/assets/TextGeometry-DOWAEUV0.js +1 -0
  110. package/dist/assets/ThreeBoardDocument-CVncZyIq.js +2 -0
  111. package/dist/assets/ThreeIsolationSceneContent-C8C8whSF.js +1 -0
  112. package/dist/assets/ToolObject3DPreview-BRoDWe4Q.js +1 -0
  113. package/dist/assets/TrackballControls-DfznuQTh.js +1 -0
  114. package/dist/assets/TransientHint-BB1fwrvK.js +1 -0
  115. package/dist/assets/UnrealBloomPass-CR-cS_K5.js +83 -0
  116. package/dist/assets/VideoViewer-C6kocgIU.js +1 -0
  117. package/dist/assets/_commonjsHelpers-D6-XlEtG.js +1 -0
  118. package/dist/assets/active-ingest-CT6jSGhr.js +3 -0
  119. package/dist/assets/adapter-surface-Y37Nzcyw.js +1 -0
  120. package/dist/assets/after-paint-CB4bz1-e.js +1 -0
  121. package/dist/assets/agx-base-srgb-BjWZJgrZ.lut +0 -0
  122. package/dist/assets/agx-look-medium-high-contrast-D5F_ob1L.lut +0 -0
  123. package/dist/assets/agx-look-punchy-dxsnycNs.lut +0 -0
  124. package/dist/assets/animation-clock-CnjZqfJp.js +1 -0
  125. package/dist/assets/asset-budget-asset.menu-Di3KiQCd.js +1 -0
  126. package/dist/assets/asset-budget.action-DCzzcuTh.js +1 -0
  127. package/dist/assets/asset-budget.document-BWU8wnVW.js +1 -0
  128. package/dist/assets/asset-budget.menu-BGZpT_31.js +1 -0
  129. package/dist/assets/asset-editor-persistence-BBaONOJt.js +1 -0
  130. package/dist/assets/asset-loaders-BOy5Ss8k.js +2 -0
  131. package/dist/assets/asset-parse-error-BHMwBRU3.js +3 -0
  132. package/dist/assets/assets-BuSoGjBX.js +1 -0
  133. package/dist/assets/audio-meter-CyiToeUW.js +1 -0
  134. package/dist/assets/audio-unlock.service-CfUgUQVy.js +1 -0
  135. package/dist/assets/audio.utility-C6K6jem8.js +1 -0
  136. package/dist/assets/authoring-CgcOFhGS.js +1 -0
  137. package/dist/assets/autoplay.service-BZBGNSvt.js +1 -0
  138. package/dist/assets/axe-LkjAB6Lv.js +30 -0
  139. package/dist/assets/babylon-authoring-adapter-CL02gWDn.js +1 -0
  140. package/dist/assets/binding-Bmxqznjk.js +1 -0
  141. package/dist/assets/binding-identity-DcTpbvyg.js +1 -0
  142. package/dist/assets/binding-resolver-Dsh-6Vor.js +2 -0
  143. package/dist/assets/blender-emscripten-engine-9ZIRYNhe.js +2 -0
  144. package/dist/assets/blender-node-editor.document-iIDkB9sI.js +1 -0
  145. package/dist/assets/blender-properties-tab-C5CyPydi.js +3 -0
  146. package/dist/assets/blender-runtime-host-BpBwGv7r.js +4 -0
  147. package/dist/assets/blender-runtime-skin-DyQ9B6CR.js +7140 -0
  148. package/dist/assets/blender-runtime.document-lIYhhPtv.js +8 -0
  149. package/dist/assets/blender-timeline.document-77fx96N7.js +1 -0
  150. package/dist/assets/blender-uv-editor.document-fsT7B4gV.js +1 -0
  151. package/dist/assets/blender-version.status-o6dBm3n6.js +1 -0
  152. package/dist/assets/blender-wali-engine-wTOcrOMC.js +2 -0
  153. package/dist/assets/blender.command-BtJeWk-A.js +1 -0
  154. package/dist/assets/blender.keymap-GAQgeklK.js +1 -0
  155. package/dist/assets/blender.status-ujwGU6dU.js +1 -0
  156. package/dist/assets/blender.style-Dcem5f_T.js +1 -0
  157. package/dist/assets/board-guides-DziKSTss.js +1 -0
  158. package/dist/assets/body-marks-h_xDxiDE.js +1 -0
  159. package/dist/assets/bridge.command-o5gDNEwa.js +3 -0
  160. package/dist/assets/browserAll-DeWblVez.js +1 -0
  161. package/dist/assets/build-profiles.document-kW8iw8Xj.js +2 -0
  162. package/dist/assets/build-progress.status-BNRLytg2.js +1 -0
  163. package/dist/assets/build-session-C_sg3CEn.js +9 -0
  164. package/dist/assets/build.action-lYjTLb7K.js +1 -0
  165. package/dist/assets/build.command-DIGgUPU_.js +1 -0
  166. package/dist/assets/build.header-CKajx5U3.js +1 -0
  167. package/dist/assets/build.menu-BdU0QXny.js +1 -0
  168. package/dist/assets/build.service-CbBAyeu3.js +1 -0
  169. package/dist/assets/bus-mixer-CGfWDP3C.js +1 -0
  170. package/dist/assets/camera-KEsKvOL7.js +1 -0
  171. package/dist/assets/camera-runtime.inspector-B7unX4g1.js +1 -0
  172. package/dist/assets/canvas-entry-runtime-SfBxaR9M.js +2 -0
  173. package/dist/assets/capability-coverage-Czas4C18.js +7 -0
  174. package/dist/assets/chrome-trace-YZHMz60-.js +1 -0
  175. package/dist/assets/client-DNXj_opT.js +25 -0
  176. package/dist/assets/clip-planes-CnAI6DZn.js +1 -0
  177. package/dist/assets/collaboration-presence-DLoZ7EA3.js +1 -0
  178. package/dist/assets/collider-Bw0mXlsW.js +1 -0
  179. package/dist/assets/collider-dimensions-9xoFADXN.js +1 -0
  180. package/dist/assets/collision-system-DpNUJRJ3.js +1 -0
  181. package/dist/assets/command-results-CUQD49AB.js +1 -0
  182. package/dist/assets/component-board-registry-DrIa2csL.js +1 -0
  183. package/dist/assets/component-board.service-DCwcUssB.js +2 -0
  184. package/dist/assets/component-board.service-DidCyM4O.js +2 -0
  185. package/dist/assets/component-verbs.command-7xzfjGMZ.js +1 -0
  186. package/dist/assets/component-verbs.service-Bv9jId3q.js +1 -0
  187. package/dist/assets/config-CEZk921y.js +6 -0
  188. package/dist/assets/configurations-DTBMdaTL.js +1 -0
  189. package/dist/assets/connection.status-BifnQdOs.js +1 -0
  190. package/dist/assets/console-bridge-CHcb-xk5.js +1 -0
  191. package/dist/assets/constraint-De8f1Uiq.js +1 -0
  192. package/dist/assets/constraints.inspector-B9U9Yj-u.js +1 -0
  193. package/dist/assets/countdown-timer-CUm74dom.js +1 -0
  194. package/dist/assets/coverage.service-VTX5Mcsx.js +3 -0
  195. package/dist/assets/crash-null-boundary-v3a0uEgw.js +1 -0
  196. package/dist/assets/create-runtime-CVCc1BDx.js +1 -0
  197. package/dist/assets/curve-7Q79VgU4.js +1 -0
  198. package/dist/assets/data-asset-4hjS642k.js +5 -0
  199. package/dist/assets/data-check-core-CQEjuFyt.js +1 -0
  200. package/dist/assets/data-ref-CQI28C0D.js +1 -0
  201. package/dist/assets/debug-bridge-CCohG-d-.js +1 -0
  202. package/dist/assets/debug-registry-Dgvitx8M.js +1 -0
  203. package/dist/assets/defaults-DKiBJCPN.js +1 -0
  204. package/dist/assets/deferred-commands-CEMmvL47.js +1 -0
  205. package/dist/assets/deferred-ingest-play-DCJi_Pgu.js +2 -0
  206. package/dist/assets/design-time-layers-wYgOTgni.js +1 -0
  207. package/dist/assets/design-time-mount-registry-DUpXCg1U.js +1 -0
  208. package/dist/assets/design-time-mount.service-BzY21e1F.js +2 -0
  209. package/dist/assets/design-time-react-mount-DM9WLrF2.js +1 -0
  210. package/dist/assets/dev-build-gDYrq68M.js +1 -0
  211. package/dist/assets/directional-shadow-fit-BVkOKAv2.js +1 -0
  212. package/dist/assets/document-preview-three-Kbg2GCLm.js +1 -0
  213. package/dist/assets/dom-authoring-adapter-CY8C7f4T.js +1 -0
  214. package/dist/assets/draco3d-BBUTcNkz.js +1 -0
  215. package/dist/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  216. package/dist/assets/draco_encoder-2Tcju8Ns.wasm +0 -0
  217. package/dist/assets/edit-mode-audio.service-Bzetwd5B.js +1 -0
  218. package/dist/assets/edit-mode-authoring-B47TfLqu.js +1 -0
  219. package/dist/assets/edit-mode-networking.service-YCX6RRb_.js +1 -0
  220. package/dist/assets/editor-api-BoJIcyZG.js +1 -0
  221. package/dist/assets/editor-port-C_FeBdeV.js +1 -0
  222. package/dist/assets/editor-presentation-activity-C7SNSmTd.js +1 -0
  223. package/dist/assets/entry-adjudication-Cq8Yeb74.js +1 -0
  224. package/dist/assets/environment-capture-7himRh2L.js +1 -0
  225. package/dist/assets/esbuild-BHljloGq.wasm +0 -0
  226. package/dist/assets/events-156d8d12.esm-ErOhDXXB.js +36 -0
  227. package/dist/assets/extend-B-eOccUm.js +36 -0
  228. package/dist/assets/extends-CF3RwP-h.js +1 -0
  229. package/dist/assets/eyedropper-session-bjPXnfeX.js +1 -0
  230. package/dist/assets/fflate.module-DPuTF8aG.js +6 -0
  231. package/dist/assets/filmic-srgb-Ct35FIH6.lut +0 -0
  232. package/dist/assets/first-party-audio-system-DxnGJOER.js +1 -0
  233. package/dist/assets/first-party-navigation-system-CL5Bquub.js +1 -0
  234. package/dist/assets/fork-menu-BoVyw2Wu.js +1 -0
  235. package/dist/assets/frame-debugger-store-D1Mf_5up.js +1 -0
  236. package/dist/assets/frame-delta-D2nsw_Ht.js +1 -0
  237. package/dist/assets/frame-pacing-D5acoOzO.js +1 -0
  238. package/dist/assets/frame-selector-cache-cCeUbcjG.js +1 -0
  239. package/dist/assets/game-DHOZVb-7.js +1 -0
  240. package/dist/assets/game-contract-seams-a5YXejOu.js +1 -0
  241. package/dist/assets/game-css-scope-transform-Eng4wS8E.js +33 -0
  242. package/dist/assets/game-document.service-DbRfx2V0.js +1 -0
  243. package/dist/assets/game-eval.command-D-7Dxbj_.js +2 -0
  244. package/dist/assets/game-input-seams-CIQdetRw.js +1 -0
  245. package/dist/assets/game-loop-CZvXlY00.js +1 -0
  246. package/dist/assets/game-module-access-B2mFxv-i.js +1 -0
  247. package/dist/assets/game.layout-CBASrvQu.js +1 -0
  248. package/dist/assets/gameplay-export-BoKXm51x.js +1 -0
  249. package/dist/assets/gameplay-replay-e5umMlXv.js +1 -0
  250. package/dist/assets/gameplay-rng-trap-CTNhm-UW.js +3 -0
  251. package/dist/assets/gameplay.command-DxcVYX6F.js +1 -0
  252. package/dist/assets/generation-DhwCUHPf.css +1 -0
  253. package/dist/assets/generation-jobs-BQrXbGp_.js +1 -0
  254. package/dist/assets/generation.service-CDIgObuw.js +1 -0
  255. package/dist/assets/generations-DzUogZsM.js +1 -0
  256. package/dist/assets/generations.status-CgOUBCum.js +1 -0
  257. package/dist/assets/hierarchy-marks-BLPfSIym.js +1 -0
  258. package/dist/assets/host-pointer-DbGSCjE9.js +1 -0
  259. package/dist/assets/hosted-asset-materialization-CiIvWkGU.js +2 -0
  260. package/dist/assets/ibl-override-material-BlmCxFKq.js +6 -0
  261. package/dist/assets/index-96FBF1Oj.js +1 -0
  262. package/dist/assets/index-BAWkB0dv.js +1 -0
  263. package/dist/assets/index-BDnS2b6S.js +1 -0
  264. package/dist/assets/index-BHp07vN0.js +2 -0
  265. package/dist/assets/index-BMxv2MNV.js +1 -0
  266. package/dist/assets/index-BP6wv2A0.js +5 -0
  267. package/dist/assets/index-BcCBqgM_.js +952 -0
  268. package/dist/assets/index-Bge9JUr6.js +1 -0
  269. package/dist/assets/index-BjTCPEQ7.js +134 -0
  270. package/dist/assets/index-BncK9THk.js +1 -0
  271. package/dist/assets/index-CJbYy-CF.js +3 -0
  272. package/dist/assets/index-CVCb_ImQ.js +1 -0
  273. package/dist/assets/index-CZd9o7Lu.js +1 -0
  274. package/dist/assets/index-CanA16PU.js +2 -0
  275. package/dist/assets/index-CinJrH5C.js +9 -0
  276. package/dist/assets/index-CtzuWnCS.js +1 -0
  277. package/dist/assets/index-D2fNXpHa.css +1 -0
  278. package/dist/assets/index-DUpHH6JR.js +3120 -0
  279. package/dist/assets/index-DUqP1RMl.js +1 -0
  280. package/dist/assets/index-D_NuPfjr.js +9 -0
  281. package/dist/assets/index-Da9JhEvW.js +25 -0
  282. package/dist/assets/index-DgapqSPi.js +7 -0
  283. package/dist/assets/index-FUuS4qwW.js +562 -0
  284. package/dist/assets/index-GIL7oGEO.js +4894 -0
  285. package/dist/assets/index-IEjydI9d.js +1 -0
  286. package/dist/assets/index-OrYXPk3Z.js +487 -0
  287. package/dist/assets/index-V1R-e5Bd.js +1 -0
  288. package/dist/assets/index-bQE2NA0D.js +1 -0
  289. package/dist/assets/index-gHZae-lw.js +13 -0
  290. package/dist/assets/index-mjWQmGhM.js +1 -0
  291. package/dist/assets/index-nmcBqr7Y.js +1 -0
  292. package/dist/assets/inference-diagnostics-Dhmtfbq_.js +1 -0
  293. package/dist/assets/ingest-BbYMC2vQ.js +1 -0
  294. package/dist/assets/ingest-boot-viewport-D5rI_Kk_.js +1 -0
  295. package/dist/assets/ingest-siblings-Bop2kqVl.js +1 -0
  296. package/dist/assets/ingest-source-persistence-C_6Hiyra.js +1 -0
  297. package/dist/assets/ingest.service-BTDnQfM1.js +1 -0
  298. package/dist/assets/input-manager-DH_hGW6d.js +1 -0
  299. package/dist/assets/input-router-BNQvtIqw.js +1 -0
  300. package/dist/assets/inspect-BZfi3mmo.js +1 -0
  301. package/dist/assets/inspection-CObLhnrs.js +1 -0
  302. package/dist/assets/instance-fork-actions-D3sRzS3K.js +1 -0
  303. package/dist/assets/instances.command-D6UkZNO8.js +1 -0
  304. package/dist/assets/instruments-CKR02Y8I.js +1 -0
  305. package/dist/assets/json-history-resource-BbxllWfV.js +1 -0
  306. package/dist/assets/jsx-runtime-DPjLkJKQ.js +9 -0
  307. package/dist/assets/light-D-_wsKTq.js +1 -0
  308. package/dist/assets/light-camera-factory-CWbbe9gI.js +1 -0
  309. package/dist/assets/lil-gui.module.min-BF55BEwA.js +10 -0
  310. package/dist/assets/live-behaviors-Bxq49Maz.js +1 -0
  311. package/dist/assets/live-gesture-lock-DJbrjOqf.js +1 -0
  312. package/dist/assets/loader-CaF_I7Yt.js +1 -0
  313. package/dist/assets/logger-Dqis-7W1.js +1 -0
  314. package/dist/assets/loop-gate-report-N52EWFGD.js +1 -0
  315. package/dist/assets/matcap-texture-CVfalpWT.js +1 -0
  316. package/dist/assets/material-CpAdjDxn.js +1 -0
  317. package/dist/assets/material-factory-Bz6h__6g.js +1 -0
  318. package/dist/assets/mesh-B3WPtNKO.js +1 -0
  319. package/dist/assets/mesh-shadow-CZnXrw_l.js +1 -0
  320. package/dist/assets/mikktspace.module-DO88SLib.js +1 -0
  321. package/dist/assets/model-asset-sections-CFvvihfx.css +1 -0
  322. package/dist/assets/model-asset-sections.service-CHhFS3ZX.js +1 -0
  323. package/dist/assets/model.layout-BJUEQYUI.js +1 -0
  324. package/dist/assets/models.finder-G2jQgIRu.js +1 -0
  325. package/dist/assets/module-mode-DVBLT9d-.js +2 -0
  326. package/dist/assets/mount-canvas-ingest-root-DDuAWf_C.js +2 -0
  327. package/dist/assets/mount-dom-ingest-root-D_EiBgpn.js +2 -0
  328. package/dist/assets/mount-game-6t9cKNo4.js +1 -0
  329. package/dist/assets/mount-ingest-root-glv2SSWf.js +2 -0
  330. package/dist/assets/mount-manifest-CGVxhmAw.js +1 -0
  331. package/dist/assets/mount-readiness-Ds0HvvLH.js +4 -0
  332. package/dist/assets/mount-three-ingest-root-hrUt9VVU.js +1 -0
  333. package/dist/assets/mounted-authoring-Dmg5xmhP.js +1 -0
  334. package/dist/assets/mounted-story-viewport-source-DjP6RA9U.js +1 -0
  335. package/dist/assets/native-debug-module-CWicoSGD.js +1 -0
  336. package/dist/assets/native-entry-surface-BE8LJNdu.js +1 -0
  337. package/dist/assets/navigation-CIiCx-SM.js +1 -0
  338. package/dist/assets/navmesh-workflow-store-Cmnm57TB.js +1 -0
  339. package/dist/assets/navmesh.menu-CAKiH8PW.js +1 -0
  340. package/dist/assets/navmesh.service-jgDWlgj1.js +1 -0
  341. package/dist/assets/network.utility-CEu5nHf7.js +1 -0
  342. package/dist/assets/object-marks-DwvIEBOK.js +1 -0
  343. package/dist/assets/object3d-authoring-subject-CH5sI2oP.js +1 -0
  344. package/dist/assets/output-DK8waum9.js +195 -0
  345. package/dist/assets/particles-Y626p22p.js +1 -0
  346. package/dist/assets/particles-factory-DnLOHXT8.js +1 -0
  347. package/dist/assets/pasteboard-materialize-BqSS4QzV.js +20 -0
  348. package/dist/assets/pasteboard.action-DU3UHtJG.js +2 -0
  349. package/dist/assets/performance-profiler-D_Jey87L.js +1 -0
  350. package/dist/assets/physics-registry-B2Jkp8e6.js +1 -0
  351. package/dist/assets/physics-registry-CUB9cT9p.js +1 -0
  352. package/dist/assets/pixi-authoring-adapter-CjibysC3.js +1 -0
  353. package/dist/assets/pixi-live-write-target-DuWydlWb.js +1 -0
  354. package/dist/assets/pixi-primitive-DlWbUPN9.js +1 -0
  355. package/dist/assets/pixi-react-root-factory-DYsznYRQ.js +1 -0
  356. package/dist/assets/pixi-story-model-DNkfnWUV.js +1 -0
  357. package/dist/assets/pixi-transform-channels-VjdohT11.js +1 -0
  358. package/dist/assets/play-mode-BFU2PCkG.js +22 -0
  359. package/dist/assets/play.command-BpqqLqkq.js +2 -0
  360. package/dist/assets/playtest-BXIL1JEF.js +1 -0
  361. package/dist/assets/pose-guard-6Osb2cFu.js +1 -0
  362. package/dist/assets/presentation-BPP5pXeQ.js +1 -0
  363. package/dist/assets/product-DFhyARJj.js +1 -0
  364. package/dist/assets/profiler.action-CaXJDp3t.js +1 -0
  365. package/dist/assets/profiler.menu-BIMF6Bi4.js +1 -0
  366. package/dist/assets/profiler.utility-BExw9wBO.js +1 -0
  367. package/dist/assets/project-authoring-session-Bnt6icLr.js +2 -0
  368. package/dist/assets/project-provenance-BoP3nrle.js +1 -0
  369. package/dist/assets/prompt-labels-DSMdM2_B.js +1 -0
  370. package/dist/assets/properties-bone-constraints.inspector-ByT6p7S-.js +1 -0
  371. package/dist/assets/properties-bone.inspector-z4qJwKO5.js +1 -0
  372. package/dist/assets/properties-collection.inspector-D0_3ijWU.js +1 -0
  373. package/dist/assets/properties-constraints.inspector-CESzjuls.js +1 -0
  374. package/dist/assets/properties-data.inspector-hG8Wgyzb.js +1 -0
  375. package/dist/assets/properties-material.inspector-CzY0JCQ6.js +1 -0
  376. package/dist/assets/properties-modifiers.inspector-3dDhwf7g.js +1 -0
  377. package/dist/assets/properties-object.inspector-CiarfxkT.js +1 -0
  378. package/dist/assets/properties-output.inspector-DKJxmKSD.js +1 -0
  379. package/dist/assets/properties-particles.inspector-CCEdYWGm.js +1 -0
  380. package/dist/assets/properties-physics.inspector-B2c0KUyA.js +1 -0
  381. package/dist/assets/properties-render.inspector-Bcm0HLRW.js +1 -0
  382. package/dist/assets/properties-scene.inspector-PIM_eTb_.js +1 -0
  383. package/dist/assets/properties-texture.inspector-CgFlnyr8.js +1 -0
  384. package/dist/assets/properties-view-layer.inspector-DGjF_Aqo.js +1 -0
  385. package/dist/assets/properties-world.inspector-CrMvaRvB.js +1 -0
  386. package/dist/assets/r3f-design-session-D-4yyDO9.js +2 -0
  387. package/dist/assets/r3f-physics-binding-BBo8oma9.js +1 -0
  388. package/dist/assets/r3f-root-factory-z9llD1OU.js +1 -0
  389. package/dist/assets/r3f-source-authoring-adapter-w6Bc60K-.js +13 -0
  390. package/dist/assets/rapier-physics-adapter-CaZqWLu0.js +1 -0
  391. package/dist/assets/rapier-physics-bridge-CMoeVo3Y.js +1 -0
  392. package/dist/assets/react-18-By2b4xR9.js +1 -0
  393. package/dist/assets/react-inspector.service-B_neRnSK.js +1 -0
  394. package/dist/assets/react-root-CEz5NTck.js +1 -0
  395. package/dist/assets/react-story-board-BDDwiE2H.js +1 -0
  396. package/dist/assets/react-three-fiber.esm-CycrbDlA.js +1 -0
  397. package/dist/assets/react-three-rapier.esm-CEfKpvxT.js +1 -0
  398. package/dist/assets/react-world-authoring-adapter-DmrwM3py.js +1 -0
  399. package/dist/assets/realm-services-CobQsq_X.js +1 -0
  400. package/dist/assets/rebind-controller-Dtq19Vb3.js +1 -0
  401. package/dist/assets/recast-navigation.wasm-compat-Dsoa_MD-.js +1 -0
  402. package/dist/assets/reflection-probe-Don-M3Fj.js +1 -0
  403. package/dist/assets/reflection-probe-capture.inspector-CmFUoJCx.js +1 -0
  404. package/dist/assets/register-render-vitals-BiZk29b2.js +1 -0
  405. package/dist/assets/registry-02YZjMNp.js +1 -0
  406. package/dist/assets/relative-path-guard-GiM7Lsz8.js +1 -0
  407. package/dist/assets/render-audio-control-vMRlePWb.js +1 -0
  408. package/dist/assets/render-batch-system-0vq7p0VC.js +1 -0
  409. package/dist/assets/render-census-BrQX_CXv.js +1 -0
  410. package/dist/assets/render-control-Df3yTiyr.js +1 -0
  411. package/dist/assets/render-env-D2IV2x5k.js +1 -0
  412. package/dist/assets/render-features-CPQ_e568.js +1 -0
  413. package/dist/assets/render-seed-DF4JHy7P.js +1 -0
  414. package/dist/assets/render-settings-Bw4W9SYF.js +1 -0
  415. package/dist/assets/render-vitals-BZ5HtvMi.js +1 -0
  416. package/dist/assets/renderer-config-BqG4O8i9.js +1 -0
  417. package/dist/assets/resolve-three-SZE_WWUM.js +702 -0
  418. package/dist/assets/root-seam-contract-Z0VWX3Bk.js +1 -0
  419. package/dist/assets/rrweb-Cd2ifIRP.js +142 -0
  420. package/dist/assets/run-ticks-settled-C4ksViyA.js +1 -0
  421. package/dist/assets/runtime-inspection-l2iFQSsg.js +1 -0
  422. package/dist/assets/scene-capture-BQKzU0NT.js +1 -0
  423. package/dist/assets/scene-capture-CGYF-Viw.js +1 -0
  424. package/dist/assets/scene-document.service-qkDjUrlP.js +2 -0
  425. package/dist/assets/scene-framing-Cat0GtyJ.js +1 -0
  426. package/dist/assets/scene-index-B6BZOHMk.js +1 -0
  427. package/dist/assets/scene-query-C7_N-TpI.js +1 -0
  428. package/dist/assets/schema-kl9-2B_J.js +1 -0
  429. package/dist/assets/sculpt.layout-zJF55yCR.js +1 -0
  430. package/dist/assets/seeded-random-i3mtgBKZ.js +1 -0
  431. package/dist/assets/session-CZ5JPg-z.js +4877 -0
  432. package/dist/assets/setup-audio-S8GQF91f.js +1 -0
  433. package/dist/assets/setup-particles-GCXlnkj_.js +1 -0
  434. package/dist/assets/setup-physics-BeZ7-LKD.js +1 -0
  435. package/dist/assets/setup-renderer-DVXaSGin.js +1587 -0
  436. package/dist/assets/shading.layout-DULYZV6M.js +1 -0
  437. package/dist/assets/sim-clock-CLflx7kg.js +1 -0
  438. package/dist/assets/sky-precompute-worker-ChN7J87v.js +1 -0
  439. package/dist/assets/soft-particle-depth-DGvfQDi1.js +1 -0
  440. package/dist/assets/source-object3d-authoring-adapter-B-L6Ha6f.js +1 -0
  441. package/dist/assets/source-persistence-backend-C-aR-B_Y.js +1 -0
  442. package/dist/assets/spark-renderer-lifecycle-ynWESJcv.js +1 -0
  443. package/dist/assets/spark.module-4pXQoz5d.js +12120 -0
  444. package/dist/assets/stage-invalidation-DBg3C3Ta.js +1 -0
  445. package/dist/assets/stage-keyboard-n-5kKH-m.js +1 -0
  446. package/dist/assets/stage-overlay-set-B9zp2sLB.js +1 -0
  447. package/dist/assets/stage-presence-markers-DiGI6A45.js +1 -0
  448. package/dist/assets/standard-viewport-dressing-CzjAe_0A.js +7 -0
  449. package/dist/assets/state-bridge-DLP1jLRW.js +1 -0
  450. package/dist/assets/state-watch.action-BOvQR2qt.js +1 -0
  451. package/dist/assets/state-watch.menu-CNLvDeeY.js +1 -0
  452. package/dist/assets/state-watch.utility-Bg1tTTQU.js +1 -0
  453. package/dist/assets/static-batch-advisor-B13QEH7d.js +1 -0
  454. package/dist/assets/stats.module-BL02l9NC.js +1 -0
  455. package/dist/assets/story-arg-descriptors-Ek86ExX7.js +1 -0
  456. package/dist/assets/story-capture-_gC8dXV-.js +1 -0
  457. package/dist/assets/story-documents--TDLaPeL.js +3 -0
  458. package/dist/assets/story-documents.service-BiS_YbNQ.js +1 -0
  459. package/dist/assets/story-documents.service-CxOPYJ2j.js +1 -0
  460. package/dist/assets/story-media-presence-BHzk2IV5.js +1 -0
  461. package/dist/assets/story-mount-turn-DFRTj5hi.js +1 -0
  462. package/dist/assets/story-pixi-preview-BUrOJZXS.js +1 -0
  463. package/dist/assets/story-presentation-j2AVN-mK.js +1 -0
  464. package/dist/assets/story-three-preview-BHEDZzcl.js +2 -0
  465. package/dist/assets/structural-ids-Dc-7rEwi.js +1 -0
  466. package/dist/assets/structural-signature-0w-i_ats.js +1 -0
  467. package/dist/assets/surface-content-Dg9Kkazf.js +1 -0
  468. package/dist/assets/surface-state-DaxyruCj.js +3 -0
  469. package/dist/assets/system-adapters-rIC8FSGP.js +1 -0
  470. package/dist/assets/system-runner-DkOjxEBt.js +1 -0
  471. package/dist/assets/system-slot-E2mEdiih.js +1 -0
  472. package/dist/assets/team-playtest.service-DqOinFVA.js +1 -0
  473. package/dist/assets/texture.layout-DASpHVht.js +1 -0
  474. package/dist/assets/three-CDSueJyJ.js +1 -0
  475. package/dist/assets/three-authoring-adapter-CmSQ3Vui.js +1 -0
  476. package/dist/assets/three-authoring.service-iuHLwtf1.js +2 -0
  477. package/dist/assets/three-ingest-runtime-QzDYk6tU.js +1 -0
  478. package/dist/assets/three-projection-core-BmKMZ-nf.js +1 -0
  479. package/dist/assets/three-viewport-presentation-QBQpSdMh.js +1 -0
  480. package/dist/assets/three.module-D396a9Mn.js +3840 -0
  481. package/dist/assets/three.quarks.esm-B1C61jGo.js +1039 -0
  482. package/dist/assets/tools-B_NJsk05.js +1 -0
  483. package/dist/assets/transform-writer-BhBBTx33.js +1 -0
  484. package/dist/assets/transport.header-B-6HQlU2.js +1 -0
  485. package/dist/assets/trigger-volume-BNK2XzPL.js +1 -0
  486. package/dist/assets/tuples-CIQ4Bn1U.js +1 -0
  487. package/dist/assets/tween.module-DciWpAG6.js +1 -0
  488. package/dist/assets/ui-board-document-Bwtt--xp.js +2 -0
  489. package/dist/assets/unexported-game-trap-Dm1JYKHy.js +2 -0
  490. package/dist/assets/unmanaged-root-detector-B8CpbkSN.js +2 -0
  491. package/dist/assets/upstream-pin-BdgRH6ID.js +1 -0
  492. package/dist/assets/use-data-Dzq-MytE.js +1 -0
  493. package/dist/assets/use-live-actor-state-DErbWQWF.js +1 -0
  494. package/dist/assets/use-selection-DbR73-xd.js +1 -0
  495. package/dist/assets/user-data-BB2Of6Ht.js +1 -0
  496. package/dist/assets/utility-view-state-BHypxA33.js +1 -0
  497. package/dist/assets/uv-editing.layout-BEKHxmHw.js +1 -0
  498. package/dist/assets/vgai-shared-fontawesome-CU65D4Ij.js +1 -0
  499. package/dist/assets/vgai-shared-jsx-dev-runtime-DFDKL7fx.js +1 -0
  500. package/dist/assets/vgai-shared-jsx-runtime-DIqBpTut.js +1 -0
  501. package/dist/assets/vgai-shared-react-BT7rMc2H.js +1 -0
  502. package/dist/assets/vgai-shared-react-dom-DFajMqMT.js +1 -0
  503. package/dist/assets/vgai-shared-react-dom-client-S0ozfl6_.js +1 -0
  504. package/dist/assets/vgai-shared-three-Bc5PvpLM.js +1 -0
  505. package/dist/assets/viewport-pick-context-DkawXbEi.js +1 -0
  506. package/dist/assets/viewport-root-presentation-CWsE56K6.js +1 -0
  507. package/dist/assets/viewport-shading-BuA88e_A.js +1 -0
  508. package/dist/assets/viewport-shading-boundary-B_-_gABg.js +82 -0
  509. package/dist/assets/viewport-surface-status-B3om4Kcf.js +1 -0
  510. package/dist/assets/vision_bundle-DX0dWqEa.js +15 -0
  511. package/dist/assets/voxel-surface-DtcsSWRL.js +1 -0
  512. package/dist/assets/wav-encode-D31zWF6O.js +1 -0
  513. package/dist/assets/webgl-frame-capture-FlF9zQmW.js +1 -0
  514. package/dist/assets/webgl-gpu-timer-D3YX7RaD.js +1 -0
  515. package/dist/assets/webworkerAll-CM2o_dnz.js +1 -0
  516. package/dist/assets/worker-UrMcrc5_.js +2 -0
  517. package/dist/assets/world-documents-147tiFZe.js +1 -0
  518. package/dist/assets/world-root-stage-binding-vwTrtBz6.js +1 -0
  519. package/dist/assets/world-state-BWG-IcOd.js +1 -0
  520. package/dist/assets/writer-B-KovzkA.js +5 -0
  521. package/dist/assets/xstate-animation-binding-Brd0NST6.js +1 -0
  522. package/dist/assets/xstate-animation-meta-Q3u9zh_m.js +1 -0
  523. package/dist/assets/xstate-behavior.action-B8UluFBK.js +1 -0
  524. package/dist/assets/xstate-behavior.document-CKAsE45I.js +1 -0
  525. package/dist/assets/xstate-behavior.inspector-vJJyuifc.js +1 -0
  526. package/dist/assets/xstate-behavior.menu-BXbPICIh.js +1 -0
  527. package/dist/assets/xstate-inspection-BsZv8LS9.js +1 -0
  528. package/dist/vgai-shared-react.json +10 -0
  529. package/dist/vgai-shared-three.json +3 -0
  530. package/dist-node/cli.js +9320 -0
  531. package/dist-node/create.js +3239 -0
  532. package/package.json +91 -0
  533. package/presets.mjs +2 -0
  534. package/src/contributions.d.ts +4 -0
  535. package/src/index.ts +38 -0
  536. package/starter/cube.blend +0 -0
  537. package/starter/cube.py +81 -0
  538. package/template/.agents/references/project-manual.md +341 -0
  539. package/template/.agents/skills/editor/SKILL.md +53 -0
  540. package/template/.agents/skills/editor/agents/openai.yaml +4 -0
  541. package/template/.agents/skills/editor/references/editor-control.md +363 -0
  542. package/template/.agents/skills/vgai-3d-models/SKILL.md +64 -0
  543. package/template/.agents/skills/vgai-ingest-existing-game/SKILL.md +112 -0
  544. package/template/.agents/skills/vgai-threejs-authoring/SKILL.md +347 -0
  545. package/template/.agents/skills/vgai-threejs-authoring/agents/openai.yaml +4 -0
  546. package/template/.agents/skills/vgai-threejs-authoring/references/helper-catalog.md +65 -0
  547. package/template/.claude/skills/editor/SKILL.md +53 -0
  548. package/template/.claude/skills/editor/agents/openai.yaml +4 -0
  549. package/template/.claude/skills/editor/references/editor-control.md +363 -0
  550. package/template/.claude/skills/vgai-3d-models/SKILL.md +64 -0
  551. package/template/.claude/skills/vgai-ingest-existing-game/SKILL.md +112 -0
  552. package/template/.claude/skills/vgai-threejs-authoring/SKILL.md +347 -0
  553. package/template/.claude/skills/vgai-threejs-authoring/agents/openai.yaml +4 -0
  554. package/template/.claude/skills/vgai-threejs-authoring/references/helper-catalog.md +65 -0
  555. package/template/.github/copilot-instructions.md +6 -0
  556. package/template/.github/skills/editor/SKILL.md +53 -0
  557. package/template/.github/skills/editor/agents/openai.yaml +4 -0
  558. package/template/.github/skills/editor/references/editor-control.md +363 -0
  559. package/template/.github/skills/vgai-3d-models/SKILL.md +64 -0
  560. package/template/.github/skills/vgai-ingest-existing-game/SKILL.md +112 -0
  561. package/template/.github/skills/vgai-threejs-authoring/SKILL.md +347 -0
  562. package/template/.github/skills/vgai-threejs-authoring/agents/openai.yaml +4 -0
  563. package/template/.github/skills/vgai-threejs-authoring/references/helper-catalog.md +65 -0
  564. package/template/.mcp.json +12 -0
  565. package/template/.vgai/assets.json +1 -0
  566. package/template/.vgai/provenance.json +4 -0
  567. package/template/.volter/tracker/validation/.preset.base.mts +178 -0
  568. package/template/.volter/tracker/validation/preset.mts +186 -0
  569. package/template/.volter/tracker/visualizer/.extension.base.tsx +14 -0
  570. package/template/.volter/tracker/visualizer/extension.tsx +14 -0
  571. package/template/.volter/tracker-config.json +17 -0
  572. package/template/AGENTS.md +176 -0
  573. package/template/CLAUDE.md +1 -0
  574. package/template/DEVLOG.md +20 -0
  575. package/template/IDIOMS.md +118 -0
  576. package/template/LICENSE +30 -0
  577. package/template/README.md +83 -0
  578. package/template/ROADMAP.md +20 -0
  579. package/template/asset-manifest.json +3 -0
  580. package/template/check-idioms.ts +1533 -0
  581. package/template/index.html +18 -0
  582. package/template/manifest-entry-modules-plugin.ts +53 -0
  583. package/template/package.json +62 -0
  584. package/template/public/jsm/libs/basis/basis_transcoder.js +19 -0
  585. package/template/public/jsm/libs/basis/basis_transcoder.wasm +0 -0
  586. package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  587. package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  588. package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
  589. package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  590. package/template/public/shaders/daylight-sky.frag.glsl +15 -0
  591. package/template/public/shaders/daylight-sky.vert.glsl +7 -0
  592. package/template/scripts/patch-drei-stats-gl.mjs +35 -0
  593. package/template/server/colyseus-setup.ts +111 -0
  594. package/template/server/loopback.ts +76 -0
  595. package/template/server/main.ts +78 -0
  596. package/template/server/rooms/arena-room.ts +488 -0
  597. package/template/server/rooms/game-room.ts +274 -0
  598. package/template/server/rooms.ts +15 -0
  599. package/template/server/tsconfig.json +19 -0
  600. package/template/src/bot/QaTester.tsx +35 -0
  601. package/template/src/bot/behaviors.ts +203 -0
  602. package/template/src/bot/bot-station.ts +192 -0
  603. package/template/src/bot/tester-run.ts +238 -0
  604. package/template/src/bot/tester-station.ts +193 -0
  605. package/template/src/components/environment/DaylightSky.tsx +54 -0
  606. package/template/src/components/environment/Ground.tsx +129 -0
  607. package/template/src/components/environment/SceneAtmosphere.tsx +14 -0
  608. package/template/src/components/environment/SoftGroundShadow.tsx +62 -0
  609. package/template/src/components/environment/SunShadowFit.tsx +141 -0
  610. package/template/src/contributions/README.md +98 -0
  611. package/template/src/contributions/analytics.analytics.tsx +26 -0
  612. package/template/src/contributions/data.document.tsx +35 -0
  613. package/template/src/contributions/pages.finder.ts +61 -0
  614. package/template/src/contributions/tester.inspector.tsx +64 -0
  615. package/template/src/contributions/use-game-modules.ts +80 -0
  616. package/template/src/data/README.md +17 -0
  617. package/template/src/input.ts +97 -0
  618. package/template/src/lib/audio/capability.json +4 -0
  619. package/template/src/lib/audio/sfx.ts +255 -0
  620. package/template/src/lib/react-root/capability.json +4 -0
  621. package/template/src/lib/react-root/index.ts +20 -0
  622. package/template/src/lib/react-root/react-root-adapter.tsx +57 -0
  623. package/template/src/lib/reflections/ReflectionProbe.tsx +201 -0
  624. package/template/src/lib/reflections/capability.json +4 -0
  625. package/template/src/lib/reflections/index.ts +19 -0
  626. package/template/src/lib/reflections/probe-controller.ts +75 -0
  627. package/template/src/lib/reflections/reflection-probe-registry.ts +402 -0
  628. package/template/src/lib/reflections/volume-reflection-material.ts +416 -0
  629. package/template/src/lib/static-batch/Frozen.tsx +91 -0
  630. package/template/src/lib/static-batch/capability.json +4 -0
  631. package/template/src/lib/static-batch/freeze.ts +579 -0
  632. package/template/src/lib/static-batch/index.ts +44 -0
  633. package/template/src/lib/static-batch/mutation-watch.ts +149 -0
  634. package/template/src/lib/static-batch/shadow-cache.ts +171 -0
  635. package/template/src/main.ts +63 -0
  636. package/template/src/manifest-entry-modules.d.ts +3 -0
  637. package/template/src/prefabs/HeroBox.stories.tsx +23 -0
  638. package/template/src/prefabs/HeroBox.tsx +35 -0
  639. package/template/src/scenes/MainScene.tsx +77 -0
  640. package/template/src/world.tsx +87 -0
  641. package/template/tsconfig.json +35 -0
  642. package/template/tsconfig.node.json +32 -0
  643. package/template/tsconfig.tools.json +22 -0
  644. package/template/validate-asset-content.ts +27 -0
  645. package/template/validate-assets.ts +60 -0
  646. package/template/validate-manifest.ts +55 -0
  647. package/template/vgai.adapter.ts +76 -0
  648. package/template/vgai.project.json +24 -0
  649. package/template/vite.config.ts +247 -0
  650. package/template-dotfiles/gitignore +63 -0
  651. package/workbench/src/media/game-cover.css +83 -0
  652. package/workbench/src/product.contribution.ts +188 -0
  653. package/workbench/src/vgaiGameSkew.ts +276 -0
@@ -0,0 +1,657 @@
1
+ import * as THREE from 'three';
2
+ import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
3
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
4
+ import { createImageBitmapFromBlob, NodeImageData, NodeOffscreenCanvas } from '../bake/node-image';
5
+ import { disposeObject3D } from '../bake/object3d-lifecycle';
6
+
7
+ const polyfillKeys = [
8
+ 'self',
9
+ 'ProgressEvent',
10
+ 'createImageBitmap',
11
+ 'FileReader',
12
+ 'ImageData',
13
+ 'OffscreenCanvas',
14
+ ] as const;
15
+
16
+ function arrayBufferToBase64(value: ArrayBuffer): string {
17
+ const bytes = new Uint8Array(value);
18
+ let binary = '';
19
+ for (let offset = 0; offset < bytes.length; offset += 0x8000) {
20
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
21
+ }
22
+ return btoa(binary);
23
+ }
24
+
25
+ /**
26
+ * The browser globals three's GLTF exporter and loader reach for, supplied
27
+ * for Node. Returns the restore function; never overwrites a global that
28
+ * already exists, so a real browser (or a jsdom-flavoured test) keeps its
29
+ * own.
30
+ *
31
+ * `ImageData` + `OffscreenCanvas` + a real `createImageBitmap` are the
32
+ * TEXTURE half, and they are what make a material MAP survive the bake:
33
+ * `GLTFExporter.processImage` draws a `DataTexture` into
34
+ * `getCanvas()` — which prefers `OffscreenCanvas` precisely when
35
+ * `document` is undefined — via `new ImageData(...)`, then asks
36
+ * `convertToBlob` for the PNG bytes it embeds; `GLTFLoader` reverses that
37
+ * through `ImageBitmapLoader`, whose `createImageBitmap` used to be
38
+ * stubbed here to an empty object, so every re-imported texture came back
39
+ * with no pixels behind it. All three live in `node-image.ts`.
40
+ */
41
+ export function installNodeThreePolyfills(): () => void {
42
+ const target = globalThis as unknown as Record<string, unknown>;
43
+ const previous = new Map(
44
+ polyfillKeys.map((key) => [key, Object.getOwnPropertyDescriptor(globalThis, key)]),
45
+ );
46
+ if (!target['self']) target['self'] = globalThis;
47
+ if (!target['ImageData']) target['ImageData'] = NodeImageData;
48
+ if (!target['OffscreenCanvas']) target['OffscreenCanvas'] = NodeOffscreenCanvas;
49
+ if (!target['ProgressEvent']) {
50
+ target['ProgressEvent'] = class {
51
+ readonly type: string;
52
+ constructor(type: string, init: Record<string, unknown> = {}) {
53
+ this.type = type;
54
+ Object.assign(this, init);
55
+ }
56
+ };
57
+ }
58
+ if (!target['createImageBitmap']) target['createImageBitmap'] = createImageBitmapFromBlob;
59
+ if (!target['FileReader']) {
60
+ target['FileReader'] = class {
61
+ result: string | ArrayBuffer | null = null;
62
+ onloadend: (() => void) | null = null;
63
+ readAsArrayBuffer(blob: Blob): void {
64
+ void blob.arrayBuffer().then((value) => {
65
+ this.result = value;
66
+ this.onloadend?.();
67
+ });
68
+ }
69
+ readAsDataURL(blob: Blob): void {
70
+ void blob.arrayBuffer().then((value) => {
71
+ this.result = `data:${blob.type || 'application/octet-stream'};base64,${arrayBufferToBase64(value)}`;
72
+ this.onloadend?.();
73
+ });
74
+ }
75
+ };
76
+ }
77
+ return () => {
78
+ for (const key of polyfillKeys) {
79
+ const descriptor = previous.get(key);
80
+ if (descriptor) Object.defineProperty(globalThis, key, descriptor);
81
+ else Reflect.deleteProperty(globalThis, key);
82
+ }
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Editor-only chrome a builder attached to its graph, flagged
88
+ * `userData.isRigHelper` (e.g. a `SkeletonHelper`). It is detached for the
89
+ * duration of the export and put back afterwards.
90
+ *
91
+ * Why it must go: the export runs with `onlyVisible: false` (a builder may
92
+ * legitimately bake hidden geometry), so an invisible helper would otherwise
93
+ * ship inside the game asset — and a `SkeletonHelper`'s vertex buffer is a
94
+ * SNAPSHOT of world bone positions, so it also skews the bounds validation
95
+ * below the moment a param moves the rig after the helper was built. Baked
96
+ * output is the model; presentation chrome stays outside the baked root
97
+ * (the `vgai-3d-assets` skill, "Parametric asset libs").
98
+ */
99
+ function detachRigHelpers(root: THREE.Object3D): () => void {
100
+ const detached: Array<{ object: THREE.Object3D; parent: THREE.Object3D }> = [];
101
+ root.traverse((object) => {
102
+ if (object.userData['isRigHelper'] === true && object.parent) {
103
+ detached.push({ object, parent: object.parent });
104
+ }
105
+ });
106
+ for (const { object, parent } of detached) parent.remove(object);
107
+ return () => {
108
+ for (const { object, parent } of detached) parent.add(object);
109
+ };
110
+ }
111
+
112
+ /** The morph targets one mesh of a source graph carries, in export order. */
113
+ export interface MorphTargetSet {
114
+ mesh: string;
115
+ names: readonly string[];
116
+ }
117
+
118
+ /**
119
+ * The morph targets a source graph will actually export — and the assertion
120
+ * that it will.
121
+ *
122
+ * `GLTFExporter` reads morph targets off the MESH (`morphTargetInfluences`
123
+ * for how many, `morphTargetDictionary` for their names), not off the
124
+ * geometry, and it exports NOTHING when the mesh half is missing — the
125
+ * classic silent loss, since `Mesh.updateMorphTargets()` only runs in the
126
+ * constructor and in `GLTFLoader`. So a mesh whose geometry carries morph
127
+ * attributes must carry the matching named influences, or this throws.
128
+ */
129
+ export function collectMorphTargets(root: THREE.Object3D): readonly MorphTargetSet[] {
130
+ const sets: MorphTargetSet[] = [];
131
+ root.traverse((object) => {
132
+ const mesh = object as THREE.Mesh;
133
+ if (!mesh.isMesh) return;
134
+ const attributes = mesh.geometry.morphAttributes['position'] ?? [];
135
+ if (attributes.length === 0) return;
136
+ const influences = mesh.morphTargetInfluences ?? [];
137
+ if (influences.length !== attributes.length) {
138
+ throw new Error(
139
+ `Mesh '${mesh.name}' geometry carries ${attributes.length} morph targets but the mesh ` +
140
+ `carries ${influences.length} influences, so the export would drop them. Call ` +
141
+ 'updateMorphTargets() after authoring morph attributes (the Mesh constructor does it ' +
142
+ 'for geometry that already carries them).',
143
+ );
144
+ }
145
+ const dictionary = mesh.morphTargetDictionary ?? {};
146
+ const names = attributes.map((attribute, index) => {
147
+ if (!attribute.name || dictionary[attribute.name] !== index) {
148
+ throw new Error(
149
+ `Mesh '${mesh.name}' morph target ${index} is unnamed or missing from its ` +
150
+ 'morphTargetDictionary, so the baked GLB would carry no target names.',
151
+ );
152
+ }
153
+ return attribute.name;
154
+ });
155
+ sets.push({ mesh: mesh.name, names });
156
+ });
157
+ return sets;
158
+ }
159
+
160
+ export async function exportObject3DToGlb(
161
+ root: THREE.Object3D,
162
+ animations: readonly THREE.AnimationClip[],
163
+ ): Promise<Uint8Array> {
164
+ collectMorphTargets(root);
165
+ const reattach = detachRigHelpers(root);
166
+ try {
167
+ const result = await new Promise<ArrayBuffer>((resolve, reject) => {
168
+ new GLTFExporter().parse(
169
+ root,
170
+ (value) => {
171
+ if (value instanceof ArrayBuffer) resolve(value);
172
+ else reject(new Error('GLTFExporter returned JSON while binary output was requested.'));
173
+ },
174
+ reject,
175
+ { binary: true, animations: [...animations], onlyVisible: false },
176
+ );
177
+ });
178
+ return new Uint8Array(result);
179
+ } finally {
180
+ reattach();
181
+ }
182
+ }
183
+
184
+ export interface GlbValidationOptions {
185
+ requiredClips: readonly string[];
186
+ requireSkinned?: boolean;
187
+ forbidSkinned?: boolean;
188
+ minimumAnimatedNodes?: number;
189
+ expectedForward?: readonly [number, number, number];
190
+ minimumHeight?: number;
191
+ maximumHeight?: number;
192
+ groundYRange?: readonly [number, number];
193
+ /** Exact bone count the reimported scene must contain — proves the whole
194
+ * skeleton survived the export/reimport round trip. */
195
+ expectedBoneCount?: number;
196
+ /** Prove vertex colors survive the round trip: the named reimported mesh
197
+ * must carry a `color` attribute whose value at `index` matches the
198
+ * source-sampled `value` (RGB, within `tolerance`, default 2e-3). */
199
+ vertexColorSample?: {
200
+ mesh: string;
201
+ index: number;
202
+ value: readonly [number, number, number];
203
+ tolerance?: number;
204
+ };
205
+ /** Every morph target the SOURCE authored, per mesh — each name must come
206
+ * back off the reimported GLB with data behind it. Derived from the source
207
+ * graph by `bakeObject3DSource`, exactly as `requiredClips` is. */
208
+ requiredMorphTargets?: readonly MorphTargetSet[];
209
+ /** UV sets the reimported GLB must still carry, in the mesh kit's channel
210
+ * vocabulary: `'uv'` is glTF `TEXCOORD_0`, `'uv2'` is `TEXCOORD_1` (which
211
+ * three loads back as its `uv1` attribute — it renamed the second set in
212
+ * r152). Presence somewhere in the scene, exactly like `requiredClips`;
213
+ * which meshes carry UVs is the source's business. Without this a
214
+ * dropped TEXCOORD is invisible until a texture renders untextured. */
215
+ requiredUvChannels?: readonly ('uv' | 'uv2')[];
216
+ /** Prove a material's TEXTURE MAP survives the round trip, the way
217
+ * `vertexColorSample` proves vertex colors do: find the material by NAME
218
+ * (`slotMaterialMesh` names each material after its slot), read the named
219
+ * map's decoded image, and compare the pixel at `pixel` against `value`
220
+ * (RGB 0–1, within `tolerance`, default one 8-bit step). A checker
221
+ * texture makes this a real correspondence check — sample a cell you know
222
+ * the color of. */
223
+ materialMapSample?: {
224
+ material: string;
225
+ /** Which map (default `'map'`). */
226
+ map?: 'map' | 'emissiveMap' | 'metalnessMap' | 'normalMap' | 'roughnessMap' | 'aoMap';
227
+ /** `[x, y]` in image pixels from the top-left. */
228
+ pixel: readonly [number, number];
229
+ value: readonly [number, number, number];
230
+ tolerance?: number;
231
+ };
232
+ }
233
+
234
+ export interface GlbValidationResult {
235
+ meshCount: number;
236
+ skinnedMeshCount: number;
237
+ boneCount: number;
238
+ animatedNodeCount: number;
239
+ clips: string[];
240
+ }
241
+
242
+ function assertFiniteValues(label: string, values: Iterable<number>): void {
243
+ let index = 0;
244
+ for (const value of values) {
245
+ if (!Number.isFinite(value))
246
+ throw new Error(`${label} contains a non-finite value at ${index}.`);
247
+ index += 1;
248
+ }
249
+ }
250
+
251
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: exhaustive attribute/index validation is clearest as one bounded graph check.
252
+ function validateGeometry(object: THREE.Mesh): void {
253
+ const geometry = object.geometry;
254
+ for (const [name, attribute] of Object.entries(geometry.attributes)) {
255
+ for (let vertex = 0; vertex < attribute.count; vertex += 1) {
256
+ for (let component = 0; component < attribute.itemSize; component += 1) {
257
+ const value = attribute.getComponent(vertex, component);
258
+ if (!Number.isFinite(value)) {
259
+ throw new Error(
260
+ `Geometry '${object.name}' attribute '${name}' contains a non-finite value at ` +
261
+ `vertex ${vertex}, component ${component}.`,
262
+ );
263
+ }
264
+ }
265
+ }
266
+ }
267
+ if (geometry.index) {
268
+ const positionCount = geometry.getAttribute('position')?.count ?? 0;
269
+ for (let index = 0; index < geometry.index.count; index += 1) {
270
+ const value = geometry.index.getX(index);
271
+ if (!Number.isInteger(value) || value < 0 || value >= positionCount) {
272
+ throw new Error(`Geometry '${object.name}' contains invalid index ${value} at ${index}.`);
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: joint, inverse-bind, index, and weight invariants form one skin validity proof.
279
+ function validateSkin(mesh: THREE.SkinnedMesh): void {
280
+ const { skeleton, geometry } = mesh;
281
+ const skinIndex = geometry.getAttribute('skinIndex');
282
+ const skinWeight = geometry.getAttribute('skinWeight');
283
+ if (!skinIndex || !skinWeight || skinIndex.itemSize < 4 || skinWeight.itemSize < 4) {
284
+ throw new Error(
285
+ `SkinnedMesh '${mesh.name}' is missing four-component skin indices or weights.`,
286
+ );
287
+ }
288
+ if (
289
+ skinIndex.count !== skinWeight.count ||
290
+ skinIndex.count !== geometry.getAttribute('position')?.count
291
+ ) {
292
+ throw new Error(`SkinnedMesh '${mesh.name}' has mismatched vertex and skin attribute counts.`);
293
+ }
294
+ if (skeleton.bones.length === 0 || skeleton.boneInverses.length !== skeleton.bones.length) {
295
+ throw new Error(`SkinnedMesh '${mesh.name}' has an incomplete skeleton or inverse bind set.`);
296
+ }
297
+ const uniqueBones = new Set(skeleton.bones);
298
+ if (uniqueBones.size !== skeleton.bones.length || skeleton.bones.some((bone) => !bone?.isBone)) {
299
+ throw new Error(`SkinnedMesh '${mesh.name}' contains missing or duplicate joints.`);
300
+ }
301
+ for (const [index, inverse] of skeleton.boneInverses.entries()) {
302
+ assertFiniteValues(`SkinnedMesh '${mesh.name}' inverse bind ${index}`, inverse.elements);
303
+ }
304
+ for (let vertex = 0; vertex < skinIndex.count; vertex += 1) {
305
+ let weightSum = 0;
306
+ for (let component = 0; component < 4; component += 1) {
307
+ const joint = skinIndex.getComponent(vertex, component);
308
+ const weight = skinWeight.getComponent(vertex, component);
309
+ if (!Number.isInteger(joint) || joint < 0 || joint >= skeleton.bones.length) {
310
+ throw new Error(
311
+ `SkinnedMesh '${mesh.name}' vertex ${vertex} references invalid joint ${joint}.`,
312
+ );
313
+ }
314
+ if (!Number.isFinite(weight) || weight < 0) {
315
+ throw new Error(
316
+ `SkinnedMesh '${mesh.name}' vertex ${vertex} contains invalid skin weight ${weight}.`,
317
+ );
318
+ }
319
+ weightSum += weight;
320
+ }
321
+ if (Math.abs(weightSum - 1) > 1e-3) {
322
+ throw new Error(
323
+ `SkinnedMesh '${mesh.name}' vertex ${vertex} skin weights sum to ${weightSum}.`,
324
+ );
325
+ }
326
+ }
327
+ }
328
+
329
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: time, value, quaternion, and continuity checks intentionally share clip context.
330
+ function validateClip(clip: THREE.AnimationClip): void {
331
+ if (!Number.isFinite(clip.duration) || clip.duration <= 0) {
332
+ throw new Error(`Animation clip '${clip.name}' has invalid duration ${clip.duration}.`);
333
+ }
334
+ if (clip.tracks.length === 0) throw new Error(`Animation clip '${clip.name}' has no tracks.`);
335
+ for (const track of clip.tracks) {
336
+ assertFiniteValues(`Animation track '${track.name}' times`, track.times);
337
+ assertFiniteValues(`Animation track '${track.name}' values`, track.values);
338
+ for (let index = 1; index < track.times.length; index += 1) {
339
+ if (track.times[index]! < track.times[index - 1]!) {
340
+ throw new Error(`Animation track '${track.name}' has decreasing key times.`);
341
+ }
342
+ }
343
+ if (!(track instanceof THREE.QuaternionKeyframeTrack)) continue;
344
+ let previous: THREE.Quaternion | undefined;
345
+ for (let offset = 0; offset < track.values.length; offset += 4) {
346
+ const value = new THREE.Quaternion(
347
+ track.values[offset],
348
+ track.values[offset + 1],
349
+ track.values[offset + 2],
350
+ track.values[offset + 3],
351
+ );
352
+ const length = value.length();
353
+ if (!Number.isFinite(length) || Math.abs(length - 1) > 1e-3) {
354
+ throw new Error(`Animation track '${track.name}' contains a non-normalized quaternion.`);
355
+ }
356
+ if (previous && previous.dot(value) < -1e-5) {
357
+ throw new Error(`Animation track '${track.name}' changes quaternion hemisphere.`);
358
+ }
359
+ previous = value;
360
+ }
361
+ }
362
+ }
363
+
364
+ /** Assert the round-tripped scene still carries the source's vertex colors:
365
+ * find the named mesh, require a `color` attribute, and compare the sampled
366
+ * RGB against the source value. Garmented characters are vertex-colored, so
367
+ * a silent color drop would ship a monochrome body. */
368
+ function validateVertexColorSample(
369
+ scene: THREE.Object3D,
370
+ sample: NonNullable<GlbValidationOptions['vertexColorSample']>,
371
+ ): void {
372
+ let mesh: THREE.Mesh | undefined;
373
+ scene.traverse((object) => {
374
+ if (!mesh && (object as THREE.Mesh).isMesh && object.name === sample.mesh) {
375
+ mesh = object as THREE.Mesh;
376
+ }
377
+ });
378
+ if (!mesh) throw new Error(`Baked GLB round-trip lost mesh '${sample.mesh}'.`);
379
+ const color = mesh.geometry.getAttribute('color');
380
+ if (!color || color.itemSize < 3) {
381
+ throw new Error(`Baked GLB mesh '${sample.mesh}' lost its vertex colors on round-trip.`);
382
+ }
383
+ if (sample.index < 0 || sample.index >= color.count) {
384
+ throw new Error(
385
+ `Vertex color sample index ${sample.index} is out of range for mesh '${sample.mesh}' ` +
386
+ `(${color.count} vertices).`,
387
+ );
388
+ }
389
+ const tolerance = sample.tolerance ?? 2e-3;
390
+ const actual = [0, 1, 2].map((component) => color.getComponent(sample.index, component));
391
+ for (const [component, expected] of sample.value.entries()) {
392
+ if (Math.abs(actual[component]! - expected) > tolerance) {
393
+ throw new Error(
394
+ `Baked GLB mesh '${sample.mesh}' vertex color at ${sample.index} drifted on ` +
395
+ `round-trip: got [${actual.join(', ')}], expected [${sample.value.join(', ')}].`,
396
+ );
397
+ }
398
+ }
399
+ }
400
+
401
+ /** Channel vocabulary → three's attribute name → the glTF accessor it came
402
+ * from. Three renamed the second UV set to `uv1` in r152; the channel names
403
+ * stay the mesh kit's `uv`/`uv2`. */
404
+ const UV_CHANNELS = {
405
+ uv: { attribute: 'uv', accessor: 'TEXCOORD_0' },
406
+ uv2: { attribute: 'uv1', accessor: 'TEXCOORD_1' },
407
+ } as const;
408
+
409
+ /** Assert the reimported scene still carries each required UV set. A
410
+ * dropped TEXCOORD does not fail any other check here — the geometry is
411
+ * perfectly valid without it — and only shows up as an untextured render. */
412
+ function validateUvChannels(scene: THREE.Object3D, required: readonly ('uv' | 'uv2')[]): void {
413
+ const present = new Set<string>();
414
+ let meshCount = 0;
415
+ scene.traverse((object) => {
416
+ const mesh = object as THREE.Mesh;
417
+ if (!mesh.isMesh) return;
418
+ meshCount += 1;
419
+ for (const name of Object.keys(mesh.geometry.attributes)) present.add(name);
420
+ });
421
+ for (const channel of required) {
422
+ const { attribute, accessor } = UV_CHANNELS[channel];
423
+ if (!present.has(attribute)) {
424
+ throw new Error(
425
+ `Baked GLB round-trip carries no '${attribute}' attribute (channel '${channel}', glTF ` +
426
+ `${accessor}) on any of its ${meshCount} meshes — the UVs were dropped on export.`,
427
+ );
428
+ }
429
+ }
430
+ }
431
+
432
+ /** Assert a named material's texture map survived the round trip with the
433
+ * right pixels behind it. Keyed by MATERIAL name rather than mesh name
434
+ * because a slot-grouped mesh exports as one glTF primitive per slot and
435
+ * comes back split into several meshes, while the material names — which
436
+ * ARE the slot names — survive intact. */
437
+ function validateMaterialMapSample(
438
+ scene: THREE.Object3D,
439
+ sample: NonNullable<GlbValidationOptions['materialMapSample']>,
440
+ ): void {
441
+ const key = sample.map ?? 'map';
442
+ const names = new Set<string>();
443
+ let material: THREE.Material | undefined;
444
+ scene.traverse((object) => {
445
+ const mesh = object as THREE.Mesh;
446
+ if (!mesh.isMesh) return;
447
+ for (const candidate of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
448
+ if (!candidate) continue;
449
+ names.add(candidate.name);
450
+ if (!material && candidate.name === sample.material) material = candidate;
451
+ }
452
+ });
453
+ if (!material) {
454
+ throw new Error(
455
+ `Baked GLB round-trip lost material '${sample.material}' (kept: ` +
456
+ `${[...names].join(', ') || 'none'}).`,
457
+ );
458
+ }
459
+ const texture = (material as unknown as Record<string, THREE.Texture | null>)[key];
460
+ if (!texture) {
461
+ throw new Error(`Baked GLB material '${sample.material}' came back with no '${key}'.`);
462
+ }
463
+ const image = texture.image as { width?: number; height?: number; data?: ArrayLike<number> };
464
+ if (!image?.data || !image.width || !image.height) {
465
+ throw new Error(
466
+ `Baked GLB material '${sample.material}' '${key}' came back with no readable pixels — the ` +
467
+ 'image was embedded but not decoded (installNodeThreePolyfills supplies the decoder).',
468
+ );
469
+ }
470
+ const [x, y] = sample.pixel;
471
+ if (x < 0 || y < 0 || x >= image.width || y >= image.height) {
472
+ throw new Error(
473
+ `Material map sample pixel [${x}, ${y}] is outside '${sample.material}' '${key}' ` +
474
+ `(${image.width}×${image.height}).`,
475
+ );
476
+ }
477
+ const tolerance = sample.tolerance ?? 4e-3;
478
+ const offset = (y * image.width + x) * 4;
479
+ const actual = [0, 1, 2].map((component) => (image.data?.[offset + component] ?? 0) / 255);
480
+ for (const [component, expected] of sample.value.entries()) {
481
+ if (Math.abs((actual[component] as number) - expected) > tolerance) {
482
+ throw new Error(
483
+ `Baked GLB material '${sample.material}' '${key}' pixel [${x}, ${y}] drifted on ` +
484
+ `round-trip: got [${actual.join(', ')}], expected [${sample.value.join(', ')}].`,
485
+ );
486
+ }
487
+ }
488
+ }
489
+
490
+ /** Assert every authored morph target survived the round trip, by NAME and
491
+ * with data behind it. A morph target is invisible in a render of the rest
492
+ * pose — nothing but this notices when a merge, an exporter quirk, or a
493
+ * missing `updateMorphTargets()` drops the face's blink on the way out. */
494
+ function validateMorphTargets(scene: THREE.Object3D, required: readonly MorphTargetSet[]): void {
495
+ const byName = new Map<string, THREE.Mesh>();
496
+ scene.traverse((object) => {
497
+ const mesh = object as THREE.Mesh;
498
+ if (mesh.isMesh && !byName.has(mesh.name)) byName.set(mesh.name, mesh);
499
+ });
500
+ for (const set of required) {
501
+ const mesh = byName.get(set.mesh);
502
+ if (!mesh) throw new Error(`Baked GLB round-trip lost mesh '${set.mesh}'.`);
503
+ const dictionary = mesh.morphTargetDictionary ?? {};
504
+ const attributes = mesh.geometry.morphAttributes['position'] ?? [];
505
+ const missing = set.names.filter((name) => dictionary[name] === undefined);
506
+ if (missing.length > 0) {
507
+ throw new Error(
508
+ `Baked GLB mesh '${set.mesh}' lost morph targets on round-trip: ${missing.join(', ')} ` +
509
+ `(kept: ${Object.keys(dictionary).join(', ') || 'none'}).`,
510
+ );
511
+ }
512
+ for (const name of set.names) {
513
+ const attribute = attributes[dictionary[name]!];
514
+ if (!attribute || attribute.count === 0) {
515
+ throw new Error(
516
+ `Baked GLB mesh '${set.mesh}' morph target '${name}' came back with no vertex data.`,
517
+ );
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ function validateBoundsAndForward(scene: THREE.Object3D, options: GlbValidationOptions): void {
524
+ scene.updateMatrixWorld(true);
525
+ const bounds = new THREE.Box3().setFromObject(scene, true);
526
+ if (bounds.isEmpty()) throw new Error('Baked GLB round-trip contained no renderable bounds.');
527
+ assertFiniteValues('Baked GLB bounds', [
528
+ bounds.min.x,
529
+ bounds.min.y,
530
+ bounds.min.z,
531
+ bounds.max.x,
532
+ bounds.max.y,
533
+ bounds.max.z,
534
+ ]);
535
+ const height = bounds.max.y - bounds.min.y;
536
+ if (options.minimumHeight !== undefined && height < options.minimumHeight) {
537
+ throw new Error(`Baked GLB height ${height} is below ${options.minimumHeight}.`);
538
+ }
539
+ if (options.maximumHeight !== undefined && height > options.maximumHeight) {
540
+ throw new Error(`Baked GLB height ${height} exceeds ${options.maximumHeight}.`);
541
+ }
542
+ if (
543
+ options.groundYRange &&
544
+ (bounds.min.y < options.groundYRange[0] || bounds.min.y > options.groundYRange[1])
545
+ ) {
546
+ throw new Error(
547
+ `Baked GLB ground Y ${bounds.min.y} is outside ` +
548
+ `[${options.groundYRange[0]}, ${options.groundYRange[1]}].`,
549
+ );
550
+ }
551
+ if (options.expectedForward) {
552
+ let declared: unknown;
553
+ scene.traverse((object) => {
554
+ if (declared === undefined && object.userData['forward'] !== undefined) {
555
+ declared = object.userData['forward'];
556
+ }
557
+ });
558
+ if (!Array.isArray(declared) || declared.length !== 3) {
559
+ throw new Error('Baked GLB is missing a three-component forward declaration in extras.');
560
+ }
561
+ const actual = new THREE.Vector3(Number(declared[0]), Number(declared[1]), Number(declared[2]));
562
+ const expected = new THREE.Vector3(...options.expectedForward);
563
+ if (
564
+ !Number.isFinite(actual.lengthSq()) ||
565
+ actual.lengthSq() === 0 ||
566
+ expected.lengthSq() === 0 ||
567
+ actual.normalize().dot(expected.normalize()) < 1 - 1e-6
568
+ ) {
569
+ throw new Error(
570
+ `Baked GLB forward declaration [${declared.join(', ')}] does not match ` +
571
+ `[${options.expectedForward.join(', ')}].`,
572
+ );
573
+ }
574
+ }
575
+ }
576
+
577
+ export async function validateExportedGlb(
578
+ bytes: Uint8Array,
579
+ options: GlbValidationOptions,
580
+ ): Promise<GlbValidationResult> {
581
+ const arrayBuffer = new ArrayBuffer(bytes.byteLength);
582
+ new Uint8Array(arrayBuffer).set(bytes);
583
+ const gltf = await new GLTFLoader().parseAsync(arrayBuffer, '');
584
+ let meshCount = 0;
585
+ let skinnedMeshCount = 0;
586
+ let boneCount = 0;
587
+ try {
588
+ gltf.scene.traverse((object) => {
589
+ const mesh = object as THREE.Mesh;
590
+ if (mesh.isMesh) meshCount += 1;
591
+ if ((object as THREE.SkinnedMesh).isSkinnedMesh) skinnedMeshCount += 1;
592
+ if ((object as THREE.Bone).isBone) boneCount += 1;
593
+ assertFiniteValues(`Object '${object.name}' transform`, [
594
+ object.position.x,
595
+ object.position.y,
596
+ object.position.z,
597
+ object.quaternion.x,
598
+ object.quaternion.y,
599
+ object.quaternion.z,
600
+ object.quaternion.w,
601
+ object.scale.x,
602
+ object.scale.y,
603
+ object.scale.z,
604
+ ...object.matrix.elements,
605
+ ]);
606
+ const quaternionLength = object.quaternion.length();
607
+ if (quaternionLength === 0 || Math.abs(quaternionLength - 1) > 1e-3) {
608
+ throw new Error(`Object '${object.name}' has a non-normalized quaternion.`);
609
+ }
610
+ if (mesh.isMesh) validateGeometry(mesh);
611
+ if ((object as THREE.SkinnedMesh).isSkinnedMesh) {
612
+ validateSkin(object as THREE.SkinnedMesh);
613
+ }
614
+ });
615
+ if (meshCount === 0) throw new Error('Baked GLB round-trip contained no meshes.');
616
+ if (options.requireSkinned && skinnedMeshCount === 0) {
617
+ throw new Error('Baked GLB round-trip contained no SkinnedMesh.');
618
+ }
619
+ if (options.forbidSkinned && skinnedMeshCount !== 0) {
620
+ throw new Error('Rigid articulated output unexpectedly contains a SkinnedMesh.');
621
+ }
622
+ if (options.expectedBoneCount !== undefined && boneCount !== options.expectedBoneCount) {
623
+ throw new Error(
624
+ `Baked GLB round-trip carries ${boneCount} bones; the source rig has ` +
625
+ `${options.expectedBoneCount}.`,
626
+ );
627
+ }
628
+ if (options.vertexColorSample) validateVertexColorSample(gltf.scene, options.vertexColorSample);
629
+ if (options.requiredUvChannels?.length) {
630
+ validateUvChannels(gltf.scene, options.requiredUvChannels);
631
+ }
632
+ if (options.materialMapSample) validateMaterialMapSample(gltf.scene, options.materialMapSample);
633
+ if (options.requiredMorphTargets?.length) {
634
+ validateMorphTargets(gltf.scene, options.requiredMorphTargets);
635
+ }
636
+ const clips = gltf.animations.map((clip) => clip.name);
637
+ for (const clip of gltf.animations) validateClip(clip);
638
+ const clipSet = new Set(clips);
639
+ const missing = options.requiredClips.filter((name) => !clipSet.has(name));
640
+ if (missing.length > 0) throw new Error(`Baked GLB is missing clips: ${missing.join(', ')}`);
641
+ const animatedNodes = new Set(
642
+ gltf.animations.flatMap((clip) =>
643
+ clip.tracks.map((track) => track.name.slice(0, track.name.lastIndexOf('.'))),
644
+ ),
645
+ );
646
+ const animatedNodeCount = animatedNodes.size;
647
+ if (animatedNodeCount < (options.minimumAnimatedNodes ?? 0)) {
648
+ throw new Error(
649
+ `Baked GLB animates ${animatedNodeCount} nodes; expected at least ${options.minimumAnimatedNodes}.`,
650
+ );
651
+ }
652
+ validateBoundsAndForward(gltf.scene, options);
653
+ return { meshCount, skinnedMeshCount, boneCount, animatedNodeCount, clips };
654
+ } finally {
655
+ disposeObject3D(gltf.scene);
656
+ }
657
+ }