@needle-tools/engine 5.1.0-canary.deec6e4 → 5.1.0-canary.e7c2511

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 (243) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{needle-engine.bundle-CvtELXh0.js → needle-engine.bundle-D-eWNCu1.js} +15969 -15550
  4. package/dist/needle-engine.bundle-D3ZUII8o.min.js +1733 -0
  5. package/dist/needle-engine.bundle-_rOpvUGL.umd.cjs +1733 -0
  6. package/dist/needle-engine.d.ts +746 -156
  7. package/dist/needle-engine.js +529 -529
  8. package/dist/needle-engine.min.js +1 -1
  9. package/dist/needle-engine.umd.cjs +1 -1
  10. package/lib/engine/api.d.ts +5 -0
  11. package/lib/engine/api.js +4 -0
  12. package/lib/engine/api.js.map +1 -1
  13. package/lib/engine/codegen/register_types.js +2 -10
  14. package/lib/engine/codegen/register_types.js.map +1 -1
  15. package/lib/engine/engine_audio.d.ts +68 -0
  16. package/lib/engine/engine_audio.js +172 -0
  17. package/lib/engine/engine_audio.js.map +1 -1
  18. package/lib/engine/engine_components.js +1 -1
  19. package/lib/engine/engine_components.js.map +1 -1
  20. package/lib/engine/engine_context.d.ts +1 -1
  21. package/lib/engine/engine_context.js +2 -2
  22. package/lib/engine/engine_context.js.map +1 -1
  23. package/lib/engine/engine_disposable.d.ts +171 -0
  24. package/lib/engine/engine_disposable.js +136 -0
  25. package/lib/engine/engine_disposable.js.map +1 -0
  26. package/lib/engine/engine_gameobject.d.ts +1 -10
  27. package/lib/engine/engine_gameobject.js +22 -120
  28. package/lib/engine/engine_gameobject.js.map +1 -1
  29. package/lib/engine/engine_gltf_builtin_components.js +7 -69
  30. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  31. package/lib/engine/engine_init.js +6 -6
  32. package/lib/engine/engine_init.js.map +1 -1
  33. package/lib/engine/engine_input.d.ts +1 -1
  34. package/lib/engine/engine_input.js +1 -1
  35. package/lib/engine/engine_input.js.map +1 -1
  36. package/lib/engine/engine_instantiate_resolve.d.ts +42 -0
  37. package/lib/engine/engine_instantiate_resolve.js +372 -0
  38. package/lib/engine/engine_instantiate_resolve.js.map +1 -0
  39. package/lib/engine/engine_license.js +1 -1
  40. package/lib/engine/engine_license.js.map +1 -1
  41. package/lib/engine/engine_mainloop_utils.js +5 -2
  42. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  43. package/lib/engine/engine_networking.js +3 -1
  44. package/lib/engine/engine_networking.js.map +1 -1
  45. package/lib/engine/engine_networking_blob.js +1 -1
  46. package/lib/engine/engine_networking_blob.js.map +1 -1
  47. package/lib/engine/engine_physics_rapier.d.ts +11 -3
  48. package/lib/engine/engine_physics_rapier.js +88 -25
  49. package/lib/engine/engine_physics_rapier.js.map +1 -1
  50. package/lib/engine/engine_scenedata.js +2 -2
  51. package/lib/engine/engine_scenedata.js.map +1 -1
  52. package/lib/engine/engine_serialization_builtin_serializer.js +28 -5
  53. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  54. package/lib/engine/engine_serialization_core.d.ts +1 -0
  55. package/lib/engine/engine_serialization_core.js +7 -0
  56. package/lib/engine/engine_serialization_core.js.map +1 -1
  57. package/lib/engine/engine_types.d.ts +17 -9
  58. package/lib/engine/engine_types.js +1 -1
  59. package/lib/engine/engine_types.js.map +1 -1
  60. package/lib/engine/engine_util_decorator.js +7 -2
  61. package/lib/engine/engine_util_decorator.js.map +1 -1
  62. package/lib/engine/engine_utils.d.ts +1 -1
  63. package/lib/engine/engine_utils.js +19 -5
  64. package/lib/engine/engine_utils.js.map +1 -1
  65. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  66. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  67. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -1
  68. package/lib/engine/webcomponents/needle menu/needle-menu.js +1 -1
  69. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  70. package/lib/engine/webcomponents/needle-engine.d.ts +10 -4
  71. package/lib/engine/webcomponents/needle-engine.js +1 -1
  72. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  73. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  74. package/lib/engine/xr/NeedleXRSession.js +50 -14
  75. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  76. package/lib/engine/xr/events.d.ts +1 -1
  77. package/lib/engine/xr/events.js.map +1 -1
  78. package/lib/engine-components/Animation.js +17 -16
  79. package/lib/engine-components/Animation.js.map +1 -1
  80. package/lib/engine-components/Animator.d.ts +6 -0
  81. package/lib/engine-components/Animator.js +17 -12
  82. package/lib/engine-components/Animator.js.map +1 -1
  83. package/lib/engine-components/AnimatorController.builder.d.ts +113 -0
  84. package/lib/engine-components/AnimatorController.builder.js +195 -0
  85. package/lib/engine-components/AnimatorController.builder.js.map +1 -0
  86. package/lib/engine-components/AnimatorController.d.ts +2 -119
  87. package/lib/engine-components/AnimatorController.js +31 -232
  88. package/lib/engine-components/AnimatorController.js.map +1 -1
  89. package/lib/engine-components/AudioSource.d.ts +19 -3
  90. package/lib/engine-components/AudioSource.js +121 -68
  91. package/lib/engine-components/AudioSource.js.map +1 -1
  92. package/lib/engine-components/Collider.d.ts +18 -9
  93. package/lib/engine-components/Collider.js +61 -14
  94. package/lib/engine-components/Collider.js.map +1 -1
  95. package/lib/engine-components/Component.d.ts +58 -6
  96. package/lib/engine-components/Component.js +77 -0
  97. package/lib/engine-components/Component.js.map +1 -1
  98. package/lib/engine-components/DragControls.d.ts +7 -0
  99. package/lib/engine-components/DragControls.js +19 -7
  100. package/lib/engine-components/DragControls.js.map +1 -1
  101. package/lib/engine-components/EventList.d.ts +31 -9
  102. package/lib/engine-components/EventList.js +37 -76
  103. package/lib/engine-components/EventList.js.map +1 -1
  104. package/lib/engine-components/Joints.d.ts +4 -2
  105. package/lib/engine-components/Joints.js +19 -3
  106. package/lib/engine-components/Joints.js.map +1 -1
  107. package/lib/engine-components/Light.js +9 -1
  108. package/lib/engine-components/Light.js.map +1 -1
  109. package/lib/engine-components/Networking.d.ts +1 -1
  110. package/lib/engine-components/Networking.js +1 -1
  111. package/lib/engine-components/OrbitControls.js +16 -11
  112. package/lib/engine-components/OrbitControls.js.map +1 -1
  113. package/lib/engine-components/RigidBody.d.ts +12 -4
  114. package/lib/engine-components/RigidBody.js +18 -4
  115. package/lib/engine-components/RigidBody.js.map +1 -1
  116. package/lib/engine-components/SeeThrough.js +2 -2
  117. package/lib/engine-components/SeeThrough.js.map +1 -1
  118. package/lib/engine-components/api.d.ts +1 -1
  119. package/lib/engine-components/api.js +1 -1
  120. package/lib/engine-components/api.js.map +1 -1
  121. package/lib/engine-components/codegen/components.d.ts +3 -9
  122. package/lib/engine-components/codegen/components.js +3 -9
  123. package/lib/engine-components/codegen/components.js.map +1 -1
  124. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  125. package/lib/engine-components/timeline/PlayableDirector.d.ts +16 -6
  126. package/lib/engine-components/timeline/PlayableDirector.js +63 -61
  127. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  128. package/lib/engine-components/timeline/SignalAsset.d.ts +3 -1
  129. package/lib/engine-components/timeline/SignalAsset.js +1 -0
  130. package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
  131. package/lib/engine-components/timeline/TimelineBuilder.d.ts +247 -0
  132. package/lib/engine-components/timeline/TimelineBuilder.js +400 -0
  133. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -0
  134. package/lib/engine-components/timeline/TimelineModels.d.ts +2 -1
  135. package/lib/engine-components/timeline/TimelineModels.js +3 -0
  136. package/lib/engine-components/timeline/TimelineModels.js.map +1 -1
  137. package/lib/engine-components/timeline/TimelineTracks.d.ts +23 -0
  138. package/lib/engine-components/timeline/TimelineTracks.js +71 -13
  139. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  140. package/lib/engine-components/timeline/index.d.ts +2 -1
  141. package/lib/engine-components/timeline/index.js +2 -0
  142. package/lib/engine-components/timeline/index.js.map +1 -1
  143. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  144. package/lib/engine-components/ui/Canvas.js +2 -8
  145. package/lib/engine-components/ui/Canvas.js.map +1 -1
  146. package/lib/engine-components/ui/Text.d.ts +1 -0
  147. package/lib/engine-components/ui/Text.js +10 -7
  148. package/lib/engine-components/ui/Text.js.map +1 -1
  149. package/lib/engine-components/web/CursorFollow.js +21 -12
  150. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  151. package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
  152. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  153. package/package.json +2 -83
  154. package/plugins/common/worker.js +9 -4
  155. package/plugins/vite/asap.js +17 -8
  156. package/plugins/vite/dependencies.js +29 -0
  157. package/plugins/vite/dependency-watcher.js +2 -2
  158. package/plugins/vite/editor-connection.js +3 -3
  159. package/plugins/vite/local-files-core.js +3 -3
  160. package/plugins/vite/local-files-utils.d.ts +3 -1
  161. package/plugins/vite/local-files-utils.js +29 -5
  162. package/plugins/vite/reload.js +1 -1
  163. package/plugins/vite/server.js +2 -1
  164. package/src/engine/api.ts +7 -0
  165. package/src/engine/codegen/register_types.ts +2 -10
  166. package/src/engine/engine_audio.ts +184 -0
  167. package/src/engine/engine_components.ts +1 -1
  168. package/src/engine/engine_context.ts +3 -3
  169. package/src/engine/engine_disposable.ts +213 -0
  170. package/src/engine/engine_gameobject.ts +54 -159
  171. package/src/engine/engine_gltf_builtin_components.ts +7 -76
  172. package/src/engine/engine_init.ts +6 -6
  173. package/src/engine/engine_input.ts +1 -1
  174. package/src/engine/engine_instantiate_resolve.ts +407 -0
  175. package/src/engine/engine_license.ts +1 -1
  176. package/src/engine/engine_mainloop_utils.ts +5 -2
  177. package/src/engine/engine_networking.ts +3 -1
  178. package/src/engine/engine_networking_blob.ts +1 -1
  179. package/src/engine/engine_physics_rapier.ts +82 -27
  180. package/src/engine/engine_scenedata.ts +3 -3
  181. package/src/engine/engine_serialization_builtin_serializer.ts +32 -9
  182. package/src/engine/engine_serialization_core.ts +9 -0
  183. package/src/engine/engine_types.ts +22 -13
  184. package/src/engine/engine_util_decorator.ts +7 -2
  185. package/src/engine/engine_utils.ts +16 -5
  186. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  187. package/src/engine/webcomponents/needle menu/needle-menu.ts +1 -1
  188. package/src/engine/webcomponents/needle-engine.ts +10 -4
  189. package/src/engine/xr/NeedleXRSession.ts +48 -13
  190. package/src/engine/xr/events.ts +1 -1
  191. package/src/engine-components/Animation.ts +19 -16
  192. package/src/engine-components/Animator.ts +18 -11
  193. package/src/engine-components/AnimatorController.builder.ts +261 -0
  194. package/src/engine-components/AnimatorController.ts +19 -291
  195. package/src/engine-components/AudioSource.ts +130 -79
  196. package/src/engine-components/Collider.ts +66 -18
  197. package/src/engine-components/Component.ts +79 -9
  198. package/src/engine-components/DragControls.ts +18 -11
  199. package/src/engine-components/EventList.ts +45 -83
  200. package/src/engine-components/Joints.ts +20 -4
  201. package/src/engine-components/Light.ts +10 -2
  202. package/src/engine-components/Networking.ts +1 -1
  203. package/src/engine-components/OrbitControls.ts +18 -9
  204. package/src/engine-components/RigidBody.ts +18 -4
  205. package/src/engine-components/SeeThrough.ts +2 -2
  206. package/src/engine-components/api.ts +1 -1
  207. package/src/engine-components/codegen/components.ts +3 -9
  208. package/src/engine-components/timeline/PlayableDirector.ts +61 -64
  209. package/src/engine-components/timeline/SignalAsset.ts +4 -1
  210. package/src/engine-components/timeline/TimelineBuilder.ts +565 -0
  211. package/src/engine-components/timeline/TimelineModels.ts +5 -1
  212. package/src/engine-components/timeline/TimelineTracks.ts +74 -13
  213. package/src/engine-components/timeline/index.ts +2 -1
  214. package/src/engine-components/ui/Canvas.ts +2 -8
  215. package/src/engine-components/ui/Text.ts +12 -8
  216. package/src/engine-components/web/CursorFollow.ts +21 -13
  217. package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
  218. package/dist/needle-engine.bundle-1s2gOoKZ.min.js +0 -1732
  219. package/dist/needle-engine.bundle-j4nGJXCs.umd.cjs +0 -1732
  220. package/lib/engine-components/AvatarLoader.d.ts +0 -80
  221. package/lib/engine-components/AvatarLoader.js +0 -232
  222. package/lib/engine-components/AvatarLoader.js.map +0 -1
  223. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +0 -11
  224. package/lib/engine-components/avatar/AvatarBlink_Simple.js +0 -77
  225. package/lib/engine-components/avatar/AvatarBlink_Simple.js.map +0 -1
  226. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +0 -14
  227. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +0 -69
  228. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js.map +0 -1
  229. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +0 -29
  230. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +0 -122
  231. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js.map +0 -1
  232. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +0 -15
  233. package/lib/engine-components/avatar/Avatar_MouthShapes.js +0 -80
  234. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +0 -1
  235. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +0 -9
  236. package/lib/engine-components/avatar/Avatar_MustacheShake.js +0 -30
  237. package/lib/engine-components/avatar/Avatar_MustacheShake.js.map +0 -1
  238. package/src/engine-components/AvatarLoader.ts +0 -264
  239. package/src/engine-components/avatar/AvatarBlink_Simple.ts +0 -70
  240. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +0 -64
  241. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +0 -140
  242. package/src/engine-components/avatar/Avatar_MouthShapes.ts +0 -84
  243. package/src/engine-components/avatar/Avatar_MustacheShake.ts +0 -32
