@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,579 @@
1
+ /**
2
+ * THE FREEZE — the whole batching mechanism, as plain functions over a
3
+ * `THREE.Object3D`. `Frozen.tsx` is a twelve-line React wrapper around it, and
4
+ * everything worth testing is here, provable headlessly with no renderer and
5
+ * no React.
6
+ *
7
+ * ── WHAT IT DOES ────────────────────────────────────────────────────────────
8
+ * A game authored as TSX draws one mesh per JSX element. That is the right
9
+ * authoring model and the wrong SUBMISSION model: a hall built from a thousand
10
+ * named panels is a thousand draw calls, and at ~2.75 µs of CPU each, a
11
+ * four-thousand-mesh scene spends ~11 ms per frame doing nothing but telling
12
+ * the GPU what to draw. {@link freezeSubtree} collapses that mechanically:
13
+ *
14
+ * 1. walk the subtree, keeping the meshes that are safe to collapse
15
+ * (`staticBatchSkipReason`, in the engine — see ELIGIBILITY below);
16
+ * 2. group them by STRUCTURAL material identity plus the shadow flags —
17
+ * by what the draw LOOKS like, never by `material.uuid` (see THE UUID
18
+ * TRAP below, which is the whole reason this is not a ten-line helper);
19
+ * 3. inside a group, members whose geometry is also structurally identical
20
+ * become ONE `InstancedMesh`; the rest — same material, different shapes
21
+ * — become ONE merged mesh;
22
+ * 4. the originals are HIDDEN, not removed. React still owns them, their
23
+ * names still resolve for `render.census` and the editor hierarchy, and
24
+ * {@link restoreFrozen} puts everything back exactly as it was.
25
+ *
26
+ * ── THE UUID TRAP ───────────────────────────────────────────────────────────
27
+ * The obvious grouping key is `material.uuid`. It is also the key that batches
28
+ * NOTHING in a freshly scaffolded world, because inline JSX materials —
29
+ * `<meshStandardMaterial color="#8a6a44" />` inside a `.map()` — construct one
30
+ * material object PER MESH. Five hundred identical crates are five hundred
31
+ * uuids. A uuid-keyed freeze reports "no groups found", renders correctly, and
32
+ * is indistinguishable from a correct freeze over an unbatchable scene. The
33
+ * key here is `materialMergeSignature` from `@volter/threejs-runtime/render/structural-
34
+ * signature` — the SAME module the engine's own batcher and the draw-call
35
+ * advisor use, so all three agree about what "the same draw" means.
36
+ *
37
+ * ── ELIGIBILITY: DEGRADE, NEVER BREAK ───────────────────────────────────────
38
+ * Anything the engine's `staticBatchSkipReason` refuses — instanced, batched,
39
+ * skinned, morph-target, transparent, or explicitly opted out — stays exactly
40
+ * as it was. Multi-material meshes are the one capability-owned refinement:
41
+ * every geometry group becomes a material-specific candidate, so batching
42
+ * preserves the renderer's ordered slot assignment instead of flattening it.
43
+ * A malformed group, missing slot, or transparent slot refuses the WHOLE mesh
44
+ * before any original is hidden. A wrapper placed over a subtree that turns
45
+ * out to be mostly ineligible loses nothing and says so on the console
46
+ * ({@link freezeSubtree}'s `skipped` tally). It never leaves the graph
47
+ * half-collapsed.
48
+ *
49
+ * ── THE CONTRACT, WHICH IS THE PART A COMPILER CANNOT CHECK ─────────────────
50
+ * Children must be MOUNT-STATIC. Nothing under a freeze may move, re-colour,
51
+ * unmount or conditionally appear afterwards: the visible pixels come from the
52
+ * batched products, so a change to a hidden original simply never shows.
53
+ * Reactive scenery belongs outside the wrapper. That contract used to live
54
+ * only in a docblock, which is where contracts go to be broken silently —
55
+ * `mutation-watch.ts` is the tripwire that now says so out loud.
56
+ *
57
+ * ── RESOURCE OWNERSHIP, STATED ONCE ─────────────────────────────────────────
58
+ * OWNER: the {@link FrozenBatch} handle {@link freezeSubtree} returns. It owns
59
+ * exactly the objects it CREATED — the merged geometries and the product
60
+ * meshes — and nothing else. It does NOT own the source geometries or the
61
+ * materials: those belong to the game's own component tree, are only borrowed
62
+ * by the products, and disposing them here would tear the originals apart.
63
+ * SHARERS: `Frozen.tsx` (one handle per mounted wrapper) and
64
+ * `mutation-watch.ts` (reads the hidden originals). TEARDOWN:
65
+ * {@link restoreFrozen}, the ONE path that ends a freeze — it un-hides every
66
+ * original, removes every product, and disposes only the merged geometries it
67
+ * created. Idempotent.
68
+ */
69
+
70
+ import { markBuiltInternal } from '@volter/threejs-runtime/adapter/hierarchy-marks';
71
+ import {
72
+ geometryLayoutSignature,
73
+ geometrySignature,
74
+ materialMergeSignature,
75
+ type StaticBatchSkipReason,
76
+ staticBatchSkipReason,
77
+ } from '@volter/threejs-runtime/render/structural-signature';
78
+ import * as THREE from 'three';
79
+ import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
80
+
81
+ /** Marks a mesh this module CREATED, so a nested or repeated freeze walks past
82
+ * its own output instead of batching a batch. */
83
+ export const FROZEN_PRODUCT_KEY = 'staticBatchProduct';
84
+
85
+ /** One original, and the state a freeze changed on it. */
86
+ export interface FrozenSource {
87
+ readonly mesh: THREE.Mesh;
88
+ /** `visible` as the game set it, restored verbatim — a freeze must not
89
+ * un-hide something the game had deliberately hidden. */
90
+ readonly wasVisible: boolean;
91
+ }
92
+
93
+ /** What one freeze produced and what it needs to undo itself. */
94
+ export interface FrozenBatch {
95
+ /** The wrapper's name, for every message about this freeze. */
96
+ readonly name: string;
97
+ /** The node the products were added to. */
98
+ readonly root: THREE.Object3D;
99
+ /** The meshes this freeze created — instanced and merged products. */
100
+ readonly products: readonly THREE.Mesh[];
101
+ /** Product geometries this freeze created. Instanced products usually
102
+ * borrow source geometry, but a material-group instance owns its extracted
103
+ * group geometry too. Every entry is disposed exactly once on restore. */
104
+ readonly ownedGeometries: readonly THREE.BufferGeometry[];
105
+ /** The originals it hid, in walk order. */
106
+ readonly sources: readonly FrozenSource[];
107
+ /** Draws submitted by the members BEFORE (one per source) and AFTER (one per
108
+ * product). The reduction this freeze actually achieved, measured rather
109
+ * than assumed. */
110
+ readonly drawsBefore: number;
111
+ readonly drawsAfter: number;
112
+ /** Eligible meshes the freeze refused, by reason. `not-a-mesh` is never
113
+ * counted: a `<group>` is not a refusal. */
114
+ readonly skipped: Readonly<Partial<Record<FrozenSkipReason, number>>>;
115
+ /** A representative refused node per reason, so a warning can name one. */
116
+ readonly skippedExample: Readonly<Partial<Record<FrozenSkipReason, string>>>;
117
+ }
118
+
119
+ /** Capability-specific refusals extend the engine's node-level answer with
120
+ * the one invalid shape only group extraction can see. */
121
+ export type FrozenSkipReason = StaticBatchSkipReason | 'invalid-material-groups';
122
+
123
+ const activeBatches = new Set<FrozenBatch>();
124
+
125
+ /**
126
+ * Publish a mounted freeze to render-side consumers. `freezeSubtree` stays a pure mechanical
127
+ * operation: its React owners activate the handle only for the interval in which the declaration
128
+ * is mounted, and the returned cleanup is the one path that retires it.
129
+ */
130
+ export function activateFrozenBatch(batch: FrozenBatch): () => void {
131
+ activeBatches.add(batch);
132
+ let active = true;
133
+ return () => {
134
+ if (!active) return;
135
+ active = false;
136
+ activeBatches.delete(batch);
137
+ };
138
+ }
139
+
140
+ /** Live mount-static regions. The wrapper itself remains mobile, so consumers must observe it. */
141
+ export function activeFrozenBatches(): readonly FrozenBatch[] {
142
+ return [...activeBatches];
143
+ }
144
+
145
+ const UNNAMED = '(unnamed)';
146
+
147
+ function address(node: THREE.Object3D): string {
148
+ return `${node.parent?.name || UNNAMED}/${node.name || UNNAMED}`;
149
+ }
150
+
151
+ /**
152
+ * Collapse every batchable mesh under `root` into as few draws as the
153
+ * structure allows, and answer the handle that undoes it.
154
+ *
155
+ * The products are added to `root` itself and baked into ROOT-LOCAL space, so
156
+ * moving the wrapper still moves the whole frozen assembly as one — a freeze
157
+ * takes away per-child mobility, not the wrapper's own.
158
+ */
159
+ export function freezeSubtree(root: THREE.Object3D, name = 'Frozen'): FrozenBatch {
160
+ root.updateWorldMatrix(true, true);
161
+ const toLocal = new THREE.Matrix4().copy(root.matrixWorld).invert();
162
+
163
+ interface Candidate {
164
+ mesh: THREE.Mesh;
165
+ material: THREE.Material;
166
+ /** Full source geometry for ordinary meshes; one physically extracted
167
+ * renderable group for a material-array mesh. */
168
+ geometry: THREE.BufferGeometry;
169
+ /** Extracted group geometries belong to this freeze until transferred to
170
+ * a product or disposed after a merge. */
171
+ ownsGeometry: boolean;
172
+ /** A material-array source must be replaced even when this candidate is a
173
+ * singleton; otherwise hiding the source drops that group's pixels. */
174
+ requiresReplacement: boolean;
175
+ /** Same shape, instanceable as one geometry. */
176
+ geometryKey: string;
177
+ /** Which vertex attributes it carries — `mergeGeometries` refuses a batch
178
+ * whose members disagree, so this is what the merge path groups by. */
179
+ layoutKey: string;
180
+ }
181
+ const groups = new Map<string, Candidate[]>();
182
+ const skipped: Partial<Record<FrozenSkipReason, number>> = {};
183
+ const skippedExample: Partial<Record<FrozenSkipReason, string>> = {};
184
+ const temporaryGeometries = new Set<THREE.BufferGeometry>();
185
+
186
+ interface GeometryGroup {
187
+ readonly start: number;
188
+ readonly count: number;
189
+ readonly materialIndex?: number | undefined;
190
+ }
191
+
192
+ const groupRange = (
193
+ geometry: THREE.BufferGeometry,
194
+ group: GeometryGroup,
195
+ ): { start: number; count: number } | null => {
196
+ const elements = geometry.getIndex()?.count ?? geometry.getAttribute('position')?.count ?? 0;
197
+ if (
198
+ !Number.isInteger(group.start) ||
199
+ !Number.isInteger(group.count) ||
200
+ group.start < 0 ||
201
+ group.count < 0 ||
202
+ group.start + group.count > elements
203
+ ) {
204
+ return null;
205
+ }
206
+ const drawStart = geometry.drawRange.start;
207
+ const drawCount = geometry.drawRange.count;
208
+ const drawEnd = drawCount === Number.POSITIVE_INFINITY ? elements : drawStart + drawCount;
209
+ if (
210
+ !Number.isInteger(drawStart) ||
211
+ drawStart < 0 ||
212
+ (drawCount !== Number.POSITIVE_INFINITY && (!Number.isInteger(drawCount) || drawCount < 0)) ||
213
+ (!Number.isInteger(drawEnd) && drawEnd !== Number.POSITIVE_INFINITY)
214
+ ) {
215
+ return null;
216
+ }
217
+ const start = Math.max(group.start, drawStart);
218
+ const end = Math.min(group.start + group.count, drawEnd, elements);
219
+ return end <= start ? { start, count: 0 } : { start, count: end - start };
220
+ };
221
+
222
+ const extractGroup = (
223
+ geometry: THREE.BufferGeometry,
224
+ range: { start: number; count: number },
225
+ ): THREE.BufferGeometry | null => {
226
+ const extracted = geometry.clone();
227
+ extracted.clearGroups();
228
+ extracted.setDrawRange(0, Number.POSITIVE_INFINITY);
229
+ const index = geometry.getIndex();
230
+ if (index) {
231
+ const values = index.array.slice(range.start, range.start + range.count);
232
+ extracted.setIndex(new THREE.BufferAttribute(values, index.itemSize, index.normalized));
233
+ return extracted;
234
+ }
235
+ for (const [attributeName, attribute] of Object.entries(geometry.attributes)) {
236
+ if (
237
+ 'isInterleavedBufferAttribute' in attribute &&
238
+ attribute.isInterleavedBufferAttribute === true
239
+ ) {
240
+ extracted.dispose();
241
+ return null;
242
+ }
243
+ if (attribute.count < range.start + range.count) {
244
+ extracted.dispose();
245
+ return null;
246
+ }
247
+ const values = attribute.array.slice(
248
+ range.start * attribute.itemSize,
249
+ (range.start + range.count) * attribute.itemSize,
250
+ );
251
+ extracted.setAttribute(
252
+ attributeName,
253
+ new THREE.BufferAttribute(values, attribute.itemSize, attribute.normalized),
254
+ );
255
+ }
256
+ return extracted;
257
+ };
258
+
259
+ const addCandidate = (candidate: Candidate): void => {
260
+ const key = `${materialMergeSignature(candidate.material)}|${candidate.mesh.castShadow ? 1 : 0}${candidate.mesh.receiveShadow ? 1 : 0}|layers:${candidate.mesh.layers.mask}`;
261
+ const existing = groups.get(key);
262
+ if (existing) existing.push(candidate);
263
+ else groups.set(key, [candidate]);
264
+ };
265
+
266
+ const multiMaterialCandidates = (mesh: THREE.Mesh): Candidate[] | null => {
267
+ const materials = mesh.material as THREE.Material[];
268
+ const geometry = mesh.geometry as THREE.BufferGeometry;
269
+ if (
270
+ materials.length === 0 ||
271
+ materials.some((material) => !material?.isMaterial || material.transparent) ||
272
+ geometry.groups.length === 0
273
+ ) {
274
+ return null;
275
+ }
276
+ const candidates: Candidate[] = [];
277
+ const refuse = (): null => {
278
+ for (const candidate of candidates) {
279
+ temporaryGeometries.delete(candidate.geometry);
280
+ candidate.geometry.dispose();
281
+ }
282
+ return null;
283
+ };
284
+ for (const group of geometry.groups) {
285
+ const materialIndex = group.materialIndex ?? 0;
286
+ const range = groupRange(geometry, group);
287
+ if (
288
+ !Number.isInteger(materialIndex) ||
289
+ materialIndex < 0 ||
290
+ materialIndex >= materials.length
291
+ ) {
292
+ return refuse();
293
+ }
294
+ if (range === null) {
295
+ return refuse();
296
+ }
297
+ if (range.count === 0) continue;
298
+ const extracted = extractGroup(geometry, range);
299
+ if (!extracted) {
300
+ return refuse();
301
+ }
302
+ temporaryGeometries.add(extracted);
303
+ const layoutKey = geometryLayoutSignature(extracted);
304
+ candidates.push({
305
+ mesh,
306
+ material: materials[materialIndex] as THREE.Material,
307
+ geometry: extracted,
308
+ ownsGeometry: true,
309
+ requiresReplacement: true,
310
+ geometryKey: `${geometrySignature(geometry)}|group:${range.start}:${range.count}|${layoutKey}`,
311
+ layoutKey,
312
+ });
313
+ }
314
+ if (candidates.length === 0) return null;
315
+ return candidates;
316
+ };
317
+
318
+ const walk = (node: THREE.Object3D): void => {
319
+ if (node.userData[FROZEN_PRODUCT_KEY] === true) return;
320
+ // Hierarchical visibility: a hidden parent hides its subtree, and a freeze
321
+ // that swallowed the children of a hidden group would make them appear.
322
+ if (!node.visible) return;
323
+ const reason = staticBatchSkipReason(node);
324
+ if (reason === 'opted-out') {
325
+ skipped['opted-out'] = (skipped['opted-out'] ?? 0) + 1;
326
+ skippedExample['opted-out'] ??= address(node);
327
+ return; // the opt-out covers the whole subtree
328
+ }
329
+ if (reason === null) {
330
+ const mesh = node as THREE.Mesh;
331
+ const material = mesh.material as THREE.Material;
332
+ const geometry = mesh.geometry as THREE.BufferGeometry;
333
+ const layoutKey = geometryLayoutSignature(geometry);
334
+ const candidate: Candidate = {
335
+ mesh,
336
+ material,
337
+ geometry,
338
+ ownsGeometry: false,
339
+ requiresReplacement: false,
340
+ // The layout rides in the instancing key too: two geometries can be
341
+ // structurally identical primitives and still disagree about, say, a
342
+ // second UV set added downstream.
343
+ geometryKey: `${geometrySignature(geometry)}|${layoutKey}`,
344
+ layoutKey,
345
+ };
346
+ addCandidate(candidate);
347
+ } else if (reason === 'multi-material') {
348
+ const mesh = node as THREE.Mesh;
349
+ const candidates = multiMaterialCandidates(mesh);
350
+ if (candidates) {
351
+ for (const candidate of candidates) addCandidate(candidate);
352
+ } else {
353
+ const invalidReason: FrozenSkipReason =
354
+ Array.isArray(mesh.material) && mesh.material.some((material) => material?.transparent)
355
+ ? 'transparent'
356
+ : 'invalid-material-groups';
357
+ skipped[invalidReason] = (skipped[invalidReason] ?? 0) + 1;
358
+ skippedExample[invalidReason] ??= address(node);
359
+ }
360
+ } else if (reason !== 'not-a-mesh') {
361
+ skipped[reason] = (skipped[reason] ?? 0) + 1;
362
+ skippedExample[reason] ??= address(node);
363
+ }
364
+ for (const child of node.children) walk(child);
365
+ };
366
+ for (const child of root.children) walk(child);
367
+
368
+ const products: THREE.Mesh[] = [];
369
+ const sources: FrozenSource[] = [];
370
+ const sourceRecords = new Map<THREE.Mesh, FrozenSource>();
371
+ const ownedGeometries = new Set<THREE.BufferGeometry>();
372
+ const relative = new THREE.Matrix4();
373
+ let drawsBefore = 0;
374
+
375
+ const adopt = (product: THREE.Mesh, members: Candidate[], label: string): void => {
376
+ product.name = `${name} · ${label}`;
377
+ product.castShadow = members[0]?.mesh.castShadow ?? false;
378
+ product.receiveShadow = members[0]?.mesh.receiveShadow ?? false;
379
+ product.layers.mask = members[0]?.mesh.layers.mask ?? 1;
380
+ product.matrixAutoUpdate = false;
381
+ product.userData[FROZEN_PRODUCT_KEY] = true;
382
+ // A product is MACHINERY, not content: the hierarchy's job is to show the scenery the author
383
+ // wrote, and the originals are still there under their own names to be selected and inspected.
384
+ // Without this mark a freeze that collapsed 800 panels into two draws would ADD two rows to the
385
+ // panel it was supposed to make cheaper — `hierarchy-marks` names this exact case ("the
386
+ // instanced-mesh pools behind a tile field").
387
+ markBuiltInternal(product);
388
+ root.add(product);
389
+ products.push(product);
390
+ for (const member of members) {
391
+ drawsBefore += 1;
392
+ if (!sourceRecords.has(member.mesh)) {
393
+ const source = { mesh: member.mesh, wasVisible: member.mesh.visible };
394
+ sourceRecords.set(member.mesh, source);
395
+ sources.push(source);
396
+ }
397
+ member.mesh.visible = false;
398
+ }
399
+ };
400
+
401
+ const transferGeometry = (geometry: THREE.BufferGeometry): void => {
402
+ temporaryGeometries.delete(geometry);
403
+ ownedGeometries.add(geometry);
404
+ };
405
+
406
+ const disposeCandidateGeometry = (candidate: Candidate): void => {
407
+ if (!candidate.ownsGeometry || !temporaryGeometries.delete(candidate.geometry)) return;
408
+ candidate.geometry.dispose();
409
+ };
410
+
411
+ const adoptSingleton = (member: Candidate): void => {
412
+ const geometry = member.geometry;
413
+ relative.multiplyMatrices(toLocal, member.mesh.matrixWorld);
414
+ geometry.applyMatrix4(relative);
415
+ geometry.computeBoundingSphere();
416
+ transferGeometry(geometry);
417
+ adopt(new THREE.Mesh(geometry, member.material), [member], `group ${productNo++}`);
418
+ };
419
+
420
+ let productNo = 0;
421
+ for (const members of groups.values()) {
422
+ if (members.length < 2) {
423
+ const member = members[0];
424
+ if (member?.requiresReplacement) adoptSingleton(member);
425
+ continue; // one ordinary mesh is already one draw
426
+ }
427
+
428
+ // Same material AND same geometry → instancing, which keeps ONE copy of
429
+ // the vertices on the GPU. Different geometries under the same material
430
+ // have nothing to share but the shader, so they merge.
431
+ const byGeometry = new Map<string, Candidate[]>();
432
+ for (const member of members) {
433
+ const bucket = byGeometry.get(member.geometryKey);
434
+ if (bucket) bucket.push(member);
435
+ else byGeometry.set(member.geometryKey, [member]);
436
+ }
437
+
438
+ const heterogeneous: Candidate[] = [];
439
+ for (const bucket of byGeometry.values()) {
440
+ if (bucket.length < 2) {
441
+ heterogeneous.push(...bucket);
442
+ continue;
443
+ }
444
+ const prototype = bucket[0] as Candidate;
445
+ const instanced = new THREE.InstancedMesh(
446
+ prototype.geometry,
447
+ prototype.material,
448
+ bucket.length,
449
+ );
450
+ bucket.forEach((member, index) => {
451
+ relative.multiplyMatrices(toLocal, member.mesh.matrixWorld);
452
+ instanced.setMatrixAt(index, relative);
453
+ });
454
+ instanced.instanceMatrix.needsUpdate = true;
455
+ instanced.computeBoundingSphere();
456
+ if (prototype.ownsGeometry) transferGeometry(prototype.geometry);
457
+ for (const member of bucket.slice(1)) disposeCandidateGeometry(member);
458
+ adopt(instanced, bucket, `instanced ${productNo++}`);
459
+ }
460
+
461
+ // The leftovers merge, but only WITHIN one attribute layout.
462
+ // `mergeGeometries` answers `null` for a batch whose members disagree
463
+ // about attributes, and one such member in the pile would take the whole
464
+ // merge down with it — a wrapper that batched nothing because one panel
465
+ // carried a second UV set is the silent failure this splits apart.
466
+ const byLayout = new Map<string, Candidate[]>();
467
+ for (const member of heterogeneous) {
468
+ const bucket = byLayout.get(member.layoutKey);
469
+ if (bucket) bucket.push(member);
470
+ else byLayout.set(member.layoutKey, [member]);
471
+ }
472
+
473
+ for (const bucket of byLayout.values()) {
474
+ if (bucket.length < 2) {
475
+ const member = bucket[0];
476
+ if (member?.requiresReplacement) adoptSingleton(member);
477
+ continue; // an ordinary lone leftover is already one draw
478
+ }
479
+ const parts = bucket.map((member) => {
480
+ const geometry = member.geometry.clone();
481
+ relative.multiplyMatrices(toLocal, member.mesh.matrixWorld);
482
+ geometry.applyMatrix4(relative);
483
+ return geometry;
484
+ });
485
+ const merged = mergeGeometries(parts, false);
486
+ for (const part of parts) part.dispose();
487
+ // Still `null`? Then they were incompatible for a reason the layout
488
+ // signature does not see. Ordinary sources stay untouched; group
489
+ // candidates fall back to one replacement apiece so hiding their source
490
+ // can never make pixels disappear.
491
+ if (!merged) {
492
+ for (const member of bucket) {
493
+ if (member.requiresReplacement) adoptSingleton(member);
494
+ }
495
+ continue;
496
+ }
497
+ merged.computeBoundingSphere();
498
+ ownedGeometries.add(merged);
499
+ for (const member of bucket) disposeCandidateGeometry(member);
500
+ adopt(
501
+ new THREE.Mesh(merged, (bucket[0] as Candidate).material),
502
+ bucket,
503
+ `merged ${productNo++}`,
504
+ );
505
+ }
506
+ }
507
+
508
+ // Defensive ownership closure: every extracted group is either a product's
509
+ // geometry, consumed by a merge, or disposed here. No refused/failed path
510
+ // may strand GPU buffers.
511
+ for (const geometry of temporaryGeometries) geometry.dispose();
512
+ temporaryGeometries.clear();
513
+
514
+ return {
515
+ name,
516
+ root,
517
+ products,
518
+ ownedGeometries: [...ownedGeometries],
519
+ sources,
520
+ drawsBefore,
521
+ drawsAfter: products.length,
522
+ skipped,
523
+ skippedExample,
524
+ };
525
+ }
526
+
527
+ /** Idempotency without mutating the handle: the batch is a record of what
528
+ * happened, and a record that empties itself cannot be inspected afterwards. */
529
+ const alreadyRestored = new WeakSet<FrozenBatch>();
530
+
531
+ /**
532
+ * Undo a freeze: every original visible again exactly as the game left it,
533
+ * every product removed and its merged geometry disposed. Idempotent — the
534
+ * second call finds nothing to do.
535
+ *
536
+ * Only geometries this freeze CREATED are disposed. An ordinary instanced
537
+ * product borrows the prototype's source geometry; a group-instanced product
538
+ * owns its extracted geometry and lists it in `ownedGeometries`. Every product
539
+ * borrows its material from the live component tree.
540
+ */
541
+ export function restoreFrozen(batch: FrozenBatch): void {
542
+ if (alreadyRestored.has(batch)) return;
543
+ alreadyRestored.add(batch);
544
+ for (const source of batch.sources) source.mesh.visible = source.wasVisible;
545
+ for (const product of batch.products) {
546
+ batch.root.remove(product);
547
+ const instanced = product as THREE.InstancedMesh;
548
+ // `InstancedMesh.dispose()` releases the instance buffers only. Geometry
549
+ // ownership is handled uniformly by the batch record below.
550
+ if (instanced.isInstancedMesh) instanced.dispose();
551
+ }
552
+ for (const geometry of batch.ownedGeometries) geometry.dispose();
553
+ }
554
+
555
+ /**
556
+ * The console line for a freeze that had to refuse members, or `null` when it
557
+ * refused nothing.
558
+ *
559
+ * Refusals are aggregated into ONE line per wrapper rather than one per mesh:
560
+ * a subtree of 800 transparent panels would otherwise produce 800 warnings and
561
+ * teach the reader to mute the channel. An OPTED-OUT member is never in the
562
+ * count — it is a declaration, not a surprise, and warning about it would
563
+ * punish exactly the discipline this asks for.
564
+ */
565
+ export function describeFrozenSkips(batch: FrozenBatch): string | null {
566
+ const reasons = (Object.keys(batch.skipped) as FrozenSkipReason[])
567
+ .filter((reason) => reason !== 'opted-out')
568
+ .sort((a, b) => (batch.skipped[b] ?? 0) - (batch.skipped[a] ?? 0));
569
+ if (reasons.length === 0) return null;
570
+ const total = reasons.reduce((sum, reason) => sum + (batch.skipped[reason] ?? 0), 0);
571
+ const detail = reasons
572
+ .map((reason) => `${batch.skipped[reason]} ${reason} (e.g. ${batch.skippedExample[reason]})`)
573
+ .join(', ');
574
+ return (
575
+ `[static-batch] <Frozen name="${batch.name}"> left ${total} mesh(es) unbatched: ${detail}. ` +
576
+ 'They still render — each is simply its own draw. Move them out of the wrapper, or declare ' +
577
+ 'the exclusion where it is deliberate: userData={{ staticBatch: false }}.'
578
+ );
579
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * STATIC BATCHING, in one wrapper.
3
+ *
4
+ * A TSX world draws one mesh per JSX element. Past a few hundred elements the
5
+ * frame stops being about pixels and starts being about SUBMISSION: ~2.75 µs
6
+ * of CPU per draw call, so a four-thousand-mesh scene burns ~11 ms telling the
7
+ * GPU what to draw before it draws anything. `<Frozen>` collapses a
8
+ * mount-static subtree into a handful of draws, with no change to how the
9
+ * scenery is authored:
10
+ *
11
+ * ```tsx
12
+ * import { Frozen } from './lib/static-batch';
13
+ *
14
+ * <Frozen name="Terminal">{panels}</Frozen>
15
+ * ```
16
+ *
17
+ * Read `Frozen.tsx` for the contract (children must be mount-static) and
18
+ * `freeze.ts` for what gets collapsed, what is refused, and why the grouping
19
+ * key is structural rather than `material.uuid` — that last one is the trap
20
+ * this capability exists to keep a game out of.
21
+ *
22
+ * Nothing here runs at import: the wrapper does its work when it mounts.
23
+ */
24
+
25
+ export { Frozen, type FrozenProps } from './Frozen';
26
+ export {
27
+ activateFrozenBatch,
28
+ activeFrozenBatches,
29
+ describeFrozenSkips,
30
+ FROZEN_PRODUCT_KEY,
31
+ type FrozenBatch,
32
+ type FrozenSource,
33
+ freezeSubtree,
34
+ restoreFrozen,
35
+ } from './freeze';
36
+ export {
37
+ type FrozenMutationWatch,
38
+ MUTATION_SAMPLE_MS,
39
+ watchFrozenMutations,
40
+ } from './mutation-watch';
41
+ export {
42
+ createFrozenDirectionalShadowController,
43
+ type FrozenDirectionalShadowController,
44
+ } from './shadow-cache';