@needle-tools/engine 4.3.0-alpha → 4.3.0-alpha.3

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 (122) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/needle-engine.bundle.js +6683 -5393
  3. package/dist/needle-engine.bundle.light.js +6687 -5397
  4. package/dist/needle-engine.bundle.light.min.js +121 -118
  5. package/dist/needle-engine.bundle.light.umd.cjs +128 -125
  6. package/dist/needle-engine.bundle.min.js +119 -116
  7. package/dist/needle-engine.bundle.umd.cjs +124 -121
  8. package/dist/needle-engine.d.ts +9 -9
  9. package/dist/needle-engine.light.d.ts +9 -9
  10. package/lib/engine/engine_context.js +1 -1
  11. package/lib/engine/engine_context.js.map +1 -1
  12. package/lib/engine/engine_mainloop_utils.js +2 -4
  13. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  14. package/lib/engine/engine_serialization_core.js +1 -1
  15. package/lib/engine/engine_serialization_core.js.map +1 -1
  16. package/lib/engine/engine_types.d.ts +162 -17
  17. package/lib/engine/xr/NeedleXRSession.d.ts +6 -1
  18. package/lib/engine/xr/NeedleXRSession.js +6 -1
  19. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  20. package/lib/engine-components/Animator.d.ts +129 -21
  21. package/lib/engine-components/Animator.js +115 -21
  22. package/lib/engine-components/Animator.js.map +1 -1
  23. package/lib/engine-components/AnimatorController.d.ts +161 -32
  24. package/lib/engine-components/AnimatorController.js +176 -29
  25. package/lib/engine-components/AnimatorController.js.map +1 -1
  26. package/lib/engine-components/AudioListener.d.ts +16 -5
  27. package/lib/engine-components/AudioListener.js +16 -5
  28. package/lib/engine-components/AudioListener.js.map +1 -1
  29. package/lib/engine-components/AudioSource.d.ts +120 -28
  30. package/lib/engine-components/AudioSource.js +121 -40
  31. package/lib/engine-components/AudioSource.js.map +1 -1
  32. package/lib/engine-components/AvatarLoader.d.ts +61 -0
  33. package/lib/engine-components/AvatarLoader.js +61 -1
  34. package/lib/engine-components/AvatarLoader.js.map +1 -1
  35. package/lib/engine-components/AxesHelper.d.ts +19 -1
  36. package/lib/engine-components/AxesHelper.js +19 -1
  37. package/lib/engine-components/AxesHelper.js.map +1 -1
  38. package/lib/engine-components/BoxHelperComponent.d.ts +26 -0
  39. package/lib/engine-components/BoxHelperComponent.js +26 -0
  40. package/lib/engine-components/BoxHelperComponent.js.map +1 -1
  41. package/lib/engine-components/Camera.d.ts +126 -37
  42. package/lib/engine-components/Camera.js +139 -37
  43. package/lib/engine-components/Camera.js.map +1 -1
  44. package/lib/engine-components/CameraUtils.js +20 -0
  45. package/lib/engine-components/CameraUtils.js.map +1 -1
  46. package/lib/engine-components/Collider.d.ts +95 -21
  47. package/lib/engine-components/Collider.js +100 -23
  48. package/lib/engine-components/Collider.js.map +1 -1
  49. package/lib/engine-components/Component.d.ts +554 -106
  50. package/lib/engine-components/Component.js +352 -81
  51. package/lib/engine-components/Component.js.map +1 -1
  52. package/lib/engine-components/DragControls.d.ts +95 -21
  53. package/lib/engine-components/DragControls.js +126 -32
  54. package/lib/engine-components/DragControls.js.map +1 -1
  55. package/lib/engine-components/DropListener.d.ts +99 -16
  56. package/lib/engine-components/DropListener.js +119 -14
  57. package/lib/engine-components/DropListener.js.map +1 -1
  58. package/lib/engine-components/Light.d.ts +102 -5
  59. package/lib/engine-components/Light.js +102 -44
  60. package/lib/engine-components/Light.js.map +1 -1
  61. package/lib/engine-components/NeedleMenu.d.ts +28 -11
  62. package/lib/engine-components/NeedleMenu.js +28 -11
  63. package/lib/engine-components/NeedleMenu.js.map +1 -1
  64. package/lib/engine-components/Networking.d.ts +37 -5
  65. package/lib/engine-components/Networking.js +37 -5
  66. package/lib/engine-components/Networking.js.map +1 -1
  67. package/lib/engine-components/SceneSwitcher.js +44 -0
  68. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  69. package/lib/engine-components/SpatialTrigger.d.ts +66 -1
  70. package/lib/engine-components/SpatialTrigger.js +74 -2
  71. package/lib/engine-components/SpatialTrigger.js.map +1 -1
  72. package/lib/engine-components/SpectatorCamera.d.ts +66 -4
  73. package/lib/engine-components/SpectatorCamera.js +132 -6
  74. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  75. package/lib/engine-components/SyncedTransform.d.ts +45 -6
  76. package/lib/engine-components/SyncedTransform.js +45 -6
  77. package/lib/engine-components/SyncedTransform.js.map +1 -1
  78. package/lib/engine-components/TransformGizmo.d.ts +49 -3
  79. package/lib/engine-components/TransformGizmo.js +49 -3
  80. package/lib/engine-components/TransformGizmo.js.map +1 -1
  81. package/lib/engine-components/particlesystem/ParticleSystem.js +1 -1
  82. package/lib/engine-components/particlesystem/ParticleSystem.js.map +1 -1
  83. package/lib/engine-components/webxr/WebARSessionRoot.d.ts +8 -3
  84. package/lib/engine-components/webxr/WebARSessionRoot.js +30 -9
  85. package/lib/engine-components/webxr/WebARSessionRoot.js.map +1 -1
  86. package/lib/engine-components/webxr/WebXR.d.ts +156 -25
  87. package/lib/engine-components/webxr/WebXR.js +160 -26
  88. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  89. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +82 -9
  90. package/lib/engine-components-experimental/networking/PlayerSync.js +76 -11
  91. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  92. package/package.json +1 -1
  93. package/src/engine/engine_context.ts +1 -1
  94. package/src/engine/engine_mainloop_utils.ts +2 -4
  95. package/src/engine/engine_serialization_core.ts +1 -1
  96. package/src/engine/engine_types.ts +179 -18
  97. package/src/engine/xr/NeedleXRSession.ts +7 -1
  98. package/src/engine-components/Animator.ts +142 -22
  99. package/src/engine-components/AnimatorController.ts +184 -34
  100. package/src/engine-components/AudioListener.ts +16 -5
  101. package/src/engine-components/AudioSource.ts +127 -39
  102. package/src/engine-components/AvatarLoader.ts +61 -2
  103. package/src/engine-components/AxesHelper.ts +21 -1
  104. package/src/engine-components/BoxHelperComponent.ts +26 -0
  105. package/src/engine-components/Camera.ts +147 -41
  106. package/src/engine-components/CameraUtils.ts +20 -0
  107. package/src/engine-components/Collider.ts +102 -27
  108. package/src/engine-components/Component.ts +605 -129
  109. package/src/engine-components/DragControls.ts +134 -38
  110. package/src/engine-components/DropListener.ts +143 -23
  111. package/src/engine-components/Light.ts +105 -44
  112. package/src/engine-components/NeedleMenu.ts +29 -11
  113. package/src/engine-components/Networking.ts +37 -6
  114. package/src/engine-components/SceneSwitcher.ts +48 -1
  115. package/src/engine-components/SpatialTrigger.ts +80 -3
  116. package/src/engine-components/SpectatorCamera.ts +136 -18
  117. package/src/engine-components/SyncedTransform.ts +50 -7
  118. package/src/engine-components/TransformGizmo.ts +49 -4
  119. package/src/engine-components/particlesystem/ParticleSystem.ts +2 -2
  120. package/src/engine-components/webxr/WebARSessionRoot.ts +31 -8
  121. package/src/engine-components/webxr/WebXR.ts +173 -29
  122. package/src/engine-components-experimental/networking/PlayerSync.ts +85 -13