@@ -4,6 +4,7 @@ import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../eng
4
4
  import { Behaviour, Component, GameObject } from "../engine-components/Component.js";
5
5
  import { CallInfo, EventList } from "../engine-components/EventList.js";
6
6
  import { AssetReference } from "./engine_addressables.js";
7
+ import { AudioClip } from "./engine_audio.js";
7
8
  import { debugExtension } from "./engine_default_parameters.js";
8
9
  import { SerializationContext, TypeSerializer } from "./engine_serialization_core.js";
9
10
  import { RenderTexture } from "./engine_texture.js";
@@ -11,7 +12,7 @@ import { IComponent } from "./engine_types.js";
11
12
  import { resolveUrl } from "./engine_utils.js";
12
13
  import { RGBAColor } from "./js-extensions/index.js";
13
14
 
14
-
15
+ // #region Color
15
16
  class ColorSerializer extends TypeSerializer {
16
17
  constructor() {
17
18
  super([Color, RGBAColor], "ColorSerializer")
@@ -35,6 +36,8 @@ class ColorSerializer extends TypeSerializer {
35
36
  }
36
37
  }
37
38
 
39
+ // #region Euler
40
+
38
41
  class EulerSerializer extends TypeSerializer {
39
42
  constructor() {
40
43
  super([Euler], "EulerSerializer");
@@ -58,6 +61,8 @@ declare type ObjectData = {
58
61
  node?: number;
59
62
  guid?: string;
60
63
  }
64
+
65
+ // #region ObjectSerializer
61
66
  class ObjectSerializer extends TypeSerializer {
62
67
  constructor() {
63
68
  super(Object3D, "ObjectSerializer");
@@ -119,7 +124,7 @@ class ObjectSerializer extends TypeSerializer {
119
124
  }
120
125
  if (!res) {
121
126
  if (isDevEnvironment() || debugExtension)
122
- console.warn("Could not resolve object reference", context.path, data, context.target, context.context.scene);
127
+ console.warn(`Could not resolve object reference \"${context.path}\" (guid: ${data.guid}). The referenced object may have been deleted — check if the reference is still valid in your scene.`);
123
128
  data["could_not_resolve"] = true;
124
129
  }
125
130
  else {
@@ -137,7 +142,7 @@ class ObjectSerializer extends TypeSerializer {
137
142
  }
138
143
  }
139
144
 
140
-
145
+ // #region ComponentSerializer
141
146
  class ComponentSerializer extends TypeSerializer {
142
147
 
143
148
  constructor() {
@@ -226,6 +231,7 @@ declare type EventListCall = {
226
231
 
227
232
  const $eventListDebugInfo = Symbol("eventListDebugInfo");
228
233
 
234
+ // #region EventListSerializer
229
235
  class EventListSerializer extends TypeSerializer {
230
236
  constructor() {
231
237
  super([EventList]);
@@ -328,11 +334,6 @@ class EventListSerializer extends TypeSerializer {
328
334
  if (debugExtension)
329
335
  console.log(evt);
330
336
 
331
- const eventListOwner = context.target;
332
- if (eventListOwner !== undefined && context.path !== undefined) {
333
- evt.setEventTarget(context.path, eventListOwner);
334
- }
335
-
336
337
  return evt;
337
338
  }
338
339
  return undefined;
@@ -371,6 +372,7 @@ class EventListSerializer extends TypeSerializer {
371
372
  */
372
373
  const cloneOriginalMap = new WeakMap<Texture, Texture>();
373
374
 
375
+ // #region RenderTextureSerializer
374
376
  export class RenderTextureSerializer extends TypeSerializer {
375
377
  constructor() {
376
378
  super([RenderTexture, WebGLRenderTarget]);
@@ -412,7 +414,7 @@ export class RenderTextureSerializer extends TypeSerializer {
412
414
  }
413
415
  }
414
416
 
415
-
417
+ // #region UriSerializer
416
418
  export class UriSerializer extends TypeSerializer {
417
419
  constructor() {
418
420
  super([URL]);
@@ -430,7 +432,27 @@ export class UriSerializer extends TypeSerializer {
430
432
  }
431
433
  }
432
434
 
435
+ // #region AudioClipSerializer
436
+ class AudioClipSerializer extends TypeSerializer {
437
+ constructor() {
438
+ super([AudioClip]);
439
+ }
440
+
441
+ onSerialize(_data: AudioClip, _context: SerializationContext) {
442
+ return null;
443
+ }
444
+
445
+ onDeserialize(data: string, context: SerializationContext) {
446
+ if (typeof data === "string" && data.length > 0) {
447
+ const url = resolveUrl(context.gltfId, data);
448
+ if (url) return new AudioClip(url);
449
+ }
450
+ return undefined;
451
+ }
452
+ }
453
+
433
454
 
455
+ // #region Init serializer
434
456
  // Module-level references used by EventListSerializer internally
435
457
  export let colorSerializer: ColorSerializer;
436
458
  export let objectSerializer: ObjectSerializer;
@@ -460,4 +482,5 @@ export function initBuiltinSerializers() {
460
482
  eventListSerializer = new EventListSerializer();
461
483
  new RenderTextureSerializer();
462
484
  new UriSerializer();
485
+ new AudioClipSerializer();
463
486
  }
@@ -207,6 +207,15 @@ export interface ISerializable {
207
207
  onAfterDeserialize?(data: any, context: SerializationContext): void;
208
208
  };
209
209
 
210
+ export function isSerializable(obj: any): obj is ISerializable {
211
+ return obj && typeof obj === "object" && (
212
+ typeof obj.$serializedTypes === "object" ||
213
+ typeof obj.onBeforeDeserialize === "function" ||
214
+ typeof obj.onBeforeDeserializeMember === "function" ||
215
+ typeof obj.onAfterDeserializeMember === "function" ||
216
+ typeof obj.onAfterDeserialize === "function"
217
+ )
218
+ }
210
219
 
211
220
  export function serializeObject(obj: ISerializable, context: SerializationContext): object | null {
212
221
  const types = obj.$serializedTypes;
@@ -5,7 +5,6 @@ import { type GLTF as THREE_GLTF } from "three/examples/jsm/loaders/GLTFLoader.j
5
5
 
6
6
  import type { Camera as CameraComponent } from "../engine-components/api.js";
7
7
  import type { Context } from "./engine_context.js";
8
- import { InstantiateContext } from "./engine_gameobject.js";
9
8
  import { CollisionDetectionMode, type PhysicsMaterial, RigidbodyConstraints } from "./engine_physics.types.js";
10
9
  import { CircularBuffer } from "./engine_utils.js";
11
10
  import type { NeedleXRSession } from "./engine_xr.js";
@@ -201,8 +200,6 @@ export interface IComponent extends IHasGuid {
201
200
  __internalDisable(isAddingOrRemovingFromScene?: boolean);
202
201
  /** @internal */
203
202
  __internalDestroy();
204
- /** @internal */
205
- resolveGuids?(guidsMap: GuidsMap): void;
206
203
 
207
204
  /** experimental, called when the script is registered for the first time, this is called even if the component is not enabled. */
208
205
  registering?();
@@ -240,14 +237,14 @@ export interface IComponent extends IHasGuid {
240
237
  }
241
238
 
242
239
  export function isComponent(obj: any): obj is IComponent {
243
- return obj && obj.isComponent;
240
+ return obj && typeof obj === "object" && obj.isComponent;
244
241
  }
245
242
 
246
243
  export type ICamera = CameraComponent;
247
244
 
248
245
  export type IAnimationComponent = Pick<IComponent, "gameObject"> & {
249
246
  isAnimationComponent: boolean;
250
- addClip?(clip: AnimationClip);
247
+ addClip?(clip: AnimationClip): void;
251
248
  }
252
249
 
253
250
  /** Interface for a camera controller component that can be attached to a camera to control it */
@@ -274,7 +271,10 @@ export declare interface IRenderer extends IComponent {
274
271
 
275
272
  export declare interface IEventList {
276
273
  readonly isEventList: true;
277
- __internalOnInstantiate(map: InstantiateContext): IEventList;
274
+ }
275
+
276
+ export declare interface ISignalReceiver {
277
+ readonly isSignalReceiver: true;
278
278
  }
279
279
 
280
280
  // export declare interface IPhysicsComponent extends IComponent {
@@ -302,12 +302,18 @@ export declare interface ICollider extends IComponent {
302
302
  * Default: [0]
303
303
  */
304
304
  membership?: number[];
305
- /** The collider filter indicates what groups the collider can interact with (e.g. group 3 and 4)
306
- * An `undefined` array indicates that the collider can interact with all groups
307
- * Note: Make sure to call updateProperties after having changed this property
305
+ /** The collider filter indicates what groups the collider can interact with (e.g. group 3 and 4)
306
+ * An `undefined` array indicates that the collider can interact with all groups
307
+ * Note: Make sure to call updateProperties after having changed this property
308
308
  * Default: undefined
309
309
  */
310
310
  filter?: number[];
311
+ /** The density of the collider used for automatic mass calculation.
312
+ * When the attached Rigidbody has `autoMass` enabled, the mass is computed as `density × volume`.
313
+ * Note: Make sure to call updateProperties after having changed this property
314
+ * Default: undefined (uses physics engine default of 1.0)
315
+ */
316
+ density?: number;
311
317
  }
312
318
 
313
319
  export declare interface ISphereCollider extends ICollider {
@@ -475,8 +481,10 @@ export interface IPhysicsEngine {
475
481
  postStep();
476
482
  /** Indicates whether the physics engine is currently updating */
477
483
  get isUpdating(): boolean;
478
- /** Clears all cached data (e.g., mesh data when creating scaled mesh colliders) */
479
- clearCaches();
484
+ /** Tears down the physics world and frees all resources. The world will be re-created on next use. */
485
+ dispose(): void;
486
+ /** @deprecated Use {@link dispose} instead. */
487
+ clearCaches(): void;
480
488
 
481
489
  /** Enables or disables the physics engine */
482
490
  enabled: boolean;
@@ -738,8 +746,9 @@ export interface IPhysicsEngine {
738
746
  getBody(obj: ICollider | IRigidbody): null | any;
739
747
 
740
748
  // Joints
741
- addFixedJoint(body1: IRigidbody, body2: IRigidbody);
742
- addHingeJoint(body1: IRigidbody, body2: IRigidbody, anchor: Vec3, axis: Vec3);
749
+ addFixedJoint(body1: IRigidbody, body2: IRigidbody): Promise<any> | any;
750
+ addHingeJoint(body1: IRigidbody, body2: IRigidbody, anchor: Vec3, axis: Vec3): Promise<any> | any;
751
+ removeJoint(joint: any): void;
743
752
 
744
753
  /** Enable to render collider shapes */
745
754
  debugRenderColliders: boolean;
@@ -79,8 +79,12 @@ function createPropertyWrapper(target: IComponent | any, _propertyKey: string |
79
79
  return;
80
80
  }
81
81
 
82
- // only build wrapper once per type
83
- if (this[$prop] === undefined) {
82
+ // Only install the getter/setter once per instance.
83
+ // processStart may call __internalAwake again for components that were inactive
84
+ // during processNewScripts — the base Component.__internalAwake has a __didAwake guard
85
+ // but this decorator wrapper runs before reaching it, so we need our own idempotency check.
86
+ const desc = Object.getOwnPropertyDescriptor(this, propertyKey);
87
+ if (!desc || !desc.get) {
84
88
 
85
89
  // make sure the field is initialized in a hidden property
86
90
  this[$prop] = this[propertyKey];
@@ -99,6 +103,7 @@ function createPropertyWrapper(target: IComponent | any, _propertyKey: string |
99
103
  }
100
104
 
101
105
  Object.defineProperty(this, propertyKey, {
106
+ configurable: true,
102
107
  set: function (v) {
103
108
  if (this[$isAssigningProperties] === true) {
104
109
  this[$prop] = v;
@@ -279,24 +279,35 @@ declare type deepClonePredicate = (owner: any, propertyName: string, current: an
279
279
  * return true;
280
280
  * });
281
281
  * */
282
- export function deepClone(obj: any, predicate?: deepClonePredicate): any {
282
+ export function deepClone(obj: any, predicate?: deepClonePredicate, _visited?: WeakSet<object>): any {
283
283
  if (obj !== null && obj !== undefined && typeof obj === "object") {
284
+ if (!_visited) _visited = new WeakSet();
285
+ if (_visited.has(obj)) return obj;
286
+ _visited.add(obj);
287
+
284
288
  let clone;
285
289
  if (Array.isArray(obj)) clone = [];
286
290
  else {
287
- clone = Object.create(obj);
288
- Object.assign(clone, obj);
291
+ clone = Object.create(Object.getPrototypeOf(obj));
292
+ // Copy own properties, skipping getter-only properties that can't be set
293
+ const descriptors = Object.getOwnPropertyDescriptors(obj);
294
+ for (const key in descriptors) {
295
+ const desc = descriptors[key];
296
+ if (desc.set || desc.writable !== false) {
297
+ try { clone[key] = obj[key]; }
298
+ catch { /* skip read-only properties */ }
299
+ }
300
+ }
289
301
  }
290
302
  for (const key of Object.keys(obj)) {
291
303
  const val = obj[key];
292
304
  if (predicate && !predicate(obj, key, val)) {
293
- // console.log("SKIP", val);
294
305
  clone[key] = val;
295
306
  }
296
307
  else if (val?.clone !== undefined && typeof val.clone === "function")
297
308
  clone[key] = val.clone();
298
309
  else
299
- clone[key] = deepClone(val, predicate);
310
+ clone[key] = deepClone(val, predicate, _visited);
300
311
  }
301
312
  return clone;
302
313
  }
@@ -35,7 +35,7 @@ export class GenerateMeshBVHWorker extends WorkerBase {
35
35
 
36
36
  worker.onerror = e => {
37
37
 
38
- reject(new Error(`[GenerateMeshBVHWorker] ${e.message || "Unknown error. Please check the server console. If you're using vite try adding 'three-mesh-bvh' to 'optimizeDeps.exclude' in your vite.config.js"}`));
38
+ reject(new Error(`[GenerateMeshBVHWorker] ${e.message || "Could not load worker."}`));
39
39
 
40
40
  };
41
41
 
@@ -1,8 +1,8 @@
1
1
  import { showBalloonMessage } from "../../debug/debug.js";
2
- import { HTMLElementBase } from "../../engine_ssr.js";
3
2
  import type { Context } from "../../engine_context.js";
4
3
  import { hasCommercialLicense, onLicenseCheckResultChanged, Telemetry } from "../../engine_license.js";
5
4
  import { isLocalNetwork } from "../../engine_networking_utils.js";
5
+ import { HTMLElementBase } from "../../engine_ssr.js";
6
6
  import { DeviceUtilities, getParam } from "../../engine_utils.js";
7
7
  import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
8
8
  import { ButtonsFactory } from "../buttons.js";
@@ -1,11 +1,11 @@
1
1
  import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
2
- import { HTMLElementBase } from "../engine_ssr.js";
3
2
  import { PUBLIC_KEY, VERSION } from "../engine_constants.js";
4
3
  import { ContextEvent, ContextRegistry } from "../engine_context_registry.js";
5
4
  import { hasCommercialLicense } from "../engine_license.js";
6
5
  import { onStart } from "../engine_lifecycle_api.js";
7
6
  import { setDracoDecoderPath, setDracoDecoderType, setKtx2TranscoderPath } from "../engine_loaders.gltf.js";
8
7
  import { Context, ContextCreateArgs } from "../engine_setup.js";
8
+ import { HTMLElementBase } from "../engine_ssr.js";
9
9
  import { nameToThreeTonemapping } from "../engine_tonemapping.js";
10
10
  import { type INeedleEngineComponent, type LoadedModel } from "../engine_types.js";
11
11
  import type { addAttributeChangeCallback } from "../engine_utils.js";
@@ -42,11 +42,17 @@ export interface NeedleEngineAttributes {
42
42
  'hash': string;
43
43
  /** Set to automatically add OrbitControls to the loaded scene. */
44
44
  'camera-controls': string;
45
- /** Override the default draco decoder path location. */
45
+ /** Override the default Draco decoder/decompressor path. Can be a URL or a local path to a directory containing the Draco decoder files.
46
+ * @default "https://www.gstatic.com/draco/versioned/decoders/1.5.7/"
47
+ * @example <needle-engine dracoDecoderPath="./decoders/draco/"></needle-engine>
48
+ */
46
49
  'dracoDecoderPath': string;
47
- /** Override the default draco library type. */
50
+ /** Override the default Draco decoder type. */
48
51
  'dracoDecoderType': 'wasm' | 'js';
49
- /** Override the default KTX2 transcoder/decoder path. */
52
+ /** Override the default KTX2 transcoder/decoder path. Can be a URL or a local path to a directory containing the KTX2 transcoder files.
53
+ * @default "https://cdn.needle.tools/static/three/0.179.1/basis2/"
54
+ * @example <needle-engine ktx2DecoderPath="./decoders/ktx2/"></needle-engine>
55
+ */
50
56
  'ktx2DecoderPath': string;
51
57
  /** Prevent context from being disposed when element is removed from DOM. */
52
58
  'keep-alive': 'true' | 'false';
@@ -840,15 +840,16 @@ export class NeedleXRSession implements INeedleXRSession {
840
840
  get viewerPose(): XRViewerPose | undefined { return this._viewerPose; }
841
841
 
842
842
 
843
- /** @returns `true` if any image is currently being tracked */
844
- /** returns true if images are currently being tracked */
843
+ /** @returns `true` if any image is currently being tracked or emulated */
845
844
  get isTrackingImages() {
846
845
  if (this.frame && "getImageTrackingResults" in this.frame && typeof this.frame.getImageTrackingResults === "function") {
847
846
  try {
848
847
  const trackingResult = this.frame.getImageTrackingResults();
849
848
  for (const result of trackingResult) {
850
849
  const state = result.trackingState;
851
- if (state === "tracked") return true;
850
+ if (state === "tracked" || state === "emulated") {
851
+ return true;
852
+ }
852
853
  }
853
854
  }
854
855
  catch {
@@ -1053,6 +1054,8 @@ export class NeedleXRSession implements INeedleXRSession {
1053
1054
  private readonly _xr_update_scripts: INeedleXRSessionEventReceiver[] = [];
1054
1055
  /** scripts that are in the scene but inactive (e.g. disabled parent gameObject) */
1055
1056
  private readonly _inactive_scripts: INeedleXRSessionEventReceiver[] = [];
1057
+ /** tracks scripts that have received onEnterXR — prevents spurious onLeaveXR calls */
1058
+ private readonly _scripts_in_xr = new Set<INeedleXRSessionEventReceiver>();
1056
1059
  private readonly _controllerAdded: ControllerChangedEvt[];
1057
1060
  private readonly _controllerRemoved: ControllerChangedEvt[];
1058
1061
  private readonly _originalCameraWorldPosition?: Vector3 | null;
@@ -1163,7 +1166,18 @@ export class NeedleXRSession implements INeedleXRSession {
1163
1166
  // we set the session on the webxr manager at the end because we want to receive inputsource events first
1164
1167
  // e.g. in case there's a bug in the threejs codebase
1165
1168
  this.context.xr = this;
1166
- this.context.renderer.xr.setSession(this.session).then(this.onRendererSessionSet);
1169
+ this.context.renderer.xr.setSession(this.session).then(this.onRendererSessionSet)
1170
+ .catch(err => {
1171
+ // Workaround for WebXR emulators on localhost: the polyfilled XRSession fails the
1172
+ // native XRWebGLBinding constructor's instanceof check. Retry with layers disabled.
1173
+ // See https://github.com/meta-quest/immersive-web-emulator/issues/80
1174
+ if (isDevEnvironment() && err instanceof TypeError && typeof XRWebGLBinding !== "undefined") {
1175
+ console.error(`XRWebGLBinding failed (${err.message}).\nSee https://github.com/meta-quest/immersive-web-emulator/issues/80`);
1176
+ }
1177
+ else {
1178
+ console.error("renderer.xr.setSession failed:", err);
1179
+ }
1180
+ });
1167
1181
  // disable three.js renderer controller autoUpdate (added in ac67b31e3548386f8a93e23a4176554c92bbd0d9)
1168
1182
  if ("controllerAutoUpdate" in this.context.renderer.xr) {
1169
1183
  console.debug("Disabling three.js controllerAutoUpdate");
@@ -1331,8 +1345,10 @@ export class NeedleXRSession implements INeedleXRSession {
1331
1345
  // even if they might already be destroyed e.g. by the WebXR component (it destroys the default controller scripts)
1332
1346
  // they should still receive this callback to be properly cleaned up
1333
1347
  for (const listener of this._xr_scripts) {
1348
+ this._scripts_in_xr.delete(listener);
1334
1349
  listener?.onLeaveXR?.({ xr: this });
1335
1350
  }
1351
+ this._scripts_in_xr.clear();
1336
1352
 
1337
1353
  this.sync?.onExitXR(this);
1338
1354
 
@@ -1399,10 +1415,17 @@ export class NeedleXRSession implements INeedleXRSession {
1399
1415
  }
1400
1416
 
1401
1417
  // make sure the camera is parented to the active rig
1402
- if (this.rig && this._mainCamera?.gameObject) {
1403
- const currentParent = this._mainCamera?.gameObject?.parent;
1404
- if (currentParent !== this.rig.gameObject) {
1405
- this.rig.gameObject.add(this._mainCamera?.gameObject);
1418
+ // Note: applyCustomForward() inserts _cameraRenderParent between rig and camera,
1419
+ // so the hierarchy is: rig → _cameraRenderParent → camera. We check the effective parent.
1420
+ if (this.rig) {
1421
+ const cameraObject = this._mainCamera?.gameObject ?? this.context.mainCamera;
1422
+ if (cameraObject) {
1423
+ const effectiveParent = cameraObject.parent === this._cameraRenderParent
1424
+ ? this._cameraRenderParent.parent
1425
+ : cameraObject.parent;
1426
+ if (effectiveParent !== this.rig.gameObject) {
1427
+ this.rig.gameObject.add(cameraObject);
1428
+ }
1406
1429
  }
1407
1430
  }
1408
1431
 
@@ -1534,8 +1557,12 @@ export class NeedleXRSession implements INeedleXRSession {
1534
1557
  }
1535
1558
 
1536
1559
  //performance.mark('NeedleXRSession update scripts start');
1537
- // invoke update on all scripts
1538
- for (const script of this._xr_update_scripts) {
1560
+ // check all XR scripts for destroyed or inactive state
1561
+ // this must cover _xr_scripts (not just _xr_update_scripts) so that scripts
1562
+ // without onUpdateXR are also detected as inactive when removed from the scene
1563
+ // iterate backwards since markInactive/removeScript modifies the array
1564
+ for (let i = this._xr_scripts.length - 1; i >= 0; i--) {
1565
+ const script = this._xr_scripts[i];
1539
1566
  if (script.destroyed === true) {
1540
1567
  this._script_to_remove.push(script);
1541
1568
  continue;
@@ -1544,6 +1571,10 @@ export class NeedleXRSession implements INeedleXRSession {
1544
1571
  this.markInactive(script);
1545
1572
  continue;
1546
1573
  }
1574
+ }
1575
+ // invoke update on scripts that have onUpdateXR
1576
+ for (const script of this._xr_update_scripts) {
1577
+ if (script.destroyed || script.activeAndEnabled === false) continue;
1547
1578
  if (script.onUpdateXR) script.onUpdateXR(args);
1548
1579
  }
1549
1580
  //performance.mark('NeedleXRSession update scripts end');
@@ -1666,9 +1697,11 @@ export class NeedleXRSession implements INeedleXRSession {
1666
1697
  const script = this._inactive_scripts[i];
1667
1698
  if (script.activeAndEnabled) {
1668
1699
  this._inactive_scripts.splice(i, 1);
1669
- this.addScript(script);
1670
- this.invokeCallback_EnterXR(script);
1671
- for (const ctrl of this.controllers) this.invokeCallback_ControllerAdded(script, ctrl);
1700
+ // addScript returns false if already re-added (e.g. via new_scripts_xr processing)
1701
+ if (this.addScript(script)) {
1702
+ this.invokeCallback_EnterXR(script);
1703
+ for (const ctrl of this.controllers) this.invokeCallback_ControllerAdded(script, ctrl);
1704
+ }
1672
1705
  }
1673
1706
  }
1674
1707
  }
@@ -1689,6 +1722,7 @@ export class NeedleXRSession implements INeedleXRSession {
1689
1722
  }
1690
1723
 
1691
1724
  private invokeCallback_EnterXR(script: INeedleXRSessionEventReceiver) {
1725
+ this._scripts_in_xr.add(script);
1692
1726
  if (script.onEnterXR) {
1693
1727
  script.onEnterXR({ xr: this });
1694
1728
  }
@@ -1704,6 +1738,7 @@ export class NeedleXRSession implements INeedleXRSession {
1704
1738
  }
1705
1739
  }
1706
1740
  private invokeCallback_LeaveXR(script: INeedleXRSessionEventReceiver) {
1741
+ if (!this._scripts_in_xr.delete(script)) return;
1707
1742
  if (script.onLeaveXR && !script.destroyed) {
1708
1743
  script.onLeaveXR({ xr: this });
1709
1744
  }
@@ -1,5 +1,5 @@
1
- import { NeedleXRSession } from "./NeedleXRSession.js";
2
1
  import type { Context } from "../engine_context.js";
2
+ import { NeedleXRSession } from "./NeedleXRSession.js";
3
3
 
4
4
  export declare type XRSessionEventArgs = { session: NeedleXRSession };
5
5
 
@@ -447,25 +447,13 @@ export class Animation extends Behaviour implements IAnimationComponent {
447
447
 
448
448
  private internalOnPlay(action: AnimationAction, options: PlayOptions): Promise<AnimationAction> {
449
449
  var existing = this.actions.find(a => a === action);
450
- if (existing === action && existing.isRunning() && existing.time < existing.getClip().duration) {
451
- const handle = this.tryFindHandle(action);
452
- if (existing.paused) {
453
- existing.paused = false;
454
- }
455
- if (handle) return handle.waitForFinish();
456
- }
457
450
 
458
- // Assign defaults
459
- if (options.loop === undefined) options.loop = this.loop;
460
- if (options.clampWhenFinished === undefined) options.clampWhenFinished = this.clampWhenFinished;
461
- if (options.minMaxOffsetNormalized === undefined && this.randomStartTime) options.minMaxOffsetNormalized = this.minMaxOffsetNormalized;
462
- if (options.minMaxSpeed === undefined) options.minMaxSpeed = this.minMaxSpeed;
463
-
464
- // Reset currently running animations
451
+ // Stop other animations before the early-return check so exclusive always applies,
452
+ // even when the target animation is already running.
465
453
  const stopOther = options?.exclusive ?? true;
466
454
  if (stopOther) {
467
455
  for (const act of this.actions) {
468
- if (act != existing) {
456
+ if (act !== action) {
469
457
  if (options.fadeDuration) {
470
458
  act.fadeOut(options.fadeDuration);
471
459
  }
@@ -475,6 +463,20 @@ export class Animation extends Behaviour implements IAnimationComponent {
475
463
  }
476
464
  }
477
465
  }
466
+
467
+ if (existing === action && existing.isRunning() && existing.time < existing.getClip().duration) {
468
+ const handle = this.tryFindHandle(action);
469
+ if (existing.paused) {
470
+ existing.paused = false;
471
+ }
472
+ if (handle) return handle.waitForFinish();
473
+ }
474
+
475
+ // Assign defaults
476
+ if (options.loop === undefined) options.loop = this.loop;
477
+ if (options.clampWhenFinished === undefined) options.clampWhenFinished = this.clampWhenFinished;
478
+ if (options.minMaxOffsetNormalized === undefined && this.randomStartTime) options.minMaxOffsetNormalized = this.minMaxOffsetNormalized;
479
+ if (options.minMaxSpeed === undefined) options.minMaxSpeed = this.minMaxSpeed;
478
480
  if (options?.fadeDuration) {
479
481
  action.fadeIn(options.fadeDuration);
480
482
  }
@@ -515,10 +517,11 @@ export class Animation extends Behaviour implements IAnimationComponent {
515
517
 
516
518
  action.paused = false;
517
519
  action.play();
520
+ if (debug) console.log("[Animation] Now Playing", action.getClip().name, action)
521
+
518
522
 
519
523
  window.requestAnimationFrame(() => AnimationUtils.testIfRootCanAnimate(action));
520
524
 
521
- if (debug) console.log("PLAY", action.getClip().name, action)
522
525
  const handle = new AnimationHandle(action, this.mixer!, options, _ => {
523
526
  this._handles.splice(this._handles.indexOf(handle), 1);
524
527
  });
@@ -89,6 +89,15 @@ export class Animator extends Behaviour implements IAnimationComponent {
89
89
  return true;
90
90
  }
91
91
 
92
+ /**
93
+ * The current animator mixer, used for low-level control of animations. Owned by the AnimatorController
94
+ * @returns The current AnimationMixer, or null if no controller is assigned
95
+ * @see AnimatorController.mixer
96
+ */
97
+ get mixer() {
98
+ return this._animatorController?.mixer || null;
99
+ }
100
+
92
101
  /**
93
102
  * When enabled, animation will affect the root transform position and rotation
94
103
  */
@@ -401,17 +410,15 @@ export class Animator extends Behaviour implements IAnimationComponent {
401
410
  initializeRuntimeAnimatorController(force: boolean = false) {
402
411
  const shouldRun = (force || this.runtimeAnimatorController !== this._initializeWithRuntimeAnimatorController);
403
412
  if (this.runtimeAnimatorController && shouldRun) {
404
- const clone = this.runtimeAnimatorController.clone();
405
- this._initializeWithRuntimeAnimatorController = clone;
406
- if (clone) {
407
- console.assert(this.runtimeAnimatorController !== clone);
408
- this.runtimeAnimatorController = clone;
409
- console.assert(this.runtimeAnimatorController === clone);
410
- this.runtimeAnimatorController.bind(this);
411
- this.runtimeAnimatorController.setSpeed(this._speed);
412
- this.runtimeAnimatorController.normalizedStartOffset = this._normalizedStartOffset;
413
- }
414
- else console.warn("Could not clone animator controller", this.runtimeAnimatorController);
413
+ // Create a fresh controller from the (possibly remapped) model.
414
+ // The constructor deep-clones the model, preserving Object3D refs that
415
+ // the resolve system has already remapped to cloned objects.
416
+ const controller = new AnimatorController(this.runtimeAnimatorController.model);
417
+ this.runtimeAnimatorController = controller;
418
+ this._initializeWithRuntimeAnimatorController = controller;
419
+ controller.bind(this);
420
+ controller.setSpeed(this._speed);
421
+ controller.normalizedStartOffset = this._normalizedStartOffset;
415
422
  }
416
423
  }
417
424