@relu-interactives/spatial-ecs 0.5.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/dist/{System-V_mv07le.d.cts → System-8D-XBDKZ.d.cts} +734 -8
  2. package/dist/{System-V_mv07le.d.ts → System-DPMAW30g.d.ts} +734 -8
  3. package/dist/{XRHandPose-CU3vNFuS.d.ts → XRHandPose-G0Dl-F0w.d.ts} +93 -3
  4. package/dist/{XRHandPose-CQTLGxFC.d.cts → XRHandPose-VQMT1WXj.d.cts} +93 -3
  5. package/dist/animation/index.cjs +252 -0
  6. package/dist/animation/index.cjs.map +1 -0
  7. package/dist/animation/index.d.cts +100 -0
  8. package/dist/animation/index.d.ts +100 -0
  9. package/dist/animation/index.js +4 -0
  10. package/dist/animation/index.js.map +1 -0
  11. package/dist/chunk-CKTHDXPS.js +11 -0
  12. package/dist/chunk-CKTHDXPS.js.map +1 -0
  13. package/dist/chunk-G4XJ53LS.js +1237 -0
  14. package/dist/chunk-G4XJ53LS.js.map +1 -0
  15. package/dist/chunk-GSWC6LLD.js +241 -0
  16. package/dist/chunk-GSWC6LLD.js.map +1 -0
  17. package/dist/{chunk-DF5SSRN6.js → chunk-JIJQFW6P.js} +1216 -2920
  18. package/dist/chunk-JIJQFW6P.js.map +1 -0
  19. package/dist/{chunk-BUEKOSR5.js → chunk-NNDUOQ7O.js} +188 -14
  20. package/dist/chunk-NNDUOQ7O.js.map +1 -0
  21. package/dist/chunk-OHCNPPMX.js +334 -0
  22. package/dist/chunk-OHCNPPMX.js.map +1 -0
  23. package/dist/{chunk-RXBLOO5B.js → chunk-SGF73YTF.js} +105 -715
  24. package/dist/chunk-SGF73YTF.js.map +1 -0
  25. package/dist/chunk-U727NTWQ.js +2856 -0
  26. package/dist/chunk-U727NTWQ.js.map +1 -0
  27. package/dist/{chunk-OY7ZODZP.js → chunk-XE4OXN2W.js} +3 -9
  28. package/dist/chunk-XE4OXN2W.js.map +1 -0
  29. package/dist/{chunk-OWQSESTO.js → chunk-XU2DVWCP.js} +4 -3
  30. package/dist/chunk-XU2DVWCP.js.map +1 -0
  31. package/dist/components/index.cjs +183 -10
  32. package/dist/components/index.cjs.map +1 -1
  33. package/dist/components/index.d.cts +5 -3
  34. package/dist/components/index.d.ts +5 -3
  35. package/dist/components/index.js +3 -2
  36. package/dist/docs/api/Behaviour/classes/Behaviour.md +366 -79
  37. package/dist/docs/api/Behaviour/index.md +7 -3
  38. package/dist/docs/api/Behaviour/type-aliases/UiInteractionEvent.md +35 -0
  39. package/dist/docs/api/Component/classes/Component.md +14 -1
  40. package/dist/docs/api/components/classes/AnimationComponent.md +26 -26
  41. package/dist/docs/api/components/classes/AudioComponent.md +19 -19
  42. package/dist/docs/api/components/classes/CameraComponent.md +14 -14
  43. package/dist/docs/api/components/classes/EntityType.md +4 -4
  44. package/dist/docs/api/components/classes/Environment.md +5 -4
  45. package/dist/docs/api/components/classes/ImageComponent.md +7 -7
  46. package/dist/docs/api/components/classes/ImageTarget.md +8 -8
  47. package/dist/docs/api/components/classes/ImageTargetAnchor.md +4 -4
  48. package/dist/docs/api/components/classes/InstanceSlotComponent.md +4 -4
  49. package/dist/docs/api/components/classes/InstancedMeshComponent.md +10 -10
  50. package/dist/docs/api/components/classes/LightComponent.md +6 -6
  51. package/dist/docs/api/components/classes/MaterialComponent.md +16 -16
  52. package/dist/docs/api/components/classes/MeshComponent.md +3 -3
  53. package/dist/docs/api/components/classes/MeshGeometryComponent.md +9 -9
  54. package/dist/docs/api/components/classes/ModelComponent.md +8 -5
  55. package/dist/docs/api/components/classes/Name.md +3 -3
  56. package/dist/docs/api/components/classes/Object3DRef.md +4 -4
  57. package/dist/docs/api/components/classes/ParentId.md +3 -3
  58. package/dist/docs/api/components/classes/PhysicsComponent.md +7 -6
  59. package/dist/docs/api/components/classes/Postprocessing.md +3 -3
  60. package/dist/docs/api/components/classes/PrefabInstance.md +5 -5
  61. package/dist/docs/api/components/classes/PrefabNode.md +3 -3
  62. package/dist/docs/api/components/classes/ScriptComponent.md +5 -5
  63. package/dist/docs/api/components/classes/ScriptObjectComponent.md +9 -9
  64. package/dist/docs/api/components/classes/Selectable.md +3 -3
  65. package/dist/docs/api/components/classes/SelectionTransformState.md +5 -5
  66. package/dist/docs/api/components/classes/SpriteComponent.md +7 -7
  67. package/dist/docs/api/components/classes/TransformComponent.md +12 -12
  68. package/dist/docs/api/components/classes/UIButtonComponent.md +63 -0
  69. package/dist/docs/api/components/classes/UIPanelComponent.md +62 -0
  70. package/dist/docs/api/components/classes/UISliderComponent.md +59 -0
  71. package/dist/docs/api/components/classes/UITextComponent.md +59 -0
  72. package/dist/docs/api/components/classes/UIToggleComponent.md +56 -0
  73. package/dist/docs/api/components/classes/VRTeleportAreaComponent.md +53 -0
  74. package/dist/docs/api/components/classes/VideoComponent.md +28 -28
  75. package/dist/docs/api/components/classes/XRAnchorComponent.md +64 -0
  76. package/dist/docs/api/components/classes/XREnvironmentComponent.md +64 -0
  77. package/dist/docs/api/components/classes/XRHandPoseComponent.md +50 -0
  78. package/dist/docs/api/components/classes/XRInteractableComponent.md +74 -0
  79. package/dist/docs/api/components/classes/XRInteractorComponent.md +57 -0
  80. package/dist/docs/api/components/classes/XRInteractorSideComponent.md +50 -0
  81. package/dist/docs/api/components/classes/XRRigComponent.md +68 -0
  82. package/dist/docs/api/components/functions/createAttachPoint.md +25 -0
  83. package/dist/docs/api/components/functions/createHandPoseEntry.md +25 -0
  84. package/dist/docs/api/components/functions/defaultMaterialValue.md +1 -1
  85. package/dist/docs/api/components/functions/handPoseDefinitionFor.md +33 -0
  86. package/dist/docs/api/components/functions/normalizeEnvironmentLighting.md +30 -0
  87. package/dist/docs/api/components/functions/normalizePhysicsState.md +1 -1
  88. package/dist/docs/api/components/functions/setModelLoadState.md +33 -0
  89. package/dist/docs/api/components/functions/setPhysicsLiveRef.md +3 -9
  90. package/dist/docs/api/components/index.md +61 -4
  91. package/dist/docs/api/components/type-aliases/AnimationComponentOptions.md +9 -9
  92. package/dist/docs/api/components/type-aliases/CameraComponentOptions.md +13 -13
  93. package/dist/docs/api/components/type-aliases/CameraProjection.md +1 -1
  94. package/dist/docs/api/components/type-aliases/CameraRuntimeType.md +5 -3
  95. package/dist/docs/api/components/type-aliases/ColliderShape.md +1 -1
  96. package/dist/docs/api/components/type-aliases/ColliderState.md +20 -20
  97. package/dist/docs/api/components/type-aliases/CubeMeshParameters.md +4 -4
  98. package/dist/docs/api/components/type-aliases/EditableMeshObjectKind.md +1 -1
  99. package/dist/docs/api/components/type-aliases/EnvironmentFogState.md +8 -8
  100. package/dist/docs/api/components/type-aliases/EnvironmentFogType.md +1 -1
  101. package/dist/docs/api/components/type-aliases/EnvironmentGradientEffect.md +1 -1
  102. package/dist/docs/api/components/type-aliases/EnvironmentLightingState.md +40 -0
  103. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxImageProjection.md +22 -0
  104. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxState.md +9 -7
  105. package/dist/docs/api/components/type-aliases/EnvironmentSkyboxType.md +1 -1
  106. package/dist/docs/api/components/type-aliases/EnvironmentState.md +7 -5
  107. package/dist/docs/api/components/type-aliases/EnvironmentToneMapping.md +16 -0
  108. package/dist/docs/api/components/type-aliases/ImageTargetAnchorOptions.md +3 -3
  109. package/dist/docs/api/components/type-aliases/ImageTargetReference.md +7 -7
  110. package/dist/docs/api/components/type-aliases/InstanceTransform.md +13 -13
  111. package/dist/docs/api/components/type-aliases/InstancedMeshKind.md +1 -1
  112. package/dist/docs/api/components/type-aliases/LightShadowState.md +37 -0
  113. package/dist/docs/api/components/type-aliases/LightValueState.md +13 -11
  114. package/dist/docs/api/components/type-aliases/MaterialTypeValue.md +1 -1
  115. package/dist/docs/api/components/type-aliases/MaterialValueState.md +28 -28
  116. package/dist/docs/api/components/type-aliases/MeshGeometryParameters.md +1 -1
  117. package/dist/docs/api/components/type-aliases/MeshGeometryParametersByKind.md +7 -7
  118. package/dist/docs/api/components/type-aliases/ModelLoadState.md +15 -0
  119. package/dist/docs/api/components/type-aliases/PhysicsState.md +3 -3
  120. package/dist/docs/api/components/type-aliases/PlaneMeshParameters.md +3 -3
  121. package/dist/docs/api/components/type-aliases/PostprocessingState.md +62 -62
  122. package/dist/docs/api/components/type-aliases/RadiusHeightMeshParameters.md +3 -3
  123. package/dist/docs/api/components/type-aliases/RadiusMeshParameters.md +2 -2
  124. package/dist/docs/api/components/type-aliases/RigidbodyState.md +17 -17
  125. package/dist/docs/api/components/type-aliases/RigidbodyType.md +1 -1
  126. package/dist/docs/api/components/type-aliases/ScriptEntry.md +8 -8
  127. package/dist/docs/api/components/type-aliases/ScriptObjectComponentInput.md +1 -1
  128. package/dist/docs/api/components/type-aliases/ScriptObjectRuntimeState.md +1 -1
  129. package/dist/docs/api/components/type-aliases/TextureFilteringValue.md +1 -1
  130. package/dist/docs/api/components/type-aliases/TransformData.md +4 -4
  131. package/dist/docs/api/components/type-aliases/TransformModeValue.md +1 -1
  132. package/dist/docs/api/components/type-aliases/TransformSpaceValue.md +1 -1
  133. package/dist/docs/api/components/type-aliases/UIButtonOptions.md +45 -0
  134. package/dist/docs/api/components/type-aliases/UIPanelOptions.md +39 -0
  135. package/dist/docs/api/components/type-aliases/UISliderOptions.md +39 -0
  136. package/dist/docs/api/components/type-aliases/UITextOptions.md +41 -0
  137. package/dist/docs/api/components/type-aliases/UIToggleOptions.md +35 -0
  138. package/dist/docs/api/components/type-aliases/UiFollowMode.md +15 -0
  139. package/dist/docs/api/components/type-aliases/UiTextAlign.md +13 -0
  140. package/dist/docs/api/components/type-aliases/UiTextVAlign.md +13 -0
  141. package/dist/docs/api/components/type-aliases/VRTeleportAreaOptions.md +29 -0
  142. package/dist/docs/api/components/type-aliases/Vec3.md +4 -4
  143. package/dist/docs/api/components/type-aliases/VrTeleportShape.md +15 -0
  144. package/dist/docs/api/components/type-aliases/XRAnchorOptions.md +23 -0
  145. package/dist/docs/api/components/type-aliases/XRHandPoseOptions.md +23 -0
  146. package/dist/docs/api/components/type-aliases/XRInteractableOptions.md +75 -0
  147. package/dist/docs/api/components/type-aliases/XRInteractorOptions.md +37 -0
  148. package/dist/docs/api/components/type-aliases/XRRigOptions.md +51 -0
  149. package/dist/docs/api/components/type-aliases/XrAttachPoint.md +53 -0
  150. package/dist/docs/api/components/type-aliases/XrDistanceGrabBehavior.md +19 -0
  151. package/dist/docs/api/components/type-aliases/XrEnvironmentKind.md +15 -0
  152. package/dist/docs/api/components/type-aliases/XrEnvironmentOrientation.md +15 -0
  153. package/dist/docs/api/components/type-aliases/XrEnvironmentPhysics.md +25 -0
  154. package/dist/docs/api/components/type-aliases/XrGrabGesture.md +19 -0
  155. package/dist/docs/api/components/type-aliases/XrGrabHands.md +20 -0
  156. package/dist/docs/api/components/type-aliases/XrGrabReach.md +20 -0
  157. package/dist/docs/api/components/type-aliases/XrHandPoseEntry.md +49 -0
  158. package/dist/docs/api/components/type-aliases/XrHandPreset.md +19 -0
  159. package/dist/docs/api/components/type-aliases/XrInteractorSide.md +15 -0
  160. package/dist/docs/api/components/type-aliases/XrInteractorType.md +13 -0
  161. package/dist/docs/api/components/type-aliases/XrLocomotionMode.md +15 -0
  162. package/dist/docs/api/components/type-aliases/XrTrackingOrigin.md +24 -0
  163. package/dist/docs/api/components/type-aliases/XrTwoHandRotation.md +15 -0
  164. package/dist/docs/api/components/variables/DEFAULT_COLLIDER_STATE.md +1 -1
  165. package/dist/docs/api/components/variables/DEFAULT_ENVIRONMENT_LIGHTING.md +15 -0
  166. package/dist/docs/api/components/variables/DEFAULT_ENVIRONMENT_STATE.md +1 -1
  167. package/dist/docs/api/components/variables/DEFAULT_INSTANCED_MESH_PARAMETERS.md +1 -1
  168. package/dist/docs/api/components/variables/DEFAULT_INSTANCE_TRANSFORM.md +1 -1
  169. package/dist/docs/api/components/variables/DEFAULT_MESH_GEOMETRY_PARAMETERS.md +1 -1
  170. package/dist/docs/api/components/variables/DEFAULT_PHYSICS_STATE.md +1 -1
  171. package/dist/docs/api/components/variables/DEFAULT_POSE_CURLS.md +13 -0
  172. package/dist/docs/api/components/variables/DEFAULT_POSE_DIRECTIONS.md +13 -0
  173. package/dist/docs/api/components/variables/DEFAULT_POSTPROCESSING_STATE.md +1 -1
  174. package/dist/docs/api/components/variables/DEFAULT_RIGIDBODY_STATE.md +1 -1
  175. package/dist/docs/api/components/variables/MODEL_NO_PATH_MESSAGE.md +15 -0
  176. package/dist/docs/api/index/classes/AnimationStateMachine.md +16 -16
  177. package/dist/docs/api/index/classes/AnimationStateManager.md +13 -13
  178. package/dist/docs/api/index/classes/AnimationSystem.md +3 -3
  179. package/dist/docs/api/index/classes/AudioSystem.md +4 -4
  180. package/dist/docs/api/index/classes/CameraSyncSystem.md +2 -2
  181. package/dist/docs/api/index/classes/EnvironmentSyncSystem.md +19 -4
  182. package/dist/docs/api/index/classes/HandPoseTracker.md +114 -0
  183. package/dist/docs/api/index/classes/InputSystem.md +33 -33
  184. package/dist/docs/api/index/classes/InstancedMeshSystem.md +2 -2
  185. package/dist/docs/api/index/classes/LightSyncSystem.md +2 -2
  186. package/dist/docs/api/index/classes/MaterialSyncSystem.md +3 -3
  187. package/dist/docs/api/index/classes/MeshGeometrySystem.md +2 -2
  188. package/dist/docs/api/index/classes/ObjectManagementSystem.md +14 -14
  189. package/dist/docs/api/index/classes/ObjectVisibilitySystem.md +2 -2
  190. package/dist/docs/api/index/classes/PhysicsSyncSystem.md +4 -4
  191. package/dist/docs/api/index/classes/PostprocessingSyncSystem.md +34 -10
  192. package/dist/docs/api/index/classes/RenderSystem.md +13 -4
  193. package/dist/docs/api/index/classes/ScriptBehaviourSystem.md +9 -5
  194. package/dist/docs/api/index/classes/ScriptObject.md +10 -10
  195. package/dist/docs/api/index/classes/ScriptObjectSystem.md +6 -6
  196. package/dist/docs/api/index/classes/TeleportAimGate.md +114 -0
  197. package/dist/docs/api/index/classes/ThrowVelocityTracker.md +89 -0
  198. package/dist/docs/api/index/classes/TransformSyncSystem.md +2 -2
  199. package/dist/docs/api/index/classes/UIInteractionSystem.md +60 -0
  200. package/dist/docs/api/index/classes/UIPanelSystem.md +79 -0
  201. package/dist/docs/api/index/classes/VRTeleportAreaSystem.md +49 -0
  202. package/dist/docs/api/index/classes/VideoSystem.md +8 -6
  203. package/dist/docs/api/index/classes/World.md +282 -100
  204. package/dist/docs/api/index/classes/XRAnchorSystem.md +90 -0
  205. package/dist/docs/api/index/classes/XRControllerVisualSystem.md +77 -0
  206. package/dist/docs/api/index/classes/XREnvironmentSystem.md +137 -0
  207. package/dist/docs/api/index/classes/XRHandPoseSystem.md +107 -0
  208. package/dist/docs/api/index/classes/XRHitTestSystem.md +93 -0
  209. package/dist/docs/api/index/classes/XRHoverVisualSystem.md +76 -0
  210. package/dist/docs/api/index/classes/XRInputSystem.md +189 -0
  211. package/dist/docs/api/index/classes/XRInteractionSystem.md +323 -0
  212. package/dist/docs/api/index/classes/XRLocomotionSystem.md +128 -0
  213. package/dist/docs/api/index/classes/XRSessionSystem.md +116 -0
  214. package/dist/docs/api/index/classes/XRTeleportVisualSystem.md +75 -0
  215. package/dist/docs/api/index/functions/angularVelocityFrom.md +33 -0
  216. package/dist/docs/api/index/functions/applyHandMaterial.md +26 -0
  217. package/dist/docs/api/index/functions/applyPrefabToSnapshotInstances.md +1 -1
  218. package/dist/docs/api/index/functions/arcGroundCrossing.md +30 -0
  219. package/dist/docs/api/index/functions/buildComponentValueMaps.md +1 -1
  220. package/dist/docs/api/index/functions/buildNodeKeyMapsFromSnapshot.md +1 -1
  221. package/dist/docs/api/index/functions/buildNodeMapsFromSnapshot.md +1 -1
  222. package/dist/docs/api/index/functions/canonicalizeComponentValue.md +1 -1
  223. package/dist/docs/api/index/functions/clearModelCache.md +1 -1
  224. package/dist/docs/api/index/functions/clearPrefabCache.md +1 -1
  225. package/dist/docs/api/index/functions/cloneHandPose.md +23 -0
  226. package/dist/docs/api/index/functions/collectDiffPaths.md +1 -1
  227. package/dist/docs/api/index/functions/collectDirectPrefabDependencies.md +1 -1
  228. package/dist/docs/api/index/functions/collectEntitySubtree.md +1 -1
  229. package/dist/docs/api/index/functions/collectSnapshotSubtreeIds.md +1 -1
  230. package/dist/docs/api/index/functions/computeFingerCurl.md +29 -0
  231. package/dist/docs/api/index/functions/computeHandShape.md +29 -0
  232. package/dist/docs/api/index/functions/computeInstanceOverrides.md +1 -1
  233. package/dist/docs/api/index/functions/computePalmPose.md +30 -0
  234. package/dist/docs/api/index/functions/computePalmWidth.md +25 -0
  235. package/dist/docs/api/index/functions/computePinch.md +29 -0
  236. package/dist/docs/api/index/functions/computeSpread.md +33 -0
  237. package/dist/docs/api/index/functions/createAnimationComponent.md +1 -1
  238. package/dist/docs/api/index/functions/createAudioComponent.md +1 -1
  239. package/dist/docs/api/index/functions/createCameraComponent.md +1 -1
  240. package/dist/docs/api/index/functions/createEntityTypeComponent.md +2 -2
  241. package/dist/docs/api/index/functions/createEnvironmentComponent.md +1 -1
  242. package/dist/docs/api/index/functions/createHandMaterial.md +30 -0
  243. package/dist/docs/api/index/functions/createImageComponent.md +1 -1
  244. package/dist/docs/api/index/functions/createImageTargetAnchorComponent.md +1 -1
  245. package/dist/docs/api/index/functions/createImageTargetComponent.md +2 -2
  246. package/dist/docs/api/index/functions/createInstanceSlotComponent.md +1 -1
  247. package/dist/docs/api/index/functions/createInstancedMeshComponent.md +1 -1
  248. package/dist/docs/api/index/functions/createLightComponent.md +2 -2
  249. package/dist/docs/api/index/functions/createMaterialComponent.md +2 -2
  250. package/dist/docs/api/index/functions/createMeshComponent.md +1 -1
  251. package/dist/docs/api/index/functions/createMeshGeometryComponent.md +1 -1
  252. package/dist/docs/api/index/functions/createModelComponent.md +1 -1
  253. package/dist/docs/api/index/functions/createNameComponent.md +1 -1
  254. package/dist/docs/api/index/functions/createObject3DRefComponent.md +1 -1
  255. package/dist/docs/api/index/functions/createParentIdComponent.md +1 -1
  256. package/dist/docs/api/index/functions/createPhysicsComponent.md +1 -1
  257. package/dist/docs/api/index/functions/createPostprocessingComponent.md +1 -1
  258. package/dist/docs/api/index/functions/createScriptComponent.md +1 -1
  259. package/dist/docs/api/index/functions/createScriptObjectComponent.md +1 -1
  260. package/dist/docs/api/index/functions/createSelectableComponent.md +1 -1
  261. package/dist/docs/api/index/functions/createSelectionTransformStateComponent.md +1 -1
  262. package/dist/docs/api/index/functions/createSpriteComponent.md +1 -1
  263. package/dist/docs/api/index/functions/createTransformComponent.md +1 -1
  264. package/dist/docs/api/index/functions/createUIButtonComponent.md +28 -0
  265. package/dist/docs/api/index/functions/createUIPanelComponent.md +28 -0
  266. package/dist/docs/api/index/functions/createUISliderComponent.md +28 -0
  267. package/dist/docs/api/index/functions/createUITextComponent.md +28 -0
  268. package/dist/docs/api/index/functions/createUIToggleComponent.md +28 -0
  269. package/dist/docs/api/index/functions/createVRTeleportAreaComponent.md +28 -0
  270. package/dist/docs/api/index/functions/createVideoComponent.md +1 -1
  271. package/dist/docs/api/index/functions/createXRAnchorComponent.md +28 -0
  272. package/dist/docs/api/index/functions/createXRHandPoseComponent.md +28 -0
  273. package/dist/docs/api/index/functions/createXRInteractableComponent.md +28 -0
  274. package/dist/docs/api/index/functions/createXRInteractorComponent.md +28 -0
  275. package/dist/docs/api/index/functions/createXRInteractorSideComponent.md +28 -0
  276. package/dist/docs/api/index/functions/createXRRigComponent.md +28 -0
  277. package/dist/docs/api/index/functions/decanonicalizeComponentValue.md +1 -1
  278. package/dist/docs/api/index/functions/despawnPrefab.md +1 -1
  279. package/dist/docs/api/index/functions/encodeOverride.md +1 -1
  280. package/dist/docs/api/index/functions/estimateHandScale.md +28 -0
  281. package/dist/docs/api/index/functions/findSnapshotRootEntityIds.md +1 -1
  282. package/dist/docs/api/index/functions/findTopLevelNestedRegions.md +1 -1
  283. package/dist/docs/api/index/functions/getHandPosePreset.md +31 -0
  284. package/dist/docs/api/index/functions/groupOverridePaths.md +1 -1
  285. package/dist/docs/api/index/functions/handPoseFromCurls.md +42 -0
  286. package/dist/docs/api/index/functions/hydrateWorldFromSnapshot.md +1 -1
  287. package/dist/docs/api/index/functions/hydrateWorldFromSnapshotDetailed.md +1 -1
  288. package/dist/docs/api/index/functions/indexSnapshotByEntity.md +1 -1
  289. package/dist/docs/api/index/functions/instantiateSubtree.md +1 -1
  290. package/dist/docs/api/index/functions/loadPrefab.md +1 -1
  291. package/dist/docs/api/index/functions/loadSharedGltf.md +1 -1
  292. package/dist/docs/api/index/functions/matchHandPose.md +34 -0
  293. package/dist/docs/api/index/functions/mergeComponentValue.md +1 -1
  294. package/dist/docs/api/index/functions/parseOverride.md +1 -1
  295. package/dist/docs/api/index/functions/parsePrefabAsset.md +1 -1
  296. package/dist/docs/api/index/functions/prefabFileVersionFor.md +1 -1
  297. package/dist/docs/api/index/functions/readXrCapabilities.md +30 -0
  298. package/dist/docs/api/index/functions/registerDefaultComponentFactories.md +1 -1
  299. package/dist/docs/api/index/functions/registerDefaultFactories.md +1 -1
  300. package/dist/docs/api/index/functions/remapSnapshotEntityIds.md +3 -3
  301. package/dist/docs/api/index/functions/resolveDracoDecoderPath.md +1 -1
  302. package/dist/docs/api/index/functions/restoreComponentState.md +1 -1
  303. package/dist/docs/api/index/functions/restorePostCreateComponents.md +1 -1
  304. package/dist/docs/api/index/functions/sampleTeleportArc.md +40 -0
  305. package/dist/docs/api/index/functions/serializeEntitySubtree.md +1 -1
  306. package/dist/docs/api/index/functions/setDracoDecoderPath.md +1 -1
  307. package/dist/docs/api/index/functions/setPrefabAssetUrlResolver.md +1 -1
  308. package/dist/docs/api/index/functions/spawnPrefab.md +1 -1
  309. package/dist/docs/api/index/index.md +116 -4
  310. package/dist/docs/api/index/interfaces/ChromaKeyOptions.md +20 -0
  311. package/dist/docs/api/index/interfaces/EcsSystem.md +3 -3
  312. package/dist/docs/api/index/interfaces/PostprocessingBuildContext.md +21 -0
  313. package/dist/docs/api/index/interfaces/PostprocessingPipeline.md +84 -0
  314. package/dist/docs/api/index/interfaces/RenderBackend.md +168 -0
  315. package/dist/docs/api/index/interfaces/StateMachine.md +13 -13
  316. package/dist/docs/api/index/type-aliases/AnimationStateCallbacks.md +4 -4
  317. package/dist/docs/api/index/type-aliases/ApplyPrefabToSnapshotResult.md +3 -3
  318. package/dist/docs/api/index/type-aliases/ComponentFactory.md +1 -1
  319. package/dist/docs/api/index/type-aliases/ComponentValueMaps.md +50 -26
  320. package/dist/docs/api/index/type-aliases/EcsComponentFactoryKind.md +14 -2
  321. package/dist/docs/api/index/type-aliases/EcsRenderer.md +17 -0
  322. package/dist/docs/api/index/type-aliases/EcsWorldComponentSnapshot.md +3 -3
  323. package/dist/docs/api/index/type-aliases/EcsWorldStateSnapshotLike.md +4 -4
  324. package/dist/docs/api/index/type-aliases/ExternalRefWarning.md +5 -5
  325. package/dist/docs/api/index/type-aliases/HandMaterialOptions.md +32 -0
  326. package/dist/docs/api/index/type-aliases/HandPoseTrackerOptions.md +27 -0
  327. package/dist/docs/api/index/type-aliases/HydrateOptions.md +3 -3
  328. package/dist/docs/api/index/type-aliases/HydrateWorldFromSnapshotResult.md +4 -4
  329. package/dist/docs/api/index/type-aliases/InstantiateSubtreeResult.md +3 -3
  330. package/dist/docs/api/index/type-aliases/KeyboardKey.md +1 -1
  331. package/dist/docs/api/index/type-aliases/ModuleLoader.md +1 -1
  332. package/dist/docs/api/index/type-aliases/MouseButton.md +1 -1
  333. package/dist/docs/api/index/type-aliases/NestedRegion.md +5 -5
  334. package/dist/docs/api/index/type-aliases/NodeKeyMaps.md +5 -5
  335. package/dist/docs/api/index/type-aliases/NodeMaps.md +4 -4
  336. package/dist/docs/api/index/type-aliases/PrefabAsset.md +5 -5
  337. package/dist/docs/api/index/type-aliases/RemapSnapshotOptions.md +2 -2
  338. package/dist/docs/api/index/type-aliases/RendererKind.md +15 -0
  339. package/dist/docs/api/index/type-aliases/ScriptBehaviourSystemOptions.md +23 -0
  340. package/dist/docs/api/index/type-aliases/ScriptErrorEvent.md +39 -0
  341. package/dist/docs/api/index/type-aliases/ScriptErrorListener.md +25 -0
  342. package/dist/docs/api/index/type-aliases/ScriptErrorPhase.md +28 -0
  343. package/dist/docs/api/index/type-aliases/ScriptErrorReport.md +31 -0
  344. package/dist/docs/api/index/type-aliases/ScriptErrorSource.md +15 -0
  345. package/dist/docs/api/index/type-aliases/SelectionContext.md +4 -4
  346. package/dist/docs/api/index/type-aliases/SerializeEntitySubtreeResult.md +4 -4
  347. package/dist/docs/api/index/type-aliases/SpawnPrefabOptions.md +5 -5
  348. package/dist/docs/api/index/type-aliases/SpawnPrefabSource.md +1 -1
  349. package/dist/docs/api/index/type-aliases/SubtreeSnapshot.md +3 -3
  350. package/dist/docs/api/index/type-aliases/UiCanvasLike.md +46 -0
  351. package/dist/docs/api/index/type-aliases/VelocitySample.md +23 -0
  352. package/dist/docs/api/index/type-aliases/XrCapabilities.md +41 -0
  353. package/dist/docs/api/index/type-aliases/XrFinger.md +13 -0
  354. package/dist/docs/api/index/type-aliases/XrFingerCurl.md +27 -0
  355. package/dist/docs/api/index/type-aliases/XrFingerTarget.md +31 -0
  356. package/dist/docs/api/index/type-aliases/XrHandJointName.md +40 -0
  357. package/dist/docs/api/index/type-aliases/XrHandJoints.md +18 -0
  358. package/dist/docs/api/index/type-aliases/XrHandPoseAxis.md +15 -0
  359. package/dist/docs/api/index/type-aliases/XrHandPoseContext.md +23 -0
  360. package/dist/docs/api/index/type-aliases/XrHandPoseDefinition.md +47 -0
  361. package/dist/docs/api/index/type-aliases/XrHandPoseDirection.md +21 -0
  362. package/dist/docs/api/index/type-aliases/XrHandPoseEvent.md +13 -0
  363. package/dist/docs/api/index/type-aliases/XrHandPoseEventDetail.md +27 -0
  364. package/dist/docs/api/index/type-aliases/XrHandPoseMatch.md +25 -0
  365. package/dist/docs/api/index/type-aliases/XrHandPoseOrientation.md +25 -0
  366. package/dist/docs/api/index/type-aliases/XrHandShape.md +46 -0
  367. package/dist/docs/api/index/type-aliases/XrHandedness.md +15 -0
  368. package/dist/docs/api/index/type-aliases/XrHitResult.md +25 -0
  369. package/dist/docs/api/index/type-aliases/XrInputKind.md +19 -0
  370. package/dist/docs/api/index/type-aliases/XrInputState.md +60 -0
  371. package/dist/docs/api/index/type-aliases/XrPalmPose.md +31 -0
  372. package/dist/docs/api/index/variables/ADDED_NODE_MARKER_COMPONENT.md +2 -2
  373. package/dist/docs/api/index/variables/ARC_GRAVITY.md +15 -0
  374. package/dist/docs/api/index/variables/ARC_MAX_TIME.md +15 -0
  375. package/dist/docs/api/index/variables/ARC_SPEED.md +15 -0
  376. package/dist/docs/api/index/variables/ARC_STEPS.md +15 -0
  377. package/dist/docs/api/index/variables/CURLED_COS.md +13 -0
  378. package/dist/docs/api/index/variables/DEFAULT_DIRECTION_TOLERANCE.md +15 -0
  379. package/dist/docs/api/index/variables/DEFAULT_ENTER_CONFIDENCE.md +13 -0
  380. package/dist/docs/api/index/variables/DEFAULT_EXIT_CONFIDENCE.md +13 -0
  381. package/dist/docs/api/index/variables/DEFAULT_LOST_GRACE_SECONDS.md +13 -0
  382. package/dist/docs/api/index/variables/DEFAULT_MIN_HOLD_SECONDS.md +13 -0
  383. package/dist/docs/api/index/variables/DEFAULT_SCRIPT_ERROR_RETRY_DELAY.md +17 -0
  384. package/dist/docs/api/index/variables/FALLBACK_HAND_SCALE.md +15 -0
  385. package/dist/docs/api/index/variables/HAND_POSE_PRESETS.md +19 -0
  386. package/dist/docs/api/index/variables/HAND_POSE_PRESET_IDS.md +15 -0
  387. package/dist/docs/api/index/variables/HYDRATABLE_ENTITY_KINDS.md +1 -1
  388. package/dist/docs/api/index/variables/KeyboardKeys.md +89 -89
  389. package/dist/docs/api/index/variables/MATCH_FALLOFF.md +15 -0
  390. package/dist/docs/api/index/variables/MAX_PLAUSIBLE_SPEED.md +25 -0
  391. package/dist/docs/api/index/variables/MAX_SAMPLE_AGE_SECONDS.md +18 -0
  392. package/dist/docs/api/index/variables/NODE_KEY_SEPARATOR.md +1 -1
  393. package/dist/docs/api/index/variables/NO_XR_CAPABILITIES.md +15 -0
  394. package/dist/docs/api/index/variables/OVERRIDE_DIFF_EXCLUDED_COMPONENTS.md +1 -1
  395. package/dist/docs/api/index/variables/OVERRIDE_ROOT_EXCLUDED_COMPONENTS.md +1 -1
  396. package/dist/docs/api/index/variables/PINCH_FINGERS.md +15 -0
  397. package/dist/docs/api/index/variables/PREFAB_FILE_VERSION.md +1 -1
  398. package/dist/docs/api/index/variables/PREFAB_FILE_VERSION_NESTED.md +1 -1
  399. package/dist/docs/api/index/variables/SCRIPT_OBJECT_ROOT_FLAG.md +1 -1
  400. package/dist/docs/api/index/variables/SHARED_MODEL_RESOURCE_FLAG.md +1 -1
  401. package/dist/docs/api/index/variables/STRAIGHT_COS.md +19 -0
  402. package/dist/docs/api/index/variables/VELOCITY_SAMPLES.md +15 -0
  403. package/dist/docs/api/index/variables/XR_FINGERS.md +13 -0
  404. package/dist/docs/api/index/variables/XR_HOVER_SHELL_FLAG.md +15 -0
  405. package/dist/docs/api/index/variables/webglBackend.md +16 -0
  406. package/dist/docs/api/typedoc-sidebar.json +1 -1
  407. package/dist/docs/api/types/type-aliases/AssetInput.md +3 -3
  408. package/dist/docs/api/types/type-aliases/CheckboxInput.md +3 -3
  409. package/dist/docs/api/types/type-aliases/ColorInput.md +1 -1
  410. package/dist/docs/api/types/type-aliases/ComponentInput.md +4 -4
  411. package/dist/docs/api/types/type-aliases/ComponentKind.md +1 -1
  412. package/dist/docs/api/types/type-aliases/DropdownInput.md +3 -3
  413. package/dist/docs/api/types/type-aliases/DropdownOption.md +3 -3
  414. package/dist/docs/api/types/type-aliases/EcsEntityEditorKind.md +1 -1
  415. package/dist/docs/api/types/type-aliases/EcsEntityKind.md +10 -2
  416. package/dist/docs/api/types/type-aliases/EntityId.md +1 -1
  417. package/dist/docs/api/types/type-aliases/EntityInput.md +3 -3
  418. package/dist/docs/api/types/type-aliases/FloatInput.md +1 -1
  419. package/dist/docs/api/types/type-aliases/IntersectionResult.md +3 -3
  420. package/dist/docs/api/types/type-aliases/ParentLookup.md +3 -3
  421. package/dist/docs/api/types/type-aliases/SelectionListener.md +1 -1
  422. package/dist/docs/api/types/type-aliases/SliderInput.md +5 -5
  423. package/dist/docs/api/types/type-aliases/TextInput.md +1 -1
  424. package/dist/docs/api/types/type-aliases/ToggleInput.md +1 -1
  425. package/dist/docs/api/types/type-aliases/Vec3.md +1 -1
  426. package/dist/docs/api/types/type-aliases/Vector2Input.md +3 -3
  427. package/dist/docs/api/types/type-aliases/Vector3Input.md +4 -4
  428. package/dist/docs/api/types/type-aliases/WorldEntityView.md +1 -1
  429. package/dist/docs/guide/audio-mixing-and-sound-banks.md +71 -0
  430. package/dist/docs/guide/authoring-a-behaviour.md +3 -3
  431. package/dist/docs/guide/authoring-a-script-object.md +12 -9
  432. package/dist/docs/guide/behaviour-data-binding/index.md +11 -11
  433. package/dist/docs/guide/behaviour-lifecycle.md +9 -0
  434. package/dist/docs/guide/bone-attachments-and-ik.md +128 -0
  435. package/dist/docs/guide/component-catalog.md +10 -2
  436. package/dist/docs/guide/components/light.md +42 -0
  437. package/dist/docs/guide/components/model.md +32 -3
  438. package/dist/docs/guide/concepts.md +2 -2
  439. package/dist/docs/guide/getting-started/creating-objects.md +2 -2
  440. package/dist/docs/guide/getting-started/editing-objects.md +2 -2
  441. package/dist/docs/guide/getting-started/editor/ai-agent.md +227 -0
  442. package/dist/docs/guide/getting-started/editor/hierarchy.md +2 -2
  443. package/dist/docs/guide/getting-started/editor/index.md +3 -2
  444. package/dist/docs/guide/getting-started/editor/inspector.md +2 -2
  445. package/dist/docs/guide/getting-started/editor/scene-inspector.md +3 -3
  446. package/dist/docs/guide/getting-started/editor/toolbar.md +11 -11
  447. package/dist/docs/guide/getting-started/editor/viewport.md +17 -9
  448. package/dist/docs/guide/getting-started/index.md +1 -1
  449. package/dist/docs/guide/getting-started/installation.md +1 -1
  450. package/dist/docs/guide/getting-started/preview-and-test.md +7 -7
  451. package/dist/docs/guide/getting-started/project-types/image-tracking.md +23 -11
  452. package/dist/docs/guide/getting-started/project-types/index.md +4 -4
  453. package/dist/docs/guide/getting-started/project-types/vr.md +270 -2
  454. package/dist/docs/guide/getting-started/projects.md +4 -5
  455. package/dist/docs/guide/getting-started/shortcuts.md +5 -4
  456. package/dist/docs/guide/multiplayer.md +92 -0
  457. package/dist/docs/guide/scene/environment.md +101 -4
  458. package/dist/docs/guide/scene/index.md +4 -4
  459. package/dist/docs/guide/scene/postprocessing.md +67 -5
  460. package/dist/docs/guide/spawning/area-light.md +2 -0
  461. package/dist/docs/guide/systems/environment-sync-system.md +11 -6
  462. package/dist/docs/guide/systems/light-sync-system.md +5 -3
  463. package/dist/docs/guide/systems/postprocessing-sync-system.md +16 -6
  464. package/dist/docs/guide/systems/render-system.md +7 -6
  465. package/dist/docs/guide/systems/script-behaviour-system.md +7 -5
  466. package/dist/docs/guide/systems/script-object-system.md +3 -1
  467. package/dist/docs/guide/systems/video-system.md +1 -1
  468. package/dist/docs/guide/webgpu-and-tsl.md +415 -0
  469. package/dist/docs/guide/world-api.md +52 -2
  470. package/dist/factories/index.cjs +770 -283
  471. package/dist/factories/index.cjs.map +1 -1
  472. package/dist/factories/index.d.cts +14 -3
  473. package/dist/factories/index.d.ts +14 -3
  474. package/dist/factories/index.js +7 -4
  475. package/dist/{index-D2Jt_JzH.d.ts → index-C61n65PE.d.ts} +135 -42
  476. package/dist/{index-DaG03vyN.d.cts → index-C70N4BYG.d.cts} +135 -42
  477. package/dist/{index-C3ik3sA1.d.ts → index-D9DjrUn3.d.cts} +25 -3
  478. package/dist/{index-Dlr-7-4m.d.cts → index-DppafVu4.d.ts} +25 -3
  479. package/dist/index.cjs +3642 -1263
  480. package/dist/index.cjs.map +1 -1
  481. package/dist/index.d.cts +113 -11
  482. package/dist/index.d.ts +113 -11
  483. package/dist/index.js +187 -12
  484. package/dist/index.js.map +1 -1
  485. package/dist/network/index.cjs +4896 -0
  486. package/dist/network/index.cjs.map +1 -0
  487. package/dist/network/index.d.cts +98 -0
  488. package/dist/network/index.d.ts +98 -0
  489. package/dist/network/index.js +493 -0
  490. package/dist/network/index.js.map +1 -0
  491. package/dist/systems/index.cjs +3958 -1762
  492. package/dist/systems/index.cjs.map +1 -1
  493. package/dist/systems/index.d.cts +4 -2
  494. package/dist/systems/index.d.ts +4 -2
  495. package/dist/systems/index.js +8 -4
  496. package/dist/types.d.cts +3 -1
  497. package/dist/types.d.ts +3 -1
  498. package/dist/webgpu/index.d.ts +113 -0
  499. package/dist/webgpu/index.js +302 -0
  500. package/dist/webgpu/index.js.map +1 -0
  501. package/dist/worldSnapshotUtils.d.cts +3 -1
  502. package/dist/worldSnapshotUtils.d.ts +3 -1
  503. package/dist/worldSnapshotUtils.js +3 -2
  504. package/package.json +19 -3
  505. package/dist/chunk-BUEKOSR5.js.map +0 -1
  506. package/dist/chunk-DF5SSRN6.js.map +0 -1
  507. package/dist/chunk-OWQSESTO.js.map +0 -1
  508. package/dist/chunk-OY7ZODZP.js.map +0 -1
  509. package/dist/chunk-RXBLOO5B.js.map +0 -1
  510. package/dist/chunk-WJD2VKEH.js +0 -315
  511. package/dist/chunk-WJD2VKEH.js.map +0 -1