@@ -13,84 +13,121 @@ import { Behaviour, GameObject } from "./Component.js";
13
13
  const debug = getParam("debugaudio");
14
14
 
15
15
  /**
16
- * The AudioRolloffMode enum describes different ways that audio can attenuate with distance.
16
+ * Defines how audio volume attenuates over distance from the listener.
17
17
  */
18
18
  export enum AudioRolloffMode {
19
- /// <summary>
20
- /// <para>Use this mode when you want a real-world rolloff.</para>
21
- /// </summary>
19
+ /**
20
+ * Logarithmic rolloff provides a natural, real-world attenuation where volume decreases
21
+ * exponentially with distance.
22
+ */
22
23
  Logarithmic = 0,
23
- /// <summary>
24
- /// <para>Use this mode when you want to lower the volume of your sound over the distance.</para>
25
- /// </summary>
24
+
25
+ /**
26
+ * Linear rolloff provides a straightforward volume reduction that decreases at a constant
27
+ * rate with distance.
28
+ */
26
29
  Linear = 1,
27
- /// <summary>
28
- /// <para>Use this when you want to use a custom rolloff.</para>
29
- /// </summary>
30
+
31
+ /**
32
+ * Custom rolloff allows for defining specialized distance-based attenuation curves.
33
+ * Note: Custom rolloff is not fully implemented in this version.
34
+ */
30
35
  Custom = 2,
31
36
  }
