@needle-tools/engine 5.1.0-alpha.1 → 5.1.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.
- package/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/CHANGELOG.md +52 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
- package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
- package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
- package/dist/needle-engine.bundle-C-ixARur.umd.cjs +1733 -0
- package/dist/needle-engine.bundle-CHmXdnE1.min.js +1733 -0
- package/dist/{needle-engine.bundle-BGyKqxBH.js → needle-engine.bundle-DF01sSGQ.js} +10841 -10434
- package/dist/needle-engine.d.ts +244 -54
- package/dist/needle-engine.js +541 -536
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
- package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
- package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
- package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
- package/dist/vendor-CJmyOrCq.min.js +1116 -0
- package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
- package/lib/engine/api.d.ts +14 -0
- package/lib/engine/api.js +4 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug_environment.js +1 -1
- package/lib/engine/debug/debug_environment.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
- package/lib/engine/debug/debug_spatial_console.js +10 -7
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +2 -0
- package/lib/engine/engine_addressables.js +6 -3
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_application.js +8 -6
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_audio.d.ts +68 -0
- package/lib/engine/engine_audio.js +172 -0
- package/lib/engine/engine_audio.js.map +1 -1
- package/lib/engine/engine_components.js +1 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_constants.js +6 -0
- package/lib/engine/engine_constants.js.map +1 -1
- package/lib/engine/engine_context.d.ts +33 -7
- package/lib/engine/engine_context.js +40 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_registry.js +1 -1
- package/lib/engine/engine_context_registry.js.map +1 -1
- package/lib/engine/engine_gameobject.js +2 -2
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_init.js +16 -1
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.d.ts +3 -2
- package/lib/engine/engine_input.js +3 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +25 -15
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_mainloop_utils.js +5 -2
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_networking_blob.d.ts +1 -1
- package/lib/engine/engine_networking_blob.js +5 -11
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +0 -1
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +32 -0
- package/lib/engine/engine_scenedata.js +138 -0
- package/lib/engine/engine_scenedata.js.map +1 -0
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
- package/lib/engine/engine_serialization_builtin_serializer.js +55 -41
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_ssr.d.ts +18 -0
- package/lib/engine/engine_ssr.js +40 -0
- package/lib/engine/engine_ssr.js.map +1 -0
- package/lib/engine/engine_three_utils.d.ts +14 -7
- package/lib/engine/engine_three_utils.js +14 -7
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +2 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_hash.d.ts +9 -0
- package/lib/engine/engine_utils_hash.js +112 -0
- package/lib/engine/engine_utils_hash.js.map +1 -0
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/jsx.d.ts +51 -0
- package/lib/engine/webcomponents/logo-element.d.ts +2 -1
- package/lib/engine/webcomponents/logo-element.js +2 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +2 -1
- package/lib/engine/webcomponents/needle-button.js +2 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +11 -4
- package/lib/engine/webcomponents/needle-engine.js +2 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
- package/lib/engine/xr/NeedleXRSession.js +51 -15
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/events.d.ts +1 -1
- package/lib/engine/xr/events.js.map +1 -1
- package/lib/engine-components/Animation.js +17 -16
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +2 -0
- package/lib/engine-components/AnimatorController.js +4 -1
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +19 -3
- package/lib/engine-components/AudioSource.js +121 -68
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +7 -0
- package/lib/engine-components/DragControls.js +19 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +40 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +1 -1
- package/lib/engine-components/Networking.js +1 -1
- package/lib/engine-components/OrbitControls.js +16 -11
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.js +2 -0
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/RigidBody.js +3 -3
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +2 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SeeThrough.js +2 -2
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -1
- package/lib/engine-components/api.js +1 -1
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
- package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
- package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
- package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
- package/lib/engine-components/ui/Canvas.d.ts +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -8
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +10 -7
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.js +21 -12
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
- package/lib/engine-components/web/ScrollFollow.js +3 -2
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +6 -4
- package/plugins/dts-generator/dts.codegen.js +334 -0
- package/plugins/dts-generator/dts.scan.js +99 -0
- package/plugins/dts-generator/dts.writer.js +59 -0
- package/plugins/dts-generator/glb.discovery.js +279 -0
- package/plugins/dts-generator/glb.extractor.js +215 -0
- package/plugins/dts-generator/glb.reader.js +167 -0
- package/plugins/dts-generator/index.js +36 -0
- package/plugins/dts-generator/manifest.types.js +174 -0
- package/plugins/types/index.d.ts +2 -1
- package/plugins/types/needle-bindings.d.ts +30 -0
- package/plugins/types/userconfig.d.ts +21 -2
- package/plugins/vite/asap.js +18 -9
- package/plugins/vite/dependencies.js +29 -0
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +3 -4
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +7 -0
- package/plugins/vite/dts-generator.js +191 -0
- package/plugins/vite/index.d.ts +10 -3
- package/plugins/vite/index.js +27 -10
- package/plugins/vite/local-files-core.js +3 -3
- package/plugins/vite/local-files-utils.d.ts +3 -1
- package/plugins/vite/local-files-utils.js +29 -5
- package/plugins/vite/logging.js +2 -2
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +23 -22
- package/src/engine/api.ts +18 -1
- package/src/engine/debug/debug_environment.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +10 -7
- package/src/engine/engine_addressables.ts +6 -3
- package/src/engine/engine_application.ts +8 -6
- package/src/engine/engine_audio.ts +184 -0
- package/src/engine/engine_components.ts +1 -1
- package/src/engine/engine_constants.ts +11 -6
- package/src/engine/engine_context.ts +50 -7
- package/src/engine/engine_context_registry.ts +1 -1
- package/src/engine/engine_gameobject.ts +2 -2
- package/src/engine/engine_init.ts +15 -1
- package/src/engine/engine_input.ts +3 -2
- package/src/engine/engine_license.ts +23 -19
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_mainloop_utils.ts +5 -2
- package/src/engine/engine_networking_blob.ts +5 -11
- package/src/engine/engine_physics_rapier.ts +0 -3
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +136 -0
- package/src/engine/engine_serialization_builtin_serializer.ts +63 -46
- package/src/engine/engine_ssr.ts +48 -0
- package/src/engine/engine_three_utils.ts +15 -7
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/engine_utils.ts +1 -0
- package/src/engine/engine_utils_hash.ts +65 -0
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/src/engine/webcomponents/jsx.d.ts +51 -0
- package/src/engine/webcomponents/logo-element.ts +3 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -2
- package/src/engine/webcomponents/needle-button.ts +3 -1
- package/src/engine/webcomponents/needle-engine.ts +12 -4
- package/src/engine/xr/NeedleXRSession.ts +49 -14
- package/src/engine/xr/events.ts +1 -1
- package/src/engine-components/Animation.ts +19 -16
- package/src/engine-components/AnimatorController.ts +4 -1
- package/src/engine-components/AudioSource.ts +130 -79
- package/src/engine-components/DragControls.ts +18 -2
- package/src/engine-components/Light.ts +40 -26
- package/src/engine-components/Networking.ts +1 -1
- package/src/engine-components/OrbitControls.ts +18 -9
- package/src/engine-components/ReflectionProbe.ts +2 -0
- package/src/engine-components/RigidBody.ts +3 -3
- package/src/engine-components/SceneSwitcher.ts +1 -0
- package/src/engine-components/SeeThrough.ts +2 -2
- package/src/engine-components/api.ts +1 -1
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
- package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
- package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
- package/src/engine-components/ui/Canvas.ts +2 -8
- package/src/engine-components/ui/Text.ts +12 -8
- package/src/engine-components/web/CursorFollow.ts +21 -13
- package/src/engine-components/web/ScrollFollow.ts +2 -2
- package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
- package/src/needle-engine.ts +3 -0
- package/src/vite-env.d.ts +16 -0
- package/dist/needle-engine.bundle-CiYtOO2O.min.js +0 -1732
- package/dist/needle-engine.bundle-DzVx9Z8D.umd.cjs +0 -1732
- package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
- package/dist/vendor-HRlxIBga.min.js +0 -1116
|
@@ -4,7 +4,6 @@ import { PositionalAudioHelper } from 'three/examples/jsm/helpers/PositionalAudi
|
|
|
4
4
|
import { isDevEnvironment } from "../engine/debug/index.js";
|
|
5
5
|
import { Application, ApplicationEvents } from "../engine/engine_application.js";
|
|
6
6
|
import { findObjectOfType } from "../engine/engine_components.js";
|
|
7
|
-
import { Mathf } from "../engine/engine_math.js";
|
|
8
7
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
|
9
8
|
import { DeviceUtilities, getParam } from "../engine/engine_utils.js";
|
|
10
9
|
import { AudioListener } from "./AudioListener.js";
|
|
@@ -140,12 +139,23 @@ export class AudioSource extends Behaviour {
|
|
|
140
139
|
get isPlaying(): boolean { return this.sound?.isPlaying ?? false; }
|
|
141
140
|
|
|
142
141
|
/**
|
|
143
|
-
* The total duration of the
|
|
142
|
+
* The total duration of the currently loaded audio clip in seconds.
|
|
144
143
|
*
|
|
145
144
|
* @returns Duration in seconds or undefined if no clip is loaded
|
|
145
|
+
* @remarks For MediaStream clips, duration is not directly available and will return undefined. If the audio clip has not started loading or is still loading, duration may also be undefined until the audio buffer is ready.
|
|
146
146
|
*/
|
|
147
147
|
get duration() {
|
|
148
|
-
|
|
148
|
+
if (this.sound?.buffer?.duration) {
|
|
149
|
+
return this.sound.buffer.duration;
|
|
150
|
+
}
|
|
151
|
+
else if (this._audioElement?.duration) {
|
|
152
|
+
return this._audioElement.duration;
|
|
153
|
+
}
|
|
154
|
+
else if (this._loadedClip instanceof MediaStream) {
|
|
155
|
+
// MediaStream duration is not directly available; return undefined or estimate if possible
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
/**
|
|
@@ -200,7 +210,8 @@ export class AudioSource extends Behaviour {
|
|
|
200
210
|
/**
|
|
201
211
|
* Controls how the audio is positioned in space.
|
|
202
212
|
* Values range from 0 (2D, non-positional) to 1 (fully 3D positioned).
|
|
203
|
-
*
|
|
213
|
+
* Internally uses a dual-path audio graph to crossfade between a spatialized (PannerNode)
|
|
214
|
+
* and a non-spatialized (direct) signal path.
|
|
204
215
|
*/
|
|
205
216
|
@serializable()
|
|
206
217
|
get spatialBlend(): number {
|
|
@@ -292,12 +303,18 @@ export class AudioSource extends Behaviour {
|
|
|
292
303
|
// set this from audio context time, used to set clip offset when setting "time" property
|
|
293
304
|
// there is maybe a better way to set a audio clip current time?!
|
|
294
305
|
private _lastClipStartedLoading: string | MediaStream | null = null;
|
|
306
|
+
private _loadedClip: string | MediaStream | null = null;
|
|
295
307
|
private _audioElement: HTMLAudioElement | null = null;
|
|
296
308
|
|
|
309
|
+
// Spatial blend dual-path audio nodes
|
|
310
|
+
private _entryNode: GainNode | null = null;
|
|
311
|
+
private _spatialGain: GainNode | null = null;
|
|
312
|
+
private _bypassGain: GainNode | null = null;
|
|
313
|
+
|
|
297
314
|
/**
|
|
298
315
|
* Returns the underlying {@link PositionalAudio} object, creating it if necessary.
|
|
299
316
|
* The audio source needs a user interaction to be initialized due to browser autoplay policies.
|
|
300
|
-
*
|
|
317
|
+
*
|
|
301
318
|
* @returns The three.js PositionalAudio object or null if unavailable
|
|
302
319
|
*/
|
|
303
320
|
public get Sound(): PositionalAudio | null {
|
|
@@ -312,45 +329,12 @@ export class AudioSource extends Behaviour {
|
|
|
312
329
|
if (listener?.listener) {
|
|
313
330
|
this.sound = new PositionalAudio(listener.listener);
|
|
314
331
|
this.gameObject?.add(this.sound);
|
|
315
|
-
|
|
316
|
-
// this._listener = listener;
|
|
317
|
-
// this._originalSoundMatrixWorldFunction = this.sound.updateMatrixWorld;
|
|
318
|
-
// this.sound.updateMatrixWorld = this._onSoundMatrixWorld;
|
|
319
332
|
}
|
|
320
333
|
else if (debug) console.warn("No audio listener found in scene - can not play audio");
|
|
321
334
|
}
|
|
322
335
|
return this.sound;
|
|
323
336
|
}
|
|
324
337
|
|
|
325
|
-
// This is a hacky workaround to get the PositionalAudio behave like a 2D audio source
|
|
326
|
-
// private _listener: AudioListener | null = null;
|
|
327
|
-
// private _originalSoundMatrixWorldFunction: Function | null = null;
|
|
328
|
-
// private _onSoundMatrixWorld = (force: boolean) => {
|
|
329
|
-
// if (this._spatialBlend > .05) {
|
|
330
|
-
// if (this._originalSoundMatrixWorldFunction) {
|
|
331
|
-
// this._originalSoundMatrixWorldFunction.call(this.sound, force);
|
|
332
|
-
// }
|
|
333
|
-
// }
|
|
334
|
-
// else {
|
|
335
|
-
// // we use another object's matrix world function (but bound to the positional audio)
|
|
336
|
-
// // this is just a little trick to prevent calling the PositionalAudio's updateMatrixWorld function
|
|
337
|
-
// this.gameObject.updateMatrixWorld?.call(this.sound, force);
|
|
338
|
-
// if (this.sound && this._listener) {
|
|
339
|
-
// this.sound.gain.connect(this._listener.listener.getInput());
|
|
340
|
-
// // const pos = getTempVector().setFromMatrixPosition(this._listener.gameObject.matrixWorld);
|
|
341
|
-
// // const ctx = this.sound.context;
|
|
342
|
-
// // const delay = this._listener.listener.timeDelta;
|
|
343
|
-
// // const time = ctx.currentTime ;
|
|
344
|
-
// // this.sound.panner.positionX.setValueAtTime(pos.x, time);
|
|
345
|
-
// // this.sound.panner.positionY.setValueAtTime(pos.y, time);
|
|
346
|
-
// // this.sound.panner.positionZ.setValueAtTime(pos.z, time);
|
|
347
|
-
// // this.sound.panner.orientationX.setValueAtTime(0, time);
|
|
348
|
-
// // this.sound.panner.orientationY.setValueAtTime(0, time);
|
|
349
|
-
// // this.sound.panner.orientationZ.setValueAtTime(-1, time);
|
|
350
|
-
// }
|
|
351
|
-
// }
|
|
352
|
-
// }
|
|
353
|
-
|
|
354
338
|
/**
|
|
355
339
|
* Indicates whether the audio source is queued to play when possible.
|
|
356
340
|
* This may be true before user interaction has been registered.
|
|
@@ -376,8 +360,10 @@ export class AudioSource extends Behaviour {
|
|
|
376
360
|
if (this.playOnAwake) this.shouldPlay = true;
|
|
377
361
|
|
|
378
362
|
if (this.preload) {
|
|
379
|
-
if (typeof this.clip === "string") {
|
|
380
|
-
this.audioLoader.load(this.clip, this.createAudio, () => { },
|
|
363
|
+
if (typeof this.clip === "string" && this.clip.length > 0) {
|
|
364
|
+
this.audioLoader.load(this.clip, this.createAudio, () => { }, e => {
|
|
365
|
+
console.error(`[AudioSource] Error preloading audio clip "${this.clip}":`, e);
|
|
366
|
+
});
|
|
381
367
|
}
|
|
382
368
|
}
|
|
383
369
|
}
|
|
@@ -433,9 +419,9 @@ export class AudioSource extends Behaviour {
|
|
|
433
419
|
this.sound?.setVolume(this.volume);
|
|
434
420
|
}
|
|
435
421
|
|
|
436
|
-
private createAudio = (buffer?: AudioBuffer) => {
|
|
437
|
-
if(this.destroyed) {
|
|
438
|
-
if(debug) console.warn("AudioSource destroyed, not creating audio", this.name);
|
|
422
|
+
private createAudio = (buffer?: AudioBuffer) => {
|
|
423
|
+
if (this.destroyed) {
|
|
424
|
+
if (debug) console.warn("AudioSource destroyed, not creating audio", this.name);
|
|
439
425
|
return;
|
|
440
426
|
}
|
|
441
427
|
|
|
@@ -451,26 +437,17 @@ export class AudioSource extends Behaviour {
|
|
|
451
437
|
sound.stop();
|
|
452
438
|
|
|
453
439
|
if (buffer) sound.setBuffer(buffer);
|
|
440
|
+
this._loadedClip = this.clip;
|
|
454
441
|
sound.loop = this._loop;
|
|
455
442
|
if (this.context.application.muted) sound.setVolume(0);
|
|
456
443
|
else sound.setVolume(this.volume);
|
|
457
444
|
sound.autoplay = this.shouldPlay && AudioSource.userInteractionRegistered;
|
|
445
|
+
this.setupSpatialBlendNodes();
|
|
458
446
|
this.applySpatialDistanceSettings();
|
|
459
447
|
|
|
460
448
|
if (sound.isPlaying)
|
|
461
449
|
sound.stop();
|
|
462
450
|
|
|
463
|
-
// const panner = sound.panner;
|
|
464
|
-
// panner.coneOuterGain = 1;
|
|
465
|
-
// sound.setDirectionalCone(360, 360, 1);
|
|
466
|
-
// const src = sound.context.createBufferSource();
|
|
467
|
-
// src.buffer = sound.buffer;
|
|
468
|
-
// src.connect(sound.panner);
|
|
469
|
-
// src.start(this.audioContext?.currentTime);
|
|
470
|
-
// const gain = sound.context.createGain();
|
|
471
|
-
// gain.gain.value = 1 - this.spatialBlend;
|
|
472
|
-
// src.connect(gain);
|
|
473
|
-
|
|
474
451
|
// make sure we only play the sound if the user has interacted with the page
|
|
475
452
|
AudioSource.registerWaitForAllowAudio(this.__onAllowAudioCallback);
|
|
476
453
|
}
|
|
@@ -479,30 +456,97 @@ export class AudioSource extends Behaviour {
|
|
|
479
456
|
this.play();
|
|
480
457
|
}
|
|
481
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Sets up the dual-path audio graph for spatial blend crossfading.
|
|
461
|
+
* Creates two parallel signal paths from source to output:
|
|
462
|
+
* - 3D path: entry → panner → spatialGain → gain (spatialized)
|
|
463
|
+
* - 2D path: entry → bypassGain → gain (non-spatialized)
|
|
464
|
+
*/
|
|
465
|
+
private setupSpatialBlendNodes() {
|
|
466
|
+
const sound = this.sound;
|
|
467
|
+
if (!sound || this._entryNode) return;
|
|
468
|
+
|
|
469
|
+
const ctx = sound.context;
|
|
470
|
+
this._entryNode = ctx.createGain();
|
|
471
|
+
this._spatialGain = ctx.createGain();
|
|
472
|
+
this._bypassGain = ctx.createGain();
|
|
473
|
+
|
|
474
|
+
// Disconnect the default panner → gain connection set up by three.js
|
|
475
|
+
try { sound.panner.disconnect(sound.gain); } catch { /* not connected */ }
|
|
476
|
+
|
|
477
|
+
// 3D path: entry → panner → spatialGain → gain
|
|
478
|
+
this._entryNode.connect(sound.panner);
|
|
479
|
+
sound.panner.connect(this._spatialGain);
|
|
480
|
+
this._spatialGain.connect(sound.gain);
|
|
481
|
+
|
|
482
|
+
// 2D path: entry → bypassGain → gain
|
|
483
|
+
this._entryNode.connect(this._bypassGain);
|
|
484
|
+
this._bypassGain.connect(sound.gain);
|
|
485
|
+
|
|
486
|
+
// Override getOutput so three.js connects source → entryNode instead of panner
|
|
487
|
+
sound.getOutput = () => this._entryNode! as unknown as PannerNode;
|
|
488
|
+
|
|
489
|
+
// Wrap connect() to undo PositionalAudio's redundant panner → gain reconnection
|
|
490
|
+
const origConnect = sound.connect;
|
|
491
|
+
sound.connect = function () {
|
|
492
|
+
origConnect.call(sound);
|
|
493
|
+
try { sound.panner.disconnect(sound.gain); } catch { /* expected */ }
|
|
494
|
+
return sound;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// Wrap disconnect() to prevent error when panner → gain doesn't exist
|
|
498
|
+
const origDisconnect = sound.disconnect;
|
|
499
|
+
sound.disconnect = function () {
|
|
500
|
+
try { origDisconnect.call(sound); } catch { /* panner → gain already removed */ }
|
|
501
|
+
return sound;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
this.updateSpatialBlendGains();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Updates the spatial/bypass gain values based on the current spatialBlend. */
|
|
508
|
+
private updateSpatialBlendGains() {
|
|
509
|
+
if (!this._spatialGain || !this._bypassGain || !this.sound) return;
|
|
510
|
+
const t = this.sound.context.currentTime;
|
|
511
|
+
// Use setTargetAtTime for smooth transitions (avoids clicks/pops)
|
|
512
|
+
this._spatialGain.gain.setTargetAtTime(this._spatialBlend, t, 0.01);
|
|
513
|
+
this._bypassGain.gain.setTargetAtTime(1 - this._spatialBlend, t, 0.01);
|
|
514
|
+
}
|
|
515
|
+
|
|
482
516
|
private applySpatialDistanceSettings() {
|
|
483
517
|
const sound = this.sound;
|
|
484
518
|
if (!sound) return;
|
|
485
519
|
this._needUpdateSpatialDistanceSettings = false;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
520
|
+
|
|
521
|
+
const ref = Math.max(0.0001, this._minDistance);
|
|
522
|
+
const max = Math.max(0.01, this._maxDistance);
|
|
523
|
+
if (debug) console.log(this.name, ref, max, this.spatialBlend);
|
|
524
|
+
sound.setRefDistance(ref);
|
|
525
|
+
sound.setMaxDistance(max);
|
|
492
526
|
|
|
493
527
|
// https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/distanceModel
|
|
494
528
|
switch (this.rollOffMode) {
|
|
495
529
|
case AudioRolloffMode.Logarithmic:
|
|
496
|
-
|
|
530
|
+
// Unity's logarithmic rolloff matches Web Audio's 'inverse' model:
|
|
531
|
+
// gain = refDistance / (refDistance + rolloffFactor * (distance - refDistance))
|
|
532
|
+
// We compute rolloffFactor so gain reaches ~0.01 (-40dB) at maxDistance,
|
|
533
|
+
// matching Unity's behavior of effective silence at maxDistance.
|
|
534
|
+
sound.setDistanceModel('inverse');
|
|
535
|
+
sound.setRolloffFactor(99 * ref / Math.max(0.001, max - ref));
|
|
497
536
|
break;
|
|
498
537
|
case AudioRolloffMode.Linear:
|
|
538
|
+
// gain = 1 - rolloffFactor * (distance - refDistance) / (maxDistance - refDistance)
|
|
539
|
+
// With rolloffFactor=1 this reaches exactly 0 at maxDistance.
|
|
499
540
|
sound.setDistanceModel('linear');
|
|
541
|
+
sound.setRolloffFactor(1);
|
|
500
542
|
break;
|
|
501
543
|
case AudioRolloffMode.Custom:
|
|
502
544
|
console.warn("Custom rolloff for AudioSource is not supported: " + this.name);
|
|
503
545
|
break;
|
|
504
546
|
}
|
|
505
547
|
|
|
548
|
+
this.updateSpatialBlendGains();
|
|
549
|
+
|
|
506
550
|
if (this.spatialBlend > 0) {
|
|
507
551
|
if (debug && !this.helper) {
|
|
508
552
|
this.helper = new PositionalAudioHelper(sound, sound.getRefDistance());
|
|
@@ -519,7 +563,7 @@ export class AudioSource extends Behaviour {
|
|
|
519
563
|
if (typeof clip === "string") {
|
|
520
564
|
if (debug)
|
|
521
565
|
console.log(clip);
|
|
522
|
-
if (clip.endsWith(".mp3") || clip.endsWith(".wav")) {
|
|
566
|
+
if (clip.endsWith(".mp3") || clip.endsWith(".wav") || clip.endsWith(".ogg") || clip.endsWith(".flac") || clip.endsWith(".aac") || clip.endsWith(".webm")) {
|
|
523
567
|
if (!this.audioLoader)
|
|
524
568
|
this.audioLoader = new AudioLoader();
|
|
525
569
|
this.shouldPlay = true;
|
|
@@ -530,9 +574,12 @@ export class AudioSource extends Behaviour {
|
|
|
530
574
|
this._lastClipStartedLoading = clip;
|
|
531
575
|
if (debug)
|
|
532
576
|
console.log("load audio", clip);
|
|
533
|
-
const buffer = await this.audioLoader.loadAsync(clip).catch(
|
|
534
|
-
|
|
535
|
-
|
|
577
|
+
const buffer = await this.audioLoader.loadAsync(clip).catch(e => {
|
|
578
|
+
console.error(`[AudioSource] Error loading audio clip "${clip}":`, e);
|
|
579
|
+
return null;
|
|
580
|
+
});
|
|
581
|
+
if (this.destroyed) return;
|
|
582
|
+
if (this._lastClipStartedLoading === clip) this._lastClipStartedLoading = null;
|
|
536
583
|
if (buffer) this.createAudio(buffer);
|
|
537
584
|
}
|
|
538
585
|
else console.warn("Unsupported audio clip type", clip)
|
|
@@ -548,8 +595,9 @@ export class AudioSource extends Behaviour {
|
|
|
548
595
|
* If no argument is provided, plays the currently assigned clip.
|
|
549
596
|
*
|
|
550
597
|
* @param clip - Optional audio clip or {@link MediaStream} to play
|
|
598
|
+
* @returns A promise that resolves when playback starts successfully, or rejects on error
|
|
551
599
|
*/
|
|
552
|
-
play(clip: string | MediaStream | undefined = undefined) {
|
|
600
|
+
async play(clip: string | MediaStream | undefined = undefined): Promise<boolean> {
|
|
553
601
|
// use audio source's clip when no clip is passed in
|
|
554
602
|
if (!clip && this.clip)
|
|
555
603
|
clip = this.clip;
|
|
@@ -557,27 +605,27 @@ export class AudioSource extends Behaviour {
|
|
|
557
605
|
// We only support strings and media stream
|
|
558
606
|
// TODO: maybe we should return here if an invalid value is passed in
|
|
559
607
|
if (clip !== undefined && typeof clip !== "string" && !(clip instanceof MediaStream)) {
|
|
560
|
-
if (isDevEnvironment())
|
|
561
|
-
console.warn("Called play on AudioSource with unknown argument type:", clip + "\nUsing the assigned clip instead:", this.clip)
|
|
562
608
|
// e.g. when a AudioSource.Play is called from SpatialTrigger onEnter this event is called with the TriggerReceiver... to still make this work we *re-use* our already assigned clip. Because otherwise calling `play` would not play the clip...
|
|
563
609
|
clip = this.clip;
|
|
610
|
+
if (isDevEnvironment()) console.warn("[AudioSource] Called play on AudioSource with unknown argument type:", clip + "\nUsing the assigned clip instead:", this.clip)
|
|
564
611
|
}
|
|
565
612
|
|
|
566
|
-
//
|
|
567
|
-
let needsLoading = !this.sound || (clip && clip !== this.
|
|
613
|
+
// Load if the sound hasn't been created yet or if the clip changed since last load
|
|
614
|
+
let needsLoading = !this.sound || (clip && clip !== this._loadedClip);
|
|
568
615
|
if (typeof clip === "string" && !this.audioLoader) needsLoading = true;
|
|
569
616
|
if (clip instanceof MediaStream || typeof clip === "string")
|
|
570
617
|
this.clip = clip;
|
|
571
618
|
if (needsLoading) {
|
|
572
619
|
this.shouldPlay = true;
|
|
573
|
-
this.onNewClip(clip);
|
|
574
|
-
return;
|
|
620
|
+
return this.onNewClip(clip).then(() => true).catch(() => false);
|
|
575
621
|
}
|
|
576
622
|
|
|
577
623
|
this.shouldPlay = true;
|
|
578
624
|
this._hasEnded = false;
|
|
579
|
-
|
|
580
|
-
|
|
625
|
+
|
|
626
|
+
if (debug) console.log("[AudioSource] play", this.sound?.getVolume(), this.sound);
|
|
627
|
+
// If a different clip was passed in, needsLoading above would be true and onNewClip handles it.
|
|
628
|
+
// This guard prevents double-playing the same already-playing sound (which would throw in three.js).
|
|
581
629
|
if (this.sound && !this.sound.isPlaying) {
|
|
582
630
|
const muted = this.context.application.muted;
|
|
583
631
|
if (muted) this.sound.setVolume(0);
|
|
@@ -597,13 +645,17 @@ export class AudioSource extends Behaviour {
|
|
|
597
645
|
this.context.domElement.shadowRoot?.append(this._audioElement);
|
|
598
646
|
this._audioElement.srcObject = this.clip;
|
|
599
647
|
this._audioElement.autoplay = false;
|
|
648
|
+
return true;
|
|
600
649
|
|
|
601
650
|
}
|
|
602
651
|
else {
|
|
603
652
|
if (this._audioElement) this._audioElement.remove();
|
|
604
653
|
this.sound.play(muted ? .1 : 0);
|
|
654
|
+
return true;
|
|
605
655
|
}
|
|
606
656
|
}
|
|
657
|
+
|
|
658
|
+
return false;
|
|
607
659
|
}
|
|
608
660
|
|
|
609
661
|
/**
|
|
@@ -611,7 +663,7 @@ export class AudioSource extends Behaviour {
|
|
|
611
663
|
* Use play() to resume from the paused position.
|
|
612
664
|
*/
|
|
613
665
|
pause() {
|
|
614
|
-
if (debug) console.log("
|
|
666
|
+
if (debug) console.log("[AudioSource] pause", this);
|
|
615
667
|
this._hasEnded = true;
|
|
616
668
|
this.shouldPlay = false;
|
|
617
669
|
if (this.sound && this.sound.isPlaying && this.sound.source) {
|
|
@@ -626,13 +678,13 @@ export class AudioSource extends Behaviour {
|
|
|
626
678
|
* Unlike pause(), calling play() after stop() will start from the beginning.
|
|
627
679
|
*/
|
|
628
680
|
stop() {
|
|
629
|
-
if (debug) console.log("
|
|
681
|
+
if (debug) console.log("[AudioSource] stop", this);
|
|
630
682
|
this._hasEnded = true;
|
|
631
683
|
this.shouldPlay = false;
|
|
632
684
|
if (this.sound && this.sound.source) {
|
|
633
685
|
this._lastContextTime = this.sound?.context.currentTime;
|
|
634
686
|
if (debug)
|
|
635
|
-
console.log(this._lastContextTime)
|
|
687
|
+
console.log("[AudioSource] lastContextTime", this._lastContextTime);
|
|
636
688
|
this.sound.stop();
|
|
637
689
|
}
|
|
638
690
|
this._audioElement?.remove();
|
|
@@ -657,8 +709,7 @@ export class AudioSource extends Behaviour {
|
|
|
657
709
|
|
|
658
710
|
if (this.sound && !this.sound.isPlaying && this.shouldPlay && !this._hasEnded) {
|
|
659
711
|
this._hasEnded = true;
|
|
660
|
-
if (debug)
|
|
661
|
-
console.log("Audio clip ended", this.clip);
|
|
712
|
+
if (debug) console.log("[AudioSource] Audio clip ended", this.clip);
|
|
662
713
|
this.dispatchEvent(new CustomEvent("ended", { detail: this }));
|
|
663
714
|
}
|
|
664
715
|
|
|
@@ -12,6 +12,7 @@ import { getParam } from "../engine/engine_utils.js";
|
|
|
12
12
|
import { NeedleXRSession } from "../engine/engine_xr.js";
|
|
13
13
|
import { Avatar_POI } from "./avatar/Avatar_Brain_LookAt.js";
|
|
14
14
|
import { Behaviour, GameObject } from "./Component.js";
|
|
15
|
+
import { EventList } from "./EventList.js";
|
|
15
16
|
import { UsageMarker } from "./Interactable.js";
|
|
16
17
|
import { Rigidbody } from "./RigidBody.js";
|
|
17
18
|
import { SyncedTransform } from "./SyncedTransform.js";
|
|
@@ -155,7 +156,19 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
155
156
|
@serializable()
|
|
156
157
|
public showGizmo: boolean = false;
|
|
157
158
|
|
|
158
|
-
/**
|
|
159
|
+
/** Invoked once when a drag begins (after the minimum drag distance threshold is met). */
|
|
160
|
+
@serializable(EventList)
|
|
161
|
+
dragStarted: EventList = new EventList();
|
|
162
|
+
|
|
163
|
+
/** Invoked every frame while the object is being dragged. */
|
|
164
|
+
@serializable(EventList)
|
|
165
|
+
dragUpdated: EventList = new EventList();
|
|
166
|
+
|
|
167
|
+
/** Invoked once when the last pointer is released and the drag ends. */
|
|
168
|
+
@serializable(EventList)
|
|
169
|
+
dragEnded: EventList = new EventList();
|
|
170
|
+
|
|
171
|
+
/**
|
|
159
172
|
* Returns the object currently being dragged by this DragControls component, if any.
|
|
160
173
|
* @returns The object being dragged or null if no object is currently dragged
|
|
161
174
|
*/
|
|
@@ -457,6 +470,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
457
470
|
if (!object) return;
|
|
458
471
|
|
|
459
472
|
this._isDragging = true;
|
|
473
|
+
this.dragStarted?.invoke();
|
|
460
474
|
|
|
461
475
|
const sync = GameObject.getComponentInChildren(object, SyncedTransform);
|
|
462
476
|
if (debug) console.log("DRAG START", sync, object);
|
|
@@ -497,9 +511,10 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
497
511
|
const object = this._targetObject || this.gameObject;
|
|
498
512
|
|
|
499
513
|
InstancingUtil.markDirty(object);
|
|
514
|
+
this.dragUpdated?.invoke();
|
|
500
515
|
}
|
|
501
516
|
|
|
502
|
-
/**
|
|
517
|
+
/**
|
|
503
518
|
* Called when the last pointer has been removed from this object.
|
|
504
519
|
* Cleans up drag state and applies final velocities to rigidbodies.
|
|
505
520
|
* @param evt Pointer event data for the last pointer that was lifted
|
|
@@ -507,6 +522,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
507
522
|
private onLastDragEnd(evt: PointerEventData | null) {
|
|
508
523
|
if (!this || !this._isDragging) return;
|
|
509
524
|
this._isDragging = false;
|
|
525
|
+
this.dragEnded?.invoke();
|
|
510
526
|
for (const rb of this._draggingRigidbodies) {
|
|
511
527
|
rb.setVelocity(rb.smoothedVelocity.multiplyScalar(this.context.time.deltaTime));
|
|
512
528
|
}
|
|
@@ -106,11 +106,40 @@ enum LightShadows {
|
|
|
106
106
|
export class Light extends Behaviour implements ILight {
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* The type of light
|
|
110
|
-
* Can not be changed at runtime.
|
|
109
|
+
* The type of light as a lowercase string: `"directional"`, `"point"`, `"spot"`.
|
|
110
|
+
* Implements {@link ILight.type}. Can not be changed at runtime.
|
|
111
111
|
*/
|
|
112
|
+
get type(): ILight["type"] {
|
|
113
|
+
return this._type;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Numeric LightType serialized from Unity/Blender — converts to string on write */
|
|
112
117
|
@serializable()
|
|
113
|
-
|
|
118
|
+
set type(value: LightType | ILight["type"]) {
|
|
119
|
+
if (this.light && this.__didAwake) {
|
|
120
|
+
throw new Error("Changing the light type at runtime is not supported");
|
|
121
|
+
}
|
|
122
|
+
switch (value) {
|
|
123
|
+
case LightType.Directional:
|
|
124
|
+
this._type = "directional";
|
|
125
|
+
break;
|
|
126
|
+
case LightType.Point:
|
|
127
|
+
this._type = "point";
|
|
128
|
+
break;
|
|
129
|
+
case LightType.Spot:
|
|
130
|
+
this._type = "spot";
|
|
131
|
+
break;
|
|
132
|
+
case "directional":
|
|
133
|
+
case "point":
|
|
134
|
+
case "spot":
|
|
135
|
+
this._type = value;
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
throw new Error("Invalid light type: " + value);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
private _type: ILight["type"] = "point";
|
|
142
|
+
|
|
114
143
|
|
|
115
144
|
/**
|
|
116
145
|
* The maximum distance the light affects.
|
|
@@ -345,7 +374,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
345
374
|
*/
|
|
346
375
|
public getWorldPosition(vec: Vector3): Vector3 {
|
|
347
376
|
if (this.light) {
|
|
348
|
-
if (this.type ===
|
|
377
|
+
if (this.type === "directional") {
|
|
349
378
|
return this.light.getWorldPosition(vec).multiplyScalar(1);
|
|
350
379
|
}
|
|
351
380
|
return this.light.getWorldPosition(vec);
|
|
@@ -372,12 +401,13 @@ export class Light extends Behaviour implements ILight {
|
|
|
372
401
|
else if (this.light.parent !== this.gameObject)
|
|
373
402
|
this.gameObject.add(this.light);
|
|
374
403
|
}
|
|
375
|
-
|
|
376
|
-
this.startCoroutine(this.updateMainLightRoutine(), FrameEvent.LateUpdate);
|
|
404
|
+
this.context.lights.push(this);
|
|
377
405
|
}
|
|
378
406
|
|
|
379
407
|
onDisable() {
|
|
380
408
|
if (debug) console.log("DISABLE LIGHT", this.name);
|
|
409
|
+
const index = this.context.lights.indexOf(this);
|
|
410
|
+
if (index !== -1) this.context.lights.splice(index, 1);
|
|
381
411
|
if (this.light) {
|
|
382
412
|
if (this.selfIsLight)
|
|
383
413
|
this.light.intensity = 0;
|
|
@@ -399,14 +429,14 @@ export class Light extends Behaviour implements ILight {
|
|
|
399
429
|
this._intensity = this.light.intensity;
|
|
400
430
|
|
|
401
431
|
switch (this.type) {
|
|
402
|
-
case
|
|
432
|
+
case "directional":
|
|
403
433
|
this.setDirectionalLight(this.light as DirectionalLight);
|
|
404
434
|
break;
|
|
405
435
|
}
|
|
406
436
|
}
|
|
407
437
|
else if (!this.light) {
|
|
408
438
|
switch (this.type) {
|
|
409
|
-
case
|
|
439
|
+
case "directional":
|
|
410
440
|
// console.log(this);
|
|
411
441
|
const dirLight = new DirectionalLight(this.color, this.intensity * Math.PI);
|
|
412
442
|
// directional light target is at 0 0 0 by default
|
|
@@ -425,7 +455,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
425
455
|
}
|
|
426
456
|
break;
|
|
427
457
|
|
|
428
|
-
case
|
|
458
|
+
case "spot":
|
|
429
459
|
const spotLight = new SpotLight(this.color, this.intensity * Math.PI, this.range, toRadians(this.spotAngle / 2), 1 - toRadians(this.innerSpotAngle / 2) / toRadians(this.spotAngle / 2), 2);
|
|
430
460
|
spotLight.position.set(0, 0, 0);
|
|
431
461
|
spotLight.rotation.set(0, 0, 0);
|
|
@@ -437,7 +467,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
437
467
|
spotLightTarget.rotation.set(0, 0, 0);
|
|
438
468
|
break;
|
|
439
469
|
|
|
440
|
-
case
|
|
470
|
+
case "point":
|
|
441
471
|
const pointLight = new PointLight(this.color, this.intensity * Math.PI, this.range);
|
|
442
472
|
this.light = pointLight;
|
|
443
473
|
|
|
@@ -526,22 +556,6 @@ export class Light extends Behaviour implements ILight {
|
|
|
526
556
|
|
|
527
557
|
}
|
|
528
558
|
|
|
529
|
-
/**
|
|
530
|
-
* Coroutine that updates the main light reference in the context
|
|
531
|
-
* if this directional light should be the main light
|
|
532
|
-
*/
|
|
533
|
-
*updateMainLightRoutine() {
|
|
534
|
-
while (true) {
|
|
535
|
-
if (this.type === LightType.Directional) {
|
|
536
|
-
if (!this.context.mainLight || this.intensity > this.context.mainLight.intensity) {
|
|
537
|
-
this.context.mainLight = this;
|
|
538
|
-
}
|
|
539
|
-
yield;
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
559
|
/**
|
|
546
560
|
* Controls whether the renderer's shadow map type can be changed when soft shadows are used
|
|
547
561
|
*/
|
|
@@ -37,7 +37,7 @@ const debug = getParam("debugnet");
|
|
|
37
37
|
* @see {@link RoomEvents} for room lifecycle events
|
|
38
38
|
* @see {@link isLocalNetwork} for local network detection
|
|
39
39
|
* @link https://engine.needle.tools/docs/how-to-guides/networking/
|
|
40
|
-
* @summary
|
|
40
|
+
* @summary Configures the websocket server URL for multiplayer networking
|
|
41
41
|
* @category Networking
|
|
42
42
|
* @group Components
|
|
43
43
|
*/
|
|
@@ -758,19 +758,26 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
758
758
|
|
|
759
759
|
if (this.targetBounds) {
|
|
760
760
|
// #region target bounds
|
|
761
|
-
const targetVector = this._controls.target;
|
|
762
761
|
const boundsCenter = this.targetBounds.worldPosition;
|
|
763
762
|
const boundsHalfSize = getTempVector(this.targetBounds.worldScale).multiplyScalar(0.5);
|
|
764
763
|
const min = getTempVector(boundsCenter).sub(boundsHalfSize);
|
|
765
764
|
const max = getTempVector(boundsCenter).add(boundsHalfSize);
|
|
766
|
-
|
|
767
|
-
const duration = .1;
|
|
768
|
-
if (duration <= 0) targetVector.copy(newTarget);
|
|
769
|
-
else targetVector.lerp(newTarget, this.context.time.deltaTime / duration);
|
|
765
|
+
|
|
770
766
|
if (this._lookTargetLerpActive) {
|
|
771
|
-
|
|
772
|
-
|
|
767
|
+
// During a programmatic transition (fitCamera / setLookTargetPosition with immediate: false),
|
|
768
|
+
// only clamp the destination. The look-target lerp (above) handles moving _controls.target
|
|
769
|
+
// towards the endpoint — we must not fight it by also lerping _controls.target here.
|
|
770
|
+
this._lookTargetEndPosition.clamp(min, max);
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
// Interactive use (pan/orbit): smoothly push the target back into bounds
|
|
774
|
+
const targetVector = this._controls.target;
|
|
775
|
+
const newTarget = getTempVector(targetVector).clamp(min, max);
|
|
776
|
+
const duration = .1;
|
|
777
|
+
if (duration <= 0) targetVector.copy(newTarget);
|
|
778
|
+
else targetVector.lerp(newTarget, Math.min(1, this.context.time.deltaTime / duration));
|
|
773
779
|
}
|
|
780
|
+
|
|
774
781
|
if (debug) {
|
|
775
782
|
Gizmos.DrawWireBox(boundsCenter, boundsHalfSize.multiplyScalar(2), 0xffaa00);
|
|
776
783
|
}
|
|
@@ -847,7 +854,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
847
854
|
this._controls.update(this.context.time.deltaTime);
|
|
848
855
|
|
|
849
856
|
if (debug) {
|
|
850
|
-
|
|
857
|
+
const distance = this._controls.getDistance();
|
|
858
|
+
Gizmos.DrawWireSphere(this._controls.target, 0.01 * distance, 0x00ff00);
|
|
851
859
|
}
|
|
852
860
|
}
|
|
853
861
|
}
|
|
@@ -1022,7 +1030,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
|
|
|
1022
1030
|
|
|
1023
1031
|
if (debug) {
|
|
1024
1032
|
console.warn("OrbitControls: setLookTargetPosition", position, immediateOrDuration);
|
|
1025
|
-
|
|
1033
|
+
const distance = this._controls.getDistance();
|
|
1034
|
+
Gizmos.DrawWireSphere(this._lookTargetEndPosition, 0.01 * distance, 0xff5500, 2);
|
|
1026
1035
|
}
|
|
1027
1036
|
|
|
1028
1037
|
if (immediateOrDuration === true) {
|
|
@@ -290,6 +290,8 @@ export class ReflectionProbe extends Behaviour {
|
|
|
290
290
|
const current = block.getOverride("envMap")?.value;
|
|
291
291
|
if (current === this.texture) {
|
|
292
292
|
block.removeOveride("envMap");
|
|
293
|
+
block.removeOveride("envMapRotation");
|
|
294
|
+
block.removeOveride("envMapIntensity");
|
|
293
295
|
}
|
|
294
296
|
}
|
|
295
297
|
}
|
|
@@ -382,12 +382,12 @@ export class Rigidbody extends Behaviour implements IRigidbody {
|
|
|
382
382
|
this._watch.start(true, true);
|
|
383
383
|
this.startCoroutine(this.beforePhysics(), FrameEvent.LateUpdate);
|
|
384
384
|
if (isDevEnvironment()) {
|
|
385
|
-
if (
|
|
386
|
-
console.warn(`
|
|
385
|
+
if (globalThis["NEEDLE_USE_RAPIER"] === false)
|
|
386
|
+
console.warn(`RAPIER physics are disabled in your build. Enable them by setting NEEDLE_USE_RAPIER to true in your build config: Rigidbody could not be created.`);
|
|
387
387
|
else {
|
|
388
388
|
MODULES.RAPIER_PHYSICS.ready().then(async () => {
|
|
389
389
|
await delayForFrames(3);
|
|
390
|
-
if (!this.context.physics.engine?.getBody(this))
|
|
390
|
+
if (this.activeAndEnabled && !this.context.physics.engine?.getBody(this))
|
|
391
391
|
console.warn(`Rigidbody could not be created. Ensure \"${this.name}\" has a Collider component.`);
|
|
392
392
|
})
|
|
393
393
|
}
|