@@ -1,36 +1,15 @@
1
- import { resolveDracoDecoderPath, applyTransform, SHARED_MODEL_RESOURCE_FLAG } from './chunk-WJD2VKEH.js';
2
- import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ParentId, ScriptComponent, ScriptObjectComponent, XRRigComponent, VRTeleportAreaComponent, XRInteractableComponent, createAttachPoint, XRHandPoseComponent, createHandPoseEntry, XRAnchorComponent, UIPanelComponent, UITextComponent, UIButtonComponent, UIToggleComponent, UISliderComponent, XRInteractorComponent, XRInteractorSideComponent, InstanceSlotComponent, Name, ImageComponent, SpriteComponent, VideoComponent, AudioComponent, ImageTarget, Selectable, PrefabNode, PrefabInstance, XREnvironmentComponent, handPoseDefinitionFor, getHandPosePreset } from './chunk-BUEKOSR5.js';
3
- import { __name } from './chunk-OY7ZODZP.js';
1
+ import { getRendererKindOf, readWorldRenderBackend, readWorldRendererKind } from './chunk-OHCNPPMX.js';
2
+ import { resolveScriptCtor, Behaviour, normalizeScriptData, defaultModuleLoader, reportScriptError, serializeDataForStorage, toErrorMessage, readScriptPhase, withScriptPhase, mergeDataByCurrentDefaults, resolveInputData, resolvePrefabAssetUrl, reportBehaviourHookError } from './chunk-U727NTWQ.js';
3
+ import { createGLTFLoader, SHARED_MODEL_RESOURCE_FLAG, applyTransform, findModelBone } from './chunk-G4XJ53LS.js';
4
+ import { AnimationComponent, ModelComponent, CameraComponent, Object3DRef, normalizeEnvironmentLighting, Environment, Postprocessing, TransformComponent, PhysicsComponent, setPhysicsLiveRef, LightComponent, MaterialComponent, DEFAULT_MESH_GEOMETRY_PARAMETERS, MeshGeometryComponent, InstancedMeshComponent, ScriptComponent, ScriptObjectComponent, AudioComponent, VideoComponent, XRInteractableComponent, XRRigComponent, VRTeleportAreaComponent, ParentId, XREnvironmentComponent, UIButtonComponent, UIToggleComponent, UISliderComponent, Name, UIPanelComponent, UITextComponent, XRAnchorComponent, IKComponent, XRInteractorComponent, XRInteractorSideComponent, handPoseDefinitionFor, getHandPosePreset, XRHandPoseComponent } from './chunk-NNDUOQ7O.js';
5
+ import { __name } from './chunk-XE4OXN2W.js';
4
6
  import * as THREE19 from 'three';
5
- import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
6
7
  import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
7
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
8
- import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
9
- import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
10
- import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
11
- import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass.js';
12
- import { GlitchPass } from 'three/examples/jsm/postprocessing/GlitchPass.js';
13
- import { FilmPass } from 'three/examples/jsm/postprocessing/FilmPass.js';
14
- import { BokehPass } from 'three/examples/jsm/postprocessing/BokehPass.js';
15
- import { SSAOPass } from 'three/examples/jsm/postprocessing/SSAOPass.js';
16
- import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
17
- import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
18
- import { HueSaturationShader } from 'three/examples/jsm/shaders/HueSaturationShader.js';
19
- import { SepiaShader } from 'three/examples/jsm/shaders/SepiaShader.js';
20
- import { VignetteShader } from 'three/examples/jsm/shaders/VignetteShader.js';
21
8
  import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerModelFactory.js';
22
9
  import { XRHandModelFactory } from 'three/examples/jsm/webxr/XRHandModelFactory.js';
23
10
 
24
11
  function createClipSourceLoader() {
25
- const loader = new GLTFLoader();
26
- try {
27
- const dracoLoader = new DRACOLoader();
28
- dracoLoader.setDecoderPath(resolveDracoDecoderPath());
29
- loader.setDRACOLoader(dracoLoader);
30
- } catch (error) {
31
- console.warn("DRACOLoader could not be configured for clip source loading.", error);
32
- }
33
- return loader;
12
+ return createGLTFLoader();
34
13
  }
35
14
  __name(createClipSourceLoader, "createClipSourceLoader");