32
37
 
33
38
 
34
- /** The AudioSource can be used to play audio in the scene.
35
- * Use `clip` to set the audio file to play.
39
+ /**
40
+ * Plays audio clips in the scene, with support for spatial positioning.
41
+ *
42
+ * The AudioSource component can play audio files or media streams with
43
+ * options for spatial blending, volume control, looping, and more.
44
+ *
45
+ * When a page loses visibility (tab becomes inactive), audio will automatically
46
+ * pause unless {@link playInBackground} is set to true. On mobile devices, audio always
47
+ * pauses regardless of this setting. When the page becomes visible again,
48
+ * previously playing audio will resume.
49
+ *
50
+ * AudioSource also responds to application mute state changes. When the application
51
+ * is muted, the volume is set to 0. When unmuted, the volume
52
+ * returns to its previous value.
53
+ *
36
54
  * @category Multimedia
37
55
  * @group Components
38
56
  */
39
57
  export class AudioSource extends Behaviour {
40
58
 
41
- /** Check if the user has interacted with the page to allow audio playback.
42
- * Internally calling {@link Application.userInteractionRegistered}
59
+ /**
60
+ * Checks if the user has interacted with the page to allow audio playback.
61
+ * Audio playback often requires a user gesture first due to browser autoplay policies.
62
+ * This is the same as calling {@link Application.userInteractionRegistered}.
63
+ *
64
+ * @returns Whether user interaction has been registered to allow audio playback
43
65
  */
44
66
  public static get userInteractionRegistered(): boolean {
45
67
  return Application.userInteractionRegistered;
46
68
  }
47
69
 
48
- /** Register a callback that is called when the user has interacted with the page to allow audio playback.
49
- * Internally calling {@link Application.registerWaitForInteraction}
70
+ /**
71
+ * Registers a callback that will be executed once the user has interacted with the page,
72
+ * allowing audio playback to begin.
73
+ * This is the same as calling {@link Application.registerWaitForInteraction}.
74
+ *
75
+ * @param cb - The callback function to execute when user interaction is registered
50
76
  */
51
77
  public static registerWaitForAllowAudio(cb: Function) {
52
78
  Application.registerWaitForInteraction(cb);
53
79
  }
54
80
 
55
81
  /**
56
- * The audio clip to play. Can be a string (URL) or a MediaStream.
82
+ * The audio clip to play. Can be a URL string pointing to an audio file or a {@link MediaStream} object.
57
83
  */
58
84
  @serializable(URL)
59
85
  clip: string | MediaStream = "";
60
86
 
61
87
  /**
62
- * If true, the audio source will start playing as soon as the scene starts.
63
- * If false, you can call play() to start the audio.
88
+ * When true, the audio will automatically start playing when the component is enabled.
89
+ * When false, you must call play() manually to start audio playback.
64
90
  * @default false
65
- */
91
+ */
66
92
  @serializable()
67
93
  playOnAwake: boolean = false;
68
94
 
69
95
  /**
70
- * If true, the audio source will start loading the audio clip as soon as the scene starts.
71
- * If false, the audio clip will be loaded when play() is called.
96
+ * When true, the audio clip will be loaded during initialization rather than when play() is called.
97
+ * This can reduce playback delay but increases initial loading time.
72
98
  * @default false
73
99
  */
74
100
  @serializable()
75
101
  preload: boolean = false;
76
102
 
77
103
  /**
78
- * When true, the audio will play in the background. This means it will continue playing if the browser tab is not focused/active or minimized
104
+ * When true, audio will continue playing when the browser tab loses focus.
105
+ * When false, audio will pause when the tab is minimized or not active.
79
106
  * @default true
80
107
  */
81
108
  @serializable()
82
109
  playInBackground: boolean = true;
83
110
 
84
111
  /**
85
- * If true, the audio is currently playing.
112
+ * Indicates whether the audio is currently playing.
113
+ *
114
+ * @returns True if the audio is playing, false otherwise
86
115
  */
87
116
  get isPlaying(): boolean { return this.sound?.isPlaying ?? false; }
88
117
 
89
- /** The duration of the audio clip in seconds. */
118
+ /**
119
+ * The total duration of the current audio clip in seconds.
120
+ *
121
+ * @returns Duration in seconds or undefined if no clip is loaded
122
+ */
90
123
  get duration() {
91
124
  return this.sound?.buffer?.duration;
92
125
  }
93
- /** The current time of the audio clip in 0-1 range. */
126
+
127
+ /**
128
+ * The current playback position as a normalized value between 0 and 1.
129
+ * Can be set to seek to a specific position in the audio.
130
+ */
94
131
  get time01() {
95
132
  const duration = this.duration;
96
133
  if (duration && this.sound) {
@@ -106,7 +143,8 @@ export class AudioSource extends Behaviour {
106
143
  }
107
144
 
108
145
  /**
109
- * The current time of the audio clip in seconds.
146
+ * The current playback position in seconds.
147
+ * Can be set to seek to a specific time in the audio.
110
148
  */
111
149
  get time(): number { return this.sound?.source ? (this.sound.source?.context.currentTime - this._lastContextTime + this.sound.offset) : 0; }
112
150
  set time(val: number) {
@@ -121,8 +159,8 @@ export class AudioSource extends Behaviour {
121
159
  }
122
160
 
123
161
  /**
124
- * If true, the audio source will loop the audio clip.
125
- * If false, the audio clip will play once.
162
+ * When true, the audio will repeat after reaching the end.
163
+ * When false, audio will play once and stop.
126
164
  * @default false
127
165
  */
128
166
  @serializable()
@@ -134,10 +172,12 @@ export class AudioSource extends Behaviour {
134
172
  this._loop = val;
135
173
  if (this.sound) this.sound.setLoop(val);
136
174
  }
137
- /** Can be used to play the audio clip in 2D or 3D space.
138
- * 2D Playback is currently not fully supported.
139
- * 0 = 2D, 1 = 3D
140
- * */
175
+
176
+ /**
177
+ * Controls how the audio is positioned in space.
178
+ * Values range from 0 (2D, non-positional) to 1 (fully 3D positioned).
179
+ * Note: 2D playback is not fully supported in the current implementation.
180
+ */
141
181
  @serializable()
142
182
  get spatialBlend(): number {
143
183
  return this._spatialBlend;
@@ -147,6 +187,11 @@ export class AudioSource extends Behaviour {
147
187
  this._spatialBlend = val;
148
188
  this._needUpdateSpatialDistanceSettings = true;
149
189
  }
190
+
191
+ /**
192
+ * The minimum distance from the audio source at which the volume starts to attenuate.
193
+ * Within this radius, the audio plays at full volume regardless of distance.
194
+ */
150
195
  @serializable()
151
196
  get minDistance(): number {
152
197
  return this._minDistance;
@@ -156,6 +201,11 @@ export class AudioSource extends Behaviour {
156
201
  this._minDistance = val;
157
202
  this._needUpdateSpatialDistanceSettings = true;
158
203
  }
204
+
205
+ /**
206
+ * The maximum distance from the audio source beyond which the volume no longer decreases.
207
+ * This defines the outer limit of the attenuation curve.
208
+ */
159
209
  @serializable()
160
210
  get maxDistance(): number {
161
211
  return this._maxDistance;
@@ -170,6 +220,11 @@ export class AudioSource extends Behaviour {
170
220
  private _minDistance: number = 1;
171
221
  private _maxDistance: number = 100;
172
222
 
223
+ /**
224
+ * Controls the overall volume/loudness of the audio.
225
+ * Values range from 0 (silent) to 1 (full volume).
226
+ * @default 1
227
+ */
173
228
  @serializable()
174
229
  get volume(): number { return this._volume; }
175
230
  set volume(val: number) {
@@ -181,6 +236,12 @@ export class AudioSource extends Behaviour {
181
236
  }
182
237
  private _volume: number = 1;
183
238
 
239
+ /**
240
+ * Controls the playback rate (speed) of the audio.
241
+ * Values greater than 1 increase speed, values less than 1 decrease it.
242
+ * This affects both speed and pitch of the audio.
243
+ * @default 1
244
+ */
184
245
  @serializable()
185
246
  set pitch(val: number) {
186
247
  if (this.sound) this.sound.setPlaybackRate(val);
@@ -189,6 +250,11 @@ export class AudioSource extends Behaviour {
189
250
  return this.sound ? this.sound.getPlaybackRate() : 1;
190
251
  }
191
252
 
253
+ /**
254
+ * Determines how audio volume decreases with distance from the listener.
255
+ * @default AudioRolloffMode.Logarithmic
256
+ * @see {@link AudioRolloffMode}
257
+ */
192
258
  @serializable()
193
259
  rollOffMode: AudioRolloffMode = 0;
194
260
 
@@ -204,10 +270,15 @@ export class AudioSource extends Behaviour {
204
270
  private _lastClipStartedLoading: string | MediaStream | null = null;
205
271
  private _audioElement: HTMLAudioElement | null = null;
206
272
 
273
+ /**
274
+ * Returns the underlying {@link PositionalAudio} object, creating it if necessary.
275
+ * The audio source needs a user interaction to be initialized due to browser autoplay policies.
276
+ *
277
+ * @returns The three.js PositionalAudio object or null if unavailable
278
+ */
207
279
  public get Sound(): PositionalAudio | null {
208
280
  if (!this.sound && AudioSource.userInteractionRegistered) {
209
- let listener = GameObject.getComponent(this.context.mainCamera, AudioListener) ?? GameObject.findObjectOfType(AudioListener, this.context);
210
- if (!listener && this.context.mainCamera) listener = GameObject.addComponent(this.context.mainCamera, AudioListener);
281
+ const listener = GameObject.getOrAddComponent(this.gameObject, AudioListener);
211
282
  if (listener?.listener) {
212
283
  this.sound = new PositionalAudio(listener.listener);
213
284
  this.gameObject?.add(this.sound);
@@ -250,9 +321,19 @@ export class AudioSource extends Behaviour {
250
321
  // }
251
322
  // }
252
323
 
324
+ /**
325
+ * Indicates whether the audio source is queued to play when possible.
326
+ * This may be true before user interaction has been registered.
327
+ *
328
+ * @returns Whether the audio source intends to play
329
+ */
253
330
  public get ShouldPlay(): boolean { return this.shouldPlay; }
254
331
 
255
- /** Get the audio context from the Sound */
332
+ /**
333
+ * Returns the Web Audio API context associated with this audio source.
334
+ *
335
+ * @returns The {@link AudioContext} or null if not available
336
+ */
256
337
  public get audioContext() {
257
338
  return this.sound?.context;
258
339
  }
@@ -424,7 +505,12 @@ export class AudioSource extends Behaviour {
424
505
  }
425
506
  }
426
507
 
427
- /** Play a audioclip or mediastream */
508
+ /**
509
+ * Plays the audio clip or media stream.
510
+ * If no argument is provided, plays the currently assigned clip.
511
+ *
512
+ * @param clip - Optional audio clip or {@link MediaStream} to play
513
+ */
428
514
  play(clip: string | MediaStream | undefined = undefined) {
429
515
  // use audio source's clip when no clip is passed in
430
516
  if (!clip && this.clip)
@@ -483,7 +569,8 @@ export class AudioSource extends Behaviour {
483
569
  }
484
570
 
485
571
  /**
486
- * Pause the audio
572
+ * Pauses audio playback while maintaining the current position.
573
+ * Use play() to resume from the paused position.
487
574
  */
488
575
  pause() {
489
576
  if (debug) console.log("Pause", this);
@@ -497,7 +584,8 @@ export class AudioSource extends Behaviour {
497
584
  }
498
585
 
499
586
  /**
500
- * Stop the audio and reset the time to 0
587
+ * Stops audio playback completely and resets the playback position to the beginning.
588
+ * Unlike pause(), calling play() after stop() will start from the beginning.
501
589
  */
502
590
  stop() {
503
591
  if (debug) console.log("Pause", this);
@@ -10,17 +10,37 @@ import { GameObject } from "./Component.js";
10
10
 
11
11
  const debug = utils.getParam("debugavatar");
12
12
 
13
+ /**
14
+ * Represents an avatar model with head and hands references.
15
+ * Used for representing characters in 3D space.
16
+ */
13
17
  export class AvatarModel {
18
+ /** The root object of the avatar model */
14
19
  root: Object3D;
20
+ /** The head object of the avatar model */
15
21
  head: Object3D;
22
+ /** The left hand object of the avatar model, if available */
16
23
  leftHand: Object3D | null;
24
+ /** The right hand object of the avatar model, if available */
17
25
  rigthHand: Object3D | null;
18
26
 
19
27
 
28
+ /**
29
+ * Checks if the avatar model has a valid configuration.
30
+ * An avatar is considered valid if it has a head.
31
+ * @returns Whether the avatar has a valid setup
32
+ */
20
33
  get isValid(): boolean {
21
34
  return this.head !== null && this.head !== undefined;
22
35
  }
23
36
 
37
+ /**
38
+ * Creates a new avatar model.
39
+ * @param root The root object of the avatar
40
+ * @param head The head object of the avatar
41
+ * @param leftHand The left hand object of the avatar
42
+ * @param rigthHand The right hand object of the avatar
43
+ */
24
44
  constructor(root: Object3D, head: Object3D, leftHand: Object3D | null, rigthHand: Object3D | null) {
25
45
  this.root = root;
26
46
  this.head = head;
@@ -33,12 +53,25 @@ export class AvatarModel {
33
53
  }
34
54
  }
35
55
 
56
+ /**
57
+ * Handles loading and instantiating avatar models from various sources.
58
+ * Provides functionality to find and extract important parts of an avatar (head, hands).
59
+ *
60
+ * Debug mode can be enabled with the URL parameter `?debugavatar`,
61
+ * which will log detailed information about avatar loading and configuration.
62
+ */
36
63
  export class AvatarLoader {
37
64
 
38
65
  private readonly avatarRegistryUrl: string | null = null;
39
66
  // private loader: GLTFLoader | null;
40
67
  // private avatarModelCache: Map<string, AvatarModel | null> = new Map<string, AvatarModel | null>();
41
68
 
69
+ /**
70
+ * Retrieves or creates a new avatar instance from an ID or existing Object3D.
71
+ * @param context The application context
72
+ * @param avatarId Either a string ID to load an avatar or an existing Object3D to use as avatar
73
+ * @returns Promise resolving to an AvatarModel if successful, or null if failed
74
+ */
42
75
  public async getOrCreateNewAvatarInstance(context: Context, avatarId: string | Object3D): Promise<AvatarModel | null> {
43
76
 
44
77
  if (!avatarId) {
@@ -76,6 +109,12 @@ export class AvatarLoader {
76
109
  }
77
110
 
78
111
 
112
+ /**
113
+ * Loads an avatar model from a file or registry using the provided ID.
114
+ * @param context The engine context
115
+ * @param avatarId The ID of the avatar to load
116
+ * @returns Promise resolving to the loaded avatar's Object3D, or null if failed
117
+ */
79
118
  private async loadAvatar(context: Context, avatarId: string): Promise<Object3D | null> {
80
119
 
81
120
  console.assert(avatarId !== undefined && avatarId !== null && typeof avatarId === "string", "Avatar id must not be null");
@@ -140,11 +179,20 @@ export class AvatarLoader {
140
179
  });
141
180
  }
142
181
 
182
+ /**
183
+ * Caches an avatar model for reuse.
184
+ * @param _id The ID to associate with the model
185
+ * @param _model The avatar model to cache
186
+ */
143
187
  private cacheModel(_id: string, _model: AvatarModel) {
144
188
  // this.avatarModelCache.set(id, model);
145
189
  }
146
190
 
147
- // TODO this should be burned to the ground once 🤞 we have proper extras that define object relations.
191
+ /**
192
+ * Analyzes an Object3D to find avatar parts (head, hands) based on naming conventions.
193
+ * @param obj The Object3D to search for avatar parts
194
+ * @returns A structured AvatarModel with references to found parts
195
+ */
148
196
  private findAvatar(obj: Object3D): AvatarModel {
149
197
 
150
198
  const root: Object3D = obj;
@@ -175,7 +223,12 @@ export class AvatarLoader {
175
223
  return model;
176
224
  }
177
225
 
178
-
226
+ /**
227
+ * Recursively searches for an avatar part by name within an Object3D hierarchy.
228
+ * @param obj The Object3D to search within
229
+ * @param searchString Array of strings that should all be present in the object name
230
+ * @returns The found Object3D part or null if not found
231
+ */
179
232
  private findAvatarPart(obj: Object3D, searchString: string[]): Object3D | null {
180
233
 
181
234
  const name = obj.name.toLowerCase();
@@ -196,6 +249,12 @@ export class AvatarLoader {
196
249
  return null;
197
250
  }
198
251
 
252
+ /**
253
+ * Handles HTTP response errors from avatar loading operations.
254
+ * @param response The fetch API response to check
255
+ * @returns The response if it was ok
256
+ * @throws Error with status text if response was not ok
257
+ */
199
258
  private handleCustomAvatarErrors(response) {
200
259
  if (!response.ok) {
201
260
  throw Error(response.statusText);
@@ -5,20 +5,37 @@ import { serializable } from "../engine/engine_serialization_decorator.js";
5
5
  import { Behaviour } from "./Component.js";
6
6
 
7
7
  /**
8
- * AxesHelper is a component that displays the axes of the object in the scene.
8
+ * Component that visualizes the axes of an object in the scene.
9
+ * Renders colored lines representing the X (red), Y (green) and Z (blue) axes.
9
10
  * @category Helpers
10
11
  * @group Components
11
12
  */
12
13
  export class AxesHelper extends Behaviour {
14
+ /**
15
+ * The length of each axis line in scene units.
16
+ */
13
17
  @serializable()
14
18
  public length: number = 1;
19
+
20
+ /**
21
+ * Whether the axes should be occluded by objects in the scene.
22
+ * When set to false, axes will always appear on top regardless of their depth.
23
+ */
15
24
  @serializable()
16
25
  public depthTest: boolean = true;
26
+
27
+ /**
28
+ * When true, this helper will only be visible if the debug flag `?gizmos` is enabled.
29
+ */
17
30
  @serializable()
18
31
  public isGizmo: boolean = false;
19
32
 
20
33
  private _axes: _AxesHelper | null = null;
21
34
 
35
+ /**
36
+ * Creates and adds the axes visualization to the scene when the component is enabled.
37
+ * If marked as a gizmo, it will only be shown when gizmos are enabled in the global parameters.
38
+ */
22
39
  onEnable(): void {
23
40
  if (this.isGizmo && !params.showGizmos) return;
24
41
  if (!this._axes)
@@ -33,6 +50,9 @@ export class AxesHelper extends Behaviour {
33
50
  }
34
51
  }
35
52
 
53
+ /**
54
+ * Removes the axes visualization from the scene when the component is disabled.
55
+ */
36
56
  onDisable(): void {
37
57
  if (!this._axes) return;
38
58
  this.gameObject.remove(this._axes);
@@ -9,11 +9,17 @@ const gizmos = getParam("gizmos");
9
9
  const debug = getParam("debugboxhelper");
10
10
 
11
11
  /**
12
+ * A component that creates a bounding box around an object and provides intersection testing functionality.
13
+ *
14
+ * Debug mode can be enabled with the URL parameter `?debugboxhelper`, which will visualize intersection tests.
15
+ * Helper visualization can be enabled with the URL parameter `?gizmos`.
16
+ *
12
17
  * @category Helpers
13
18
  * @group Components
14
19
  */
15
20
  export class BoxHelperComponent extends Behaviour {
16
21
 
22
+ /** The bounding box for this component */
17
23
  private box: Box3 | null = null;
18
24
  private static testBox: Box3 = new Box3();
19
25
  private _lastMatrixUpdateFrame: number = -1;
@@ -21,6 +27,11 @@ export class BoxHelperComponent extends Behaviour {
21
27
  private static _size: Vector3 = new Vector3(.01, .01, .01);
22
28
  private static _emptyObjectSize: Vector3 = new Vector3(.01, .01, .01);
23
29
 
30
+ /**
31
+ * Tests if an object intersects with this helper's bounding box
32
+ * @param obj The object to test for intersection
33
+ * @returns True if objects intersect, false if not, undefined if the provided object is invalid
34
+ */
24
35
  public isInBox(obj: Object3D): boolean | undefined {
25
36
  if (!obj) return undefined;
26
37
 
@@ -44,11 +55,21 @@ export class BoxHelperComponent extends Behaviour {
44
55
  return intersects;
45
56
  }
46
57
 
58
+ /**
59
+ * Tests if this helper's bounding box intersects with another box
60
+ * @param box The {@link Box3} to test for intersection
61
+ * @returns True if boxes intersect, false otherwise
62
+ */
47
63
  public intersects(box: Box3): boolean {
48
64
  if (!box) return false;
49
65
  return this.updateBox(false).intersectsBox(box);
50
66
  }
51
67
 
68
+ /**
69
+ * Updates the helper's bounding box based on the gameObject's position and scale
70
+ * @param force Whether to force an update regardless of frame count
71
+ * @returns The updated {@link Box3}
72
+ */
52
73
  public updateBox(force: boolean = false): Box3 {
53
74
  if (!this.box) {
54
75
  this.box = new Box3();
@@ -74,6 +95,11 @@ export class BoxHelperComponent extends Behaviour {
74
95
  this.box = null;
75
96
  }
76
97
 
98
+ /**
99
+ * Creates and displays a visual wireframe representation of this box helper
100
+ * @param col Optional color for the wireframe. If not provided, uses default color
101
+ * @param force If true, shows the helper even if gizmos are disabled
102
+ */
77
103
  public showHelper(col: ColorRepresentation | null = null, force: boolean = false) {
78
104
  if (!gizmos && !force) return;
79
105
  if (this._helper) {