@needle-tools/engine 3.2.0-alpha → 3.2.2-alpha
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/CHANGELOG.md +9 -0
- package/dist/needle-engine.js +10056 -9949
- package/dist/needle-engine.min.js +263 -263
- package/dist/needle-engine.umd.cjs +272 -272
- package/lib/engine/engine_gltf.js +4 -0
- package/lib/engine/engine_gltf.js.map +1 -1
- package/lib/engine/engine_rendererdata.d.ts +1 -1
- package/lib/engine/engine_rendererdata.js +3 -1
- package/lib/engine/engine_rendererdata.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lighting_settings.js +1 -1
- package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
- package/lib/engine-components/ParticleSystem.js +2 -1
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/ParticleSystemModules.d.ts +17 -2
- package/lib/engine-components/ParticleSystemModules.js +105 -14
- package/lib/engine-components/ParticleSystemModules.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +2 -1
- package/lib/engine-components/SceneSwitcher.js +29 -11
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js +6 -2
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/SignalAsset.d.ts +5 -7
- package/lib/engine-components/timeline/SignalAsset.js +38 -6
- package/lib/engine-components/timeline/SignalAsset.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.js +31 -9
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/engine/engine_gltf.ts +6 -2
- package/src/engine/engine_rendererdata.ts +2 -1
- package/src/engine/extensions/NEEDLE_lighting_settings.ts +1 -1
- package/src/engine-components/ParticleSystem.ts +2 -1
- package/src/engine-components/ParticleSystemModules.ts +1483 -1400
- package/src/engine-components/SceneSwitcher.ts +32 -14
- package/src/engine-components/timeline/PlayableDirector.ts +13 -10
- package/src/engine-components/timeline/SignalAsset.ts +35 -8
- package/src/engine-components/timeline/TimelineTracks.ts +36 -12
|
@@ -7,6 +7,8 @@ import { getParam, isMobileDevice, setParamWithoutReload } from "../engine/engin
|
|
|
7
7
|
import { serializable } from "../engine/engine_serialization";
|
|
8
8
|
import { Behaviour, GameObject } from "./Component";
|
|
9
9
|
|
|
10
|
+
const debug = getParam("debugsceneswitcher");
|
|
11
|
+
|
|
10
12
|
const ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME = "scene";
|
|
11
13
|
|
|
12
14
|
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, _ => {
|
|
@@ -25,6 +27,9 @@ export class SceneSwitcher extends Behaviour {
|
|
|
25
27
|
@serializable()
|
|
26
28
|
queryParameterName: string = "scene";
|
|
27
29
|
|
|
30
|
+
@serializable()
|
|
31
|
+
clamp: boolean = true;
|
|
32
|
+
|
|
28
33
|
/** when enabled the new scene is pushed to the browser navigation history, only works with a valid query parameter set */
|
|
29
34
|
@serializable()
|
|
30
35
|
useHistory: boolean = true;
|
|
@@ -37,18 +42,21 @@ export class SceneSwitcher extends Behaviour {
|
|
|
37
42
|
@serializable()
|
|
38
43
|
useSwipe: boolean = true;
|
|
39
44
|
|
|
45
|
+
|
|
40
46
|
private _currentIndex: number = -1;
|
|
41
47
|
private _currentScene: AssetReference | undefined = undefined;
|
|
42
48
|
private _engineElementOverserver: MutationObserver | undefined = undefined;
|
|
43
49
|
|
|
44
|
-
start() {
|
|
45
|
-
if (!this.tryLoadFromQueryParam()) {
|
|
50
|
+
async start() {
|
|
51
|
+
if (this._currentIndex === -1 && !await this.tryLoadFromQueryParam()) {
|
|
46
52
|
const value = this.context.domElement.getAttribute(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
|
|
47
53
|
// let locked = this.lock;
|
|
48
54
|
try {
|
|
49
55
|
// this.lock = false;
|
|
50
|
-
if (value === null || !this.trySelectSceneFromValue(value))
|
|
51
|
-
this.
|
|
56
|
+
if (value === null || !await this.trySelectSceneFromValue(value)) {
|
|
57
|
+
if (this._currentIndex === -1)
|
|
58
|
+
this.select(0);
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
61
|
finally {
|
|
54
62
|
// this.lock = locked;
|
|
@@ -146,18 +154,25 @@ export class SceneSwitcher extends Behaviour {
|
|
|
146
154
|
}
|
|
147
155
|
}
|
|
148
156
|
|
|
149
|
-
selectNext() {
|
|
157
|
+
selectNext(): Promise<boolean> {
|
|
150
158
|
return this.select(this._currentIndex + 1);
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
selectPrev() {
|
|
161
|
+
selectPrev(): Promise<boolean> {
|
|
154
162
|
return this.select(this._currentIndex - 1);
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
select(index: number) {
|
|
165
|
+
select(index: number): Promise<boolean> {
|
|
166
|
+
if (debug) console.log("select", index)
|
|
158
167
|
if (!this.scenes?.length) return couldNotLoadScenePromise;
|
|
159
|
-
if (index < 0)
|
|
160
|
-
|
|
168
|
+
if (index < 0) {
|
|
169
|
+
if (this.clamp) return couldNotLoadScenePromise;
|
|
170
|
+
index = this.scenes.length - 1;
|
|
171
|
+
}
|
|
172
|
+
else if (index >= this.scenes.length) {
|
|
173
|
+
if (this.clamp) return couldNotLoadScenePromise;
|
|
174
|
+
index = 0;
|
|
175
|
+
}
|
|
161
176
|
const scene = this.scenes[index];
|
|
162
177
|
return this.switchScene(scene);
|
|
163
178
|
}
|
|
@@ -165,12 +180,15 @@ export class SceneSwitcher extends Behaviour {
|
|
|
165
180
|
async switchScene(scene: AssetReference): Promise<boolean> {
|
|
166
181
|
if (scene === this._currentScene) return true;
|
|
167
182
|
if (this._currentScene)
|
|
168
|
-
|
|
183
|
+
this._currentScene.unload();
|
|
169
184
|
const index = this._currentIndex = this.scenes?.indexOf(scene) ?? -1;
|
|
170
185
|
this._currentScene = scene;
|
|
171
186
|
try {
|
|
172
187
|
await scene.loadAssetAsync();
|
|
173
|
-
if (!scene.asset)
|
|
188
|
+
if (!scene.asset) {
|
|
189
|
+
if (debug) console.warn("Failed loading scene:", scene);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
174
192
|
if (this._currentIndex === index) {
|
|
175
193
|
GameObject.add(scene.asset, this.gameObject);
|
|
176
194
|
// save the loaded scene as an url parameter
|
|
@@ -186,10 +204,10 @@ export class SceneSwitcher extends Behaviour {
|
|
|
186
204
|
}
|
|
187
205
|
|
|
188
206
|
private tryLoadFromQueryParam() {
|
|
189
|
-
if (!this.queryParameterName?.length) return
|
|
207
|
+
if (!this.queryParameterName?.length) return couldNotLoadScenePromise;
|
|
190
208
|
// try restore the scene from the url
|
|
191
209
|
const value = getParam(this.queryParameterName);
|
|
192
|
-
if (typeof value === "boolean") return
|
|
210
|
+
if (typeof value === "boolean") return couldNotLoadScenePromise;
|
|
193
211
|
return this.trySelectSceneFromValue(value);
|
|
194
212
|
}
|
|
195
213
|
|
|
@@ -217,7 +235,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
217
235
|
}
|
|
218
236
|
}
|
|
219
237
|
|
|
220
|
-
if (isLocalNetwork()) console.warn("
|
|
238
|
+
if (isLocalNetwork()) console.warn("Can not find scene: \"" + value + "\"", this)
|
|
221
239
|
|
|
222
240
|
return couldNotLoadScenePromise;
|
|
223
241
|
}
|
|
@@ -172,7 +172,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
172
172
|
await Promise.all(promises);
|
|
173
173
|
if (!this._isPlaying) return;
|
|
174
174
|
}
|
|
175
|
-
while(this._audioTracks.length > 0 && this._isPlaying && !AudioSource.userInteractionRegistered && this.waitForAudio)
|
|
175
|
+
while (this._audioTracks.length > 0 && this._isPlaying && !AudioSource.userInteractionRegistered && this.waitForAudio)
|
|
176
176
|
await delay(200);
|
|
177
177
|
}
|
|
178
178
|
this._internalUpdateRoutine = this.startCoroutine(this.internalUpdate());
|
|
@@ -211,9 +211,9 @@ export class PlayableDirector extends Behaviour {
|
|
|
211
211
|
let t = this._time;
|
|
212
212
|
switch (this.extrapolationMode) {
|
|
213
213
|
case DirectorWrapMode.Hold:
|
|
214
|
-
if(this._speed > 0)
|
|
214
|
+
if (this._speed > 0)
|
|
215
215
|
t = Math.min(t, this._duration);
|
|
216
|
-
else if(this._speed < 0)
|
|
216
|
+
else if (this._speed < 0)
|
|
217
217
|
t = Math.max(t, 0);
|
|
218
218
|
this._time = t;
|
|
219
219
|
break;
|
|
@@ -242,7 +242,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
get audioTracks()
|
|
245
|
+
get audioTracks(): Tracks.AudioTrackHandler[] {
|
|
246
246
|
return this._audioTracks;
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -362,7 +362,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
362
362
|
if (debug)
|
|
363
363
|
console.log("Resolved binding", binding, "to", obj);
|
|
364
364
|
track.outputs[i] = obj;
|
|
365
|
-
if(obj instanceof Animator) {
|
|
365
|
+
if (obj instanceof Animator) {
|
|
366
366
|
// TODO: should disable? animator but this is not the animator that is currently on the object? needs investigation
|
|
367
367
|
// console.log("DISABLE ANIMATOR", obj, obj.name, binding, this._guidsMap);
|
|
368
368
|
// obj.enabled = false;
|
|
@@ -376,7 +376,7 @@ export class PlayableDirector extends Behaviour {
|
|
|
376
376
|
continue;
|
|
377
377
|
}
|
|
378
378
|
// if the binding is missing remove it to avoid unnecessary loops
|
|
379
|
-
if (track.type !== Models.TrackType.Audio && track.type !== Models.TrackType.Control && track.type !== Models.TrackType.Marker)
|
|
379
|
+
if (track.type !== Models.TrackType.Audio && track.type !== Models.TrackType.Control && track.type !== Models.TrackType.Marker && track.type !== Models.TrackType.Signal)
|
|
380
380
|
console.warn("Missing binding", binding, track.name, track.type, this.name, this.playableAsset.name);
|
|
381
381
|
}
|
|
382
382
|
}
|
|
@@ -416,8 +416,11 @@ export class PlayableDirector extends Behaviour {
|
|
|
416
416
|
for (const clip of track.clips) {
|
|
417
417
|
if (clip.end > this._duration) this._duration = clip.end;
|
|
418
418
|
}
|
|
419
|
+
for(const marker of track.markers){
|
|
420
|
+
if (marker.time > this._duration) this._duration = marker.time + .001;
|
|
421
|
+
}
|
|
419
422
|
}
|
|
420
|
-
// console.log("timeline duration", this._duration);
|
|
423
|
+
// console.log("timeline duration", this._duration, this.playableAsset);
|
|
421
424
|
}
|
|
422
425
|
|
|
423
426
|
private setupAndCreateTrackHandlers() {
|
|
@@ -442,15 +445,15 @@ export class PlayableDirector extends Behaviour {
|
|
|
442
445
|
// only handle animation tracks
|
|
443
446
|
if (track.type === Models.TrackType.Animation) {
|
|
444
447
|
if (track.clips.length <= 0) {
|
|
445
|
-
if(debug) console.warn("Animation track has no clips", track);
|
|
448
|
+
if (debug) console.warn("Animation track has no clips", track);
|
|
446
449
|
continue;
|
|
447
450
|
}
|
|
448
451
|
// loop outputs / bindings, they should contain animator references
|
|
449
452
|
for (let i = track.outputs.length - 1; i >= 0; i--) {
|
|
450
453
|
let binding = track.outputs[i] as Animator;
|
|
451
|
-
if(binding instanceof Object3D){
|
|
454
|
+
if (binding instanceof Object3D) {
|
|
452
455
|
const anim = GameObject.getOrAddComponent(binding, Animator);
|
|
453
|
-
if(anim) binding = anim;
|
|
456
|
+
if (anim) binding = anim;
|
|
454
457
|
}
|
|
455
458
|
const animationClips = binding?.gameObject?.animations;
|
|
456
459
|
if (animationClips) {
|
|
@@ -7,28 +7,55 @@ import { serializable } from "../../engine/engine_serialization_decorator";
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
export class SignalAsset {
|
|
10
|
+
@serializable()
|
|
10
11
|
guid!: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export class SignalReceiverEvent
|
|
14
|
+
export class SignalReceiverEvent {
|
|
15
|
+
@serializable(SignalAsset)
|
|
14
16
|
signal!: SignalAsset;
|
|
17
|
+
@serializable(EventList)
|
|
15
18
|
reaction!: EventList;
|
|
16
|
-
|
|
17
|
-
$serializedTypes = {
|
|
18
|
-
signal: SignalAsset,
|
|
19
|
-
reaction: EventList
|
|
20
|
-
}
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export class SignalReceiver extends Behaviour {
|
|
24
22
|
|
|
23
|
+
private static receivers: { [key: string]: SignalReceiver[] } = {};
|
|
24
|
+
|
|
25
|
+
static invoke(guid: string) {
|
|
26
|
+
if (SignalReceiver.receivers[guid]) {
|
|
27
|
+
const receivers = SignalReceiver.receivers[guid];
|
|
28
|
+
if (!receivers) return;
|
|
29
|
+
for (const rec of receivers)
|
|
30
|
+
rec.invoke(guid);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
25
35
|
|
|
26
36
|
@serializable(SignalReceiverEvent)
|
|
27
37
|
events?: SignalReceiverEvent[];
|
|
28
38
|
|
|
39
|
+
onEnable(): void {
|
|
40
|
+
if (this.events) {
|
|
41
|
+
for (const evt of this.events) {
|
|
42
|
+
if (!SignalReceiver.receivers[evt.signal.guid])
|
|
43
|
+
SignalReceiver.receivers[evt.signal.guid] = [];
|
|
44
|
+
SignalReceiver.receivers[evt.signal.guid].push(this);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
29
48
|
|
|
30
|
-
|
|
31
|
-
|
|
49
|
+
onDisable(): void {
|
|
50
|
+
if (this.events) {
|
|
51
|
+
for (const evt of this.events) {
|
|
52
|
+
if (SignalReceiver.receivers[evt.signal.guid]) {
|
|
53
|
+
const idx = SignalReceiver.receivers[evt.signal.guid].indexOf(this);
|
|
54
|
+
if (idx >= 0)
|
|
55
|
+
SignalReceiver.receivers[evt.signal.guid].splice(idx, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
32
59
|
}
|
|
33
60
|
|
|
34
61
|
invoke(sig: SignalAsset | string) {
|
|
@@ -225,8 +225,7 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
225
225
|
// We need to disable the animator component in case it also animates
|
|
226
226
|
// which overrides the timeline
|
|
227
227
|
this._animator = GameObject.getComponent(this.target, Animator) ?? null;
|
|
228
|
-
if (this._animator)
|
|
229
|
-
{
|
|
228
|
+
if (this._animator) {
|
|
230
229
|
this._animatorWasEnabled = this._animator.enabled;
|
|
231
230
|
this._animator.enabled = false;
|
|
232
231
|
}
|
|
@@ -331,7 +330,7 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
331
330
|
weight *= this.director.weight;
|
|
332
331
|
|
|
333
332
|
let handleLoop = isInTimeRange;
|
|
334
|
-
if(doPreExtrapolate){
|
|
333
|
+
if (doPreExtrapolate) {
|
|
335
334
|
if (preExtrapolation !== Models.ClipExtrapolation.Hold) {
|
|
336
335
|
time += model.start;
|
|
337
336
|
handleLoop = true;
|
|
@@ -666,7 +665,7 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
666
665
|
public static dispose() {
|
|
667
666
|
AudioTrackHandler._currentlyLoading.clear();
|
|
668
667
|
}
|
|
669
|
-
|
|
668
|
+
|
|
670
669
|
private handleAudioLoading(model: Models.ClipModel, audio: THREE.Audio): Promise<AudioBuffer | null> | null {
|
|
671
670
|
if (!this._audioLoader) {
|
|
672
671
|
this._audioLoader = new THREE.AudioLoader();
|
|
@@ -681,7 +680,7 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
681
680
|
});
|
|
682
681
|
return promise;
|
|
683
682
|
}
|
|
684
|
-
|
|
683
|
+
|
|
685
684
|
if (debug) console.warn("LOAD audio track", path, this.director.sourceId);
|
|
686
685
|
const loadingPromise = new Promise<AudioBuffer | null>((resolve, _reject) => {
|
|
687
686
|
this._audioLoader!.load(path,
|
|
@@ -705,23 +704,48 @@ export class SignalTrackHandler extends TrackHandler {
|
|
|
705
704
|
didTrigger: boolean[] = [];
|
|
706
705
|
receivers: Array<SignalReceiver | null> = [];
|
|
707
706
|
|
|
707
|
+
// private _lastTime: number = -1;
|
|
708
|
+
|
|
708
709
|
evaluate(time: number) {
|
|
709
|
-
if (this.receivers.length <= 0) return;
|
|
710
710
|
if (this.track.muted) return;
|
|
711
|
+
|
|
712
|
+
// let lastTime = this._lastTime;
|
|
713
|
+
// if (lastTime === -1) lastTime = time;
|
|
714
|
+
// this._lastTime = time;
|
|
715
|
+
|
|
711
716
|
for (let i = 0; i < this.models.length; i++) {
|
|
712
717
|
const model = this.models[i];
|
|
713
718
|
const wasTriggered = this.didTrigger[i];
|
|
714
719
|
const td = model.time - time;
|
|
715
|
-
let isActive =
|
|
720
|
+
let isActive = false;
|
|
721
|
+
if (model.retroActive) {
|
|
722
|
+
isActive = td <= 0.000001;
|
|
723
|
+
}
|
|
724
|
+
// TODO: handle signal asset at time 0 (only trigger when time is 0)
|
|
725
|
+
// else if (model.time < .001) {
|
|
726
|
+
// if (td <= 0.000001 && lastTime > time)
|
|
727
|
+
// isActive = true;
|
|
728
|
+
// }
|
|
729
|
+
else {
|
|
730
|
+
const abs = Math.abs(td);
|
|
731
|
+
if (abs >= .00001 && abs < .1) {
|
|
732
|
+
isActive = true;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
// console.log(time, td, isActive);
|
|
716
736
|
if (isActive) {
|
|
717
737
|
if (!wasTriggered) {
|
|
718
738
|
this.didTrigger[i] = true;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
739
|
+
// If a signal doesnt have any explicit receivers it will invoke the signal globally
|
|
740
|
+
if (this.receivers?.length <= 0) {
|
|
741
|
+
SignalReceiver.invoke(model.asset);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
for (const rec of this.receivers) {
|
|
745
|
+
if (!rec) continue;
|
|
746
|
+
rec.invoke(model.asset);
|
|
747
|
+
}
|
|
722
748
|
}
|
|
723
|
-
// console.log("TRIGGER " + model.asset);
|
|
724
|
-
// TimelineSignals.invoke(model.asset);
|
|
725
749
|
}
|
|
726
750
|
}
|
|
727
751
|
else {
|