36
15
  function normalizeDeltaTime(deltaTime) {
@@ -579,6 +558,88 @@ var _ObjectVisibilitySystem = class _ObjectVisibilitySystem {
579
558
  };
580
559
  __name(_ObjectVisibilitySystem, "ObjectVisibilitySystem");
581
560
  var ObjectVisibilitySystem = _ObjectVisibilitySystem;
561
+ function getUrlExtension(url) {
562
+ const path = url.split(/[?#]/)[0] ?? "";
563
+ const lastSegment = path.split("/").pop() ?? "";
564
+ const dot = lastSegment.lastIndexOf(".");
565
+ return dot >= 0 ? lastSegment.slice(dot + 1).toLowerCase() : "";
566
+ }
567
+ __name(getUrlExtension, "getUrlExtension");
568
+ async function loadEquirectTexture(url) {
569
+ const extension = getUrlExtension(url);
570
+ let texture;
571
+ if (extension === "hdr") {
572
+ const { HDRLoader } = await import('three/examples/jsm/loaders/HDRLoader.js');
573
+ texture = await new HDRLoader().loadAsync(url);
574
+ } else if (extension === "exr") {
575
+ const { EXRLoader } = await import('three/examples/jsm/loaders/EXRLoader.js');
576
+ texture = await new EXRLoader().loadAsync(url);
577
+ } else {
578
+ texture = await new THREE19.TextureLoader().loadAsync(url);
579
+ texture.colorSpace = THREE19.SRGBColorSpace;
580
+ }
581
+ texture.mapping = THREE19.EquirectangularReflectionMapping;
582
+ texture.needsUpdate = true;
583
+ return texture;
584
+ }
585
+ __name(loadEquirectTexture, "loadEquirectTexture");
586
+ var rendererStates = /* @__PURE__ */ new WeakMap();
587
+ function toThreeToneMapping(mode) {
588
+ switch (mode) {
589
+ case "aces":
590
+ return THREE19.ACESFilmicToneMapping;
591
+ case "agx":
592
+ return THREE19.AgXToneMapping;
593
+ case "neutral":
594
+ return THREE19.NeutralToneMapping;
595
+ default:
596
+ return THREE19.NoToneMapping;
597
+ }
598
+ }
599
+ __name(toThreeToneMapping, "toThreeToneMapping");
600
+ function resolveToneMapping(environment, postprocessing) {
601
+ const environmentMode = environment && environment.mode !== "none" ? environment.mode : null;
602
+ const mode = environmentMode ?? (postprocessing ? "aces" : "none");
603
+ const exposure = postprocessing ? postprocessing.exposure : environment?.exposure ?? 1;
604
+ return { mode, exposure };
605
+ }
606
+ __name(resolveToneMapping, "resolveToneMapping");
607
+ function setRendererToneMappingSource(renderer, source, request) {
608
+ if (!renderer || typeof renderer !== "object") {
609
+ return;
610
+ }
611
+ const target = renderer;
612
+ let state = rendererStates.get(renderer);
613
+ if (!state) {
614
+ if (request === null) return;
615
+ state = {
616
+ baseline: {
617
+ toneMapping: target.toneMapping ?? THREE19.NoToneMapping,
618
+ exposure: typeof target.toneMappingExposure === "number" ? target.toneMappingExposure : 1
619
+ },
620
+ environment: null,
621
+ postprocessing: null
622
+ };
623
+ rendererStates.set(renderer, state);
624
+ }
625
+ if (source === "environment") {
626
+ state.environment = request;
627
+ } else {
628
+ state.postprocessing = request;
629
+ }
630
+ if (!state.environment && !state.postprocessing) {
631
+ target.toneMapping = state.baseline.toneMapping;
632
+ target.toneMappingExposure = state.baseline.exposure;
633
+ rendererStates.delete(renderer);
634
+ return;
635
+ }
636
+ const resolved = resolveToneMapping(state.environment, state.postprocessing);
637
+ target.toneMapping = toThreeToneMapping(resolved.mode);
638
+ target.toneMappingExposure = resolved.exposure;
639
+ }
640
+ __name(setRendererToneMappingSource, "setRendererToneMappingSource");
641
+
642
+ // core/systems/EnvironmentSyncSystem.ts
582
643
  var MIN_FOG_DELTA = 1e-3;
583
644
  var MIN_DENSITY = 0;
584
645
  function createGradientTexture(effect, colors) {
@@ -627,14 +688,48 @@ function createGradientTexture(effect, colors) {
627
688
  return texture;
628
689
  }
629
690
  __name(createGradientTexture, "createGradientTexture");
691
+ function readRenderer(world) {
692
+ const getRenderer = world.getRenderer;
693
+ if (typeof getRenderer !== "function") return null;
694
+ try {
695
+ return getRenderer.call(world) ?? null;
696
+ } catch {
697
+ return null;
698
+ }
699
+ }
700
+ __name(readRenderer, "readRenderer");
630
701
  var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
631
702
  constructor(scene) {
632
703
  this.textureLoader = new THREE19.TextureLoader();
633
- this.signature = null;
704
+ /** `true` once an Environment entity has been seen (drives the reset path). */
705
+ this.hasEnvironment = false;
706
+ this.skyboxSignature = null;
707
+ this.fogSignature = null;
708
+ this.lightingSignature = null;
634
709
  this.activeTexture = null;
710
+ /** Load key (`url`, or `equirect|url`) of the current/pending image skybox. */
635
711
  this.pendingImageUrl = null;
712
+ /** What the skybox wants as the background, before passthrough/env overrides. */
713
+ this.skyboxBackground = null;
636
714
  /** Whether the skybox is currently withheld because passthrough is on. */
637
715
  this.passthroughSuppressed = false;
716
+ // ── Image-based lighting ────────────────────────────────────────────────
717
+ this.renderer = null;
718
+ this.lighting = null;
719
+ /** URL of the environment map currently loaded or loading. */
720
+ this.envUrl = null;
721
+ /** Incremented per env-map load; a load whose token is stale is discarded. */
722
+ this.envLoadToken = 0;
723
+ /** Equirect source texture of the current environment map. */
724
+ this.envSource = null;
725
+ /**
726
+ * PMREM render target built from {@link envSource} (WebGL only; on WebGPU
727
+ * the renderer prefilters `scene.environment` itself and this stays null).
728
+ */
729
+ this.envTarget = null;
730
+ this.sceneLightingBaseline = null;
731
+ this.sceneBackgroundBaseline = null;
732
+ this.disposed = false;
638
733
  this.scene = scene;
639
734
  }
640
735
  disposeActiveTexture() {
@@ -643,18 +738,53 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
643
738
  this.activeTexture = null;
644
739
  }
645
740
  }
646
- applySkybox(component) {
647
- const skybox = component.values.skybox;
741
+ /** Write the background the current state calls for. */
742
+ commitBackground() {
743
+ if (this.passthroughSuppressed) {
744
+ this.scene.background = null;
745
+ return;
746
+ }
747
+ const lighting = this.lighting;
748
+ if (lighting?.useAsBackground && this.envSource) {
749
+ if (!this.sceneBackgroundBaseline) {
750
+ this.sceneBackgroundBaseline = {
751
+ backgroundBlurriness: this.scene.backgroundBlurriness,
752
+ backgroundIntensity: this.scene.backgroundIntensity,
753
+ backgroundRotationY: this.scene.backgroundRotation.y
754
+ };
755
+ }
756
+ this.scene.background = this.envSource;
757
+ this.scene.backgroundBlurriness = lighting.backgroundBlurriness;
758
+ this.scene.backgroundIntensity = lighting.backgroundIntensity;
759
+ this.scene.backgroundRotation.y = lighting.envRotationY;
760
+ return;
761
+ }
762
+ this.restoreBackgroundBaseline();
763
+ this.scene.background = this.skyboxBackground;
764
+ }
765
+ restoreBackgroundBaseline() {
766
+ const baseline = this.sceneBackgroundBaseline;
767
+ if (!baseline) return;
768
+ this.scene.backgroundBlurriness = baseline.backgroundBlurriness;
769
+ this.scene.backgroundIntensity = baseline.backgroundIntensity;
770
+ this.scene.backgroundRotation.y = baseline.backgroundRotationY;
771
+ this.sceneBackgroundBaseline = null;
772
+ }
773
+ setSkyboxBackground(value) {
774
+ this.skyboxBackground = value;
775
+ this.commitBackground();
776
+ }
777
+ applySkybox(skybox) {
648
778
  if (skybox.type === "none") {
649
779
  this.disposeActiveTexture();
650
780
  this.pendingImageUrl = null;
651
- this.scene.background = null;
781
+ this.setSkyboxBackground(null);
652
782
  return;
653
783
  }
654
784
  if (skybox.type === "color") {
655
785
  this.disposeActiveTexture();
656
786
  this.pendingImageUrl = null;
657
- this.scene.background = new THREE19.Color(skybox.color || "#1e1e1e");
787
+ this.setSkyboxBackground(new THREE19.Color(skybox.color || "#1e1e1e"));
658
788
  return;
659
789
  }
660
790
  if (skybox.type === "gradient") {
@@ -665,44 +795,54 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
665
795
  skybox.gradient.colors
666
796
  );
667
797
  this.activeTexture = texture;
668
- this.scene.background = texture;
798
+ this.setSkyboxBackground(texture);
669
799
  return;
670
800
  }
671
801
  const imageUrl = skybox.imageUrl?.trim() ?? "";
672
802
  if (!imageUrl) {
673
803
  this.disposeActiveTexture();
674
804
  this.pendingImageUrl = null;
675
- this.scene.background = null;
805
+ this.setSkyboxBackground(null);
676
806
  return;
677
807
  }
678
- if (this.pendingImageUrl === imageUrl && this.activeTexture) {
679
- this.scene.background = this.activeTexture;
808
+ const equirect = skybox.imageProjection === "equirect";
809
+ const loadKey = equirect ? `equirect|${imageUrl}` : imageUrl;
810
+ if (this.pendingImageUrl === loadKey && this.activeTexture) {
811
+ this.setSkyboxBackground(this.activeTexture);
680
812
  return;
681
813
  }
682
814
  this.disposeActiveTexture();
683
- this.pendingImageUrl = imageUrl;
684
- this.textureLoader.load(
685
- imageUrl,
686
- (texture) => {
687
- if (this.pendingImageUrl !== imageUrl) {
688
- texture.dispose();
689
- return;
690
- }
815
+ this.pendingImageUrl = loadKey;
816
+ const onLoaded = /* @__PURE__ */ __name((texture) => {
817
+ if (this.pendingImageUrl !== loadKey) {
818
+ texture.dispose();
819
+ return;
820
+ }
821
+ if (!equirect) {
691
822
  texture.colorSpace = THREE19.SRGBColorSpace;
692
- this.activeTexture = texture;
693
- this.scene.background = texture;
694
- },
695
- void 0,
696
- () => {
697
- if (this.pendingImageUrl === imageUrl) {
698
- this.pendingImageUrl = null;
699
- this.scene.background = null;
700
- }
701
823
  }
702
- );
824
+ this.activeTexture = texture;
825
+ this.setSkyboxBackground(texture);
826
+ }, "onLoaded");
827
+ const onFailed = /* @__PURE__ */ __name(() => {
828
+ if (this.pendingImageUrl === loadKey) {
829
+ this.pendingImageUrl = null;
830
+ this.setSkyboxBackground(null);
831
+ }
832
+ }, "onFailed");
833
+ if (equirect) {
834
+ loadEquirectTexture(imageUrl).then(onLoaded, (error) => {
835
+ console.error(
836
+ `[EnvironmentSyncSystem] Failed to load skybox image ${imageUrl}`,
837
+ error
838
+ );
839
+ onFailed();
840
+ });
841
+ return;
842
+ }
843
+ this.textureLoader.load(imageUrl, onLoaded, void 0, onFailed);
703
844
  }
704
- applyFog(component) {
705
- const fog = component.values.fog;
845
+ applyFog(fog) {
706
846
  if (fog.type === "none") {
707
847
  this.scene.fog = null;
708
848
  return;
@@ -718,27 +858,167 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
718
858
  const density = Number.isFinite(fog.exponential.density) ? Math.max(MIN_DENSITY, fog.exponential.density) : 2e-3;
719
859
  this.scene.fog = new THREE19.FogExp2(fogColor, density);
720
860
  }
721
- update(world) {
722
- const passthrough = world.isXrPassthrough();
723
- if (passthrough !== this.passthroughSuppressed) {
724
- this.passthroughSuppressed = passthrough;
725
- if (passthrough) {
861
+ // ── Image-based lighting ────────────────────────────────────────────────
862
+ disposeEnvResources() {
863
+ if (this.envTarget) {
864
+ this.envTarget.dispose();
865
+ this.envTarget = null;
866
+ }
867
+ if (this.envSource) {
868
+ this.envSource.dispose();
869
+ this.envSource = null;
870
+ }
871
+ }
872
+ /** Drop the environment map (any in-flight load is abandoned). */
873
+ clearEnvMap() {
874
+ this.envLoadToken += 1;
875
+ this.envUrl = null;
876
+ const owned = this.envTarget?.texture ?? this.envSource;
877
+ if (owned && this.scene.environment === owned) {
878
+ this.scene.environment = this.sceneLightingBaseline?.environment ?? null;
879
+ }
880
+ this.disposeEnvResources();
881
+ }
882
+ /**
883
+ * Undo everything the lighting block wrote to the scene and renderer.
884
+ * `commit` re-applies the background afterwards (off during teardown, which
885
+ * must not touch the background).
886
+ */
887
+ clearLighting(commit = true) {
888
+ this.clearEnvMap();
889
+ const baseline = this.sceneLightingBaseline;
890
+ if (baseline) {
891
+ this.scene.environment = baseline.environment;
892
+ this.scene.environmentIntensity = baseline.environmentIntensity;
893
+ this.scene.environmentRotation.y = baseline.environmentRotationY;
894
+ this.sceneLightingBaseline = null;
895
+ }
896
+ if (this.lighting) {
897
+ setRendererToneMappingSource(this.renderer, "environment", null);
898
+ }
899
+ const hadEnvBackground = this.sceneBackgroundBaseline !== null;
900
+ this.lighting = null;
901
+ if (commit) {
902
+ this.commitBackground();
903
+ } else if (hadEnvBackground) {
904
+ this.restoreBackgroundBaseline();
905
+ if (!this.passthroughSuppressed) {
726
906
  this.scene.background = null;
727
- } else {
728
- this.signature = null;
729
907
  }
730
908
  }
731
- if (passthrough) {
909
+ }
910
+ /**
911
+ * PMREM-filter an equirect texture. `null` when no usable renderer exists,
912
+ * or on WebGPU: there the WebGL `PMREMGenerator` would silently produce a
913
+ * black map, while `WebGPURenderer` prefilters an equirect
914
+ * `scene.environment` (and a blurred background) by itself.
915
+ */
916
+ buildPmrem(texture) {
917
+ const renderer = this.renderer;
918
+ if (!renderer || getRendererKindOf(renderer) === "webgpu") return null;
919
+ let generator = null;
920
+ try {
921
+ generator = new THREE19.PMREMGenerator(renderer);
922
+ return generator.fromEquirectangular(texture);
923
+ } catch (error) {
924
+ console.warn(
925
+ "[EnvironmentSyncSystem] PMREM prefilter failed; using the equirect texture directly.",
926
+ error
927
+ );
928
+ return null;
929
+ } finally {
930
+ generator?.dispose();
931
+ }
932
+ }
933
+ loadEnvMap(url) {
934
+ this.envLoadToken += 1;
935
+ const token = this.envLoadToken;
936
+ this.envUrl = url;
937
+ loadEquirectTexture(url).then(
938
+ (texture) => {
939
+ if (this.disposed || token !== this.envLoadToken) {
940
+ texture.dispose();
941
+ return;
942
+ }
943
+ const target = this.buildPmrem(texture);
944
+ this.disposeEnvResources();
945
+ this.envSource = texture;
946
+ this.envTarget = target;
947
+ this.scene.environment = target ? target.texture : texture;
948
+ this.applyEnvironmentParams();
949
+ this.commitBackground();
950
+ },
951
+ (error) => {
952
+ if (this.disposed || token !== this.envLoadToken) return;
953
+ console.error(
954
+ `[EnvironmentSyncSystem] Failed to load environment map ${url}`,
955
+ error
956
+ );
957
+ this.clearEnvMap();
958
+ this.commitBackground();
959
+ }
960
+ );
961
+ }
962
+ applyEnvironmentParams() {
963
+ const lighting = this.lighting;
964
+ if (!lighting) return;
965
+ this.scene.environmentIntensity = lighting.envIntensity;
966
+ this.scene.environmentRotation.y = lighting.envRotationY;
967
+ }
968
+ applyLighting(value) {
969
+ const lighting = normalizeEnvironmentLighting(value);
970
+ if (!lighting) {
971
+ if (this.lighting || this.sceneLightingBaseline) {
972
+ this.clearLighting();
973
+ }
732
974
  return;
733
975
  }
976
+ if (!this.sceneLightingBaseline) {
977
+ this.sceneLightingBaseline = {
978
+ environment: this.scene.environment,
979
+ environmentIntensity: this.scene.environmentIntensity,
980
+ environmentRotationY: this.scene.environmentRotation.y
981
+ };
982
+ }
983
+ this.lighting = lighting;
984
+ setRendererToneMappingSource(this.renderer, "environment", {
985
+ mode: lighting.toneMapping,
986
+ exposure: lighting.exposure
987
+ });
988
+ const url = lighting.envMapUrl.trim();
989
+ if (!url) {
990
+ if (this.envUrl !== null || this.envSource) {
991
+ this.clearEnvMap();
992
+ }
993
+ } else if (url !== this.envUrl) {
994
+ this.loadEnvMap(url);
995
+ }
996
+ this.applyEnvironmentParams();
997
+ this.commitBackground();
998
+ }
999
+ resetAll() {
1000
+ this.hasEnvironment = false;
1001
+ this.skyboxSignature = null;
1002
+ this.fogSignature = null;
1003
+ this.lightingSignature = null;
1004
+ this.pendingImageUrl = null;
1005
+ this.disposeActiveTexture();
1006
+ this.skyboxBackground = null;
1007
+ this.clearLighting();
1008
+ this.scene.background = null;
1009
+ this.scene.fog = null;
1010
+ }
1011
+ update(world) {
1012
+ this.renderer = readRenderer(world);
1013
+ const passthrough = world.isXrPassthrough();
1014
+ if (passthrough !== this.passthroughSuppressed) {
1015
+ this.passthroughSuppressed = passthrough;
1016
+ this.commitBackground();
1017
+ }
734
1018
  const entityIds = world.query(Environment);
735
1019
  if (entityIds.length === 0) {
736
- if (this.signature !== null) {
737
- this.signature = null;
738
- this.pendingImageUrl = null;
739
- this.disposeActiveTexture();
740
- this.scene.background = null;
741
- this.scene.fog = null;
1020
+ if (this.hasEnvironment) {
1021
+ this.resetAll();
742
1022
  }
743
1023
  return;
744
1024
  }
@@ -747,29 +1027,50 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
747
1027
  if (!environment) {
748
1028
  return;
749
1029
  }
750
- const nextSignature = JSON.stringify(environment.values);
751
- if (this.signature === nextSignature) {
752
- return;
1030
+ this.hasEnvironment = true;
1031
+ const values = environment.values;
1032
+ const skyboxSignature = JSON.stringify(values.skybox);
1033
+ if (skyboxSignature !== this.skyboxSignature) {
1034
+ this.skyboxSignature = skyboxSignature;
1035
+ this.applySkybox(values.skybox);
1036
+ }
1037
+ const fogSignature = JSON.stringify(values.fog);
1038
+ if (fogSignature !== this.fogSignature) {
1039
+ this.fogSignature = fogSignature;
1040
+ this.applyFog(values.fog);
1041
+ }
1042
+ const lightingSignature = JSON.stringify(values.lighting ?? null);
1043
+ if (lightingSignature !== this.lightingSignature) {
1044
+ this.lightingSignature = lightingSignature;
1045
+ this.applyLighting(values.lighting);
753
1046
  }
754
- this.signature = nextSignature;
755
- this.applySkybox(environment);
756
- this.applyFog(environment);
757
1047
  }
758
1048
  dispose() {
759
1049
  this.pendingImageUrl = null;
760
1050
  this.disposeActiveTexture();
1051
+ this.clearLighting(false);
1052
+ this.disposed = true;
761
1053
  }
762
1054
  };
763
1055
  __name(_EnvironmentSyncSystem, "EnvironmentSyncSystem");
764
1056
  var EnvironmentSyncSystem = _EnvironmentSyncSystem;
1057
+
1058
+ // core/systems/PostprocessingSyncSystem.ts
765
1059
  var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
766
- constructor(renderer, scene, camera) {
1060
+ /**
1061
+ * @param backend - Builds the chain. When omitted, the world's backend
1062
+ * (`world.getRenderBackend()`) is used.
1063
+ */
1064
+ constructor(renderer, scene, camera, backend) {
767
1065
  this.signature = null;
1066
+ /** Deep copy of the values the current chain reflects. */
1067
+ this.appliedValues = null;
768
1068
  this.composer = null;
769
1069
  this.composerEnabled = false;
770
1070
  this.renderer = renderer;
771
1071
  this.scene = scene;
772
1072
  this.camera = camera;
1073
+ this.backend = backend ?? null;
773
1074
  }
774
1075
  setComposerEnabled(enabled) {
775
1076
  this.composerEnabled = enabled;
@@ -779,6 +1080,7 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
779
1080
  }
780
1081
  }
781
1082
  disposeComposer() {
1083
+ this.appliedValues = null;
782
1084
  if (!this.composer) {
783
1085
  this.setComposerEnabled(false);
784
1086
  return;
@@ -793,101 +1095,59 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
793
1095
  this.composer = null;
794
1096
  this.setComposerEnabled(false);
795
1097
  }
796
- buildComposer(values) {
1098
+ /** Ask the renderer for tone mapping while the effect is active. */
1099
+ applyToneMapping(values) {
1100
+ const active = Boolean(values?.postEnabled && values.toneMapping.enabled);
1101
+ setRendererToneMappingSource(
1102
+ this.renderer,
1103
+ "postprocessing",
1104
+ active && values ? { exposure: Math.max(0, values.toneMapping.exposure) } : null
1105
+ );
1106
+ }
1107
+ resolveBackend(world) {
1108
+ return this.backend ?? readWorldRenderBackend(world);
1109
+ }
1110
+ buildComposer(world, values) {
797
1111
  this.disposeComposer();
1112
+ this.applyToneMapping(values);
798
1113
  if (!values.postEnabled) {
799
1114
  return;
800
1115
  }
801
- const composer = new EffectComposer(this.renderer);
802
- composer.addPass(new RenderPass(this.scene, this.camera));
803
- if (values.dof.enabled) {
804
- composer.addPass(
805
- new BokehPass(this.scene, this.camera, {
806
- focus: values.dof.focusDistance,
807
- aperture: Math.max(0, values.dof.focalLength),
808
- maxblur: Math.max(0, values.dof.bokehScale)
809
- })
810
- );
811
- }
812
- if (values.bloom.enabled) {
813
- const size = this.renderer.getSize(new THREE19.Vector2());
814
- composer.addPass(
815
- new UnrealBloomPass(
816
- new THREE19.Vector2(size.width, size.height),
817
- values.bloom.intensity,
818
- values.bloom.radius,
819
- values.bloom.threshold
820
- )
821
- );
822
- }
823
- if (values.vignette.enabled) {
824
- const pass = new ShaderPass(VignetteShader);
825
- const offset = pass.uniforms.offset;
826
- const darkness = pass.uniforms.darkness;
827
- if (offset) {
828
- offset.value = values.vignette.offset;
829
- }
830
- if (darkness) {
831
- darkness.value = values.vignette.darkness;
832
- }
833
- composer.addPass(pass);
834
- }
835
- if (values.hueSaturation.enabled) {
836
- const pass = new ShaderPass(HueSaturationShader);
837
- const hue = pass.uniforms.hue;
838
- const saturation = pass.uniforms.saturation;
839
- if (hue) {
840
- hue.value = values.hueSaturation.hue;
841
- }
842
- if (saturation) {
843
- saturation.value = values.hueSaturation.saturation;
844
- }
845
- composer.addPass(pass);
846
- }
847
- if (values.sepia.enabled) {
848
- const pass = new ShaderPass(SepiaShader);
849
- const amount = pass.uniforms.amount;
850
- if (amount) {
851
- amount.value = values.sepia.intensity;
852
- }
853
- composer.addPass(pass);
854
- }
855
- if (values.noise.enabled) {
856
- composer.addPass(new FilmPass(values.noise.opacity));
857
- }
858
- if (values.dotScreen.enabled) {
859
- composer.addPass(
860
- new DotScreenPass(
861
- new THREE19.Vector2(0, 0),
862
- values.dotScreen.angle ?? values.dotScreen.dotScreenAngle,
863
- values.dotScreen.scale ?? values.dotScreen.dotScreenScale
864
- )
865
- );
866
- }
867
- if (values.ssao.enabled) {
868
- const pass = new SSAOPass(this.scene, this.camera);
869
- pass.kernelRadius = values.ssao.radius;
870
- pass.minDistance = 1e-3;
871
- pass.maxDistance = Math.max(0.01, values.ssao.intensity);
872
- composer.addPass(pass);
873
- }
874
- if (values.glitch.enabled) {
875
- const pass = new GlitchPass();
876
- pass.enabled = true;
877
- pass.goWild = Boolean(values.glitch.active);
878
- composer.addPass(pass);
1116
+ const composer = this.resolveBackend(world).createPostprocessing({
1117
+ renderer: this.renderer,
1118
+ scene: this.scene,
1119
+ camera: this.camera,
1120
+ values,
1121
+ world
1122
+ });
1123
+ if (!composer) {
1124
+ return;
879
1125
  }
880
- composer.addPass(new OutputPass());
881
1126
  this.composer = composer;
1127
+ this.appliedValues = structuredCloneValues(values);
882
1128
  this.scene.userData.ecsPostprocessingComposer = composer;
883
1129
  this.setComposerEnabled(true);
884
1130
  }
1131
+ /** Update the existing chain in place; `false` when it must be rebuilt. */
1132
+ tryUpdateInPlace(world, values) {
1133
+ const composer = this.composer;
1134
+ const previous = this.appliedValues;
1135
+ if (!composer || !previous || !values.postEnabled) return false;
1136
+ if (this.scene.userData.ecsPostprocessingComposer !== composer) return false;
1137
+ const backend = this.resolveBackend(world);
1138
+ if (typeof backend.updatePostprocessing !== "function") return false;
1139
+ if (!backend.updatePostprocessing(composer, previous, values)) return false;
1140
+ this.appliedValues = structuredCloneValues(values);
1141
+ this.applyToneMapping(values);
1142
+ return true;
1143
+ }
885
1144
  update(world) {
886
1145
  const entityIds = world.query(Postprocessing);
887
1146
  if (entityIds.length === 0) {
888
1147
  if (this.signature !== null) {
889
1148
  this.signature = null;
890
1149
  this.disposeComposer();
1150
+ this.applyToneMapping(null);
891
1151
  delete this.scene.userData.ecsPostprocessing;
892
1152
  }
893
1153
  return;
@@ -901,12 +1161,15 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
901
1161
  return;
902
1162
  }
903
1163
  this.signature = nextSignature;
904
- this.buildComposer(postprocessing.values);
1164
+ if (!this.tryUpdateInPlace(world, postprocessing.values)) {
1165
+ this.buildComposer(world, postprocessing.values);
1166
+ }
905
1167
  this.scene.userData.ecsPostprocessing = postprocessing.values;
906
1168
  }
907
1169
  dispose() {
908
1170
  this.signature = null;
909
1171
  this.disposeComposer();
1172
+ this.applyToneMapping(null);
910
1173
  delete this.scene.userData.ecsPostprocessing;
911
1174
  delete this.scene.userData.ecsPostprocessingComposerActive;
912
1175
  delete this.scene.userData.ecsPostprocessingComposer;
@@ -914,6 +1177,10 @@ var _PostprocessingSyncSystem = class _PostprocessingSyncSystem {
914
1177
  };
915
1178
  __name(_PostprocessingSyncSystem, "PostprocessingSyncSystem");
916
1179
  var PostprocessingSyncSystem = _PostprocessingSyncSystem;
1180
+ function structuredCloneValues(values) {
1181
+ return JSON.parse(JSON.stringify(values));
1182
+ }
1183
+ __name(structuredCloneValues, "structuredCloneValues");
917
1184
  var MIN_SIZE = 1e-4;
918
1185
  function toBodyType(RAPIER, bodyType) {
919
1186
  switch (bodyType) {
@@ -1840,7 +2107,140 @@ var InputSystem = _InputSystem;
1840
2107
  var MIN_LIGHT_DISTANCE = 0;
1841
2108
  var MIN_LIGHT_INTENSITY = 0;
1842
2109
  var MIN_AREA_SIZE = 0.01;
1843
- function applyLightValues(lightComponent) {
2110
+ var MIN_SHADOW_MAP_SIZE = 16;
2111
+ var MAX_SHADOW_MAP_SIZE = 8192;
2112
+ var MIN_SHADOW_NEAR = 1e-3;
2113
+ var MIN_SHADOW_CAMERA_SIZE = 0.01;
2114
+ var shadowRecords = /* @__PURE__ */ new WeakMap();
2115
+ function finiteOrUndefined(value) {
2116
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2117
+ }
2118
+ __name(finiteOrUndefined, "finiteOrUndefined");
2119
+ function readShadowRecord(shadow) {
2120
+ let record = shadowRecords.get(shadow);
2121
+ if (!record) {
2122
+ const camera = shadow.camera;
2123
+ record = {
2124
+ baseline: {
2125
+ mapSize: shadow.mapSize.x,
2126
+ bias: shadow.bias,
2127
+ normalBias: shadow.normalBias,
2128
+ radius: shadow.radius,
2129
+ cameraSize: camera.isOrthographicCamera === true && typeof camera.right === "number" ? camera.right : null,
2130
+ near: typeof camera.near === "number" ? camera.near : 0.5,
2131
+ far: typeof camera.far === "number" ? camera.far : 500
2132
+ },
2133
+ applied: /* @__PURE__ */ new Set()
2134
+ };
2135
+ shadowRecords.set(shadow, record);
2136
+ }
2137
+ return record;
2138
+ }
2139
+ __name(readShadowRecord, "readShadowRecord");
2140
+ function resolveShadowField(record, key, provided, fallback) {
2141
+ if (provided !== void 0) {
2142
+ record.applied.add(key);
2143
+ return provided;
2144
+ }
2145
+ if (record.applied.has(key)) {
2146
+ record.applied.delete(key);
2147
+ return fallback;
2148
+ }
2149
+ return void 0;
2150
+ }
2151
+ __name(resolveShadowField, "resolveShadowField");
2152
+ function setShadowMapSize(shadow, size, rendererKind) {
2153
+ if (shadow.mapSize.x === size && shadow.mapSize.y === size) {
2154
+ return;
2155
+ }
2156
+ shadow.mapSize.set(size, size);
2157
+ if (rendererKind === "webgpu") {
2158
+ return;
2159
+ }
2160
+ const map = shadow.map;
2161
+ if (map) {
2162
+ map.depthTexture?.dispose();
2163
+ map.dispose();
2164
+ shadow.map = null;
2165
+ }
2166
+ }
2167
+ __name(setShadowMapSize, "setShadowMapSize");
2168
+ function applyShadowValues(light, shadowValues, rendererKind) {
2169
+ const shadow = light.shadow;
2170
+ if (!shadow) {
2171
+ return;
2172
+ }
2173
+ const values = shadowValues && typeof shadowValues === "object" ? shadowValues : null;
2174
+ if (!values && !shadowRecords.has(shadow)) {
2175
+ return;
2176
+ }
2177
+ const record = readShadowRecord(shadow);
2178
+ const { baseline } = record;
2179
+ const input = values ?? {};
2180
+ const requestedMapSize = finiteOrUndefined(input.mapSize);
2181
+ const mapSize = resolveShadowField(
2182
+ record,
2183
+ "mapSize",
2184
+ requestedMapSize === void 0 ? void 0 : Math.round(
2185
+ Math.min(MAX_SHADOW_MAP_SIZE, Math.max(MIN_SHADOW_MAP_SIZE, requestedMapSize))
2186
+ ),
2187
+ baseline.mapSize
2188
+ );
2189
+ if (mapSize !== void 0) {
2190
+ setShadowMapSize(shadow, mapSize, rendererKind);
2191
+ }
2192
+ const bias = resolveShadowField(record, "bias", finiteOrUndefined(input.bias), baseline.bias);
2193
+ if (bias !== void 0) shadow.bias = bias;
2194
+ const normalBias = resolveShadowField(
2195
+ record,
2196
+ "normalBias",
2197
+ finiteOrUndefined(input.normalBias),
2198
+ baseline.normalBias
2199
+ );
2200
+ if (normalBias !== void 0) shadow.normalBias = normalBias;
2201
+ const radius = resolveShadowField(
2202
+ record,
2203
+ "radius",
2204
+ finiteOrUndefined(input.radius),
2205
+ baseline.radius
2206
+ );
2207
+ if (radius !== void 0) shadow.radius = Math.max(0, radius);
2208
+ const camera = shadow.camera;
2209
+ let cameraChanged = false;
2210
+ if (camera.isOrthographicCamera === true && baseline.cameraSize !== null) {
2211
+ const cameraSize = resolveShadowField(
2212
+ record,
2213
+ "cameraSize",
2214
+ finiteOrUndefined(input.cameraSize),
2215
+ baseline.cameraSize
2216
+ );
2217
+ if (cameraSize !== void 0) {
2218
+ const size = Math.max(MIN_SHADOW_CAMERA_SIZE, cameraSize);
2219
+ camera.left = -size;
2220
+ camera.right = size;
2221
+ camera.top = size;
2222
+ camera.bottom = -size;
2223
+ cameraChanged = true;
2224
+ }
2225
+ }
2226
+ const near = resolveShadowField(record, "near", finiteOrUndefined(input.near), baseline.near);
2227
+ if (near !== void 0) {
2228
+ camera.near = Math.max(MIN_SHADOW_NEAR, near);
2229
+ cameraChanged = true;
2230
+ }
2231
+ const far = resolveShadowField(record, "far", finiteOrUndefined(input.far), baseline.far);
2232
+ if (far !== void 0) {
2233
+ const currentNear = typeof camera.near === "number" ? camera.near : MIN_SHADOW_NEAR;
2234
+ camera.far = Math.max(currentNear + MIN_SHADOW_NEAR, far);
2235
+ cameraChanged = true;
2236
+ }
2237
+ if (cameraChanged) {
2238
+ camera.updateProjectionMatrix?.();
2239
+ }
2240
+ shadow.needsUpdate = true;
2241
+ }
2242
+ __name(applyShadowValues, "applyShadowValues");
2243
+ function applyLightValues(lightComponent, rendererKind) {
1844
2244
  const { light, values } = lightComponent;
1845
2245
  if (typeof values.color === "string") {
1846
2246
  light.color.set(values.color);
@@ -1884,6 +2284,7 @@ function applyLightValues(lightComponent) {
1884
2284
  light.height = Math.max(MIN_AREA_SIZE, values.height);
1885
2285
  }
1886
2286
  }
2287
+ applyShadowValues(light, values.shadow, rendererKind);
1887
2288
  }
1888
2289
  __name(applyLightValues, "applyLightValues");
1889
2290
  var _LightSyncSystem = class _LightSyncSystem {
@@ -1892,6 +2293,7 @@ var _LightSyncSystem = class _LightSyncSystem {
1892
2293
  }
1893
2294
  update(world) {
1894
2295
  const entityIds = world.query(LightComponent);
2296
+ let rendererKind = null;
1895
2297
  const alive = new Set(entityIds);
1896
2298
  for (const trackedId of this.signatures.keys()) {
1897
2299
  if (!alive.has(trackedId)) {
@@ -1907,7 +2309,8 @@ var _LightSyncSystem = class _LightSyncSystem {
1907
2309
  if (this.signatures.get(entityId) === signature) {
1908
2310
  continue;
1909
2311
  }
1910
- applyLightValues(lightComponent);
2312
+ rendererKind ?? (rendererKind = readWorldRendererKind(world));
2313
+ applyLightValues(lightComponent, rendererKind);
1911
2314
  this.signatures.set(entityId, signature);
1912
2315
  }
1913
2316
  }
@@ -2356,14 +2759,7 @@ __name(_MeshGeometrySystem, "MeshGeometrySystem");
2356
2759
  var MeshGeometrySystem = _MeshGeometrySystem;
2357
2760
  var loadGLTF = /* @__PURE__ */ __name((path) => {
2358
2761
  return new Promise((resolve, reject) => {
2359
- const loader = new GLTFLoader();
2360
- try {
2361
- const dracoLoader = new DRACOLoader();
2362
- dracoLoader.setDecoderPath(resolveDracoDecoderPath());
2363
- loader.setDRACOLoader(dracoLoader);
2364
- } catch (error) {
2365
- console.debug("DRACOLoader setup failed or unavailable", error);
2366
- }
2762
+ const loader = createGLTFLoader();
2367
2763
  loader.load(
2368
2764
  path,
2369
2765
  (gltf) => {
@@ -2663,2688 +3059,94 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
2663
3059
  im.setMatrixAt(i, _matrix);
2664
3060
  }
2665
3061
  im.instanceMatrix.needsUpdate = true;
2666
- im.castShadow = comp.castShadow;
2667
- im.receiveShadow = comp.receiveShadow;
2668
- }
2669
- }
2670
- }
2671
- }
2672
- };
2673
- __name(_InstancedMeshSystem, "InstancedMeshSystem");
2674
- var InstancedMeshSystem = _InstancedMeshSystem;
2675
-
2676
- // core/systems/RenderSystem.ts
2677
- var _RenderSystem = class _RenderSystem {
2678
- constructor(renderer, scene, camera, afterRender) {
2679
- this.renderer = renderer;
2680
- this.scene = scene;
2681
- this.camera = camera;
2682
- this.afterRender = afterRender;
2683
- }
2684
- update(_world) {
2685
- if (this.scene.userData.ecsPostprocessingComposerActive === true) {
2686
- const composer = this.scene.userData.ecsPostprocessingComposer;
2687
- if (composer) {
2688
- const width = Math.max(1, this.renderer.domElement.clientWidth);
2689
- const height = Math.max(1, this.renderer.domElement.clientHeight);
2690
- composer.setSize(width, height);
2691
- composer.render();
2692
- } else {
2693
- this.renderer.render(this.scene, this.camera);
2694
- }
2695
- this.afterRender?.();
2696
- return;
2697
- }
2698
- this.renderer.render(this.scene, this.camera);
2699
- this.afterRender?.();
2700
- }
2701
- };
2702
- __name(_RenderSystem, "RenderSystem");
2703
- var RenderSystem = _RenderSystem;
2704
-
2705
- // core/prefab/collectEntitySubtree.ts
2706
- function collectEntitySubtree(world, rootIds) {
2707
- const childrenByParent = /* @__PURE__ */ new Map();
2708
- for (const entityId of world.query(ParentId)) {
2709
- const parentId = world.getComponent(entityId, ParentId)?.value;
2710
- if (parentId === null || parentId === void 0) {
2711
- continue;
2712
- }
2713
- const siblings = childrenByParent.get(parentId);
2714
- if (siblings) {
2715
- siblings.push(entityId);
2716
- } else {
2717
- childrenByParent.set(parentId, [entityId]);
2718
- }
2719
- }
2720
- const ordered = [];
2721
- const seen = /* @__PURE__ */ new Set();
2722
- const queue = [...rootIds];
2723
- while (queue.length > 0) {
2724
- const entityId = queue.shift();
2725
- if (seen.has(entityId)) {
2726
- continue;
2727
- }
2728
- seen.add(entityId);
2729
- ordered.push(entityId);
2730
- const children = childrenByParent.get(entityId);
2731
- if (children) {
2732
- queue.push(...children.sort((a, b) => a - b));
2733
- }
2734
- }
2735
- return ordered;
2736
- }
2737
- __name(collectEntitySubtree, "collectEntitySubtree");
2738
-
2739
- // core/hydration/readers.ts
2740
- function asRecord(value) {
2741
- if (!value || typeof value !== "object") {
2742
- return null;
2743
- }
2744
- return value;
2745
- }
2746
- __name(asRecord, "asRecord");
2747
- function readString(value) {
2748
- if (typeof value === "string") {
2749
- return value;
2750
- }
2751
- const record = asRecord(value);
2752
- if (!record) {
2753
- return null;
2754
- }
2755
- return typeof record.value === "string" ? record.value : null;
2756
- }
2757
- __name(readString, "readString");
2758
- function readTransform(value) {
2759
- const record = asRecord(value);
2760
- if (!record) {
2761
- return void 0;
2762
- }
2763
- const position = asRecord(record.position);
2764
- const rotation = asRecord(record.rotation);
2765
- const scale = asRecord(record.scale);
2766
- return {
2767
- position: position ? {
2768
- x: typeof position.x === "number" ? position.x : void 0,
2769
- y: typeof position.y === "number" ? position.y : void 0,
2770
- z: typeof position.z === "number" ? position.z : void 0
2771
- } : void 0,
2772
- rotation: rotation ? {
2773
- x: typeof rotation.x === "number" ? rotation.x : void 0,
2774
- y: typeof rotation.y === "number" ? rotation.y : void 0,
2775
- z: typeof rotation.z === "number" ? rotation.z : void 0
2776
- } : void 0,
2777
- scale: scale ? {
2778
- x: typeof scale.x === "number" ? scale.x : void 0,
2779
- y: typeof scale.y === "number" ? scale.y : void 0,
2780
- z: typeof scale.z === "number" ? scale.z : void 0
2781
- } : void 0
2782
- };
2783
- }
2784
- __name(readTransform, "readTransform");
2785
- function readByComponent(components, componentName) {
2786
- const component = components.find((entry) => entry.component === componentName);
2787
- const map = /* @__PURE__ */ new Map();
2788
- if (!component) {
2789
- return map;
2790
- }
2791
- for (const entry of component.entries) {
2792
- map.set(entry.entityId, entry.value);
2793
- }
2794
- return map;
2795
- }
2796
- __name(readByComponent, "readByComponent");
2797
- function readBoolean(value) {
2798
- return typeof value === "boolean" ? value : void 0;
2799
- }
2800
- __name(readBoolean, "readBoolean");
2801
- function readNumber(value) {
2802
- return typeof value === "number" ? value : void 0;
2803
- }
2804
- __name(readNumber, "readNumber");
2805
-
2806
- // core/hydration/componentValues.ts
2807
- function buildComponentValueMaps(snapshot) {
2808
- return {
2809
- object3DRefValues: readByComponent(snapshot.components, "Object3DRef"),
2810
- nameValues: readByComponent(snapshot.components, "Name"),
2811
- transformValues: readByComponent(snapshot.components, "TransformComponent"),
2812
- modelValues: readByComponent(snapshot.components, "ModelComponent"),
2813
- imageValues: readByComponent(snapshot.components, "ImageComponent"),
2814
- spriteValues: readByComponent(snapshot.components, "SpriteComponent"),
2815
- videoValues: readByComponent(snapshot.components, "VideoComponent"),
2816
- cameraValues: readByComponent(snapshot.components, "CameraComponent"),
2817
- environmentValues: readByComponent(snapshot.components, "Environment"),
2818
- postprocessingValues: readByComponent(snapshot.components, "Postprocessing"),
2819
- audioValues: readByComponent(snapshot.components, "AudioComponent"),
2820
- imageTargetValues: readByComponent(snapshot.components, "ImageTarget"),
2821
- lightValues: readByComponent(snapshot.components, "LightComponent"),
2822
- materialValues: readByComponent(snapshot.components, "MaterialComponent"),
2823
- meshGeometryValues: readByComponent(snapshot.components, "MeshGeometryComponent"),
2824
- selectableValues: readByComponent(snapshot.components, "Selectable"),
2825
- physicsValues: readByComponent(snapshot.components, "PhysicsComponent"),
2826
- animationValues: readByComponent(snapshot.components, "AnimationComponent"),
2827
- imageTargetAnchorValues: readByComponent(snapshot.components, "ImageTargetAnchor"),
2828
- scriptValues: readByComponent(snapshot.components, "ScriptComponent"),
2829
- scriptObjectValues: readByComponent(
2830
- snapshot.components,
2831
- "ScriptObjectComponent"
2832
- ),
2833
- xrRigValues: readByComponent(snapshot.components, "XRRigComponent"),
2834
- vrTeleportAreaValues: readByComponent(
2835
- snapshot.components,
2836
- "VRTeleportAreaComponent"
2837
- ),
2838
- xrInteractableValues: readByComponent(
2839
- snapshot.components,
2840
- "XRInteractableComponent"
2841
- ),
2842
- xrInteractorValues: readByComponent(
2843
- snapshot.components,
2844
- "XRInteractorComponent"
2845
- ),
2846
- xrHandPoseValues: readByComponent(
2847
- snapshot.components,
2848
- "XRHandPoseComponent"
2849
- ),
2850
- xrAnchorValues: readByComponent(snapshot.components, "XRAnchorComponent"),
2851
- uiPanelValues: readByComponent(snapshot.components, "UIPanelComponent"),
2852
- uiTextValues: readByComponent(snapshot.components, "UITextComponent"),
2853
- uiButtonValues: readByComponent(snapshot.components, "UIButtonComponent"),
2854
- uiToggleValues: readByComponent(snapshot.components, "UIToggleComponent"),
2855
- uiSliderValues: readByComponent(snapshot.components, "UISliderComponent"),
2856
- xrInteractorSideValues: readByComponent(
2857
- snapshot.components,
2858
- "XRInteractorSideComponent"
2859
- ),
2860
- instancedMeshValues: readByComponent(snapshot.components, "InstancedMeshComponent"),
2861
- instanceSlotValues: readByComponent(snapshot.components, "InstanceSlotComponent"),
2862
- prefabNodeValues: readByComponent(snapshot.components, "PrefabNode"),
2863
- prefabInstanceValues: readByComponent(snapshot.components, "PrefabInstance")
2864
- };
2865
- }
2866
- __name(buildComponentValueMaps, "buildComponentValueMaps");
2867
-
2868
- // core/hydration/parentLinks.ts
2869
- function applyParentLinks(snapshot, idMap, objectManagementSystem, world) {
2870
- const parentValues = readByComponent(snapshot.components, "ParentId");
2871
- for (const [sourceEntityId, parentValue] of parentValues.entries()) {
2872
- const targetEntityId = idMap.get(sourceEntityId);
2873
- if (targetEntityId === void 0) {
2874
- continue;
2875
- }
2876
- const parentRecord = asRecord(parentValue);
2877
- const sourceParentId = parentRecord && typeof parentRecord.value === "number" ? parentRecord.value : null;
2878
- if (sourceParentId === null) {
2879
- objectManagementSystem.setParent(world, targetEntityId, null, {
2880
- preserveWorldTransform: false
2881
- });
2882
- continue;
2883
- }
2884
- const mappedParentId = idMap.get(sourceParentId);
2885
- objectManagementSystem.setParent(world, targetEntityId, mappedParentId ?? null, {
2886
- preserveWorldTransform: false
2887
- });
2888
- }
2889
- }
2890
- __name(applyParentLinks, "applyParentLinks");
2891
-
2892
- // core/hydration/postCreateComponents.ts
2893
- function remapEntityRefsInScriptData(data, idMap) {
2894
- const result = {};
2895
- for (const [key, value] of Object.entries(data)) {
2896
- if (value !== null && typeof value === "object" && !Array.isArray(value)) {
2897
- const v = value;
2898
- if (v.type === "entity" && typeof v.value === "number") {
2899
- result[key] = { ...v, value: idMap.get(v.value) ?? v.value };
2900
- } else if (v.type === "component" && v.value !== null && typeof v.value === "object" && !Array.isArray(v.value) && typeof v.value.entityId === "number") {
2901
- const ref = v.value;
2902
- result[key] = {
2903
- ...v,
2904
- value: { ...ref, entityId: idMap.get(ref.entityId) ?? ref.entityId }
2905
- };
2906
- } else {
2907
- result[key] = value;
2908
- }
2909
- } else {
2910
- result[key] = value;
2911
- }
2912
- }
2913
- return result;
2914
- }
2915
- __name(remapEntityRefsInScriptData, "remapEntityRefsInScriptData");
2916
- function restorePostCreateComponents(world, objectManagementSystem, targetEntityId, sourceEntityId, entityType, values, idMap) {
2917
- const objectRef = world.getComponent(targetEntityId, Object3DRef)?.object ?? null;
2918
- const sourceAudio = asRecord(values.audioValues.get(sourceEntityId));
2919
- if (sourceAudio && entityType !== "audio") {
2920
- const sourceOptions = asRecord(sourceAudio.options) ?? {};
2921
- objectManagementSystem.createComponentNow(
2922
- "audio",
2923
- world,
2924
- targetEntityId,
2925
- typeof sourceAudio.path === "string" ? sourceAudio.path : "",
2926
- sourceOptions,
2927
- {
2928
- object: objectRef,
2929
- listener: objectManagementSystem.getOrCreateAudioListener(),
2930
- isEditor: objectManagementSystem.isEditorMode()
2931
- }
2932
- );
2933
- }
2934
- const sourceImageTarget = asRecord(values.imageTargetValues.get(sourceEntityId));
2935
- if (sourceImageTarget && entityType !== "imageTarget") {
2936
- objectManagementSystem.createComponentNow(
2937
- "imageTarget",
2938
- world,
2939
- targetEntityId,
2940
- {
2941
- targets: Array.isArray(sourceImageTarget.targets) ? sourceImageTarget.targets : [],
2942
- compiledTargetUrl: typeof sourceImageTarget.compiledTargetUrl === "string" ? sourceImageTarget.compiledTargetUrl : void 0,
2943
- compiledTargetDir: typeof sourceImageTarget.compiledTargetDir === "string" ? sourceImageTarget.compiledTargetDir : void 0
2944
- }
2945
- );
2946
- }
2947
- const sourceImageTargetAnchor = asRecord(values.imageTargetAnchorValues.get(sourceEntityId));
2948
- if (sourceImageTargetAnchor) {
2949
- const mappedTargetId = typeof sourceImageTargetAnchor.targetId === "number" && Number.isFinite(sourceImageTargetAnchor.targetId) ? sourceImageTargetAnchor.targetId : null;
2950
- objectManagementSystem.createComponentNow("imageTargetAnchor", world, targetEntityId, {
2951
- targetId: mappedTargetId,
2952
- targetUrl: typeof sourceImageTargetAnchor.targetUrl === "string" ? sourceImageTargetAnchor.targetUrl : void 0
2953
- });
2954
- }
2955
- const sourceScript = asRecord(values.scriptValues.get(sourceEntityId));
2956
- if (sourceScript) {
2957
- const entries = Array.isArray(sourceScript.entries) ? sourceScript.entries.map((entry) => asRecord(entry)).filter((entry) => Boolean(entry)).map((entry) => {
2958
- const runtimeState = entry.runtimeState === "idle" || entry.runtimeState === "loading" || entry.runtimeState === "ready" || entry.runtimeState === "error" ? entry.runtimeState : void 0;
2959
- return {
2960
- entityId: targetEntityId,
2961
- className: typeof entry.className === "string" ? entry.className : "",
2962
- scriptUrl: typeof entry.scriptUrl === "string" ? entry.scriptUrl : "",
2963
- enabled: typeof entry.enabled === "boolean" ? entry.enabled : true,
2964
- data: entry.data && typeof entry.data === "object" && !Array.isArray(entry.data) ? remapEntityRefsInScriptData(entry.data, idMap) : {},
2965
- runtimeState,
2966
- runtimeMessage: typeof entry.runtimeMessage === "string" ? entry.runtimeMessage : void 0
2967
- };
2968
- }).filter((entry) => entry.className || entry.scriptUrl) : [];
2969
- if (entries.length) {
2970
- world.addComponent(targetEntityId, new ScriptComponent(entries));
2971
- }
2972
- }
2973
- const sourceScriptObject = asRecord(values.scriptObjectValues.get(sourceEntityId));
2974
- if (sourceScriptObject) {
2975
- const soc = world.getComponent(targetEntityId, ScriptObjectComponent);
2976
- const data = asRecord(sourceScriptObject.data);
2977
- if (soc && data) {
2978
- soc.data = remapEntityRefsInScriptData(data, idMap);
2979
- }
2980
- }
2981
- {
2982
- const sourceXrRig = asRecord(values.xrRigValues.get(sourceEntityId));
2983
- const rig = world.getComponent(targetEntityId, XRRigComponent);
2984
- if (sourceXrRig && rig) {
2985
- if (typeof sourceXrRig.locomotion === "string") {
2986
- rig.locomotion = sourceXrRig.locomotion;
2987
- }
2988
- if (typeof sourceXrRig.moveSpeed === "number") {
2989
- rig.moveSpeed = sourceXrRig.moveSpeed;
2990
- }
2991
- if (typeof sourceXrRig.snapTurn === "boolean") {
2992
- rig.snapTurn = sourceXrRig.snapTurn;
2993
- }
2994
- if (typeof sourceXrRig.snapTurnDegrees === "number") {
2995
- rig.snapTurnDegrees = sourceXrRig.snapTurnDegrees;
2996
- }
2997
- if (typeof sourceXrRig.teleportArcColor === "string") {
2998
- rig.teleportArcColor = sourceXrRig.teleportArcColor;
2999
- }
3000
- if (typeof sourceXrRig.teleportArcInvalidColor === "string") {
3001
- rig.teleportArcInvalidColor = sourceXrRig.teleportArcInvalidColor;
3002
- }
3003
- if (typeof sourceXrRig.teleportArcThickness === "number") {
3004
- rig.teleportArcThickness = sourceXrRig.teleportArcThickness;
3005
- }
3006
- if (typeof sourceXrRig.fallbackEyeHeight === "number") {
3007
- rig.fallbackEyeHeight = sourceXrRig.fallbackEyeHeight;
3008
- }
3009
- if (typeof sourceXrRig.passthrough === "boolean") {
3010
- rig.passthrough = sourceXrRig.passthrough;
3011
- }
3012
- if (typeof sourceXrRig.depthOcclusion === "boolean") {
3013
- rig.depthOcclusion = sourceXrRig.depthOcclusion;
3014
- }
3015
- if (typeof sourceXrRig.environmentPhysics === "string") {
3016
- rig.environmentPhysics = sourceXrRig.environmentPhysics;
3017
- }
3018
- if (typeof sourceXrRig.environmentDebug === "boolean") {
3019
- rig.environmentDebug = sourceXrRig.environmentDebug;
3020
- }
3021
- if (typeof sourceXrRig.hitTest === "boolean") {
3022
- rig.hitTest = sourceXrRig.hitTest;
3023
- }
3024
- if (typeof sourceXrRig.anchors === "boolean") {
3025
- rig.anchors = sourceXrRig.anchors;
3026
- }
3027
- }
3028
- }
3029
- {
3030
- const sourceArea = asRecord(values.vrTeleportAreaValues.get(sourceEntityId));
3031
- const area = world.getComponent(targetEntityId, VRTeleportAreaComponent);
3032
- if (sourceArea && area) {
3033
- if (typeof sourceArea.enabled === "boolean") {
3034
- area.enabled = sourceArea.enabled;
3035
- }
3036
- if (typeof sourceArea.shape === "string") {
3037
- area.shape = sourceArea.shape === "cylinder" ? "circle" : sourceArea.shape;
3038
- }
3039
- if (typeof sourceArea.width === "number") area.width = sourceArea.width;
3040
- if (typeof sourceArea.depth === "number") area.depth = sourceArea.depth;
3041
- }
3042
- }
3043
- {
3044
- const sourceInteractable = asRecord(
3045
- values.xrInteractableValues.get(sourceEntityId)
3046
- );
3047
- let interactable = world.getComponent(
3048
- targetEntityId,
3049
- XRInteractableComponent
3050
- );
3051
- if (sourceInteractable && !interactable) {
3052
- interactable = new XRInteractableComponent();
3053
- world.addComponent(targetEntityId, interactable);
3054
- }
3055
- if (sourceInteractable && interactable) {
3056
- if (typeof sourceInteractable.enabled === "boolean") {
3057
- interactable.enabled = sourceInteractable.enabled;
3058
- }
3059
- if (typeof sourceInteractable.grabbable === "boolean") {
3060
- interactable.grabbable = sourceInteractable.grabbable;
3061
- }
3062
- if (typeof sourceInteractable.pointable === "boolean") {
3063
- interactable.pointable = sourceInteractable.pointable;
3064
- }
3065
- if (typeof sourceInteractable.grabReach === "string") {
3066
- interactable.grabReach = sourceInteractable.grabReach;
3067
- }
3068
- if (typeof sourceInteractable.distanceBehavior === "string") {
3069
- interactable.distanceBehavior = sourceInteractable.distanceBehavior;
3070
- }
3071
- if (typeof sourceInteractable.grabGesture === "string") {
3072
- interactable.grabGesture = sourceInteractable.grabGesture;
3073
- }
3074
- if (typeof sourceInteractable.grabRadius === "number") {
3075
- interactable.grabRadius = sourceInteractable.grabRadius;
3076
- }
3077
- if (typeof sourceInteractable.grabHands === "string") {
3078
- interactable.grabHands = sourceInteractable.grabHands;
3079
- }
3080
- if (typeof sourceInteractable.twoHandRotation === "string") {
3081
- interactable.twoHandRotation = sourceInteractable.twoHandRotation;
3082
- }
3083
- if (typeof sourceInteractable.allowTwoHandScale === "boolean") {
3084
- interactable.allowTwoHandScale = sourceInteractable.allowTwoHandScale;
3085
- }
3086
- if (typeof sourceInteractable.minScale === "number") {
3087
- interactable.minScale = sourceInteractable.minScale;
3088
- }
3089
- if (typeof sourceInteractable.maxScale === "number") {
3090
- interactable.maxScale = sourceInteractable.maxScale;
3091
- }
3092
- if (Array.isArray(sourceInteractable.attachPoints)) {
3093
- interactable.attachPoints = sourceInteractable.attachPoints.map((point) => asRecord(point)).filter(
3094
- (point) => point !== null && typeof point.id === "string"
3095
- ).map(
3096
- (point) => createAttachPoint(
3097
- point
3098
- )
3099
- );
3100
- }
3101
- if (typeof sourceInteractable.applyVelocityOnRelease === "boolean") {
3102
- interactable.applyVelocityOnRelease = sourceInteractable.applyVelocityOnRelease;
3103
- } else if (typeof sourceInteractable.throwOnRelease === "boolean") {
3104
- interactable.applyVelocityOnRelease = sourceInteractable.throwOnRelease;
3105
- }
3106
- if (typeof sourceInteractable.velocityScale === "number") {
3107
- interactable.velocityScale = sourceInteractable.velocityScale;
3108
- } else if (typeof sourceInteractable.throwStrength === "number") {
3109
- interactable.velocityScale = sourceInteractable.throwStrength;
3110
- }
3111
- const grabPos = asRecord(sourceInteractable.grabOffsetPosition);
3112
- if (grabPos) {
3113
- interactable.grabOffsetPosition = {
3114
- x: typeof grabPos.x === "number" ? grabPos.x : 0,
3115
- y: typeof grabPos.y === "number" ? grabPos.y : 0,
3116
- z: typeof grabPos.z === "number" ? grabPos.z : 0
3117
- };
3118
- }
3119
- const grabRot = asRecord(sourceInteractable.grabOffsetRotation);
3120
- if (grabRot) {
3121
- interactable.grabOffsetRotation = {
3122
- x: typeof grabRot.x === "number" ? grabRot.x : 0,
3123
- y: typeof grabRot.y === "number" ? grabRot.y : 0,
3124
- z: typeof grabRot.z === "number" ? grabRot.z : 0
3125
- };
3126
- }
3127
- if (typeof sourceInteractable.hoverBehavior === "string") {
3128
- interactable.hoverBehavior = sourceInteractable.hoverBehavior;
3129
- } else if (sourceInteractable.highlightOnHover === false) {
3130
- interactable.hoverBehavior = "none";
3131
- }
3132
- if (typeof sourceInteractable.hoverColor === "string") {
3133
- interactable.hoverColor = sourceInteractable.hoverColor;
3134
- }
3135
- if (typeof sourceInteractable.hoverScale === "number") {
3136
- interactable.hoverScale = sourceInteractable.hoverScale;
3137
- }
3138
- }
3139
- }
3140
- {
3141
- const sourceHandPose = asRecord(
3142
- values.xrHandPoseValues.get(sourceEntityId)
3143
- );
3144
- let handPose = world.getComponent(targetEntityId, XRHandPoseComponent);
3145
- if (sourceHandPose && !handPose) {
3146
- handPose = new XRHandPoseComponent();
3147
- world.addComponent(targetEntityId, handPose);
3148
- }
3149
- if (sourceHandPose && handPose) {
3150
- if (typeof sourceHandPose.enabled === "boolean") {
3151
- handPose.enabled = sourceHandPose.enabled;
3152
- }
3153
- if (Array.isArray(sourceHandPose.poses)) {
3154
- handPose.poses = sourceHandPose.poses.map((entry) => asRecord(entry)).filter(
3155
- (entry) => entry !== null && typeof entry.id === "string"
3156
- ).map(
3157
- (entry) => createHandPoseEntry(
3158
- entry
3159
- )
3160
- );
3161
- }
3162
- }
3163
- }
3164
- {
3165
- const sourceAnchor = asRecord(values.xrAnchorValues.get(sourceEntityId));
3166
- let anchor = world.getComponent(targetEntityId, XRAnchorComponent);
3167
- if (sourceAnchor && !anchor) {
3168
- anchor = new XRAnchorComponent();
3169
- world.addComponent(targetEntityId, anchor);
3170
- }
3171
- if (sourceAnchor && anchor) {
3172
- if (typeof sourceAnchor.enabled === "boolean") {
3173
- anchor.enabled = sourceAnchor.enabled;
3174
- }
3175
- }
3176
- }
3177
- {
3178
- const restoreUi = /* @__PURE__ */ __name((map, Ctor, existing) => {
3179
- const source = asRecord(map.get(sourceEntityId));
3180
- if (!source) return;
3181
- let component = existing;
3182
- if (!component) {
3183
- component = new Ctor();
3184
- world.addComponent(targetEntityId, component);
3185
- }
3186
- const target = component;
3187
- for (const [key, value] of Object.entries(source)) {
3188
- const current = target[key];
3189
- if ((typeof value === "string" || typeof value === "number" || typeof value === "boolean") && typeof current === typeof value) {
3190
- target[key] = value;
3191
- }
3192
- }
3193
- }, "restoreUi");
3194
- restoreUi(
3195
- values.uiPanelValues,
3196
- UIPanelComponent,
3197
- world.getComponent(targetEntityId, UIPanelComponent)
3198
- );
3199
- restoreUi(
3200
- values.uiTextValues,
3201
- UITextComponent,
3202
- world.getComponent(targetEntityId, UITextComponent)
3203
- );
3204
- restoreUi(
3205
- values.uiButtonValues,
3206
- UIButtonComponent,
3207
- world.getComponent(targetEntityId, UIButtonComponent)
3208
- );
3209
- restoreUi(
3210
- values.uiToggleValues,
3211
- UIToggleComponent,
3212
- world.getComponent(targetEntityId, UIToggleComponent)
3213
- );
3214
- restoreUi(
3215
- values.uiSliderValues,
3216
- UISliderComponent,
3217
- world.getComponent(targetEntityId, UISliderComponent)
3218
- );
3219
- }
3220
- {
3221
- const sourceInteractor = asRecord(
3222
- values.xrInteractorValues.get(sourceEntityId)
3223
- );
3224
- const interactor = world.getComponent(targetEntityId, XRInteractorComponent);
3225
- if (sourceInteractor && interactor) {
3226
- if (typeof sourceInteractor.interactorType === "string") {
3227
- interactor.interactorType = sourceInteractor.interactorType;
3228
- }
3229
- if (typeof sourceInteractor.handPreset === "string") {
3230
- interactor.handPreset = sourceInteractor.handPreset;
3231
- }
3232
- if (typeof sourceInteractor.handColor === "string") {
3233
- interactor.handColor = sourceInteractor.handColor;
3234
- }
3235
- if (typeof sourceInteractor.handOpacity === "number") {
3236
- interactor.handOpacity = sourceInteractor.handOpacity;
3237
- }
3238
- if (typeof sourceInteractor.handEmissiveIntensity === "number") {
3239
- interactor.handEmissiveIntensity = sourceInteractor.handEmissiveIntensity;
3240
- }
3241
- if (typeof sourceInteractor.rayVisibility === "string") {
3242
- interactor.rayVisibility = sourceInteractor.rayVisibility;
3243
- } else if (typeof sourceInteractor.showRay === "boolean") {
3244
- interactor.rayVisibility = sourceInteractor.showRay ? "interacting" : "never";
3245
- }
3246
- if (typeof sourceInteractor.rayColor === "string") {
3247
- interactor.rayColor = sourceInteractor.rayColor;
3248
- }
3249
- if (typeof sourceInteractor.rayThickness === "number") {
3250
- interactor.rayThickness = sourceInteractor.rayThickness;
3251
- }
3252
- }
3253
- }
3254
- {
3255
- const sourceSide = asRecord(
3256
- values.xrInteractorSideValues.get(sourceEntityId)
3257
- );
3258
- if (sourceSide && typeof sourceSide.side === "string") {
3259
- const existing = world.getComponent(
3260
- targetEntityId,
3261
- XRInteractorSideComponent
3262
- );
3263
- if (existing) {
3264
- existing.side = sourceSide.side;
3265
- } else {
3266
- world.addComponent(
3267
- targetEntityId,
3268
- new XRInteractorSideComponent({
3269
- side: sourceSide.side
3270
- })
3271
- );
3272
- }
3273
- }
3274
- }
3275
- {
3276
- const sourceRigOrigin = asRecord(values.xrRigValues.get(sourceEntityId));
3277
- const rigForOrigin = world.getComponent(targetEntityId, XRRigComponent);
3278
- if (sourceRigOrigin && rigForOrigin && typeof sourceRigOrigin.trackingOrigin === "string") {
3279
- rigForOrigin.trackingOrigin = sourceRigOrigin.trackingOrigin;
3280
- }
3281
- }
3282
- {
3283
- const imComponent = world.getComponent(targetEntityId, InstancedMeshComponent);
3284
- if (imComponent && imComponent.instanceEntityIds.length > 0) {
3285
- imComponent.instanceEntityIds = imComponent.instanceEntityIds.map(
3286
- (oldId) => idMap.get(oldId) ?? oldId
3287
- );
3288
- }
3289
- }
3290
- {
3291
- const slotComponent = world.getComponent(targetEntityId, InstanceSlotComponent);
3292
- if (slotComponent && slotComponent.groupEntityId !== -1) {
3293
- slotComponent.groupEntityId = idMap.get(slotComponent.groupEntityId) ?? slotComponent.groupEntityId;
3294
- }
3295
- }
3296
- }
3297
- __name(restorePostCreateComponents, "restorePostCreateComponents");
3298
-
3299
- // core/hydration/animation.ts
3300
- function readAnimationClipName(value) {
3301
- if (typeof value === "string") {
3302
- return value;
3303
- }
3304
- const record = asRecord(value);
3305
- if (!record) {
3306
- return null;
3307
- }
3308
- if (typeof record.name === "string") {
3309
- return record.name;
3310
- }
3311
- return null;
3312
- }
3313
- __name(readAnimationClipName, "readAnimationClipName");
3314
- function readAnimationStates(value) {
3315
- if (!Array.isArray(value)) {
3316
- return [];
3317
- }
3318
- const states = [];
3319
- for (const entry of value) {
3320
- const state = asRecord(entry);
3321
- if (!state) {
3322
- continue;
3323
- }
3324
- const name = typeof state.name === "string" ? state.name : null;
3325
- const clip = readAnimationClipName(state.clip);
3326
- if (!name || !clip) {
3327
- continue;
3328
- }
3329
- states.push({
3330
- name,
3331
- clip,
3332
- clipId: typeof state.clipId === "string" ? state.clipId : void 0,
3333
- loop: typeof state.loop === "boolean" ? state.loop : false
3334
- });
3335
- }
3336
- return states;
3337
- }
3338
- __name(readAnimationStates, "readAnimationStates");
3339
- function readAnimationClipSources(value) {
3340
- if (!Array.isArray(value)) {
3341
- return [];
3342
- }
3343
- const seen = /* @__PURE__ */ new Set();
3344
- const sources = [];
3345
- for (const entry of value) {
3346
- if (typeof entry !== "string") {
3347
- continue;
3348
- }
3349
- const trimmed = entry.trim();
3350
- if (!trimmed || seen.has(trimmed)) {
3351
- continue;
3352
- }
3353
- seen.add(trimmed);
3354
- sources.push(trimmed);
3355
- }
3356
- return sources;
3357
- }
3358
- __name(readAnimationClipSources, "readAnimationClipSources");
3359
- function toAnimationOptions(sourceAnimation) {
3360
- const paused = readBoolean(sourceAnimation.paused);
3361
- const reverse = readBoolean(sourceAnimation.reverse);
3362
- const timeScale = readNumber(sourceAnimation.timeScale);
3363
- const crossFadeDuration = readNumber(sourceAnimation.crossFadeDuration);
3364
- const states = readAnimationStates(sourceAnimation.states);
3365
- const externalClipSources = readAnimationClipSources(
3366
- sourceAnimation.externalClipSources
3367
- );
3368
- const activeStateName = typeof sourceAnimation.activeStateName === "string" ? sourceAnimation.activeStateName : typeof sourceAnimation.activeState === "string" ? sourceAnimation.activeState : typeof sourceAnimation.active?.name === "string" ? sourceAnimation.active.name ?? null : null;
3369
- return {
3370
- paused,
3371
- reverse,
3372
- timeScale,
3373
- crossFadeDuration,
3374
- states,
3375
- externalClipSources,
3376
- activeState: activeStateName
3377
- };
3378
- }
3379
- __name(toAnimationOptions, "toAnimationOptions");
3380
- function restoreAnimationComponent(world, objectManagementSystem, targetEntityId, sourceAnimation) {
3381
- const options = toAnimationOptions(sourceAnimation);
3382
- let animation = world.getComponent(targetEntityId, AnimationComponent);
3383
- if (!animation) {
3384
- objectManagementSystem.createComponentNow(
3385
- "animation",
3386
- world,
3387
- targetEntityId,
3388
- options
3389
- );
3390
- animation = world.getComponent(targetEntityId, AnimationComponent);
3391
- }
3392
- if (!animation) {
3393
- return;
3394
- }
3395
- const availableClips = Array.isArray(sourceAnimation.availableClips) ? sourceAnimation.availableClips.filter(
3396
- (clip) => typeof clip === "string" && clip.length > 0
3397
- ) : [];
3398
- if (availableClips.length > 0) {
3399
- animation.availableClips = [...availableClips];
3400
- }
3401
- if (Array.isArray(options.externalClipSources)) {
3402
- animation.setExternalClipSources(options.externalClipSources);
3403
- }
3404
- const states = options.states ?? [];
3405
- const hasClips = Array.isArray(animation.clips) && animation.clips.length > 0;
3406
- if (!hasClips) {
3407
- animation.__pendingHydratedOptions = options;
3408
- if (typeof options.reverse === "boolean") {
3409
- animation.reverse = options.reverse;
3410
- }
3411
- if (typeof options.timeScale === "number") {
3412
- animation.timeScale = options.timeScale;
3413
- }
3414
- if (typeof options.crossFadeDuration === "number") {
3415
- animation.crossFadeDuration = options.crossFadeDuration;
3416
- }
3417
- if (typeof options.paused === "boolean") {
3418
- animation.paused = options.paused;
3419
- }
3420
- return;
3421
- }
3422
- if (states.length > 0) {
3423
- animation.setStates(states);
3424
- }
3425
- if (typeof options.reverse === "boolean") {
3426
- animation.reverse = options.reverse;
3427
- }
3428
- if (typeof options.timeScale === "number") {
3429
- animation.timeScale = options.timeScale;
3430
- }
3431
- if (typeof options.crossFadeDuration === "number") {
3432
- animation.crossFadeDuration = options.crossFadeDuration;
3433
- }
3434
- if (typeof options.activeState === "string" && options.activeState.length > 0) {
3435
- animation.setActiveState(options.activeState, !options.paused);
3436
- }
3437
- if (typeof options.paused === "boolean") {
3438
- if (options.paused) {
3439
- animation.pauseState();
3440
- } else if (typeof options.activeState === "string" && options.activeState.length > 0) {
3441
- animation.playState(options.activeState);
3442
- }
3443
- }
3444
- }
3445
- __name(restoreAnimationComponent, "restoreAnimationComponent");
3446
-
3447
- // core/hydration/restoreComponentState.ts
3448
- function readEnvironmentState(value) {
3449
- const record = asRecord(value);
3450
- if (!record) {
3451
- return null;
3452
- }
3453
- const nestedValues = asRecord(record.values);
3454
- if (nestedValues) {
3455
- return nestedValues;
3456
- }
3457
- return record;
3458
- }
3459
- __name(readEnvironmentState, "readEnvironmentState");
3460
- function readPostprocessingState(value) {
3461
- const record = asRecord(value);
3462
- if (!record) {
3463
- return null;
3464
- }
3465
- const nestedValues = asRecord(record.values);
3466
- if (nestedValues) {
3467
- return nestedValues;
3468
- }
3469
- return record;
3470
- }
3471
- __name(readPostprocessingState, "readPostprocessingState");
3472
- function restoreComponentState(world, objectManagementSystem, targetEntityId, sourceEntityId, entityType, values) {
3473
- const sourceName = readString(values.nameValues.get(sourceEntityId));
3474
- if (sourceName !== null) {
3475
- const nameComponent = world.getComponent(targetEntityId, Name);
3476
- if (nameComponent) {
3477
- nameComponent.value = sourceName;
3478
- }
3479
- }
3480
- const sourceObject3DRef = asRecord(values.object3DRefValues.get(sourceEntityId));
3481
- if (sourceObject3DRef) {
3482
- const object3DRefComponent = world.getComponent(targetEntityId, Object3DRef);
3483
- const visibility = readBoolean(sourceObject3DRef.visibility);
3484
- if (object3DRefComponent && typeof visibility === "boolean") {
3485
- object3DRefComponent.visibility = visibility;
3486
- }
3487
- }
3488
- const sourceTransform = readTransform(values.transformValues.get(sourceEntityId));
3489
- if (sourceTransform) {
3490
- const transformComponent = world.getComponent(targetEntityId, TransformComponent);
3491
- if (transformComponent) {
3492
- if (typeof sourceTransform.position?.x === "number") {
3493
- transformComponent.position.x = sourceTransform.position.x;
3494
- }
3495
- if (typeof sourceTransform.position?.y === "number") {
3496
- transformComponent.position.y = sourceTransform.position.y;
3497
- }
3498
- if (typeof sourceTransform.position?.z === "number") {
3499
- transformComponent.position.z = sourceTransform.position.z;
3500
- }
3501
- if (typeof sourceTransform.rotation?.x === "number") {
3502
- transformComponent.rotation.x = sourceTransform.rotation.x;
3503
- }
3504
- if (typeof sourceTransform.rotation?.y === "number") {
3505
- transformComponent.rotation.y = sourceTransform.rotation.y;
3506
- }
3507
- if (typeof sourceTransform.rotation?.z === "number") {
3508
- transformComponent.rotation.z = sourceTransform.rotation.z;
3509
- }
3510
- if (typeof sourceTransform.scale?.x === "number") {
3511
- transformComponent.scale.x = sourceTransform.scale.x;
3512
- }
3513
- if (typeof sourceTransform.scale?.y === "number") {
3514
- transformComponent.scale.y = sourceTransform.scale.y;
3515
- }
3516
- if (typeof sourceTransform.scale?.z === "number") {
3517
- transformComponent.scale.z = sourceTransform.scale.z;
3518
- }
3519
- }
3520
- }
3521
- const sourceModel = asRecord(values.modelValues.get(sourceEntityId));
3522
- if (sourceModel) {
3523
- const modelComponent = world.getComponent(targetEntityId, ModelComponent);
3524
- if (modelComponent && typeof sourceModel.path === "string") {
3525
- modelComponent.path = sourceModel.path;
3526
- }
3527
- }
3528
- const sourceImage = asRecord(values.imageValues.get(sourceEntityId));
3529
- if (sourceImage) {
3530
- const imageComponent = world.getComponent(targetEntityId, ImageComponent);
3531
- if (imageComponent) {
3532
- if (typeof sourceImage.path === "string") {
3533
- imageComponent.path = sourceImage.path;
3534
- }
3535
- const width = readNumber(sourceImage.width);
3536
- if (typeof width === "number") {
3537
- imageComponent.width = width;
3538
- }
3539
- const height = readNumber(sourceImage.height);
3540
- if (typeof height === "number") {
3541
- imageComponent.height = height;
3542
- }
3543
- }
3544
- }
3545
- const sourceSprite = asRecord(values.spriteValues.get(sourceEntityId));
3546
- if (sourceSprite) {
3547
- const spriteComponent = world.getComponent(targetEntityId, SpriteComponent);
3548
- if (spriteComponent) {
3549
- if (typeof sourceSprite.path === "string") {
3550
- spriteComponent.path = sourceSprite.path;
3551
- }
3552
- const width = readNumber(sourceSprite.width);
3553
- if (typeof width === "number") {
3554
- spriteComponent.width = width;
3555
- }
3556
- const height = readNumber(sourceSprite.height);
3557
- if (typeof height === "number") {
3558
- spriteComponent.height = height;
3559
- }
3560
- }
3561
- }
3562
- const sourceVideo = asRecord(values.videoValues.get(sourceEntityId));
3563
- if (sourceVideo) {
3564
- const videoComponent = world.getComponent(targetEntityId, VideoComponent);
3565
- if (videoComponent) {
3566
- if (typeof sourceVideo.path === "string") {
3567
- videoComponent.path = sourceVideo.path;
3568
- }
3569
- const videoOptions = asRecord(sourceVideo.options);
3570
- if (videoOptions) {
3571
- videoComponent.options = {
3572
- ...videoComponent.options,
3573
- ...videoOptions
3574
- };
3575
- }
3576
- }
3577
- }
3578
- const sourceAudio = asRecord(values.audioValues.get(sourceEntityId));
3579
- if (sourceAudio && entityType === "audio") {
3580
- const audioComponent = world.getComponent(targetEntityId, AudioComponent);
3581
- if (audioComponent) {
3582
- if (typeof sourceAudio.path === "string") {
3583
- audioComponent.path = sourceAudio.path;
3584
- }
3585
- const audioOptions = asRecord(sourceAudio.options);
3586
- if (audioOptions) {
3587
- audioComponent.options = {
3588
- ...audioComponent.options,
3589
- ...audioOptions
3590
- };
3591
- }
3592
- }
3593
- }
3594
- const sourceCamera = asRecord(values.cameraValues.get(sourceEntityId));
3595
- if (sourceCamera) {
3596
- const cameraComponent = world.getComponent(targetEntityId, CameraComponent);
3597
- if (cameraComponent) {
3598
- if (sourceCamera.projection === "orthographic" || sourceCamera.projection === "perspective") {
3599
- cameraComponent.projection = sourceCamera.projection;
3600
- }
3601
- if (sourceCamera.type === "3d" || sourceCamera.type === "ar") {
3602
- cameraComponent.type = sourceCamera.type;
3603
- }
3604
- if (typeof sourceCamera.isDefault === "boolean") {
3605
- cameraComponent.isDefault = sourceCamera.isDefault;
3606
- }
3607
- const fov = readNumber(sourceCamera.fov);
3608
- if (typeof fov === "number") {
3609
- cameraComponent.fov = fov;
3610
- }
3611
- const near = readNumber(sourceCamera.near);
3612
- if (typeof near === "number") {
3613
- cameraComponent.near = near;
3614
- }
3615
- const far = readNumber(sourceCamera.far);
3616
- if (typeof far === "number") {
3617
- cameraComponent.far = far;
3618
- }
3619
- const left = readNumber(sourceCamera.left);
3620
- if (typeof left === "number") {
3621
- cameraComponent.left = left;
3622
- }
3623
- const right = readNumber(sourceCamera.right);
3624
- if (typeof right === "number") {
3625
- cameraComponent.right = right;
3626
- }
3627
- const top = readNumber(sourceCamera.top);
3628
- if (typeof top === "number") {
3629
- cameraComponent.top = top;
3630
- }
3631
- const bottom = readNumber(sourceCamera.bottom);
3632
- if (typeof bottom === "number") {
3633
- cameraComponent.bottom = bottom;
3634
- }
3635
- const zoom = readNumber(sourceCamera.zoom);
3636
- if (typeof zoom === "number") {
3637
- cameraComponent.zoom = zoom;
3638
- }
3639
- }
3640
- }
3641
- const sourceEnvironment = readEnvironmentState(
3642
- values.environmentValues.get(sourceEntityId)
3643
- );
3644
- if (sourceEnvironment) {
3645
- let environmentComponent = world.getComponent(targetEntityId, Environment);
3646
- if (!environmentComponent) {
3647
- objectManagementSystem.createComponentNow(
3648
- "environment",
3649
- world,
3650
- targetEntityId,
3651
- sourceEnvironment
3652
- );
3653
- environmentComponent = world.getComponent(targetEntityId, Environment);
3654
- }
3655
- if (environmentComponent) {
3656
- environmentComponent.values = new Environment({
3657
- ...environmentComponent.values,
3658
- ...sourceEnvironment
3659
- }).values;
3660
- }
3661
- }
3662
- const sourcePostprocessing = readPostprocessingState(
3663
- values.postprocessingValues.get(sourceEntityId)
3664
- );
3665
- if (sourcePostprocessing) {
3666
- let postprocessingComponent = world.getComponent(targetEntityId, Postprocessing);
3667
- if (!postprocessingComponent) {
3668
- objectManagementSystem.createComponentNow(
3669
- "postprocessing",
3670
- world,
3671
- targetEntityId,
3672
- sourcePostprocessing
3673
- );
3674
- postprocessingComponent = world.getComponent(targetEntityId, Postprocessing);
3675
- }
3676
- if (postprocessingComponent) {
3677
- postprocessingComponent.values = new Postprocessing({
3678
- ...postprocessingComponent.values,
3679
- ...sourcePostprocessing
3680
- }).values;
3681
- }
3682
- }
3683
- const sourceImageTarget = asRecord(values.imageTargetValues.get(sourceEntityId));
3684
- if (sourceImageTarget && entityType === "imageTarget") {
3685
- const imageTargetComponent = world.getComponent(targetEntityId, ImageTarget);
3686
- if (imageTargetComponent) {
3687
- imageTargetComponent.targets = Array.isArray(sourceImageTarget.targets) ? sourceImageTarget.targets : [];
3688
- imageTargetComponent.compiledTargetUrl = typeof sourceImageTarget.compiledTargetUrl === "string" ? sourceImageTarget.compiledTargetUrl : void 0;
3689
- imageTargetComponent.compiledTargetDir = typeof sourceImageTarget.compiledTargetDir === "string" ? sourceImageTarget.compiledTargetDir : void 0;
3690
- }
3691
- }
3692
- const sourceLight = asRecord(values.lightValues.get(sourceEntityId));
3693
- if (sourceLight) {
3694
- const lightComponent = world.getComponent(targetEntityId, LightComponent);
3695
- const sourceValues = asRecord(sourceLight.values);
3696
- if (lightComponent && sourceValues) {
3697
- lightComponent.values = {
3698
- ...lightComponent.values,
3699
- ...sourceValues
3700
- };
3701
- }
3702
- }
3703
- const sourceMaterial = asRecord(values.materialValues.get(sourceEntityId));
3704
- if (sourceMaterial) {
3705
- const materialComponent = world.getComponent(targetEntityId, MaterialComponent);
3706
- const sourceValues = sourceMaterial.values;
3707
- if (materialComponent && Array.isArray(sourceValues)) {
3708
- materialComponent.values = sourceValues;
3709
- }
3710
- const sourceAssetRefs = sourceMaterial.assetRefs;
3711
- if (materialComponent && Array.isArray(sourceAssetRefs)) {
3712
- materialComponent.assetRefs = sourceAssetRefs;
3713
- }
3714
- }
3715
- const sourceMeshGeometry = asRecord(values.meshGeometryValues.get(sourceEntityId));
3716
- if (sourceMeshGeometry) {
3717
- const meshGeometryComponent = world.getComponent(
3718
- targetEntityId,
3719
- MeshGeometryComponent
3720
- );
3721
- if (meshGeometryComponent) {
3722
- const parameters = asRecord(sourceMeshGeometry.parameters);
3723
- if (parameters) {
3724
- meshGeometryComponent.parameters = parameters;
3725
- }
3726
- if (typeof sourceMeshGeometry.modelName === "string") {
3727
- meshGeometryComponent.modelName = sourceMeshGeometry.modelName;
3728
- }
3729
- if (typeof sourceMeshGeometry.castShadow === "boolean") {
3730
- meshGeometryComponent.castShadow = sourceMeshGeometry.castShadow;
3731
- }
3732
- if (typeof sourceMeshGeometry.receiveShadow === "boolean") {
3733
- meshGeometryComponent.receiveShadow = sourceMeshGeometry.receiveShadow;
3734
- }
3735
- }
3736
- }
3737
- const sourceSelectable = asRecord(values.selectableValues.get(sourceEntityId));
3738
- if (sourceSelectable) {
3739
- const enabled = readBoolean(sourceSelectable.enabled);
3740
- let selectable = world.getComponent(targetEntityId, Selectable);
3741
- if (!selectable) {
3742
- objectManagementSystem.createComponentNow(
3743
- "selectable",
3744
- world,
3745
- targetEntityId,
3746
- enabled ?? true
3747
- );
3748
- selectable = world.getComponent(targetEntityId, Selectable);
3749
- }
3750
- if (selectable && typeof enabled === "boolean") {
3751
- selectable.enabled = enabled;
3752
- }
3753
- }
3754
- const sourcePhysics = asRecord(values.physicsValues.get(sourceEntityId));
3755
- if (sourcePhysics) {
3756
- const physicsValues = asRecord(sourcePhysics.values) ?? sourcePhysics;
3757
- let physics = world.getComponent(targetEntityId, PhysicsComponent);
3758
- if (!physics) {
3759
- objectManagementSystem.createComponentNow(
3760
- "physics",
3761
- world,
3762
- targetEntityId,
3763
- physicsValues
3764
- );
3765
- physics = world.getComponent(targetEntityId, PhysicsComponent);
3766
- }
3767
- if (physics) {
3768
- physics.setValues({
3769
- rigidbody: asRecord(physicsValues.rigidbody) ?? {},
3770
- collider: asRecord(physicsValues.collider) ?? {}
3771
- });
3772
- }
3773
- }
3774
- const sourceAnimation = asRecord(values.animationValues.get(sourceEntityId));
3775
- if (sourceAnimation) {
3776
- restoreAnimationComponent(
3777
- world,
3778
- objectManagementSystem,
3779
- targetEntityId,
3780
- sourceAnimation
3781
- );
3782
- }
3783
- const sourceInstancedMesh = asRecord(values.instancedMeshValues.get(sourceEntityId));
3784
- if (sourceInstancedMesh) {
3785
- const imComponent = world.getComponent(targetEntityId, InstancedMeshComponent);
3786
- if (imComponent) {
3787
- if (typeof sourceInstancedMesh.meshKind === "string") {
3788
- imComponent.meshKind = sourceInstancedMesh.meshKind;
3789
- }
3790
- if (typeof sourceInstancedMesh.modelPath === "string") {
3791
- imComponent.modelPath = sourceInstancedMesh.modelPath;
3792
- }
3793
- const parameters = asRecord(sourceInstancedMesh.meshParameters);
3794
- if (parameters) {
3795
- imComponent.meshParameters = parameters;
3796
- }
3797
- if (typeof sourceInstancedMesh.castShadow === "boolean") {
3798
- imComponent.castShadow = sourceInstancedMesh.castShadow;
3799
- }
3800
- if (typeof sourceInstancedMesh.receiveShadow === "boolean") {
3801
- imComponent.receiveShadow = sourceInstancedMesh.receiveShadow;
3802
- }
3803
- if (Array.isArray(sourceInstancedMesh.instanceEntityIds)) {
3804
- imComponent.instanceEntityIds = sourceInstancedMesh.instanceEntityIds;
3805
- }
3806
- imComponent.instancedMesh = null;
3807
- }
3808
- }
3809
- const sourceInstanceSlot = asRecord(values.instanceSlotValues.get(sourceEntityId));
3810
- if (sourceInstanceSlot) {
3811
- const slotComponent = world.getComponent(targetEntityId, InstanceSlotComponent);
3812
- if (slotComponent) {
3813
- if (typeof sourceInstanceSlot.slotIndex === "number") {
3814
- slotComponent.slotIndex = sourceInstanceSlot.slotIndex;
3815
- }
3816
- }
3817
- }
3818
- const sourcePrefabNode = asRecord(values.prefabNodeValues.get(sourceEntityId));
3819
- if (sourcePrefabNode && typeof sourcePrefabNode.nodeId === "string") {
3820
- if (!world.getComponent(targetEntityId, PrefabNode)) {
3821
- world.addComponent(targetEntityId, new PrefabNode(sourcePrefabNode.nodeId));
3822
- }
3823
- }
3824
- const sourcePrefabInstance = asRecord(
3825
- values.prefabInstanceValues.get(sourceEntityId)
3826
- );
3827
- if (sourcePrefabInstance && typeof sourcePrefabInstance.url === "string") {
3828
- if (!world.getComponent(targetEntityId, PrefabInstance)) {
3829
- const overrides = Array.isArray(sourcePrefabInstance.overrides) ? sourcePrefabInstance.overrides.filter(
3830
- (entry) => typeof entry === "string"
3831
- ) : [];
3832
- world.addComponent(
3833
- targetEntityId,
3834
- new PrefabInstance(
3835
- sourcePrefabInstance.url,
3836
- overrides,
3837
- // Preserve the placement identity; the constructor generates a
3838
- // fresh one for legacy snapshots that predate instanceId.
3839
- typeof sourcePrefabInstance.instanceId === "string" && sourcePrefabInstance.instanceId.length > 0 ? sourcePrefabInstance.instanceId : void 0
3840
- )
3841
- );
3842
- }
3843
- }
3844
- }
3845
- __name(restoreComponentState, "restoreComponentState");
3846
-
3847
- // core/hydration/types.ts
3848
- var HYDRATABLE_ENTITY_KINDS = /* @__PURE__ */ new Set([
3849
- "cube",
3850
- "sphere",
3851
- "plane",
3852
- "capsule",
3853
- "cylinder",
3854
- "empty",
3855
- "model",
3856
- "image",
3857
- "imageTarget",
3858
- "sprite",
3859
- "audio",
3860
- "camera",
3861
- "video",
3862
- "ambientLight",
3863
- "directionalLight",
3864
- "pointLight",
3865
- "spotLight",
3866
- "areaLight",
3867
- "instancedMesh",
3868
- "instanceSlot",
3869
- "scriptObject",
3870
- "xrRig",
3871
- "xrInteractor",
3872
- "teleportArea",
3873
- "uiPanel",
3874
- "uiText",
3875
- "uiButton",
3876
- "uiToggle",
3877
- "uiSlider"
3878
- ]);
3879
-
3880
- // core/hydration/hydrateWorld.ts
3881
- function buildEntityCreationPayload(sourceEntityId, kind, componentValues) {
3882
- const payload = {
3883
- name: readString(componentValues.nameValues.get(sourceEntityId)) ?? void 0,
3884
- transform: readTransform(componentValues.transformValues.get(sourceEntityId))
3885
- };
3886
- if (kind === "model") {
3887
- const model = asRecord(componentValues.modelValues.get(sourceEntityId));
3888
- if (typeof model?.path === "string") {
3889
- payload.path = model.path;
3890
- }
3891
- }
3892
- if (kind === "image") {
3893
- const image = asRecord(componentValues.imageValues.get(sourceEntityId));
3894
- if (typeof image?.path === "string") {
3895
- payload.path = image.path;
3896
- }
3897
- }
3898
- if (kind === "sprite") {
3899
- const sprite = asRecord(componentValues.spriteValues.get(sourceEntityId));
3900
- if (typeof sprite?.path === "string") {
3901
- payload.path = sprite.path;
3902
- }
3903
- }
3904
- if (kind === "video") {
3905
- const video = asRecord(componentValues.videoValues.get(sourceEntityId));
3906
- if (typeof video?.path === "string") {
3907
- payload.path = video.path;
3908
- }
3909
- const videoOptions = asRecord(video?.options);
3910
- if (videoOptions) {
3911
- payload.options = videoOptions;
3912
- }
3913
- }
3914
- if (kind === "audio") {
3915
- const audio = asRecord(componentValues.audioValues.get(sourceEntityId));
3916
- if (typeof audio?.path === "string") {
3917
- payload.path = audio.path;
3918
- }
3919
- const audioOptions = asRecord(audio?.options);
3920
- if (audioOptions) {
3921
- payload.options = audioOptions;
3922
- }
3923
- }
3924
- if (kind === "camera") {
3925
- const camera = asRecord(componentValues.cameraValues.get(sourceEntityId));
3926
- if (camera) {
3927
- const projection = camera.projection === "orthographic" || camera.projection === "perspective" ? camera.projection : void 0;
3928
- const type = camera.type === "3d" || camera.type === "ar" ? camera.type : void 0;
3929
- payload.options = {
3930
- isDefault: typeof camera.isDefault === "boolean" ? camera.isDefault : void 0,
3931
- projection,
3932
- type,
3933
- fov: typeof camera.fov === "number" ? camera.fov : void 0,
3934
- near: typeof camera.near === "number" ? camera.near : void 0,
3935
- far: typeof camera.far === "number" ? camera.far : void 0,
3936
- left: typeof camera.left === "number" ? camera.left : void 0,
3937
- right: typeof camera.right === "number" ? camera.right : void 0,
3938
- top: typeof camera.top === "number" ? camera.top : void 0,
3939
- bottom: typeof camera.bottom === "number" ? camera.bottom : void 0,
3940
- zoom: typeof camera.zoom === "number" ? camera.zoom : void 0
3941
- };
3942
- }
3943
- }
3944
- if (kind === "imageTarget") {
3945
- const imageTarget = asRecord(componentValues.imageTargetValues.get(sourceEntityId));
3946
- if (imageTarget) {
3947
- payload.options = {
3948
- targets: Array.isArray(imageTarget.targets) ? imageTarget.targets : [],
3949
- compiledTargetUrl: typeof imageTarget.compiledTargetUrl === "string" ? imageTarget.compiledTargetUrl : void 0,
3950
- compiledTargetDir: typeof imageTarget.compiledTargetDir === "string" ? imageTarget.compiledTargetDir : void 0
3951
- };
3952
- }
3953
- }
3954
- if (kind === "instancedMesh") {
3955
- const im = asRecord(componentValues.instancedMeshValues.get(sourceEntityId));
3956
- if (im) {
3957
- payload.options = {
3958
- meshKind: typeof im.meshKind === "string" ? im.meshKind : "cube",
3959
- modelPath: typeof im.modelPath === "string" ? im.modelPath : void 0,
3960
- meshParameters: asRecord(im.meshParameters) ?? void 0,
3961
- castShadow: typeof im.castShadow === "boolean" ? im.castShadow : false,
3962
- receiveShadow: typeof im.receiveShadow === "boolean" ? im.receiveShadow : false,
3963
- // New format: signal the factory to skip child entity creation.
3964
- // Old format (instances[]): pass through for migration — factory will
3965
- // create slot entities from the legacy InstanceTransform array.
3966
- ...Array.isArray(im.instanceEntityIds) ? { instanceEntityIds: im.instanceEntityIds } : Array.isArray(im.instances) ? { instances: im.instances } : {}
3967
- };
3968
- }
3969
- }
3970
- if (kind === "instanceSlot") {
3971
- const slot = asRecord(componentValues.instanceSlotValues.get(sourceEntityId));
3972
- if (slot) {
3973
- payload.options = {
3974
- groupEntityId: typeof slot.groupEntityId === "number" ? slot.groupEntityId : -1,
3975
- slotIndex: typeof slot.slotIndex === "number" ? slot.slotIndex : 0
3976
- };
3977
- }
3978
- }
3979
- if (kind === "scriptObject") {
3980
- const so = asRecord(componentValues.scriptObjectValues.get(sourceEntityId));
3981
- if (so) {
3982
- payload.options = {
3983
- scriptUrl: typeof so.scriptUrl === "string" ? so.scriptUrl : null,
3984
- className: typeof so.className === "string" ? so.className : null,
3985
- enabled: typeof so.enabled === "boolean" ? so.enabled : true
3986
- };
3987
- }
3988
- }
3989
- return payload;
3990
- }
3991
- __name(buildEntityCreationPayload, "buildEntityCreationPayload");
3992
- function resolvePrimaryCameraSourceEntityId(snapshot, componentValues, entityTypeValues) {
3993
- let firstCameraSourceEntityId = null;
3994
- for (const sourceEntityId of snapshot.entities) {
3995
- const entityType = readString(entityTypeValues.get(sourceEntityId));
3996
- if (entityType !== "camera") {
3997
- continue;
3998
- }
3999
- if (firstCameraSourceEntityId === null) {
4000
- firstCameraSourceEntityId = sourceEntityId;
4001
- }
4002
- const cameraValue = asRecord(componentValues.cameraValues.get(sourceEntityId));
4003
- if (cameraValue?.isDefault === true) {
4004
- return sourceEntityId;
4005
- }
4006
- }
4007
- return firstCameraSourceEntityId;
4008
- }
4009
- __name(resolvePrimaryCameraSourceEntityId, "resolvePrimaryCameraSourceEntityId");
4010
- function applyLocalTransformsAfterHierarchyRestore(world, scopeEntityIds) {
4011
- const transformEntities = scopeEntityIds ? Array.from(scopeEntityIds) : world.query(TransformComponent, Object3DRef);
4012
- for (const entityId of transformEntities) {
4013
- const transform = world.getComponent(entityId, TransformComponent);
4014
- const object = world.getComponent(entityId, Object3DRef)?.object;
4015
- if (!transform || !object) {
4016
- continue;
4017
- }
4018
- applyTransform(object, transform);
4019
- object.updateMatrixWorld(true);
4020
- }
4021
- }
4022
- __name(applyLocalTransformsAfterHierarchyRestore, "applyLocalTransformsAfterHierarchyRestore");
4023
- function hydrateWorldFromSnapshotDetailed(snapshot, world, objectManagementSystem, options) {
4024
- if (!snapshot || !snapshot.entities.length) {
4025
- return {
4026
- restoredCount: 0,
4027
- selectedEntityId: null,
4028
- idMap: /* @__PURE__ */ new Map()
4029
- };
4030
- }
4031
- const assignPrimaryCamera = options?.assignPrimaryCamera !== false;
4032
- const entityTypeValues = readByComponent(snapshot.components, "EntityType");
4033
- const componentValues = buildComponentValueMaps(snapshot);
4034
- const primaryCameraSourceEntityId = assignPrimaryCamera ? resolvePrimaryCameraSourceEntityId(
4035
- snapshot,
4036
- componentValues,
4037
- entityTypeValues
4038
- ) : null;
4039
- const idMap = /* @__PURE__ */ new Map();
4040
- for (const sourceEntityId of snapshot.entities) {
4041
- const entityTypeValue = entityTypeValues.get(sourceEntityId);
4042
- const entityType = readString(entityTypeValue);
4043
- if (!entityType || !HYDRATABLE_ENTITY_KINDS.has(entityType)) {
4044
- continue;
4045
- }
4046
- const kind = entityType;
4047
- const payload = buildEntityCreationPayload(sourceEntityId, kind, componentValues);
4048
- if (kind === "camera" && sourceEntityId === primaryCameraSourceEntityId) {
4049
- payload.useWorldCamera = true;
4050
- const options2 = asRecord(payload.options);
4051
- payload.options = {
4052
- ...options2 ?? {},
4053
- isDefault: true
4054
- };
4055
- }
4056
- payload.skipDefaultChildren = true;
4057
- const createdEntityId = objectManagementSystem.createNow(kind, world, payload);
4058
- idMap.set(sourceEntityId, createdEntityId);
4059
- }
4060
- for (const sourceEntityId of snapshot.entities) {
4061
- const targetEntityId = idMap.get(sourceEntityId);
4062
- if (targetEntityId === void 0) {
4063
- continue;
4064
- }
4065
- const entityType = readString(entityTypeValues.get(sourceEntityId));
4066
- restoreComponentState(
4067
- world,
4068
- objectManagementSystem,
4069
- targetEntityId,
4070
- sourceEntityId,
4071
- entityType,
4072
- componentValues
4073
- );
4074
- restorePostCreateComponents(
4075
- world,
4076
- objectManagementSystem,
4077
- targetEntityId,
4078
- sourceEntityId,
4079
- entityType,
4080
- componentValues,
4081
- idMap
4082
- );
4083
- }
4084
- applyParentLinks(snapshot, idMap, objectManagementSystem, world);
4085
- objectManagementSystem.reconcileSceneFromWorld(world);
4086
- applyLocalTransformsAfterHierarchyRestore(
4087
- world,
4088
- options?.applyTransformsScope === "created" ? idMap.values() : void 0
4089
- );
4090
- const selectedEntityId = typeof snapshot.selectedEntityId === "number" ? idMap.get(snapshot.selectedEntityId) ?? null : null;
4091
- return {
4092
- restoredCount: idMap.size,
4093
- selectedEntityId,
4094
- idMap
4095
- };
4096
- }
4097
- __name(hydrateWorldFromSnapshotDetailed, "hydrateWorldFromSnapshotDetailed");
4098
- function hydrateWorldFromSnapshot(snapshot, world, objectManagementSystem) {
4099
- return hydrateWorldFromSnapshotDetailed(
4100
- snapshot,
4101
- world,
4102
- objectManagementSystem
4103
- ).restoredCount;
4104
- }
4105
- __name(hydrateWorldFromSnapshot, "hydrateWorldFromSnapshot");
4106
-
4107
- // core/prefab/nodeKeys.ts
4108
- var NODE_KEY_SEPARATOR = "/";
4109
- var ADDED_NODE_MARKER_COMPONENT = "+node";
4110
- var asRecord2 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
4111
- var readBlock = /* @__PURE__ */ __name((snapshot, component) => {
4112
- const map = /* @__PURE__ */ new Map();
4113
- const block = snapshot.components.find((b) => b.component === component);
4114
- for (const entry of block?.entries ?? []) {
4115
- const record = asRecord2(entry.value);
4116
- if (record) {
4117
- map.set(entry.entityId, record);
4118
- }
4119
- }
4120
- return map;
4121
- }, "readBlock");
4122
- var fallbackInstanceId = /* @__PURE__ */ __name((entityId) => `e${entityId}`, "fallbackInstanceId");
4123
- function buildNodeKeyMapsFromSnapshot(snapshot, options) {
4124
- const parentValues = readBlock(snapshot, "ParentId");
4125
- const nodeValues = readBlock(snapshot, "PrefabNode");
4126
- const instanceValues = readBlock(snapshot, "PrefabInstance");
4127
- const inDocument = new Set(snapshot.entities);
4128
- const childrenByParent = /* @__PURE__ */ new Map();
4129
- const rootIds = [];
4130
- for (const entityId of snapshot.entities) {
4131
- const parent = parentValues.get(entityId)?.value;
4132
- if (typeof parent === "number" && inDocument.has(parent)) {
4133
- const siblings = childrenByParent.get(parent);
4134
- if (siblings) siblings.push(entityId);
4135
- else childrenByParent.set(parent, [entityId]);
4136
- } else {
4137
- rootIds.push(entityId);
4138
- }
4139
- }
4140
- const scopeRoots = options?.scopeRootEntityId !== void 0 ? [options.scopeRootEntityId] : rootIds;
4141
- const idToKey = /* @__PURE__ */ new Map();
4142
- const keyToId = /* @__PURE__ */ new Map();
4143
- const boundaryIds = /* @__PURE__ */ new Map();
4144
- const scopeRootSet = new Set(scopeRoots);
4145
- const queue = scopeRoots.map(
4146
- (entityId) => ({ entityId, prefix: "" })
4147
- );
4148
- while (queue.length > 0) {
4149
- const { entityId, prefix } = queue.shift();
4150
- const instance = instanceValues.get(entityId);
4151
- const isBoundary = Boolean(
4152
- instance && typeof instance.url === "string" && !scopeRootSet.has(entityId)
4153
- );
4154
- let ownPrefix = prefix;
4155
- let childPrefix = prefix;
4156
- if (isBoundary && instance) {
4157
- const instanceId = typeof instance.instanceId === "string" && instance.instanceId.length > 0 ? instance.instanceId : fallbackInstanceId(entityId);
4158
- childPrefix = `${prefix}${instanceId}${NODE_KEY_SEPARATOR}`;
4159
- ownPrefix = childPrefix;
4160
- boundaryIds.set(entityId, {
4161
- url: instance.url,
4162
- instanceId
4163
- });
4164
- }
4165
- const nodeId = nodeValues.get(entityId)?.nodeId;
4166
- if (typeof nodeId === "string" && nodeId.length > 0) {
4167
- const key = `${ownPrefix}${nodeId}`;
4168
- idToKey.set(entityId, key);
4169
- keyToId.set(key, entityId);
4170
- }
4171
- for (const child of childrenByParent.get(entityId) ?? []) {
4172
- queue.push({ entityId: child, prefix: childPrefix });
4173
- }
4174
- }
4175
- const rootEntityId = scopeRoots[0];
4176
- const rootNodeId = rootEntityId !== void 0 ? nodeValues.get(rootEntityId)?.nodeId : void 0;
4177
- return {
4178
- idToKey,
4179
- keyToId,
4180
- rootKey: typeof rootNodeId === "string" && rootNodeId.length > 0 ? rootNodeId : null,
4181
- boundaryIds
4182
- };
4183
- }
4184
- __name(buildNodeKeyMapsFromSnapshot, "buildNodeKeyMapsFromSnapshot");
4185
- function collectSnapshotSubtreeIds(snapshot, rootEntityId) {
4186
- const parentValues = readBlock(snapshot, "ParentId");
4187
- const childrenByParent = /* @__PURE__ */ new Map();
4188
- for (const entityId of snapshot.entities) {
4189
- const parent = parentValues.get(entityId)?.value;
4190
- if (typeof parent === "number") {
4191
- const siblings = childrenByParent.get(parent);
4192
- if (siblings) siblings.push(entityId);
4193
- else childrenByParent.set(parent, [entityId]);
4194
- }
4195
- }
4196
- const ordered = [];
4197
- const seen = /* @__PURE__ */ new Set();
4198
- const queue = [rootEntityId];
4199
- while (queue.length > 0) {
4200
- const entityId = queue.shift();
4201
- if (seen.has(entityId)) continue;
4202
- seen.add(entityId);
4203
- ordered.push(entityId);
4204
- queue.push(...childrenByParent.get(entityId) ?? []);
4205
- }
4206
- return ordered;
4207
- }
4208
- __name(collectSnapshotSubtreeIds, "collectSnapshotSubtreeIds");
4209
- function findTopLevelNestedRegions(snapshot) {
4210
- const parentValues = readBlock(snapshot, "ParentId");
4211
- const instanceValues = readBlock(snapshot, "PrefabInstance");
4212
- const inDocument = new Set(snapshot.entities);
4213
- const roots = new Set(
4214
- snapshot.entities.filter((entityId) => {
4215
- const parent = parentValues.get(entityId)?.value;
4216
- return typeof parent !== "number" || !inDocument.has(parent);
4217
- })
4218
- );
4219
- const regions = [];
4220
- for (const [entityId, instance] of instanceValues) {
4221
- if (roots.has(entityId)) continue;
4222
- if (typeof instance.url !== "string" || instance.url.length === 0) continue;
4223
- let ancestor = parentValues.get(entityId)?.value;
4224
- let nestedDeeper = false;
4225
- let hops = 0;
4226
- while (typeof ancestor === "number" && hops < 512) {
4227
- if (instanceValues.has(ancestor) && !roots.has(ancestor)) {
4228
- nestedDeeper = true;
4229
- break;
4230
- }
4231
- ancestor = parentValues.get(ancestor)?.value;
4232
- hops += 1;
4233
- }
4234
- if (nestedDeeper) continue;
4235
- regions.push({
4236
- rootEntityId: entityId,
4237
- url: instance.url,
4238
- instanceId: typeof instance.instanceId === "string" && instance.instanceId.length > 0 ? instance.instanceId : fallbackInstanceId(entityId),
4239
- overrides: Array.isArray(instance.overrides) ? instance.overrides.filter(
4240
- (entry) => typeof entry === "string"
4241
- ) : []
4242
- });
4243
- }
4244
- regions.sort((a, b) => a.rootEntityId - b.rootEntityId);
4245
- return regions;
4246
- }
4247
- __name(findTopLevelNestedRegions, "findTopLevelNestedRegions");
4248
-
4249
- // core/prefab/overrides.ts
4250
- var OVERRIDE_DIFF_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
4251
- "PrefabNode",
4252
- "PrefabInstance",
4253
- "EntityType",
4254
- "Selectable",
4255
- "SelectionTransformState"
4256
- ]);
4257
- var OVERRIDE_ROOT_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
4258
- "TransformComponent",
4259
- "Name",
4260
- "ParentId"
4261
- ]);
4262
- var asRecord3 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null, "asRecord");
4263
- var clone = /* @__PURE__ */ __name((value) => value === void 0 ? value : structuredClone(value), "clone");
4264
- var isNodeRef = /* @__PURE__ */ __name((value) => {
4265
- const record = asRecord3(value);
4266
- return Boolean(
4267
- record && typeof record.$node === "string" && Object.keys(record).length === 1
4268
- );
4269
- }, "isNodeRef");
4270
- var makeNodeRef = /* @__PURE__ */ __name((nodeId) => ({ $node: nodeId }), "makeNodeRef");
4271
- function findSnapshotRootEntityIds(snapshot) {
4272
- const parentBlock = snapshot.components.find(
4273
- (block) => block.component === "ParentId"
4274
- );
4275
- const parentByEntity = /* @__PURE__ */ new Map();
4276
- for (const entry of parentBlock?.entries ?? []) {
4277
- parentByEntity.set(entry.entityId, asRecord3(entry.value)?.value ?? null);
4278
- }
4279
- return snapshot.entities.filter(
4280
- (entityId) => typeof parentByEntity.get(entityId) !== "number"
4281
- );
4282
- }
4283
- __name(findSnapshotRootEntityIds, "findSnapshotRootEntityIds");
4284
- function buildNodeMapsFromSnapshot(snapshot) {
4285
- const idToNode = /* @__PURE__ */ new Map();
4286
- const nodeToId = /* @__PURE__ */ new Map();
4287
- const nodeBlock = snapshot.components.find(
4288
- (block) => block.component === "PrefabNode"
4289
- );
4290
- for (const entry of nodeBlock?.entries ?? []) {
4291
- const nodeId = asRecord3(entry.value)?.nodeId;
4292
- if (typeof nodeId === "string" && nodeId.length > 0) {
4293
- idToNode.set(entry.entityId, nodeId);
4294
- nodeToId.set(nodeId, entry.entityId);
4295
- }
4296
- }
4297
- const rootEntityId = findSnapshotRootEntityIds(snapshot)[0];
4298
- const rootNodeId = rootEntityId !== void 0 ? idToNode.get(rootEntityId) ?? null : null;
4299
- return { idToNode, nodeToId, rootNodeId };
4300
- }
4301
- __name(buildNodeMapsFromSnapshot, "buildNodeMapsFromSnapshot");
4302
- function indexSnapshotByEntity(snapshot) {
4303
- const byEntity = /* @__PURE__ */ new Map();
4304
- for (const block of snapshot.components) {
4305
- for (const entry of block.entries) {
4306
- let componentMap = byEntity.get(entry.entityId);
4307
- if (!componentMap) {
4308
- componentMap = /* @__PURE__ */ new Map();
4309
- byEntity.set(entry.entityId, componentMap);
4310
- }
4311
- componentMap.set(block.component, entry.value);
4312
- }
4313
- }
4314
- return byEntity;
4315
- }
4316
- __name(indexSnapshotByEntity, "indexSnapshotByEntity");
4317
- var toRefOrRaw = /* @__PURE__ */ __name((id, idToNode) => {
4318
- const nodeId = idToNode.get(id);
4319
- return nodeId !== void 0 ? makeNodeRef(nodeId) : id;
4320
- }, "toRefOrRaw");
4321
- function canonicalizeScriptDataFields(data, idToNode) {
4322
- const canonicalData = {};
4323
- for (const [key, fieldValue] of Object.entries(data)) {
4324
- const field = asRecord3(fieldValue);
4325
- if (field?.type === "entity" && typeof field.value === "number") {
4326
- canonicalData[key] = {
4327
- ...field,
4328
- value: toRefOrRaw(field.value, idToNode)
4329
- };
4330
- continue;
4331
- }
4332
- const componentRef = asRecord3(field?.value);
4333
- if (field?.type === "component" && componentRef && typeof componentRef.entityId === "number") {
4334
- canonicalData[key] = {
4335
- ...field,
4336
- value: {
4337
- ...componentRef,
4338
- entityId: toRefOrRaw(componentRef.entityId, idToNode)
4339
- }
4340
- };
4341
- continue;
4342
- }
4343
- canonicalData[key] = fieldValue;
4344
- }
4345
- return canonicalData;
4346
- }
4347
- __name(canonicalizeScriptDataFields, "canonicalizeScriptDataFields");
4348
- function canonicalizeComponentValue(componentName, value, idToNode) {
4349
- const cloned = clone(value);
4350
- const record = asRecord3(cloned);
4351
- if (!record) {
4352
- return cloned;
4353
- }
4354
- if (componentName === "ParentId") {
4355
- if (typeof record.value === "number") {
4356
- record.value = toRefOrRaw(record.value, idToNode);
4357
- }
4358
- return record;
4359
- }
4360
- if (componentName === "Object3DRef") {
4361
- return {
4362
- visibility: typeof record.visibility === "boolean" ? record.visibility : true
4363
- };
4364
- }
4365
- if (componentName === "ScriptComponent") {
4366
- const entries = Array.isArray(record.entries) ? record.entries : [];
4367
- return {
4368
- entries: entries.map((rawEntry) => {
4369
- const entry = asRecord3(rawEntry);
4370
- if (!entry) {
4371
- return rawEntry;
4372
- }
4373
- return {
4374
- className: typeof entry.className === "string" ? entry.className : "",
4375
- scriptUrl: typeof entry.scriptUrl === "string" ? entry.scriptUrl : "",
4376
- enabled: typeof entry.enabled === "boolean" ? entry.enabled : true,
4377
- data: canonicalizeScriptDataFields(asRecord3(entry.data) ?? {}, idToNode)
4378
- };
4379
- })
4380
- };
4381
- }
4382
- if (componentName === "ScriptObjectComponent") {
4383
- return {
4384
- scriptUrl: typeof record.scriptUrl === "string" ? record.scriptUrl : null,
4385
- className: typeof record.className === "string" ? record.className : null,
4386
- enabled: typeof record.enabled === "boolean" ? record.enabled : true,
4387
- data: canonicalizeScriptDataFields(asRecord3(record.data) ?? {}, idToNode)
4388
- };
4389
- }
4390
- if (componentName === "InstancedMeshComponent") {
4391
- if (Array.isArray(record.instanceEntityIds)) {
4392
- record.instanceEntityIds = record.instanceEntityIds.map(
4393
- (id) => typeof id === "number" ? toRefOrRaw(id, idToNode) : id
4394
- );
4395
- }
4396
- return record;
4397
- }
4398
- if (componentName === "InstanceSlotComponent") {
4399
- if (typeof record.groupEntityId === "number" && record.groupEntityId !== -1) {
4400
- record.groupEntityId = toRefOrRaw(record.groupEntityId, idToNode);
4401
- }
4402
- return record;
4403
- }
4404
- return record;
4405
- }
4406
- __name(canonicalizeComponentValue, "canonicalizeComponentValue");
4407
- function decanonicalizeComponentValue(value, nodeToId) {
4408
- if (isNodeRef(value)) {
4409
- return nodeToId.get(value.$node) ?? null;
4410
- }
4411
- if (Array.isArray(value)) {
4412
- return value.map((item) => decanonicalizeComponentValue(item, nodeToId));
4413
- }
4414
- const record = asRecord3(value);
4415
- if (record) {
4416
- const result = {};
4417
- for (const [key, item] of Object.entries(record)) {
4418
- result[key] = decanonicalizeComponentValue(item, nodeToId);
4419
- }
4420
- return result;
4421
- }
4422
- return value;
4423
- }
4424
- __name(decanonicalizeComponentValue, "decanonicalizeComponentValue");
4425
- var encodeOverride = /* @__PURE__ */ __name((nodeId, componentName, path) => `${nodeId}|${componentName}|${path}`, "encodeOverride");
4426
- function parseOverride(encoded) {
4427
- const firstSep = encoded.indexOf("|");
4428
- if (firstSep <= 0) {
4429
- return null;
4430
- }
4431
- const secondSep = encoded.indexOf("|", firstSep + 1);
4432
- if (secondSep <= firstSep + 1) {
4433
- return null;
4434
- }
4435
- return {
4436
- nodeId: encoded.slice(0, firstSep),
4437
- componentName: encoded.slice(firstSep + 1, secondSep),
4438
- path: encoded.slice(secondSep + 1)
4439
- };
4440
- }
4441
- __name(parseOverride, "parseOverride");
4442
- function groupOverridePaths(overrides) {
4443
- const grouped = /* @__PURE__ */ new Map();
4444
- for (const encoded of overrides) {
4445
- const parsed = parseOverride(encoded);
4446
- if (!parsed) {
4447
- continue;
4448
- }
4449
- let byComponent = grouped.get(parsed.nodeId);
4450
- if (!byComponent) {
4451
- byComponent = /* @__PURE__ */ new Map();
4452
- grouped.set(parsed.nodeId, byComponent);
4453
- }
4454
- let paths = byComponent.get(parsed.componentName);
4455
- if (!paths) {
4456
- paths = /* @__PURE__ */ new Set();
4457
- byComponent.set(parsed.componentName, paths);
4458
- }
4459
- paths.add(parsed.path);
4460
- }
4461
- return grouped;
4462
- }
4463
- __name(groupOverridePaths, "groupOverridePaths");
4464
- var isPrimitive = /* @__PURE__ */ __name((value) => value === null || typeof value !== "object", "isPrimitive");
4465
- function collectDiffPaths(base, current, prefix = "", out = []) {
4466
- if (isNodeRef(base) || isNodeRef(current)) {
4467
- const same = isNodeRef(base) && isNodeRef(current) && base.$node === current.$node;
4468
- if (!same) {
4469
- out.push(prefix);
4470
- }
4471
- return out;
4472
- }
4473
- if (isPrimitive(base) || isPrimitive(current)) {
4474
- if (base !== current) {
4475
- out.push(prefix);
4476
- }
4477
- return out;
4478
- }
4479
- const baseIsArray = Array.isArray(base);
4480
- const currentIsArray = Array.isArray(current);
4481
- if (baseIsArray !== currentIsArray) {
4482
- out.push(prefix);
4483
- return out;
4484
- }
4485
- if (baseIsArray && currentIsArray) {
4486
- if (base.length !== current.length) {
4487
- out.push(prefix);
4488
- return out;
4489
- }
4490
- base.forEach((item, index) => {
4491
- collectDiffPaths(
4492
- item,
4493
- current[index],
4494
- prefix ? `${prefix}/${index}` : String(index),
4495
- out
4496
- );
4497
- });
4498
- return out;
4499
- }
4500
- const baseRecord = base;
4501
- const currentRecord = current;
4502
- const keys = /* @__PURE__ */ new Set([
4503
- ...Object.keys(baseRecord),
4504
- ...Object.keys(currentRecord)
4505
- ]);
4506
- for (const key of keys) {
4507
- const childPrefix = prefix ? `${prefix}/${key}` : key;
4508
- if (!(key in baseRecord) || !(key in currentRecord)) {
4509
- out.push(childPrefix);
4510
- continue;
4511
- }
4512
- collectDiffPaths(baseRecord[key], currentRecord[key], childPrefix, out);
4513
- }
4514
- return out;
4515
- }
4516
- __name(collectDiffPaths, "collectDiffPaths");
4517
- function computeInstanceOverrides(baseSnapshot, instanceSnapshot, options) {
4518
- const baseMaps = buildNodeKeyMapsFromSnapshot(baseSnapshot);
4519
- const instanceMaps = buildNodeKeyMapsFromSnapshot(instanceSnapshot);
4520
- const baseByEntity = indexSnapshotByEntity(baseSnapshot);
4521
- const instanceByEntity = indexSnapshotByEntity(instanceSnapshot);
4522
- const overrides = [];
4523
- for (const [nodeKey, instanceEntityId] of instanceMaps.keyToId) {
4524
- const baseEntityId = baseMaps.keyToId.get(nodeKey);
4525
- if (baseEntityId === void 0) {
4526
- if (options?.emitAdditionMarkers) {
4527
- overrides.push(
4528
- encodeOverride(nodeKey, ADDED_NODE_MARKER_COMPONENT, "")
4529
- );
4530
- }
4531
- continue;
4532
- }
4533
- const baseComponents = baseByEntity.get(baseEntityId);
4534
- const instanceComponents = instanceByEntity.get(instanceEntityId);
4535
- if (!baseComponents) {
4536
- continue;
4537
- }
4538
- const isRoot = nodeKey === baseMaps.rootKey;
4539
- for (const [componentName, baseRaw] of baseComponents) {
4540
- if (OVERRIDE_DIFF_EXCLUDED_COMPONENTS.has(componentName)) {
4541
- continue;
4542
- }
4543
- if (isRoot && OVERRIDE_ROOT_EXCLUDED_COMPONENTS.has(componentName)) {
4544
- continue;
4545
- }
4546
- const instanceRaw = instanceComponents?.get(componentName);
4547
- if (instanceRaw === void 0) {
4548
- continue;
4549
- }
4550
- const baseCanonical = canonicalizeComponentValue(
4551
- componentName,
4552
- baseRaw,
4553
- baseMaps.idToKey
4554
- );
4555
- const instanceCanonical = canonicalizeComponentValue(
4556
- componentName,
4557
- instanceRaw,
4558
- instanceMaps.idToKey
4559
- );
4560
- for (const path of collectDiffPaths(baseCanonical, instanceCanonical)) {
4561
- overrides.push(encodeOverride(nodeKey, componentName, path));
4562
- }
4563
- }
4564
- }
4565
- return overrides;
4566
- }
4567
- __name(computeInstanceOverrides, "computeInstanceOverrides");
4568
- var PATH_TOKEN_SPLIT = "/";
4569
- function getAtPath(value, tokens) {
4570
- let cursor = value;
4571
- for (const token of tokens) {
4572
- if (Array.isArray(cursor)) {
4573
- cursor = cursor[Number(token)];
4574
- } else {
4575
- const record = asRecord3(cursor);
4576
- if (!record) {
4577
- return void 0;
4578
- }
4579
- cursor = record[token];
4580
- }
4581
- if (cursor === void 0) {
4582
- return void 0;
4583
- }
4584
- }
4585
- return cursor;
4586
- }
4587
- __name(getAtPath, "getAtPath");
4588
- function setAtPath(target, tokens, value) {
4589
- let cursor = target;
4590
- for (let i = 0; i < tokens.length - 1; i++) {
4591
- const token = tokens[i];
4592
- if (Array.isArray(cursor)) {
4593
- cursor = cursor[Number(token)];
4594
- } else {
4595
- const record = asRecord3(cursor);
4596
- if (!record) {
4597
- return;
4598
- }
4599
- cursor = record[token];
4600
- }
4601
- if (cursor === null || typeof cursor !== "object") {
4602
- return;
4603
- }
4604
- }
4605
- const last = tokens[tokens.length - 1];
4606
- if (Array.isArray(cursor)) {
4607
- cursor[Number(last)] = value;
4608
- } else {
4609
- const record = asRecord3(cursor);
4610
- if (record) {
4611
- if (value === void 0) {
4612
- delete record[last];
4613
- } else {
4614
- record[last] = value;
4615
- }
4616
- }
4617
- }
4618
- }
4619
- __name(setAtPath, "setAtPath");
4620
- function mergeComponentValue(newFileCanonical, instanceCanonical, overridePaths) {
4621
- if (!overridePaths || overridePaths.size === 0) {
4622
- return clone(newFileCanonical);
4623
- }
4624
- if (overridePaths.has("")) {
4625
- return clone(instanceCanonical);
4626
- }
4627
- const merged = clone(newFileCanonical);
4628
- for (const path of overridePaths) {
4629
- const tokens = path.split(PATH_TOKEN_SPLIT);
4630
- const instanceValue = getAtPath(instanceCanonical, tokens);
4631
- setAtPath(merged, tokens, clone(instanceValue));
4632
- }
4633
- return merged;
4634
- }
4635
- __name(mergeComponentValue, "mergeComponentValue");
4636
-
4637
- // core/prefab/instantiateSubtree.ts
4638
- var INSTANTIATE_EXCLUDED_COMPONENTS = /* @__PURE__ */ new Set([
4639
- "Environment",
4640
- "Postprocessing",
4641
- "SelectionTransformState"
4642
- ]);
4643
- function instantiateSubtree(world, objectManagementSystem, snapshot) {
4644
- const snapshotRootIds = new Set(findSnapshotRootEntityIds(snapshot));
4645
- const safeSnapshot = {
4646
- entities: snapshot.entities,
4647
- components: snapshot.components.filter((block) => !INSTANTIATE_EXCLUDED_COMPONENTS.has(block.component)).map(
4648
- (block) => block.component === "PrefabInstance" ? {
4649
- component: block.component,
4650
- entries: block.entries.filter(
4651
- (entry) => !snapshotRootIds.has(entry.entityId)
4652
- )
4653
- } : block
4654
- ).filter((block) => block.entries.length > 0)
4655
- };
4656
- const result = hydrateWorldFromSnapshotDetailed(
4657
- safeSnapshot,
4658
- world,
4659
- objectManagementSystem,
4660
- { assignPrimaryCamera: false, applyTransformsScope: "created" }
4661
- );
4662
- const rootSourceId = findSnapshotRootEntityIds(safeSnapshot).find(
4663
- (sourceId) => result.idMap.has(sourceId)
4664
- );
4665
- return {
4666
- rootId: rootSourceId !== void 0 ? result.idMap.get(rootSourceId) ?? null : null,
4667
- idMap: result.idMap
4668
- };
4669
- }
4670
- __name(instantiateSubtree, "instantiateSubtree");
4671
-
4672
- // core/prefab/types.ts
4673
- var PREFAB_FILE_VERSION = 1;
4674
- var PREFAB_FILE_VERSION_NESTED = 2;
4675
- function prefabFileVersionFor(snapshot) {
4676
- return findTopLevelNestedRegions(snapshot).length > 0 ? PREFAB_FILE_VERSION_NESTED : PREFAB_FILE_VERSION;
4677
- }
4678
- __name(prefabFileVersionFor, "prefabFileVersionFor");
4679
- var isRecord = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
4680
- function parsePrefabAsset(json) {
4681
- if (!isRecord(json)) {
4682
- return null;
4683
- }
4684
- if (typeof json.version !== "number" || json.version < 1) {
4685
- return null;
4686
- }
4687
- const snapshot = json.snapshot;
4688
- if (!isRecord(snapshot)) {
4689
- return null;
4690
- }
4691
- const entities = snapshot.entities;
4692
- const components = snapshot.components;
4693
- if (!Array.isArray(entities) || !Array.isArray(components)) {
4694
- return null;
4695
- }
4696
- if (!entities.every((id) => typeof id === "number")) {
4697
- return null;
4698
- }
4699
- if (!components.every(
4700
- (block) => isRecord(block) && typeof block.component === "string" && Array.isArray(block.entries)
4701
- )) {
4702
- return null;
4703
- }
4704
- return {
4705
- version: json.version,
4706
- type: json.type === "scene" ? "scene" : "prefab",
4707
- name: typeof json.name === "string" ? json.name : void 0,
4708
- snapshot
4709
- };
4710
- }
4711
- __name(parsePrefabAsset, "parsePrefabAsset");
4712
-
4713
- // core/prefab/spawnPrefab.ts
4714
- var prefabAssetUrlResolver = null;
4715
- function setPrefabAssetUrlResolver(resolver) {
4716
- prefabAssetUrlResolver = resolver;
4717
- }
4718
- __name(setPrefabAssetUrlResolver, "setPrefabAssetUrlResolver");
4719
- var resolveUrl = /* @__PURE__ */ __name((url) => prefabAssetUrlResolver ? prefabAssetUrlResolver(url) : url, "resolveUrl");
4720
- function transformStringsDeep(value, fn) {
4721
- if (typeof value === "string") {
4722
- return fn(value);
4723
- }
4724
- if (Array.isArray(value)) {
4725
- return value.map((item) => transformStringsDeep(item, fn));
4726
- }
4727
- if (value !== null && typeof value === "object") {
4728
- const result = {};
4729
- for (const [key, item] of Object.entries(value)) {
4730
- result[key] = transformStringsDeep(item, fn);
4731
- }
4732
- return result;
4733
- }
4734
- return value;
4735
- }
4736
- __name(transformStringsDeep, "transformStringsDeep");
4737
- var prefabCache = /* @__PURE__ */ new Map();
4738
- function loadPrefab(url) {
4739
- const cached = prefabCache.get(url);
4740
- if (cached) {
4741
- return cached;
4742
- }
4743
- const pending = (async () => {
4744
- const response = await fetch(resolveUrl(url));
4745
- if (!response.ok) {
4746
- throw new Error(`Failed to fetch prefab "${url}" (${response.status})`);
4747
- }
4748
- const json = await response.json();
4749
- const parsed = parsePrefabAsset(json);
4750
- if (!parsed) {
4751
- throw new Error(`Invalid prefab file at "${url}"`);
4752
- }
4753
- return {
4754
- ...parsed,
4755
- snapshot: transformStringsDeep(
4756
- parsed.snapshot,
4757
- resolveUrl
4758
- )
4759
- };
4760
- })();
4761
- prefabCache.set(url, pending);
4762
- pending.catch(() => {
4763
- if (prefabCache.get(url) === pending) {
4764
- prefabCache.delete(url);
4765
- }
4766
- });
4767
- return pending;
4768
- }
4769
- __name(loadPrefab, "loadPrefab");
4770
- function clearPrefabCache(url) {
4771
- if (typeof url === "string") {
4772
- prefabCache.delete(url);
4773
- } else {
4774
- prefabCache.clear();
4775
- }
4776
- }
4777
- __name(clearPrefabCache, "clearPrefabCache");
4778
- function resolveSource(source) {
4779
- if (typeof source === "string") {
4780
- return { url: source, asset: loadPrefab(source) };
4781
- }
4782
- if ("path" in source && typeof source.path === "string") {
4783
- return { url: source.path, asset: loadPrefab(source.path) };
4784
- }
4785
- const asset = source;
4786
- return { url: asset.name ?? "", asset };
4787
- }
4788
- __name(resolveSource, "resolveSource");
4789
- async function spawnPrefab(world, source, options) {
4790
- const { url, asset } = resolveSource(source);
4791
- const prefab = await asset;
4792
- const objectManagementSystem = world.getObjectManagementSystem();
4793
- const { rootId } = instantiateSubtree(
4794
- world,
4795
- objectManagementSystem,
4796
- prefab.snapshot
4797
- );
4798
- if (rootId === null) {
4799
- throw new Error(
4800
- `Prefab "${url || prefab.name || "unknown"}" contained no spawnable entities`
4801
- );
4802
- }
4803
- if (!world.getComponent(rootId, PrefabInstance)) {
4804
- world.addComponent(rootId, new PrefabInstance(url, []));
4805
- }
4806
- if (options?.parentId !== void 0 && options.parentId !== null) {
4807
- objectManagementSystem.setParent(world, rootId, options.parentId, {
4808
- preserveWorldTransform: false
4809
- });
4810
- }
4811
- const transform = world.getComponent(rootId, TransformComponent);
4812
- if (transform && (options?.position || options?.rotation || options?.scale)) {
4813
- const write = /* @__PURE__ */ __name((target, src) => {
4814
- if (!src) return;
4815
- if (typeof src.x === "number") target.x = src.x;
4816
- if (typeof src.y === "number") target.y = src.y;
4817
- if (typeof src.z === "number") target.z = src.z;
4818
- }, "write");
4819
- write(transform.position, options.position);
4820
- write(transform.rotation, options.rotation);
4821
- write(transform.scale, options.scale);
4822
- const object = world.getComponent(rootId, Object3DRef)?.object;
4823
- if (object) {
4824
- applyTransform(object, transform);
4825
- object.updateMatrixWorld(true);
4826
- }
4827
- }
4828
- return rootId;
4829
- }
4830
- __name(spawnPrefab, "spawnPrefab");
4831
- function despawnPrefab(world, rootId, dispose = true) {
4832
- const objectManagementSystem = world.getObjectManagementSystem();
4833
- const subtree = collectEntitySubtree(world, [rootId]).reverse();
4834
- let removed = 0;
4835
- for (const entityId of subtree) {
4836
- if (objectManagementSystem.deleteEntity(world, entityId, dispose)) {
4837
- removed += 1;
4838
- }
4839
- }
4840
- return removed;
4841
- }
4842
- __name(despawnPrefab, "despawnPrefab");
4843
- var _Behaviour = class _Behaviour {
4844
- /**
4845
- * Engine-managed constructor. Hosts (`ScriptBehaviourSystem`) instantiate
4846
- * subclasses with the owning world, the entity id, and the script's `data`
4847
- * payload. User code should not call this directly.
4848
- *
4849
- * @param world - The owning ECS world.
4850
- * @param entityId - The entity this script is bound to.
4851
- * @param component - The `ScriptComponent.data` payload, or any object that
4852
- * should be exposed to the script as `this.data`.
4853
- */
4854
- constructor(world, entityId, component) {
4855
- /**
4856
- * Inspector-bound payload from the entity's `ScriptComponent`. Override the
4857
- * type on subclasses with `declare data: MyDataShape` to get type safety
4858
- * inside hooks. Saved with the project so values persist across reloads.
4859
- */
4860
- this.data = {};
4861
- /**
4862
- * Seconds elapsed since the last frame. Set by the engine before each
4863
- * {@link Behaviour.onUpdate}.
4864
- */
4865
- this.deltaTime = 0;
4866
- this.world = world;
4867
- this.entityId = entityId;
4868
- if (component) this.data = component;
4869
- this.entity = this.getEntity();
4870
- this.threeRef = this.getComponent(Object3DRef);
4871
- this.transform = this.getComponent(TransformComponent);
4872
- }
4873
- /**
4874
- * Engine-managed per-frame entry point. Refreshes {@link Behaviour.deltaTime}
4875
- * and delegates to {@link Behaviour.onUpdate}.
4876
- *
4877
- * @remarks Do **not** override this method — override
4878
- * {@link Behaviour.onUpdate} instead.
4879
- *
4880
- * @param world - The owning world (may differ from the constructor world
4881
- * after hot reload).
4882
- * @param deltaTime - Seconds since the last frame.
4883
- */
4884
- update(world, deltaTime) {
4885
- this.deltaTime = deltaTime;
4886
- this.world = world;
4887
- this.onUpdate();
4888
- }
4889
- /**
4890
- * Optional cleanup hook invoked when the script is detached or the entity
4891
- * is destroyed. Override to release timers, listeners, or other resources.
4892
- *
4893
- * @param world - The owning world at disposal time.
4894
- */
4895
- dispose(world) {
4896
- }
4897
- /**
4898
- * Instantiate a prefab asset into the world at runtime.
4899
- *
4900
- * `source` is typically an `AssetInput` field from this script's `data`
4901
- * (drag a `.prefab` from the asset browser onto the field in the editor),
4902
- * a URL string, or a pre-loaded `PrefabAsset`. Resolves with the new root
4903
- * entity id.
4904
- *
4905
- * ```ts
4906
- * data = { enemyPrefab: { path: "", type: "prefab" } };
4907
- * async onUpdate() {
4908
- * if (shouldSpawn) {
4909
- * const id = await this.spawnPrefab(this.data.enemyPrefab, {
4910
- * position: { x: 0, y: 0, z: -5 },
4911
- * });
4912
- * }
4913
- * }
4914
- * ```
4915
- */
4916
- spawnPrefab(source, options) {
4917
- return spawnPrefab(this.world, source, options);
4918
- }
4919
- /**
4920
- * Remove a spawned prefab instance (the root entity and all descendants).
4921
- * Returns the number of entities removed.
4922
- */
4923
- despawn(rootEntityId) {
4924
- return despawnPrefab(this.world, rootEntityId);
4925
- }
4926
- /**
4927
- * Returns the entity's world-space position as a new `THREE.Vector3`, or
4928
- * `null` if the entity has no scene presence.
4929
- */
4930
- getWorldPosition() {
4931
- if (!this.threeRef) {
4932
- return null;
4933
- }
4934
- const position = new THREE19.Vector3();
4935
- this.threeRef.object.getWorldPosition(position);
4936
- return position;
4937
- }
4938
- /**
4939
- * Returns the entity's forward direction in world space (the +Z axis of its
4940
- * world matrix), or `null` if the entity has no scene presence.
4941
- */
4942
- getWorldDirection() {
4943
- if (!this.threeRef) {
4944
- return null;
4945
- }
4946
- const target = new THREE19.Vector3();
4947
- this.threeRef.object.getWorldDirection(target);
4948
- return target;
4949
- }
4950
- /**
4951
- * Returns a unit vector pointing from this entity toward the given world
4952
- * position, or `null` if the entity has no scene presence.
4953
- *
4954
- * @param targetPosition - Destination point in world space.
4955
- */
4956
- directionTo(targetPosition) {
4957
- const currentPosition = this.getWorldPosition();
4958
- if (!currentPosition) {
4959
- return null;
4960
- }
4961
- return targetPosition.clone().sub(currentPosition).normalize();
4962
- }
4963
- /**
4964
- * Returns the world-space distance from this entity to the given position,
4965
- * or `null` if the entity has no scene presence.
4966
- *
4967
- * @param targetPosition - Destination point in world space.
4968
- */
4969
- distanceTo(targetPosition) {
4970
- const currentPosition = this.getWorldPosition();
4971
- if (!currentPosition) {
4972
- return null;
4973
- }
4974
- return currentPosition.distanceTo(targetPosition);
4975
- }
4976
- /**
4977
- * Linearly interpolates the entity's position toward `targetPosition` and
4978
- * writes the result back through {@link TransformComponent}. No-op if the
4979
- * entity has no scene presence.
4980
- *
4981
- * @param targetPosition - Destination point in world space.
4982
- * @param alpha - Interpolation factor in [0, 1]. Multiply by `deltaTime`
4983
- * for frame-rate-independent smoothing.
4984
- */
4985
- lerpTo(targetPosition, alpha) {
4986
- if (!this.threeRef) {
4987
- return;
4988
- }
4989
- const currentPosition = this.getWorldPosition();
4990
- if (!currentPosition) {
4991
- return;
4992
- }
4993
- const newPosition = currentPosition.lerp(targetPosition, alpha);
4994
- this.transform.setPosition(newPosition);
4995
- }
4996
- /**
4997
- * Spherically interpolates the entity's rotation toward `targetRotation`
4998
- * and writes the result back through {@link TransformComponent}. No-op if
4999
- * the entity has no scene presence.
5000
- *
5001
- * @param targetRotation - Destination orientation as a quaternion.
5002
- * @param alpha - Interpolation factor in [0, 1].
5003
- */
5004
- slerpTo(targetRotation, alpha) {
5005
- if (!this.threeRef) {
5006
- return;
5007
- }
5008
- const rotation = new THREE19.Quaternion();
5009
- this.threeRef.object.getWorldQuaternion(rotation);
5010
- rotation.slerp(targetRotation, alpha);
5011
- this.transform.setRotation(rotation);
5012
- }
5013
- /**
5014
- * Rotates the entity to face `targetPosition` in world space and writes the
5015
- * resulting orientation back through {@link TransformComponent}. No-op if
5016
- * the entity has no scene presence.
5017
- *
5018
- * @param targetPosition - Point in world space to face.
5019
- */
5020
- lookAt(targetPosition) {
5021
- if (!this.threeRef) {
5022
- return;
3062
+ im.castShadow = comp.castShadow;
3063
+ im.receiveShadow = comp.receiveShadow;
3064
+ }
3065
+ }
5023
3066
  }
5024
- const clone2 = this.threeRef.object.clone();
5025
- const rotation = new THREE19.Quaternion();
5026
- clone2.lookAt(targetPosition);
5027
- clone2.getWorldQuaternion(rotation);
5028
- this.transform.setRotation(rotation);
5029
- }
5030
- /** Returns every component currently attached to this entity. */
5031
- getComponents() {
5032
- return this.world.getEntityById(this.entityId)?.components ?? [];
5033
- }
5034
- /**
5035
- * Returns the entity's component of the given class, or `null` if it has
5036
- * no such component.
5037
- *
5038
- * @typeParam T - Concrete component type.
5039
- * @param componentClass - Component class constructor to look up.
5040
- *
5041
- * @example
5042
- * ```ts
5043
- * const mesh = this.getComponent(MeshComponent);
5044
- * if (mesh) console.log(mesh.geometryRef);
5045
- * ```
5046
- */
5047
- getComponent(componentClass) {
5048
- return this.world.getComponent(this.entityId, componentClass) ?? null;
5049
3067
  }
5050
- /**
5051
- * Returns the latest {@link WorldEntityView} snapshot for this entity, or
5052
- * `null` if it was destroyed.
5053
- */
5054
- getEntity() {
5055
- return this.world.getEntityById(this.entityId);
3068
+ };
3069
+ __name(_InstancedMeshSystem, "InstancedMeshSystem");
3070
+ var InstancedMeshSystem = _InstancedMeshSystem;
3071
+
3072
+ // core/systems/RenderSystem.ts
3073
+ var _RenderSystem = class _RenderSystem {
3074
+ constructor(renderer, scene, camera, afterRender) {
3075
+ /** Composer the last size was pushed to, plus that size. */
3076
+ this.sizedComposer = null;
3077
+ this.sizedWidth = 0;
3078
+ this.sizedHeight = 0;
3079
+ this.sizedPixelRatio = 0;
3080
+ this.warnedUninitialized = false;
3081
+ this.renderer = renderer;
3082
+ this.scene = scene;
3083
+ this.camera = camera;
3084
+ this.afterRender = afterRender;
5056
3085
  }
5057
3086
  /**
5058
- * Resolves the entity's parent from the {@link ParentId} component, if any.
5059
- *
5060
- * @returns A {@link ParentLookup} with the parent component reference and
5061
- * the parent entity view (or `null` if the entity is at the root).
3087
+ * `WebGPURenderer.render()` throws until `await renderer.init()` resolved.
3088
+ * Skip those frames (warning once) rather than throwing from the loop.
5062
3089
  */
5063
- getParent() {
5064
- const parentIdComponent = this.getComponent(ParentId);
5065
- const parentEntityId = parentIdComponent?.value ?? null;
5066
- if (parentEntityId === null) {
5067
- return {
5068
- parentIdComponent,
5069
- parent: null
5070
- };
3090
+ isRendererReady() {
3091
+ const renderer = this.renderer;
3092
+ if (renderer.isWebGPURenderer !== true || renderer.initialized !== false) {
3093
+ return true;
5071
3094
  }
5072
- return {
5073
- parentIdComponent,
5074
- parent: this.world.getEntityById(parentEntityId)
5075
- };
5076
- }
5077
- /**
5078
- * Returns every direct child entity of this one (entities whose
5079
- * {@link ParentId} matches `entityId`).
5080
- */
5081
- getChildren() {
5082
- return this.world.getAllEntities().filter((entity) => {
5083
- const parentIdComponent = entity.components.find(
5084
- (component) => component instanceof ParentId
3095
+ if (!this.warnedUninitialized) {
3096
+ this.warnedUninitialized = true;
3097
+ console.warn(
3098
+ "[spatial-ecs] RenderSystem skipped a frame: the WebGPURenderer is not initialised yet. Call `await renderer.init()` before the first world.update()."
5085
3099
  );
5086
- return parentIdComponent?.value === this.entityId;
5087
- });
5088
- }
5089
- /**
5090
- * Called once before the first {@link Behaviour.onUpdate}. Override to
5091
- * default and validate {@link Behaviour.data}, cache references, or set
5092
- * initial state. Runs at preview/runtime only — never in the editor.
5093
- */
5094
- init() {
5095
- }
5096
- /**
5097
- * Called every frame while the script is enabled. Override with the
5098
- * script's main per-frame logic. Runs at preview/runtime only — never in the
5099
- * editor (use {@link ScriptObject} for editor-visible visuals).
5100
- */
5101
- onUpdate() {
5102
- }
5103
- /** Called when the editor selects this entity. Optional UX hook. */
5104
- onSelect() {
5105
- }
5106
- /** Called when the editor deselects this entity. Optional UX hook. */
5107
- onDeselect() {
5108
- }
5109
- /**
5110
- * Called when an image-tracking target this entity depends on becomes
5111
- * visible. Optional AR hook.
5112
- *
5113
- * @param target - The newly-visible target entity view.
5114
- */
5115
- onTargetFound(target) {
5116
- }
5117
- /**
5118
- * Called when an image-tracking target this entity depends on is lost.
5119
- * Optional AR hook.
5120
- *
5121
- * @param target - The target entity view that became invisible.
5122
- */
5123
- onTargetLost(target) {
5124
- }
5125
- // ─── WebXR (VR / MR) hooks ────────────────────────────────────────────────
5126
- // Dispatched by XRSessionSystem / XRInteractionSystem, following the same
5127
- // pattern as the AR hooks above: public no-op methods that subclasses opt
5128
- // into by overriding. They never fire outside an immersive session.
5129
- /**
5130
- * Called once when an immersive session starts. Optional XR hook.
5131
- *
5132
- * Use it to set up XR-only state. Inspect `capabilities` rather than guessing
5133
- * by device: feature support differs sharply per headset (for example Vision
5134
- * Pro grants no passthrough on the web today).
5135
- *
5136
- * @param capabilities - What the session actually granted, read from
5137
- * `XRSession.enabledFeatures`.
5138
- */
5139
- onXRSessionStart(capabilities) {
5140
- }
5141
- /** Called once when the immersive session ends. Optional XR hook. */
5142
- onXRSessionEnd() {
5143
- }
5144
- /**
5145
- * Called when a primary action begins — controller trigger, hand pinch, or
5146
- * Vision Pro gaze-pinch. Optional XR hook.
5147
- *
5148
- * @param input - Normalized state for the source that acted.
5149
- */
5150
- onSelectStart(input) {
5151
- }
5152
- /** Called when the primary action ends. Optional XR hook. */
5153
- onSelectEnd(input) {
5154
- }
5155
- /**
5156
- * Called when this entity is grabbed. Optional XR hook.
5157
- *
5158
- * Only fires for entities carrying an enabled, grabbable
5159
- * `XRInteractableComponent`.
5160
- *
5161
- * @param input - The source doing the grabbing.
5162
- */
5163
- /**
5164
- * A SECOND hand joined a two-hand grab on this entity.
5165
- *
5166
- * The first hand anchors the object and keeps holding it; this fires for the
5167
- * hand that came second — the foregrip on a rifle, say.
5168
- */
5169
- onSecondaryGrab(_input) {
5170
- }
5171
- /** The second hand let go, leaving the object in the first hand alone. */
5172
- onSecondaryRelease(_input) {
3100
+ }
3101
+ return false;
5173
3102
  }
5174
3103
  /**
5175
- * A hand pose declared on this entity started being held.
5176
- *
5177
- * Fires once the pose has been sustained for its hold time, not the instant
5178
- * the fingers pass through it.
3104
+ * Resize the composer only when the canvas size or pixel ratio changed, or
3105
+ * when the composer itself was rebuilt. `setSize` resizes every render
3106
+ * target in the chain, so calling it every frame wasted GPU work.
5179
3107
  */
5180
- onHandPoseStart(_event) {
5181
- }
5182
- /** A held hand pose ended, or its hand stopped being tracked. */
5183
- onHandPoseEnd(_event) {
5184
- }
5185
- onGrab(input) {
5186
- }
5187
- /** Called when this entity is released. Optional XR hook. */
5188
- onRelease(input) {
5189
- }
5190
- /** A UI button on this entity (or a descendant) was clicked. Clicks fire on
5191
- * RELEASE while still pointing at the widget — sliding off cancels. */
5192
- onUiPress(_event) {
5193
- }
5194
- /** A pointer began hovering a UI widget on this entity (or a descendant). */
5195
- onUiHoverStart(_event) {
5196
- }
5197
- /** The pointer left the widget. */
5198
- onUiHoverEnd(_event) {
5199
- }
5200
- /** A UI toggle flipped; `event.value` is the new boolean. */
5201
- onUiToggle(_event) {
5202
- }
5203
- /** A UI slider moved; `event.value` is the new number. Fires on real value
5204
- * changes during the drag and once more on release. */
5205
- onUiChange(_event) {
5206
- }
5207
- };
5208
- __name(_Behaviour, "Behaviour");
5209
- /**
5210
- * Static marker used by `ScriptBehaviourSystem` to detect `Behaviour`
5211
- * subclasses across bundle-identity splits (esbuild may produce a second
5212
- * copy of the package when scripts are bundled, breaking `instanceof`).
5213
- * Inherited by every subclass via the prototype chain.
5214
- *
5215
- * @internal
5216
- */
5217
- _Behaviour.__reluBehaviourClass = true;
5218
- var Behaviour = _Behaviour;
5219
-
5220
- // core/systems/scriptShared.ts
5221
- async function defaultModuleLoader(scriptUrl) {
5222
- return await import(
5223
- /* webpackIgnore: true */
5224
- scriptUrl
5225
- );
5226
- }
5227
- __name(defaultModuleLoader, "defaultModuleLoader");
5228
- function toErrorMessage(error) {
5229
- const sanitize = /* @__PURE__ */ __name((value) => value.replace(/localasset:\/\/[^/]+\//g, "localasset://").replace(/\/Users\/[^\s)]+/g, "[local path]"), "sanitize");
5230
- if (error instanceof Error && error.message) {
5231
- return sanitize(error.message);
5232
- }
5233
- if (typeof error === "string" && error) {
5234
- return sanitize(error);
5235
- }
5236
- return "Unknown script error.";
5237
- }
5238
- __name(toErrorMessage, "toErrorMessage");
5239
- function resolveScriptCtor(moduleValue, className, options) {
5240
- const named = moduleValue[className];
5241
- const fallback = moduleValue.default;
5242
- const candidate = typeof named === "function" ? named : typeof fallback === "function" ? fallback : null;
5243
- if (!candidate) {
5244
- return null;
5245
- }
5246
- if (!(candidate.prototype instanceof options.baseClass)) {
5247
- const marker = candidate[options.markerKey];
5248
- if (!marker) {
5249
- return null;
3108
+ syncComposerSize(composer) {
3109
+ const width = Math.max(1, this.renderer.domElement.clientWidth);
3110
+ const height = Math.max(1, this.renderer.domElement.clientHeight);
3111
+ const pixelRatio = typeof this.renderer.getPixelRatio === "function" ? this.renderer.getPixelRatio() : 1;
3112
+ const isNewComposer = composer !== this.sizedComposer;
3113
+ if (!isNewComposer && width === this.sizedWidth && height === this.sizedHeight && pixelRatio === this.sizedPixelRatio) {
3114
+ return;
5250
3115
  }
5251
- }
5252
- return candidate;
5253
- }
5254
- __name(resolveScriptCtor, "resolveScriptCtor");
5255
- function normalizeScriptData(value) {
5256
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
5257
- }
5258
- __name(normalizeScriptData, "normalizeScriptData");
5259
- function isPlainObject(value) {
5260
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5261
- }
5262
- __name(isPlainObject, "isPlainObject");
5263
- function mergeValueByDefaultShape(defaultValue, currentValue) {
5264
- if (Array.isArray(defaultValue)) {
5265
- return Array.isArray(currentValue) ? currentValue : defaultValue;
5266
- }
5267
- if (isPlainObject(defaultValue)) {
5268
- if (!isPlainObject(currentValue)) {
5269
- return defaultValue;
3116
+ if ((isNewComposer || pixelRatio !== this.sizedPixelRatio) && typeof composer.setPixelRatio === "function") {
3117
+ composer.setPixelRatio(pixelRatio);
5270
3118
  }
5271
- const next = {};
5272
- for (const key of Object.keys(defaultValue)) {
5273
- next[key] = mergeValueByDefaultShape(defaultValue[key], currentValue[key]);
3119
+ if (typeof composer.setSize === "function") {
3120
+ composer.setSize(width, height);
5274
3121
  }
5275
- return next;
5276
- }
5277
- if (typeof defaultValue === "number") {
5278
- return typeof currentValue === "number" ? currentValue : defaultValue;
5279
- }
5280
- if (typeof defaultValue === "string") {
5281
- return typeof currentValue === "string" ? currentValue : defaultValue;
5282
- }
5283
- if (typeof defaultValue === "boolean") {
5284
- return typeof currentValue === "boolean" ? currentValue : defaultValue;
5285
- }
5286
- if (defaultValue === null) {
5287
- return currentValue !== void 0 ? currentValue : null;
3122
+ this.sizedComposer = composer;
3123
+ this.sizedWidth = width;
3124
+ this.sizedHeight = height;
3125
+ this.sizedPixelRatio = pixelRatio;
5288
3126
  }
5289
- return typeof currentValue === "undefined" ? defaultValue : currentValue;
5290
- }
5291
- __name(mergeValueByDefaultShape, "mergeValueByDefaultShape");
5292
- function mergeDataByCurrentDefaults(defaults, current) {
5293
- const next = {};
5294
- for (const key of Object.keys(defaults)) {
5295
- next[key] = mergeValueByDefaultShape(defaults[key], current[key]);
5296
- }
5297
- return next;
5298
- }
5299
- __name(mergeDataByCurrentDefaults, "mergeDataByCurrentDefaults");
5300
- function isRawEntityInput(value) {
5301
- if (!isPlainObject(value)) return false;
5302
- return value.type === "entity";
5303
- }
5304
- __name(isRawEntityInput, "isRawEntityInput");
5305
- function isRawComponentInput(value) {
5306
- if (!isPlainObject(value)) return false;
5307
- return value.type === "component";
5308
- }
5309
- __name(isRawComponentInput, "isRawComponentInput");
5310
- function findComponentByKind(components, componentKind) {
5311
- return components.find(
5312
- (c) => c.constructor.name === `${componentKind}Component` || c.constructor.name === componentKind
5313
- ) ?? null;
5314
- }
5315
- __name(findComponentByKind, "findComponentByKind");
5316
- function resolveInputData(world, rawData) {
5317
- const resolved = { ...rawData };
5318
- for (const [key, value] of Object.entries(rawData)) {
5319
- if (isRawEntityInput(value)) {
5320
- const entityId = value.value;
5321
- const entityView = typeof entityId === "number" ? world.getEntityById(entityId) : null;
5322
- resolved[key] = { ...value, value: entityView };
5323
- } else if (isRawComponentInput(value)) {
5324
- const ref = value.value;
5325
- if (isPlainObject(ref) && typeof ref.entityId === "number" && typeof ref.componentKind === "string") {
5326
- const { entityId: refEntityId, componentKind } = ref;
5327
- const entityView = world.getEntityById(refEntityId);
5328
- const component = entityView ? findComponentByKind(entityView.components, componentKind) : null;
5329
- resolved[key] = { ...value, value: component };
5330
- }
3127
+ update(_world) {
3128
+ if (!this.isRendererReady()) {
3129
+ return;
5331
3130
  }
5332
- }
5333
- return resolved;
5334
- }
5335
- __name(resolveInputData, "resolveInputData");
5336
- function serializeDataForStorage(rawData, instanceData) {
5337
- const result = { ...instanceData };
5338
- for (const [key, rawValue] of Object.entries(rawData)) {
5339
- if (isRawEntityInput(rawValue) || isRawComponentInput(rawValue)) {
5340
- result[key] = rawValue;
3131
+ if (this.scene.userData.ecsPostprocessingComposerActive === true) {
3132
+ const composer = this.scene.userData.ecsPostprocessingComposer;
3133
+ if (composer) {
3134
+ this.syncComposerSize(composer);
3135
+ composer.render();
3136
+ } else {
3137
+ this.renderer.render(this.scene, this.camera);
3138
+ }
3139
+ this.afterRender?.();
3140
+ return;
5341
3141
  }
3142
+ this.sizedComposer = null;
3143
+ this.renderer.render(this.scene, this.camera);
3144
+ this.afterRender?.();
5342
3145
  }
5343
- return result;
5344
- }
5345
- __name(serializeDataForStorage, "serializeDataForStorage");
5346
-
5347
- // core/systems/ScriptBehaviourSystem.ts
3146
+ };
3147
+ __name(_RenderSystem, "RenderSystem");
3148
+ var RenderSystem = _RenderSystem;
3149
+ var DEFAULT_SCRIPT_ERROR_RETRY_DELAY = 1;
5348
3150
  function buildInstanceKey(entityId, scriptUrl, className) {
5349
3151
  return `${entityId}:${scriptUrl}:${className}`;
5350
3152
  }
@@ -5385,9 +3187,13 @@ function writeScriptEntries(component, entries) {
5385
3187
  }
5386
3188
  __name(writeScriptEntries, "writeScriptEntries");
5387
3189
  var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5388
- constructor(moduleLoader, selectionContext) {
3190
+ constructor(moduleLoader, selectionContext, options) {
5389
3191
  this.instances = /* @__PURE__ */ new Map();
5390
3192
  this.loading = /* @__PURE__ */ new Map();
3193
+ /** Seconds of world time this system has seen (sum of `deltaTime`). */
3194
+ this.clock = 0;
3195
+ /** Instance key → clock time before which a failed script is not retried. */
3196
+ this.retryAt = /* @__PURE__ */ new Map();
5391
3197
  this.selectedEntityId = null;
5392
3198
  // undefined = nothing queued; null = queued "deselect all"; number = queued entity
5393
3199
  this.pendingEntityId = void 0;
@@ -5404,6 +3210,8 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5404
3210
  this.queueRaycast(event.clientX, event.clientY);
5405
3211
  }, "onPointerUp");
5406
3212
  this.moduleLoader = moduleLoader ?? defaultModuleLoader;
3213
+ const retryDelay = options?.errorRetryDelay;
3214
+ this.errorRetryDelay = typeof retryDelay === "number" && Number.isFinite(retryDelay) ? Math.max(0, retryDelay) : DEFAULT_SCRIPT_ERROR_RETRY_DELAY;
5407
3215
  this.selectionCtx = selectionContext ?? null;
5408
3216
  this.raycaster = new THREE19.Raycaster();
5409
3217
  this.ndcPointer = new THREE19.Vector2();
@@ -5451,7 +3259,7 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5451
3259
  * Calls onDeselect() on all behaviours of the previous entity and
5452
3260
  * onSelect() on all behaviours of the newly selected entity.
5453
3261
  */
5454
- applySelection(nextEntityId) {
3262
+ applySelection(world, nextEntityId) {
5455
3263
  if (nextEntityId === this.selectedEntityId) return;
5456
3264
  const prevEntityId = this.selectedEntityId;
5457
3265
  this.selectedEntityId = nextEntityId;
@@ -5460,7 +3268,8 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5460
3268
  if (record.entityId === prevEntityId) {
5461
3269
  try {
5462
3270
  record.behaviour.onDeselect();
5463
- } catch {
3271
+ } catch (error) {
3272
+ this.reportRecordError(world, record, "onDeselect", error);
5464
3273
  }
5465
3274
  }
5466
3275
  }
@@ -5470,18 +3279,39 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5470
3279
  if (record.entityId === nextEntityId) {
5471
3280
  try {
5472
3281
  record.behaviour.onSelect();
5473
- } catch {
3282
+ } catch (error) {
3283
+ this.reportRecordError(world, record, "onSelect", error);
5474
3284
  }
5475
3285
  }
5476
3286
  }
5477
3287
  }
5478
3288
  }
3289
+ reportRecordError(world, record, phase, error) {
3290
+ reportScriptError(
3291
+ world,
3292
+ {
3293
+ entityId: record.entityId,
3294
+ scriptUrl: record.scriptUrl,
3295
+ className: record.className,
3296
+ phase,
3297
+ source: "behaviour"
3298
+ },
3299
+ error
3300
+ );
3301
+ }
3302
+ /** Hold a failed script back for {@link errorRetryDelay} seconds. */
3303
+ scheduleRetry(key) {
3304
+ this.retryAt.set(key, this.clock + this.errorRetryDelay);
3305
+ }
5479
3306
  // ── ECS lifecycle ──────────────────────────────────────────────────────────
5480
3307
  update(world, deltaTime) {
3308
+ if (Number.isFinite(deltaTime) && deltaTime > 0) {
3309
+ this.clock += deltaTime;
3310
+ }
5481
3311
  if (this.pendingEntityId !== void 0) {
5482
3312
  const next = this.pendingEntityId;
5483
3313
  this.pendingEntityId = void 0;
5484
- this.applySelection(next);
3314
+ this.applySelection(world, next);
5485
3315
  }
5486
3316
  const active = /* @__PURE__ */ new Map();
5487
3317
  const entityIds = world.query(ScriptComponent);
@@ -5571,8 +3401,15 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5571
3401
  if (script.entryIndex === 0) {
5572
3402
  script.scriptComponent.scriptInstance = null;
5573
3403
  }
5574
- record.behaviour.dispose?.(world);
3404
+ script.scriptComponent.scriptInstances.delete(script.entryIndex);
3405
+ this.reportRecordError(world, record, "update", error);
3406
+ try {
3407
+ record.behaviour.dispose?.(world);
3408
+ } catch (disposeError) {
3409
+ this.reportRecordError(world, record, "dispose", disposeError);
3410
+ }
5575
3411
  this.instances.delete(key);
3412
+ this.scheduleRetry(key);
5576
3413
  }
5577
3414
  }
5578
3415
  if (this.selectedEntityId !== null) {
@@ -5587,10 +3424,22 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5587
3424
  this.selectedEntityId = null;
5588
3425
  }
5589
3426
  }
3427
+ for (const key of this.retryAt.keys()) {
3428
+ if (!active.has(key)) {
3429
+ this.retryAt.delete(key);
3430
+ }
3431
+ }
5590
3432
  for (const [key, script] of active.entries()) {
5591
3433
  if (this.instances.has(key) || this.loading.has(key)) {
5592
3434
  continue;
5593
3435
  }
3436
+ const retryAt = this.retryAt.get(key);
3437
+ if (retryAt !== void 0) {
3438
+ if (this.clock < retryAt) {
3439
+ continue;
3440
+ }
3441
+ this.retryAt.delete(key);
3442
+ }
5594
3443
  const entries = readScriptEntries(script.scriptComponent, script.entityId);
5595
3444
  const targetEntry = entries[script.entryIndex];
5596
3445
  if (targetEntry) {
@@ -5601,24 +3450,34 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5601
3450
  };
5602
3451
  writeScriptEntries(script.scriptComponent, entries);
5603
3452
  }
5604
- const promise = this.loadBehaviour(world, script).catch((error) => {
3453
+ const promise = this.loadBehaviour(world, script).catch((thrown) => {
3454
+ const { phase, error } = readScriptPhase(thrown, "load");
3455
+ const message = toErrorMessage(error);
5605
3456
  const liveEntries = readScriptEntries(script.scriptComponent, script.entityId);
5606
3457
  const liveEntry = liveEntries[script.entryIndex];
5607
3458
  if (liveEntry) {
5608
3459
  liveEntries[script.entryIndex] = {
5609
3460
  ...liveEntry,
5610
3461
  runtimeState: "error",
5611
- runtimeMessage: "Failed to load script."
3462
+ runtimeMessage: phase === "load" ? `Failed to load script: ${message}` : message
5612
3463
  };
5613
3464
  writeScriptEntries(script.scriptComponent, liveEntries);
5614
3465
  }
5615
3466
  if (script.entryIndex === 0) {
5616
3467
  script.scriptComponent.scriptInstance = null;
5617
3468
  }
5618
- console.error(
5619
- `[ScriptBehaviourSystem] Failed loading script ${script.scriptEntry.className}`,
3469
+ reportScriptError(
3470
+ world,
3471
+ {
3472
+ entityId: script.entityId,
3473
+ scriptUrl: script.scriptEntry.scriptUrl,
3474
+ className: script.scriptEntry.className,
3475
+ phase,
3476
+ source: "behaviour"
3477
+ },
5620
3478
  error
5621
3479
  );
3480
+ this.scheduleRetry(key);
5622
3481
  }).finally(() => {
5623
3482
  this.loading.delete(key);
5624
3483
  });
@@ -5648,6 +3507,7 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5648
3507
  }
5649
3508
  this.instances.clear();
5650
3509
  this.loading.clear();
3510
+ this.retryAt.clear();
5651
3511
  }
5652
3512
  async loadBehaviour(world, script) {
5653
3513
  const { entityId, scriptComponent, entryIndex, scriptEntry } = script;
@@ -5656,11 +3516,19 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5656
3516
  scriptEntry.scriptUrl,
5657
3517
  scriptEntry.className
5658
3518
  );
5659
- const moduleValue = await this.moduleLoader(scriptEntry.scriptUrl);
3519
+ let moduleValue;
3520
+ try {
3521
+ moduleValue = await this.moduleLoader(scriptEntry.scriptUrl);
3522
+ } catch (error) {
3523
+ throw withScriptPhase("load", error);
3524
+ }
5660
3525
  const ctor = resolveBehaviourCtor(moduleValue, scriptEntry.className);
5661
3526
  if (!ctor) {
5662
- throw new Error(
5663
- `Missing Behaviour export '${scriptEntry.className}' in ${scriptEntry.scriptUrl}`
3527
+ throw withScriptPhase(
3528
+ "load",
3529
+ new Error(
3530
+ `Missing Behaviour export '${scriptEntry.className}' in ${scriptEntry.scriptUrl}`
3531
+ )
5664
3532
  );
5665
3533
  }
5666
3534
  if (!world.hasEntity(entityId)) {
@@ -5677,21 +3545,26 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
5677
3545
  }
5678
3546
  const currentData = normalizeScriptData(liveEntry.data);
5679
3547
  let behaviour;
3548
+ let mergedData;
5680
3549
  try {
5681
- behaviour = new ctor(
5682
- world,
5683
- entityId,
5684
- void 0
5685
- );
5686
- } catch {
5687
- behaviour = new ctor(world, entityId, currentData);
5688
- }
5689
- const defaults = normalizeScriptData(behaviour.data);
5690
- const mergedData = mergeDataByCurrentDefaults(defaults, currentData);
5691
- const resolvedData = resolveInputData(world, mergedData);
5692
- behaviour.data = resolvedData;
5693
- if (!world.isEditorMode()) {
5694
- behaviour.init();
3550
+ try {
3551
+ behaviour = new ctor(
3552
+ world,
3553
+ entityId,
3554
+ void 0
3555
+ );
3556
+ } catch {
3557
+ behaviour = new ctor(world, entityId, currentData);
3558
+ }
3559
+ const defaults = normalizeScriptData(behaviour.data);
3560
+ mergedData = mergeDataByCurrentDefaults(defaults, currentData);
3561
+ const resolvedData = resolveInputData(world, mergedData);
3562
+ behaviour.data = resolvedData;
3563
+ if (!world.isEditorMode()) {
3564
+ behaviour.init();
3565
+ }
3566
+ } catch (error) {
3567
+ throw withScriptPhase("init", error);
5695
3568
  }
5696
3569
  liveEntries[entryIndex] = {
5697
3570
  ...liveEntry,
@@ -5798,9 +3671,12 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5798
3671
  this.loading = /* @__PURE__ */ new Map();
5799
3672
  /** Bumped by invalidate*() so in-flight loads that predate it are discarded. */
5800
3673
  this.epoch = 0;
3674
+ /** World seen by the last update(), so invalidate*() can reset error dedupe. */
3675
+ this.lastWorld = null;
5801
3676
  this.moduleLoader = moduleLoader ?? defaultModuleLoader;
5802
3677
  }
5803
3678
  update(world, deltaTime) {
3679
+ this.lastWorld = world;
5804
3680
  const active = /* @__PURE__ */ new Map();
5805
3681
  for (const entityId of world.query(ScriptObjectComponent)) {
5806
3682
  const component = world.getComponent(entityId, ScriptObjectComponent);
@@ -5841,14 +3717,14 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5841
3717
  component.data = stored;
5842
3718
  record.lastWrittenData = stored;
5843
3719
  } catch (error) {
5844
- this.markError(world, entityId, component, error);
3720
+ this.markError(world, entityId, component, error, "update");
5845
3721
  continue;
5846
3722
  }
5847
3723
  }
5848
3724
  try {
5849
3725
  record.instance.update?.(deltaTime);
5850
3726
  } catch (error) {
5851
- this.markError(world, entityId, component, error);
3727
+ this.markError(world, entityId, component, error, "update");
5852
3728
  }
5853
3729
  }
5854
3730
  for (const [entityId, component] of active) {
@@ -5868,12 +3744,12 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5868
3744
  className,
5869
3745
  key,
5870
3746
  epochAtStart
5871
- ).catch((error) => {
5872
- this.markError(world, entityId, void 0, error);
5873
- console.error(
5874
- `[ScriptObjectSystem] Failed loading ${className} (${scriptUrl})`,
5875
- error
5876
- );
3747
+ ).catch((thrown) => {
3748
+ const { phase, error } = readScriptPhase(thrown, "load");
3749
+ this.markError(world, entityId, void 0, error, phase, {
3750
+ scriptUrl,
3751
+ className
3752
+ });
5877
3753
  }).finally(() => {
5878
3754
  if (this.loading.get(entityId)?.key === key) {
5879
3755
  this.loading.delete(entityId);
@@ -5886,7 +3762,12 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5886
3762
  }
5887
3763
  }
5888
3764
  async loadScriptObject(world, entityId, scriptUrl, className, key, epochAtStart) {
5889
- const moduleValue = await this.moduleLoader(scriptUrl);
3765
+ let moduleValue;
3766
+ try {
3767
+ moduleValue = await this.moduleLoader(scriptUrl);
3768
+ } catch (error) {
3769
+ throw withScriptPhase("load", error);
3770
+ }
5890
3771
  if (this.loading.get(entityId)?.key !== key || epochAtStart !== this.epoch || !world.hasEntity(entityId)) {
5891
3772
  return;
5892
3773
  }
@@ -5899,16 +3780,21 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5899
3780
  markerKey: "__reluScriptObjectClass"
5900
3781
  });
5901
3782
  if (!ctor) {
5902
- throw new Error(
5903
- `Missing ScriptObject export '${className}' in ${scriptUrl}`
3783
+ throw withScriptPhase(
3784
+ "load",
3785
+ new Error(`Missing ScriptObject export '${className}' in ${scriptUrl}`)
5904
3786
  );
5905
3787
  }
5906
3788
  const currentData = normalizeScriptData(component.data);
5907
3789
  let instance;
5908
3790
  try {
5909
- instance = new ctor(world, entityId, void 0);
5910
- } catch {
5911
- instance = new ctor(world, entityId, currentData);
3791
+ try {
3792
+ instance = new ctor(world, entityId, void 0);
3793
+ } catch {
3794
+ instance = new ctor(world, entityId, currentData);
3795
+ }
3796
+ } catch (error) {
3797
+ throw withScriptPhase("init", error);
5912
3798
  }
5913
3799
  const literalDefaults = normalizeScriptData(instance.data);
5914
3800
  const seed = Object.keys(literalDefaults).length > 0 ? mergeDataByCurrentDefaults(literalDefaults, currentData) : { ...currentData };
@@ -5930,14 +3816,17 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5930
3816
  instance.dispose?.();
5931
3817
  } catch {
5932
3818
  }
5933
- throw error;
3819
+ throw withScriptPhase("build", error);
5934
3820
  }
5935
3821
  if (!built || built.isObject3D !== true) {
5936
3822
  try {
5937
3823
  instance.dispose?.();
5938
3824
  } catch {
5939
3825
  }
5940
- throw new Error("build() must return a THREE.Object3D.");
3826
+ throw withScriptPhase(
3827
+ "build",
3828
+ new Error("build() must return a THREE.Object3D.")
3829
+ );
5941
3830
  }
5942
3831
  built.userData[SCRIPT_OBJECT_ROOT_FLAG] = true;
5943
3832
  parent.add(built);
@@ -5985,12 +3874,24 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
5985
3874
  this.instances.delete(entityId);
5986
3875
  }
5987
3876
  }
5988
- markError(world, entityId, component, error) {
3877
+ markError(world, entityId, component, error, phase, identity) {
5989
3878
  const comp = component ?? world.getComponent(entityId, ScriptObjectComponent) ?? null;
5990
3879
  if (comp) {
5991
3880
  comp.runtimeState = "error";
5992
3881
  comp.runtimeMessage = toErrorMessage(error);
5993
3882
  }
3883
+ const existing = this.instances.get(entityId);
3884
+ reportScriptError(
3885
+ world,
3886
+ {
3887
+ entityId,
3888
+ scriptUrl: identity?.scriptUrl ?? existing?.scriptUrl ?? comp?.scriptUrl ?? "",
3889
+ className: identity?.className ?? existing?.className ?? comp?.className ?? "",
3890
+ phase,
3891
+ source: "scriptObject"
3892
+ },
3893
+ error
3894
+ );
5994
3895
  const record = this.instances.get(entityId);
5995
3896
  if (record) {
5996
3897
  this.teardownRecord(world, entityId, record, true);
@@ -6010,6 +3911,7 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
6010
3911
  /** Dispose all live instances so the next update() reloads with fresh modules. */
6011
3912
  invalidateAll() {
6012
3913
  this.epoch += 1;
3914
+ this.lastWorld?.clearScriptErrors?.();
6013
3915
  for (const [entityId, record] of [...this.instances]) {
6014
3916
  try {
6015
3917
  record.instance?.dispose?.();
@@ -6023,6 +3925,7 @@ var _ScriptObjectSystem = class _ScriptObjectSystem {
6023
3925
  /** Invalidate only the entities whose script matches `scriptUrl`. */
6024
3926
  invalidate(scriptUrl) {
6025
3927
  this.epoch += 1;
3928
+ this.lastWorld?.clearScriptErrors?.(scriptUrl);
6026
3929
  for (const [entityId, record] of [...this.instances]) {
6027
3930
  if (record.scriptUrl !== scriptUrl) continue;
6028
3931
  try {
@@ -6068,6 +3971,112 @@ var _TransformSyncSystem = class _TransformSyncSystem {
6068
3971
  };
6069
3972
  __name(_TransformSyncSystem, "TransformSyncSystem");
6070
3973
  var TransformSyncSystem = _TransformSyncSystem;
3974
+
3975
+ // core/audio/AudioMixer.ts
3976
+ var BUSES = ["sfx", "voice", "music"];
3977
+ var DUCK_GAIN = 0.45;
3978
+ var DUCK_ATTACK = 0.05;
3979
+ var DUCK_RELEASE = 0.3;
3980
+ var _AudioMixer = class _AudioMixer {
3981
+ constructor(listener) {
3982
+ this.listener = listener;
3983
+ this.volumes = {
3984
+ sfx: 1,
3985
+ voice: 1,
3986
+ music: 1
3987
+ };
3988
+ this.routed = /* @__PURE__ */ new WeakMap();
3989
+ const input = listener.getInput();
3990
+ const make = /* @__PURE__ */ __name(() => {
3991
+ const gain = listener.context.createGain();
3992
+ gain.connect(input);
3993
+ return gain;
3994
+ }, "make");
3995
+ this.buses = { sfx: make(), voice: make(), music: make() };
3996
+ }
3997
+ /** The node a sound connects to for this bus. */
3998
+ input(bus) {
3999
+ return this.buses[bus] ?? this.buses.sfx;
4000
+ }
4001
+ /** Send a three.js sound through a bus. Does nothing when it's already there. */
4002
+ route(sound, bus) {
4003
+ if (this.routed.get(sound) === bus) return;
4004
+ const output = sound.getOutput();
4005
+ output.disconnect();
4006
+ output.connect(this.input(bus));
4007
+ this.routed.set(sound, bus);
4008
+ }
4009
+ setVolume(bus, volume) {
4010
+ const v = Math.max(0, volume);
4011
+ this.volumes[bus] = v;
4012
+ this.input(bus).gain.setTargetAtTime(v, this.listener.context.currentTime, 0.02);
4013
+ }
4014
+ getVolume(bus) {
4015
+ return this.volumes[bus];
4016
+ }
4017
+ /** Dip the music for `seconds`, then bring it back. A later duck replaces this one. */
4018
+ duckMusic(seconds) {
4019
+ const gain = this.buses.music.gain;
4020
+ const now = this.listener.context.currentTime;
4021
+ const full = this.volumes.music;
4022
+ gain.cancelScheduledValues(now);
4023
+ gain.setTargetAtTime(full * DUCK_GAIN, now, DUCK_ATTACK);
4024
+ gain.setTargetAtTime(full, now + seconds, DUCK_RELEASE);
4025
+ }
4026
+ dispose() {
4027
+ for (const bus of BUSES) this.buses[bus].disconnect();
4028
+ }
4029
+ };
4030
+ __name(_AudioMixer, "AudioMixer");
4031
+ var AudioMixer = _AudioMixer;
4032
+
4033
+ // core/audio/soundBank.ts
4034
+ function soundBankFiles(bank) {
4035
+ const files = /* @__PURE__ */ new Set();
4036
+ for (const slot of Object.values(bank.slots)) {
4037
+ for (const take of slot.takes) if (take.file) files.add(take.file);
4038
+ }
4039
+ return [...files];
4040
+ }
4041
+ __name(soundBankFiles, "soundBankFiles");
4042
+ function assertSoundBank(value) {
4043
+ const slots = value?.slots;
4044
+ if (!slots || typeof slots !== "object") {
4045
+ throw new Error("Not a sound bank: it has no slots.");
4046
+ }
4047
+ for (const [id, slot] of Object.entries(slots)) {
4048
+ if (!Array.isArray(slot?.takes)) {
4049
+ throw new Error(`Sound bank slot "${id}" has no takes list.`);
4050
+ }
4051
+ }
4052
+ }
4053
+ __name(assertSoundBank, "assertSoundBank");
4054
+ var _SlotChooser = class _SlotChooser {
4055
+ constructor(random = Math.random) {
4056
+ this.random = random;
4057
+ this.lastPlay = /* @__PURE__ */ new Map();
4058
+ this.lastTake = /* @__PURE__ */ new Map();
4059
+ }
4060
+ /** The take to play now, or `null` to stay silent. `now` is in milliseconds. */
4061
+ choose(id, slot, now) {
4062
+ const takes = slot.takes.filter((t) => t.file);
4063
+ if (takes.length === 0) return null;
4064
+ const last = this.lastPlay.get(id);
4065
+ if (last !== void 0 && now - last < (slot.cooldownMs ?? 0)) return null;
4066
+ if (this.random() >= (slot.chance ?? 1)) return null;
4067
+ const previous = this.lastTake.get(id);
4068
+ const fresh = takes.filter((t) => t.file !== previous);
4069
+ const pool = fresh.length > 0 ? fresh : takes;
4070
+ const take = pool[Math.min(pool.length - 1, Math.floor(this.random() * pool.length))];
4071
+ this.lastPlay.set(id, now);
4072
+ this.lastTake.set(id, take.file);
4073
+ return take;
4074
+ }
4075
+ };
4076
+ __name(_SlotChooser, "SlotChooser");
4077
+ var SlotChooser = _SlotChooser;
4078
+
4079
+ // core/systems/AudioSystem.ts
6071
4080
  function snapshotOptions(opts) {
6072
4081
  return {
6073
4082
  loop: opts.loop,
@@ -6087,10 +4096,106 @@ var _AudioSystem = class _AudioSystem {
6087
4096
  this.prevOptions = /* @__PURE__ */ new Map();
6088
4097
  /** Tracks entities that were stopped due to invisibility. */
6089
4098
  this.stoppedDueToInvisibility = /* @__PURE__ */ new Set();
4099
+ this.world = null;
4100
+ this.mixer = null;
4101
+ this.slots = /* @__PURE__ */ new Map();
4102
+ this.buffers = /* @__PURE__ */ new Map();
4103
+ this.chooser = new SlotChooser();
4104
+ this.voice = null;
6090
4105
  this.isEditor = isEditor;
6091
4106
  }
4107
+ /** Scripts can load a bank in `init`, before this system's first update. */
4108
+ attachWorld(world) {
4109
+ this.world = world;
4110
+ }
4111
+ /** The mixer, made with the listener on first use. */
4112
+ getMixer() {
4113
+ if (this.mixer) return this.mixer;
4114
+ const listener = this.world?.getObjectManagementSystem()?.getOrCreateAudioListener();
4115
+ if (!listener) return null;
4116
+ this.mixer = new AudioMixer(listener);
4117
+ return this.mixer;
4118
+ }
4119
+ /** Set a bus's volume (`0` silent, `1` as mixed). */
4120
+ setBusVolume(bus, volume) {
4121
+ this.getMixer()?.setVolume(bus, volume);
4122
+ }
4123
+ getBusVolume(bus) {
4124
+ return this.getMixer()?.getVolume(bus) ?? 1;
4125
+ }
4126
+ /**
4127
+ * Fetch a `.soundbank.json` and decode its recordings. Its slots add to any
4128
+ * loaded before, and a slot id used twice takes the later one.
4129
+ * @param source an asset path, or a script's `AssetInput` field
4130
+ */
4131
+ async loadSoundBank(source) {
4132
+ if (this.isEditor) return;
4133
+ const url = typeof source === "string" ? source : source.path;
4134
+ const res = await fetch(resolvePrefabAssetUrl(url));
4135
+ if (!res.ok) throw new Error(`Couldn't load the sound bank ${url}.`);
4136
+ const bank = await res.json();
4137
+ assertSoundBank(bank);
4138
+ const context = this.getMixer()?.listener.context;
4139
+ if (!context) throw new Error("Audio isn't ready yet.");
4140
+ await Promise.all(
4141
+ soundBankFiles(bank).map(async (file) => {
4142
+ if (this.buffers.has(file)) return;
4143
+ const audio = await fetch(resolvePrefabAssetUrl(file));
4144
+ if (!audio.ok) throw new Error(`Couldn't load the sound ${file}.`);
4145
+ this.buffers.set(file, await context.decodeAudioData(await audio.arrayBuffer()));
4146
+ })
4147
+ );
4148
+ for (const [id, slot] of Object.entries(bank.slots)) this.slots.set(id, slot);
4149
+ }
4150
+ /**
4151
+ * Play a named sound from a loaded bank. Returns whether it sounded: a slot's
4152
+ * chance, cooldown and priority can each keep it quiet, and that's normal.
4153
+ */
4154
+ playSlot(id, options = {}) {
4155
+ const slot = this.slots.get(id);
4156
+ const mixer = this.getMixer();
4157
+ if (!slot || !mixer) return false;
4158
+ const context = mixer.listener.context;
4159
+ const bus = slot.bus ?? "sfx";
4160
+ const now = context.currentTime;
4161
+ const priority = slot.priority ?? 0;
4162
+ const speaking = this.voice && now < this.voice.until ? this.voice : null;
4163
+ if (bus === "voice" && speaking && priority < speaking.priority) return false;
4164
+ const take = this.chooser.choose(id, slot, now * 1e3);
4165
+ const buffer = take ? this.buffers.get(take.file) : void 0;
4166
+ if (!take || !buffer) return false;
4167
+ if (context.state === "suspended") void context.resume();
4168
+ const source = context.createBufferSource();
4169
+ source.buffer = buffer;
4170
+ const gain = context.createGain();
4171
+ gain.gain.value = (slot.volume ?? 1) * (take.volume ?? 1) * (options.volume ?? 1);
4172
+ source.connect(gain);
4173
+ let tail = gain;
4174
+ if (options.position) {
4175
+ const panner = context.createPanner();
4176
+ panner.positionX.value = options.position.x;
4177
+ panner.positionY.value = options.position.y;
4178
+ panner.positionZ.value = options.position.z;
4179
+ gain.connect(panner);
4180
+ tail = panner;
4181
+ }
4182
+ tail.connect(mixer.input(bus));
4183
+ source.onended = () => {
4184
+ source.disconnect();
4185
+ gain.disconnect();
4186
+ tail.disconnect();
4187
+ };
4188
+ if (bus === "voice") {
4189
+ speaking?.source.stop();
4190
+ this.voice = { priority, until: now + buffer.duration, source };
4191
+ mixer.duckMusic(buffer.duration);
4192
+ }
4193
+ source.start();
4194
+ return true;
4195
+ }
6092
4196
  update(world, _deltaTime) {
6093
4197
  if (this.isEditor) return;
4198
+ this.world = world;
6094
4199
  const entities = world.query(AudioComponent);
6095
4200
  const liveEntityIds = new Set(entities);
6096
4201
  for (const entityId of entities) {
@@ -6111,6 +4216,8 @@ var _AudioSystem = class _AudioSystem {
6111
4216
  }
6112
4217
  }
6113
4218
  this._syncOptions(entityId, component, sound);
4219
+ const bus = component.options.bus ?? "sfx";
4220
+ this.getMixer()?.route(sound, bus);
6114
4221
  const prevWasPlaying = this.prevPlayingState.get(entityId) ?? false;
6115
4222
  if (prevWasPlaying && !sound.isPlaying && component.isPlaying && !component.isPaused && !this.stoppedDueToInvisibility.has(entityId)) {
6116
4223
  component.isPlaying = false;
@@ -6118,6 +4225,9 @@ var _AudioSystem = class _AudioSystem {
6118
4225
  if (component.isPlaying && !sound.isPlaying) {
6119
4226
  if (sound.buffer) {
6120
4227
  sound.play();
4228
+ if (bus === "voice") {
4229
+ this.getMixer()?.duckMusic(sound.buffer.duration / (component.options.pitch || 1));
4230
+ }
6121
4231
  }
6122
4232
  } else if (!component.isPlaying && sound.isPlaying) {
6123
4233
  if (component.isPaused) {
@@ -6166,6 +4276,12 @@ var _AudioSystem = class _AudioSystem {
6166
4276
  this.prevPlayingState.clear();
6167
4277
  this.prevOptions.clear();
6168
4278
  this.stoppedDueToInvisibility.clear();
4279
+ this.voice?.source.stop();
4280
+ this.voice = null;
4281
+ this.slots.clear();
4282
+ this.buffers.clear();
4283
+ this.mixer?.dispose();
4284
+ this.mixer = null;
6169
4285
  }
6170
4286
  };
6171
4287
  __name(_AudioSystem, "AudioSystem");
@@ -6219,7 +4335,7 @@ var _VideoSystem = class _VideoSystem {
6219
4335
  });
6220
4336
  }
6221
4337
  }
6222
- this._syncOptions(entityId, component, video);
4338
+ this._syncOptions(world, entityId, component, video);
6223
4339
  const prev = this.prevIntent.get(entityId) ?? { isPlaying: false, isPaused: false };
6224
4340
  if (prev.isPlaying && video.paused && !video.seeking && component.isPlaying && !component.isPaused && !this.pausedDueToInvisibility.has(entityId)) {
6225
4341
  component.isPlaying = false;
@@ -6258,7 +4374,7 @@ var _VideoSystem = class _VideoSystem {
6258
4374
  }
6259
4375
  }
6260
4376
  /** Apply any changed options from the component to the video element and mesh. */
6261
- _syncOptions(entityId, component, video) {
4377
+ _syncOptions(world, entityId, component, video) {
6262
4378
  const opts = component.options;
6263
4379
  const prev = this.prevOptions.get(entityId);
6264
4380
  if (!prev || prev.loop !== opts.loop) {
@@ -6275,19 +4391,13 @@ var _VideoSystem = class _VideoSystem {
6275
4391
  component.object.scale.set(opts.width, opts.height, 1);
6276
4392
  }
6277
4393
  const material = component.material;
6278
- if (material instanceof THREE19.ShaderMaterial) {
6279
- if (!prev || prev.backgroundColor !== opts.backgroundColor) {
6280
- material.uniforms.keyColor.value.set(opts.backgroundColor);
6281
- }
6282
- if (!prev || prev.similarity !== opts.similarity) {
6283
- material.uniforms.similarity.value = opts.similarity;
6284
- }
6285
- if (!prev || prev.smoothness !== opts.smoothness) {
6286
- material.uniforms.smoothness.value = opts.smoothness;
6287
- }
6288
- if (!prev || prev.spill !== opts.spill) {
6289
- material.uniforms.spill.value = opts.spill;
6290
- }
4394
+ if (material && (!prev || prev.backgroundColor !== opts.backgroundColor || prev.similarity !== opts.similarity || prev.smoothness !== opts.smoothness || prev.spill !== opts.spill)) {
4395
+ readWorldRenderBackend(world).updateChromaKeyMaterial(material, {
4396
+ keyColor: opts.backgroundColor,
4397
+ similarity: opts.similarity,
4398
+ smoothness: opts.smoothness,
4399
+ spill: opts.spill
4400
+ });
6291
4401
  }
6292
4402
  if (opts.positional && component.audioNode instanceof THREE19.PositionalAudio) {
6293
4403
  const posAudio = component.audioNode;
@@ -6821,7 +4931,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
6821
4931
  record.twoHand = null;
6822
4932
  this.reanchor(world, input, record, interactable);
6823
4933
  this.grabs.set(input, record);
6824
- this.dispatch(world, entityId, (b) => b.onGrab(input));
4934
+ this.dispatch(world, entityId, "onGrab", (b) => b.onGrab(input));
6825
4935
  return;
6826
4936
  }
6827
4937
  if (holders.length >= 2) return;
@@ -6884,7 +4994,12 @@ var _XRInteractionSystem = class _XRInteractionSystem {
6884
4994
  primaryHandOffset: this.handQuaternion(primary.input).invert().multiply(quatFromEulerVec(transform.rotation)),
6885
4995
  initialQuaternion: quatFromEulerVec(transform.rotation)
6886
4996
  };
6887
- this.dispatch(world, entityId, (b) => b.onSecondaryGrab(secondary.input));
4997
+ this.dispatch(
4998
+ world,
4999
+ entityId,
5000
+ "onSecondaryGrab",
5001
+ (b) => b.onSecondaryGrab(secondary.input)
5002
+ );
6888
5003
  }
6889
5004
  /** World point → the object's UNSCALED local space. */
6890
5005
  objectLocalPoint(transform, worldPoint) {
@@ -7124,7 +5239,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7124
5239
  angularVelocity: new THREE19.Vector3()
7125
5240
  });
7126
5241
  if (role === "primary") {
7127
- this.dispatch(world, entityId, (b) => b.onGrab(input));
5242
+ this.dispatch(world, entityId, "onGrab", (b) => b.onGrab(input));
7128
5243
  }
7129
5244
  }
7130
5245
  /** Drive every held object, once each, after grabs and releases settle. */
@@ -7252,6 +5367,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7252
5367
  this.dispatch(
7253
5368
  world,
7254
5369
  held.entityId,
5370
+ "onSecondaryRelease",
7255
5371
  (b) => b.onSecondaryRelease(input)
7256
5372
  );
7257
5373
  } else {
@@ -7260,7 +5376,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7260
5376
  next.record.twoHand = null;
7261
5377
  next.record.restoreBodyType = held.restoreBodyType;
7262
5378
  this.reanchor(world, next.input, next.record, interactable);
7263
- this.dispatch(world, held.entityId, (b) => b.onRelease(input));
5379
+ this.dispatch(world, held.entityId, "onRelease", (b) => b.onRelease(input));
7264
5380
  }
7265
5381
  held.tracker.clear();
7266
5382
  return;
@@ -7279,7 +5395,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7279
5395
  }
7280
5396
  }
7281
5397
  held.tracker.clear();
7282
- this.dispatch(world, held.entityId, (b) => b.onRelease(input));
5398
+ this.dispatch(world, held.entityId, "onRelease", (b) => b.onRelease(input));
7283
5399
  }
7284
5400
  releaseAll(world) {
7285
5401
  for (const input of Array.from(this.grabs.keys())) {
@@ -7306,7 +5422,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7306
5422
  input.rayDirection.clone().normalize()
7307
5423
  );
7308
5424
  }
7309
- dispatch(world, entityId, visit) {
5425
+ dispatch(world, entityId, hook, visit) {
7310
5426
  const script = world.getComponent(entityId, ScriptComponent);
7311
5427
  const instances = script?.scriptInstances;
7312
5428
  if (!instances) return;
@@ -7315,7 +5431,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
7315
5431
  try {
7316
5432
  visit(instance);
7317
5433
  } catch (error) {
7318
- console.error("[XRInteractionSystem] Behaviour hook failed:", error);
5434
+ reportBehaviourHookError(world, entityId, instance, hook, error);
7319
5435
  }
7320
5436
  }
7321
5437
  }
@@ -7854,7 +5970,7 @@ var _XRSessionSystem = class _XRSessionSystem {
7854
5970
  this.inputSystem.attachSession(session);
7855
5971
  }
7856
5972
  this.applyTrackingOrigin(world, session ?? null);
7857
- this.forEachBehaviour(world, (behaviour) => {
5973
+ this.forEachBehaviour(world, "onXRSessionStart", (behaviour) => {
7858
5974
  behaviour.onXRSessionStart(this.capabilities);
7859
5975
  });
7860
5976
  }
@@ -7909,7 +6025,7 @@ var _XRSessionSystem = class _XRSessionSystem {
7909
6025
  this.session = null;
7910
6026
  this.capabilities = NO_XR_CAPABILITIES;
7911
6027
  this.inputSystem?.detachSession();
7912
- this.forEachBehaviour(world, (behaviour) => {
6028
+ this.forEachBehaviour(world, "onXRSessionEnd", (behaviour) => {
7913
6029
  behaviour.onXRSessionEnd();
7914
6030
  });
7915
6031
  }
@@ -7924,14 +6040,18 @@ var _XRSessionSystem = class _XRSessionSystem {
7924
6040
  if (!inputs || inputs.length === 0) return;
7925
6041
  for (const input of inputs) {
7926
6042
  if (input.selectStart) {
7927
- this.forEachBehaviour(world, (b) => b.onSelectStart(input));
6043
+ this.forEachBehaviour(
6044
+ world,
6045
+ "onSelectStart",
6046
+ (b) => b.onSelectStart(input)
6047
+ );
7928
6048
  }
7929
6049
  if (input.selectEnd) {
7930
- this.forEachBehaviour(world, (b) => b.onSelectEnd(input));
6050
+ this.forEachBehaviour(world, "onSelectEnd", (b) => b.onSelectEnd(input));
7931
6051
  }
7932
6052
  }
7933
6053
  }
7934
- forEachBehaviour(world, visit) {
6054
+ forEachBehaviour(world, hook, visit) {
7935
6055
  for (const entityId of world.query(ScriptComponent)) {
7936
6056
  const script = world.getComponent(entityId, ScriptComponent);
7937
6057
  const instances = script?.scriptInstances;
@@ -7941,7 +6061,7 @@ var _XRSessionSystem = class _XRSessionSystem {
7941
6061
  try {
7942
6062
  visit(instance);
7943
6063
  } catch (error) {
7944
- console.error("[XRSessionSystem] Behaviour hook failed:", error);
6064
+ reportBehaviourHookError(world, entityId, instance, hook, error);
7945
6065
  }
7946
6066
  }
7947
6067
  }
@@ -8758,12 +6878,13 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8758
6878
  this.dispatchBubbling(
8759
6879
  world,
8760
6880
  this.mouseHover,
6881
+ "onUiHoverEnd",
8761
6882
  (b, e) => b.onUiHoverEnd(e)
8762
6883
  );
8763
6884
  }
8764
6885
  if (hovered !== null) {
8765
6886
  this.setTint(world, hovered, HOVER_TINT);
8766
- this.dispatchBubbling(world, hovered, (b, e) => b.onUiHoverStart(e));
6887
+ this.dispatchBubbling(world, hovered, "onUiHoverStart", (b, e) => b.onUiHoverStart(e));
8767
6888
  }
8768
6889
  this.mouseHover = hovered;
8769
6890
  }
@@ -8829,7 +6950,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8829
6950
  }
8830
6951
  fire(world, press, input) {
8831
6952
  if (press.kind === "button") {
8832
- this.dispatchBubbling(world, press.entityId, (b, e) => b.onUiPress(e), input);
6953
+ this.dispatchBubbling(world, press.entityId, "onUiPress", (b, e) => b.onUiPress(e), input);
8833
6954
  return;
8834
6955
  }
8835
6956
  if (press.kind === "toggle") {
@@ -8839,6 +6960,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8839
6960
  this.dispatchBubbling(
8840
6961
  world,
8841
6962
  press.entityId,
6963
+ "onUiToggle",
8842
6964
  (b, e) => b.onUiToggle(e),
8843
6965
  input,
8844
6966
  toggle.value
@@ -8850,6 +6972,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8850
6972
  this.dispatchBubbling(
8851
6973
  world,
8852
6974
  press.entityId,
6975
+ "onUiChange",
8853
6976
  (b, e) => b.onUiChange(e),
8854
6977
  input,
8855
6978
  slider.value
@@ -8879,6 +7002,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8879
7002
  this.dispatchBubbling(
8880
7003
  world,
8881
7004
  entityId,
7005
+ "onUiChange",
8882
7006
  (b, e) => b.onUiChange(e),
8883
7007
  input,
8884
7008
  next
@@ -8911,7 +7035,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8911
7035
  * panel script hears its children. Bubbling (not a world broadcast) keeps
8912
7036
  * two spawned copies of the same menu prefab independent.
8913
7037
  */
8914
- dispatchBubbling(world, entityId, visit, input = null, value) {
7038
+ dispatchBubbling(world, entityId, hook, visit, input = null, value) {
8915
7039
  const event = {
8916
7040
  targetEntityId: entityId,
8917
7041
  actionId: this.actionIdOf(world, entityId),
@@ -8931,7 +7055,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
8931
7055
  try {
8932
7056
  visit(instance, event);
8933
7057
  } catch (error) {
8934
- console.error("[UIInteractionSystem] hook failed:", error);
7058
+ reportBehaviourHookError(world, current, instance, hook, error);
8935
7059
  }
8936
7060
  }
8937
7061
  }
@@ -9507,7 +7631,8 @@ var _UIPanelSystem = class _UIPanelSystem {
9507
7631
  texture.magFilter = THREE19.LinearFilter;
9508
7632
  texture.premultiplyAlpha = true;
9509
7633
  try {
9510
- const max = world.getRenderer().capabilities?.getMaxAnisotropy?.();
7634
+ const renderer = world.getRenderer();
7635
+ const max = renderer.capabilities?.getMaxAnisotropy?.() ?? renderer.getMaxAnisotropy?.();
9511
7636
  if (typeof max === "number") texture.anisotropy = Math.min(8, max);
9512
7637
  } catch {
9513
7638
  }
@@ -9821,6 +7946,171 @@ var _XRAnchorSystem = class _XRAnchorSystem {
9821
7946
  };
9822
7947
  __name(_XRAnchorSystem, "XRAnchorSystem");
9823
7948
  var XRAnchorSystem = _XRAnchorSystem;
7949
+ var _q = new THREE19.Quaternion();
7950
+ var _q2 = new THREE19.Quaternion();
7951
+ var _s = new THREE19.Vector3();
7952
+ var _e = new THREE19.Vector3();
7953
+ var _w = new THREE19.Vector3();
7954
+ var _v = new THREE19.Vector3();
7955
+ var _p = new THREE19.Vector3();
7956
+ function setWorldQuaternion(o, q) {
7957
+ if (o.parent) {
7958
+ o.parent.getWorldQuaternion(_q2).invert();
7959
+ o.quaternion.copy(_q2.multiply(q));
7960
+ } else {
7961
+ o.quaternion.copy(q);
7962
+ }
7963
+ o.updateMatrixWorld(true);
7964
+ }
7965
+ __name(setWorldQuaternion, "setWorldQuaternion");
7966
+ function turn(o, q) {
7967
+ o.getWorldQuaternion(_q);
7968
+ setWorldQuaternion(o, q.clone().multiply(_q));
7969
+ }
7970
+ __name(turn, "turn");
7971
+ function twoBoneIK(upper, lower, end, target, pole) {
7972
+ upper.getWorldPosition(_s);
7973
+ lower.getWorldPosition(_e);
7974
+ end.getWorldPosition(_w);
7975
+ const a = _s.distanceTo(_e);
7976
+ const b = _e.distanceTo(_w);
7977
+ if (a < 1e-6 || b < 1e-6) return target.distanceTo(_w);
7978
+ const toT = _v.subVectors(target, _s);
7979
+ const want = toT.length();
7980
+ const c = THREE19.MathUtils.clamp(want, Math.abs(a - b) + 1e-4, a + b - 1e-4);
7981
+ const dir = toT.divideScalar(want || 1).clone();
7982
+ const pv = _p.subVectors(pole, _s);
7983
+ pv.addScaledVector(dir, -pv.dot(dir));
7984
+ if (pv.lengthSq() < 1e-8) pv.set(0, -1, 0).addScaledVector(dir, dir.y);
7985
+ pv.normalize();
7986
+ const cosA = THREE19.MathUtils.clamp((a * a + c * c - b * b) / (2 * a * c), -1, 1);
7987
+ const joint = _s.clone().addScaledVector(dir, a * cosA).addScaledVector(pv, a * Math.sqrt(1 - cosA * cosA));
7988
+ turn(upper, new THREE19.Quaternion().setFromUnitVectors(_e.clone().sub(_s).normalize(), joint.clone().sub(_s).normalize()));
7989
+ lower.getWorldPosition(_e);
7990
+ end.getWorldPosition(_w);
7991
+ const reach = _s.clone().addScaledVector(dir, c);
7992
+ turn(lower, new THREE19.Quaternion().setFromUnitVectors(_w.clone().sub(_e).normalize(), reach.clone().sub(_e).normalize()));
7993
+ return want - c;
7994
+ }
7995
+ __name(twoBoneIK, "twoBoneIK");
7996
+ function gripQuaternion(axis, back, forearm, out = new THREE19.Quaternion()) {
7997
+ let X = axis.clone();
7998
+ const Z = back.clone().addScaledVector(X, -back.dot(X)).normalize().negate();
7999
+ let Y = new THREE19.Vector3().crossVectors(Z, X);
8000
+ if (Y.dot(forearm) < 0) {
8001
+ X = X.negate();
8002
+ Y = Y.negate();
8003
+ }
8004
+ return out.setFromRotationMatrix(new THREE19.Matrix4().makeBasis(X, Y, Z));
8005
+ }
8006
+ __name(gripQuaternion, "gripQuaternion");
8007
+
8008
+ // core/systems/IKSystem.ts
8009
+ var LIMB_BONES = {
8010
+ leftHand: ["leftUpperArm", "leftLowerArm", "leftHand"],
8011
+ rightHand: ["rightUpperArm", "rightLowerArm", "rightHand"],
8012
+ leftFoot: ["leftUpperLeg", "leftLowerLeg", "leftFoot"],
8013
+ rightFoot: ["rightUpperLeg", "rightLowerLeg", "rightFoot"]
8014
+ };
8015
+ var _t = new THREE19.Vector3();
8016
+ var _p2 = new THREE19.Vector3();
8017
+ var _q3 = new THREE19.Quaternion();
8018
+ var _pq = new THREE19.Quaternion();
8019
+ var _IKSystem = class _IKSystem {
8020
+ constructor() {
8021
+ this.runtimes = /* @__PURE__ */ new Map();
8022
+ this.reach = /* @__PURE__ */ new Map();
8023
+ }
8024
+ /** How far a limb's end stayed from its target last frame, in metres (0 = reached). */
8025
+ getReachError(entityId, limb) {
8026
+ return this.reach.get(`${entityId}:${limb}`) ?? null;
8027
+ }
8028
+ restore(runtime) {
8029
+ for (const [bone, w] of runtime.written) {
8030
+ if (bone.quaternion.equals(w.written)) bone.quaternion.copy(w.before);
8031
+ }
8032
+ runtime.written.clear();
8033
+ }
8034
+ update(world) {
8035
+ const ids = world.query(IKComponent, ModelComponent);
8036
+ const alive = new Set(ids);
8037
+ for (const [id, runtime] of this.runtimes) {
8038
+ if (!alive.has(id)) {
8039
+ this.restore(runtime);
8040
+ this.runtimes.delete(id);
8041
+ }
8042
+ }
8043
+ if (ids.length === 0) return;
8044
+ let matricesFresh = false;
8045
+ for (const id of ids) {
8046
+ const ik = world.getComponent(id, IKComponent);
8047
+ const root = world.getComponent(id, ModelComponent)?.object;
8048
+ if (!ik || !root) continue;
8049
+ let runtime = this.runtimes.get(id);
8050
+ if (!runtime || runtime.rootUuid !== root.uuid) {
8051
+ if (runtime) this.restore(runtime);
8052
+ runtime = { rootUuid: root.uuid, limbs: /* @__PURE__ */ new Map(), written: /* @__PURE__ */ new Map() };
8053
+ this.runtimes.set(id, runtime);
8054
+ }
8055
+ for (const [bone, w] of runtime.written) {
8056
+ if (bone.quaternion.equals(w.written)) bone.quaternion.copy(w.before);
8057
+ }
8058
+ runtime.written.clear();
8059
+ if (!ik.enabled) continue;
8060
+ const active = ik.chains.filter((c) => c.enabled && c.weight > 0 && typeof c.target === "number");
8061
+ if (active.length === 0) continue;
8062
+ if (!matricesFresh) {
8063
+ world.getScene().updateMatrixWorld(true);
8064
+ matricesFresh = true;
8065
+ }
8066
+ for (const chain of active) {
8067
+ let limb = runtime.limbs.get(chain.limb);
8068
+ if (!limb) {
8069
+ const names = LIMB_BONES[chain.limb];
8070
+ const found = names.map((h) => findModelBone(root, { humanoid: h }));
8071
+ limb = { bones: found.every(Boolean) ? found : null };
8072
+ runtime.limbs.set(chain.limb, limb);
8073
+ }
8074
+ const target = world.getComponent(chain.target, Object3DRef)?.object;
8075
+ if (!limb.bones || !target) continue;
8076
+ const [upper, lower, end] = limb.bones;
8077
+ for (const bone of [upper, lower, end]) {
8078
+ if (!runtime.written.has(bone)) {
8079
+ runtime.written.set(bone, { before: bone.quaternion.clone(), written: new THREE19.Quaternion() });
8080
+ }
8081
+ }
8082
+ const animated = [upper.quaternion.clone(), lower.quaternion.clone(), end.quaternion.clone()];
8083
+ target.getWorldPosition(_t);
8084
+ const poleObject = typeof chain.pole === "number" ? world.getComponent(chain.pole, Object3DRef)?.object : null;
8085
+ if (poleObject) poleObject.getWorldPosition(_p2);
8086
+ else lower.getWorldPosition(_p2);
8087
+ const miss = twoBoneIK(upper, lower, end, _t, _p2);
8088
+ this.reach.set(`${id}:${chain.limb}`, Math.max(0, miss));
8089
+ if (chain.matchRotation) {
8090
+ target.getWorldQuaternion(_q3);
8091
+ end.parent?.getWorldQuaternion(_pq);
8092
+ end.quaternion.copy(_pq.invert().multiply(_q3));
8093
+ }
8094
+ if (chain.weight < 1) {
8095
+ [upper, lower, end].forEach((bone, i) => {
8096
+ bone.quaternion.copy(animated[i].slerp(bone.quaternion, chain.weight));
8097
+ });
8098
+ }
8099
+ for (const bone of [upper, lower, end]) {
8100
+ bone.updateMatrixWorld(true);
8101
+ const w = runtime.written.get(bone);
8102
+ if (w) w.written.copy(bone.quaternion);
8103
+ }
8104
+ }
8105
+ }
8106
+ }
8107
+ dispose() {
8108
+ for (const runtime of this.runtimes.values()) this.restore(runtime);
8109
+ this.runtimes.clear();
8110
+ }
8111
+ };
8112
+ __name(_IKSystem, "IKSystem");
8113
+ var IKSystem = _IKSystem;
9824
8114
  function buildGeometry2(area) {
9825
8115
  if (area.shape === "circle") {
9826
8116
  const radius = Math.max(5e-3, area.width / 2);
@@ -10887,7 +9177,13 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
10887
9177
  if (event === "start") instance.onHandPoseStart(detail);
10888
9178
  else instance.onHandPoseEnd(detail);
10889
9179
  } catch (error) {
10890
- console.error("[XRHandPoseSystem] Behaviour hook failed:", error);
9180
+ reportBehaviourHookError(
9181
+ world,
9182
+ entityId,
9183
+ instance,
9184
+ event === "start" ? "onHandPoseStart" : "onHandPoseEnd",
9185
+ error
9186
+ );
10891
9187
  }
10892
9188
  }
10893
9189
  }
@@ -10977,15 +9273,15 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
10977
9273
  object.traverse((child) => {
10978
9274
  if (!(child instanceof THREE19.Mesh) || !child.geometry) return;
10979
9275
  if (child.userData[XR_HOVER_SHELL_FLAG]) return;
10980
- const clone2 = new THREE19.Mesh(child.geometry, material);
9276
+ const clone = new THREE19.Mesh(child.geometry, material);
10981
9277
  child.updateWorldMatrix(true, false);
10982
9278
  object.updateWorldMatrix(true, false);
10983
- clone2.matrixAutoUpdate = false;
10984
- clone2.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE19.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
10985
- clone2.userData[XR_HOVER_SHELL_FLAG] = true;
10986
- clone2.raycast = () => {
9279
+ clone.matrixAutoUpdate = false;
9280
+ clone.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE19.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
9281
+ clone.userData[XR_HOVER_SHELL_FLAG] = true;
9282
+ clone.raycast = () => {
10987
9283
  };
10988
- group.add(clone2);
9284
+ group.add(clone);
10989
9285
  });
10990
9286
  if (group.children.length === 0) {
10991
9287
  material.dispose();
@@ -11055,6 +9351,6 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
11055
9351
  __name(_XRHoverVisualSystem, "XRHoverVisualSystem");
11056
9352
  var XRHoverVisualSystem = _XRHoverVisualSystem;
11057
9353
 
11058
- export { ADDED_NODE_MARKER_COMPONENT, ARC_GRAVITY, ARC_MAX_TIME, ARC_SPEED, ARC_STEPS, AnimationSystem, AudioSystem, Behaviour, CURLED_COS, CameraSyncSystem, DEFAULT_DIRECTION_TOLERANCE, DEFAULT_ENTER_CONFIDENCE, DEFAULT_EXIT_CONFIDENCE, DEFAULT_LOST_GRACE_SECONDS, DEFAULT_MIN_HOLD_SECONDS, EnvironmentSyncSystem, FALLBACK_HAND_SCALE, HYDRATABLE_ENTITY_KINDS, HandPoseTracker, InputSystem, InstancedMeshSystem, KeyboardKeys, LightSyncSystem, MATCH_FALLOFF, MAX_PLAUSIBLE_SPEED, MAX_SAMPLE_AGE_SECONDS, MaterialSyncSystem, MeshGeometrySystem, NODE_KEY_SEPARATOR, NO_XR_CAPABILITIES, OVERRIDE_DIFF_EXCLUDED_COMPONENTS, OVERRIDE_ROOT_EXCLUDED_COMPONENTS, ObjectVisibilitySystem, PINCH_FINGERS, PREFAB_FILE_VERSION, PREFAB_FILE_VERSION_NESTED, PhysicsSyncSystem, PostprocessingSyncSystem, RenderSystem, SCRIPT_OBJECT_ROOT_FLAG, STRAIGHT_COS, ScriptBehaviourSystem, ScriptObject, ScriptObjectSystem, TeleportAimGate, ThrowVelocityTracker, TransformSyncSystem, UIInteractionSystem, UIPanelSystem, VELOCITY_SAMPLES, VRTeleportAreaSystem, VideoSystem, XRAnchorSystem, XRControllerVisualSystem, XREnvironmentSystem, XRHandPoseSystem, XRHitTestSystem, XRHoverVisualSystem, XRInputSystem, XRInteractionSystem, XRLocomotionSystem, XRSessionSystem, XRTeleportVisualSystem, XR_FINGERS, XR_HOVER_SHELL_FLAG, angularVelocityFrom, applyHandMaterial, arcGroundCrossing, buildComponentValueMaps, buildNodeKeyMapsFromSnapshot, buildNodeMapsFromSnapshot, canonicalizeComponentValue, clearPrefabCache, collectDiffPaths, collectEntitySubtree, collectSnapshotSubtreeIds, computeFingerCurl, computeHandShape, computeInstanceOverrides, computePalmPose, computePalmWidth, computePinch, computeSpread, createHandMaterial, decanonicalizeComponentValue, despawnPrefab, encodeOverride, estimateHandScale, findSnapshotRootEntityIds, findTopLevelNestedRegions, groupOverridePaths, hydrateWorldFromSnapshot, hydrateWorldFromSnapshotDetailed, indexSnapshotByEntity, instantiateSubtree, loadPrefab, matchHandPose, mergeComponentValue, parseOverride, parsePrefabAsset, prefabFileVersionFor, readXrCapabilities, restoreComponentState, restorePostCreateComponents, sampleTeleportArc, setPrefabAssetUrlResolver, spawnPrefab };
11059
- //# sourceMappingURL=chunk-DF5SSRN6.js.map
11060
- //# sourceMappingURL=chunk-DF5SSRN6.js.map
9354
+ export { ARC_GRAVITY, ARC_MAX_TIME, ARC_SPEED, ARC_STEPS, AnimationSystem, AudioMixer, AudioSystem, CURLED_COS, CameraSyncSystem, DEFAULT_DIRECTION_TOLERANCE, DEFAULT_ENTER_CONFIDENCE, DEFAULT_EXIT_CONFIDENCE, DEFAULT_LOST_GRACE_SECONDS, DEFAULT_MIN_HOLD_SECONDS, DEFAULT_SCRIPT_ERROR_RETRY_DELAY, EnvironmentSyncSystem, FALLBACK_HAND_SCALE, HandPoseTracker, IKSystem, InputSystem, InstancedMeshSystem, KeyboardKeys, LightSyncSystem, MATCH_FALLOFF, MAX_PLAUSIBLE_SPEED, MAX_SAMPLE_AGE_SECONDS, MaterialSyncSystem, MeshGeometrySystem, NO_XR_CAPABILITIES, ObjectVisibilitySystem, PINCH_FINGERS, PhysicsSyncSystem, PostprocessingSyncSystem, RenderSystem, SCRIPT_OBJECT_ROOT_FLAG, STRAIGHT_COS, ScriptBehaviourSystem, ScriptObject, ScriptObjectSystem, SlotChooser, TeleportAimGate, ThrowVelocityTracker, TransformSyncSystem, UIInteractionSystem, UIPanelSystem, VELOCITY_SAMPLES, VRTeleportAreaSystem, VideoSystem, XRAnchorSystem, XRControllerVisualSystem, XREnvironmentSystem, XRHandPoseSystem, XRHitTestSystem, XRHoverVisualSystem, XRInputSystem, XRInteractionSystem, XRLocomotionSystem, XRSessionSystem, XRTeleportVisualSystem, XR_FINGERS, XR_HOVER_SHELL_FLAG, angularVelocityFrom, applyHandMaterial, arcGroundCrossing, computeFingerCurl, computeHandShape, computePalmPose, computePalmWidth, computePinch, computeSpread, createHandMaterial, estimateHandScale, gripQuaternion, matchHandPose, readXrCapabilities, sampleTeleportArc, setWorldQuaternion, soundBankFiles, twoBoneIK };
9355
+ //# sourceMappingURL=chunk-JIJQFW6P.js.map
9356
+ //# sourceMappingURL=chunk-JIJQFW6P.js.map