@needle-tools/engine 2.67.14-pre → 2.67.16-pre

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.67.14-pre",
3
+ "version": "2.67.16-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "module": "lib/needle-engine.js",
@@ -48,7 +48,9 @@ generateBMFont(fontPath, opts,
48
48
  (error, textures, font) => {
49
49
  if (error) throw error;
50
50
  textures.forEach((texture, index) => {
51
- const fileName = path.parse(texture.filename).name.toLocaleLowerCase() + ".png";
51
+ const directory = path.dirname(texture.filename);
52
+ // remove the directory from the filename, we can not rely on path.parse because it fails if the filename contains a dot
53
+ const fileName = texture.filename.substring(directory.length) + ".png";
52
54
  const outputPath = outputDir + "/" + fileName;
53
55
  console.log("Write to", outputPath);
54
56
  if (index > 0) console.log("WARN: Multiple font textures generated but they will override each other. You have currently " + charset?.length + " characters configured. Maybe too many?");
@@ -58,7 +60,7 @@ generateBMFont(fontPath, opts,
58
60
  });
59
61
 
60
62
  const fileName = path.parse(font.filename).name;
61
- const name = outputDir + "/" + fileName.toLocaleLowerCase() + "-msdf.json";
63
+ const name = outputDir + "/" + fileName + "-msdf.json";
62
64
  fs.writeFile(name, font.data, (err) => {
63
65
  if (err) throw err;
64
66
  });
@@ -1,5 +1,5 @@
1
1
  import { TypeStore } from "./../engine_typestore"
2
-
2
+
3
3
  // Import types
4
4
  import { __Ignore } from "../../engine-components/codegen/components";
5
5
  import { AlignmentConstraint } from "../../engine-components/AlignmentConstraint";
@@ -179,7 +179,7 @@ import { XRGrabModel } from "../../engine-components/WebXRGrabRendering";
179
179
  import { XRGrabRendering } from "../../engine-components/WebXRGrabRendering";
180
180
  import { XRRig } from "../../engine-components/WebXRRig";
181
181
  import { XRState } from "../../engine-components/XRFlag";
182
-
182
+
183
183
  // Register types
184
184
  TypeStore.add("__Ignore", __Ignore);
185
185
  TypeStore.add("AlignmentConstraint", AlignmentConstraint);
@@ -6,7 +6,7 @@ import { AudioSource } from '../AudioSource';
6
6
  import { SignalReceiver } from './SignalAsset';
7
7
  import * as Models from "./TimelineModels";
8
8
  import * as Tracks from "./TimelineTracks";
9
- import { deepClone, getParam } from '../../engine/engine_utils';
9
+ import { deepClone, delay, getParam } from '../../engine/engine_utils';
10
10
  import { GuidsMap } from '../../engine/engine_types';
11
11
  import { Object3D } from 'three';
12
12
  import { isLocalNetwork } from '../../engine/engine_networking_utils';
@@ -87,6 +87,8 @@ export class PlayableDirector extends Behaviour {
87
87
  get speed(): number { return this._speed; }
88
88
  set speed(value: number) { this._speed = value; }
89
89
 
90
+ /** When enabled the timeline will wait for audio tracks to load at the current time before starting to play */
91
+ waitForAudio: boolean = true;
90
92
 
91
93
  private _visibilityChangeEvt?: any;
92
94
  private _clonedPlayableAsset: boolean = false;
@@ -151,13 +153,28 @@ export class PlayableDirector extends Behaviour {
151
153
  this.setupAndCreateTrackHandlers();
152
154
  }
153
155
 
154
- play() {
156
+ async play() {
155
157
  if (!this.isValid()) return;
156
158
  const pauseChanged = this._isPaused == true;
157
159
  this._isPaused = false;
158
160
  if (pauseChanged) this.invokePauseChangedMethodsOnTracks();
159
161
  if (this._isPlaying) return;
160
162
  this._isPlaying = true;
163
+ if (this.waitForAudio) {
164
+ // Make sure audio tracks have loaded at the current time
165
+ const promises: Array<Promise<any>> = [];
166
+ for (const track of this._audioTracks) {
167
+ const promise = track.loadAudio(this._time, 1, 0);
168
+ if (promise)
169
+ promises.push(promise);
170
+ }
171
+ if (promises.length > 0) {
172
+ await Promise.all(promises);
173
+ if (!this._isPlaying) return;
174
+ }
175
+ while(this._audioTracks.length > 0 && this._isPlaying && !AudioSource.userInteractionRegistered && this.waitForAudio)
176
+ await delay(200);
177
+ }
161
178
  this._internalUpdateRoutine = this.startCoroutine(this.internalUpdate());
162
179
  }
163
180
 
@@ -363,6 +380,25 @@ export class PlayableDirector extends Behaviour {
363
380
  console.warn("Missing binding", binding, track.name, track.type, this.name, this.playableAsset.name);
364
381
  }
365
382
  }
383
+ if (track.type === Models.TrackType.Control) {
384
+ for (let i = 0; i < track.clips.length; i++) {
385
+ const clip = track.clips[i];
386
+ let binding = clip.asset.sourceObject;
387
+ if (typeof binding === "string") {
388
+ if (this._guidsMap && this._guidsMap[binding])
389
+ binding = this._guidsMap[binding];
390
+ const obj = GameObject.findByGuid(binding, root);
391
+ if (obj === null || typeof obj !== "object") {
392
+ console.warn("Failed to resolve sourceObject binding", binding, track.name, clip);
393
+ }
394
+ else {
395
+ if (debug)
396
+ console.log("Resolved binding", binding, "to", obj);
397
+ clip.asset.sourceObject = obj;
398
+ }
399
+ }
400
+ }
401
+ }
366
402
  }
367
403
  }
368
404
 
@@ -657,6 +657,9 @@ export class AudioTrackHandler extends TrackHandler {
657
657
  }
658
658
 
659
659
  private static _currentlyLoading: Map<string, Promise<AudioBuffer | null>> = new Map();
660
+ public static dispose() {
661
+ AudioTrackHandler._currentlyLoading.clear();
662
+ }
660
663
 
661
664
  private handleAudioLoading(model: Models.ClipModel, audio: THREE.Audio): Promise<AudioBuffer | null> | null {
662
665
  if (!this._audioLoader) {
@@ -665,8 +668,12 @@ export class AudioTrackHandler extends TrackHandler {
665
668
  // TODO: maybe we should cache the loaders / buffers here by path
666
669
  const path = this.getAudioFilePath(model.asset.clip);
667
670
 
668
- if(AudioTrackHandler._currentlyLoading.get(path)) {
669
- return AudioTrackHandler._currentlyLoading.get(path)!;
671
+ if (AudioTrackHandler._currentlyLoading.get(path)) {
672
+ const promise = AudioTrackHandler._currentlyLoading.get(path)!
673
+ promise.then((buffer) => {
674
+ if (buffer) audio.setBuffer(buffer);
675
+ });
676
+ return promise;
670
677
  }
671
678
 
672
679
  if (debug) console.warn("LOAD audio track", path, this.director.sourceId);
@@ -724,23 +731,13 @@ export class ControlTrackHandler extends TrackHandler {
724
731
  models: Array<Models.ClipModel> = [];
725
732
  timelines: Array<PlayableDirector | null> = [];
726
733
 
727
- private static resolved: { [key: string]: THREE.Object3D } = {};
728
-
729
- resolveSourceObjects(context: Context) {
734
+ resolveSourceObjects(_context: Context) {
730
735
  for (let i = this.models.length - 1; i >= 0; i--) {
731
736
  const model = this.models[i];
732
737
  const asset = model.asset as Models.ControlClipModel;
733
- if (typeof asset.sourceObject === "string") {
734
- const key = asset.sourceObject;
735
- if (ControlTrackHandler.resolved[key]) {
736
- asset.sourceObject = ControlTrackHandler.resolved[key];
737
- }
738
- else {
739
- asset.sourceObject = GameObject.findByGuid(key, context.scene) as THREE.Object3D
740
- ControlTrackHandler.resolved[key] = asset.sourceObject;
741
- }
742
- }
743
- if (!asset.sourceObject) {
738
+
739
+ if (!asset.sourceObject || typeof asset.sourceObject !== "object") {
740
+ console.log("no source object, removing model", i, asset);
744
741
  this.models.splice(i, 1);
745
742
  continue;
746
743
  }
@@ -176,7 +176,6 @@ export class Text extends Graphic {
176
176
  fontSize: fontSize,
177
177
  fontKerning: "normal",
178
178
  };
179
- this.font = this.font?.toLocaleLowerCase();
180
179
  this.setFont(textOpts, this.fontStyle);
181
180
  return textOpts;
182
181
  }