Needle Engine

Changes between version 3.37.5-alpha and 3.37.6-alpha
Files changed (4) hide show
  1. src/engine-components/DropListener.ts +30 -2
  2. src/engine/extensions/NEEDLE_progressive.ts +9 -1
  3. src/engine-components/VideoPlayer.ts +25 -13
  4. src/engine-components/webxr/WebARCameraBackground.ts +9 -0
src/engine-components/DropListener.ts CHANGED
@@ -10,12 +10,40 @@
10
10
  const debug = getParam("debugdroplistener");
11
11
 
12
12
  export enum DropListenerEvents {
13
+ /**
14
+ * Dispatched when a file is dropped into the scene. The detail of the event is the file that was dropped.
15
+ */
16
+ FileDropped = "file-dropped",
17
+ /**
18
+ * Dispatched when a new object is added to the scene. The detail of the event is the glTF that was added.
19
+ */
13
20
  ObjectAdded = "object-added",
14
- FileDropped = "file-dropped",
15
21
  }
16
22
 
17
23
  /** The DropListener component is used to listen for drag and drop events in the browser and add the dropped files to the scene
18
- * It can be used to allow users to drag and drop glTF files into the scene to add them to the scene.
24
+ * It can be used to allow users to drag and drop glTF files into the scene to add new objects.
25
+ *
26
+ * ## Events
27
+ * - **object-added** - dispatched when a new object is added to the scene
28
+ * - **file-dropped** - dispatched when a file is dropped into the scene
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { DropListener, DropListenerEvents } from "@needle-tools/engine";
33
+ *
34
+ * const dropListener = new DropListener();
35
+ *
36
+ * gameObject.addComponent(dropListener);
37
+ * dropListener.on(DropListenerEvents.FileDropped, (evt) => {
38
+ * console.log("File dropped", evt.detail);
39
+ * const file = evt.detail as File;
40
+ * });
41
+ *
42
+ * dropListener.on(DropListenerEvents.ObjectAdded, (evt) => {
43
+ * console.log("Object added", evt.detail);
44
+ * const gltf = evt.detail as GLTF;
45
+ * });
46
+ * ```
19
47
  */
20
48
  export class DropListener extends Behaviour {
21
49
 
src/engine/extensions/NEEDLE_progressive.ts CHANGED
@@ -72,6 +72,7 @@
72
72
 
73
73
  declare type NEEDLE_progressive_model_LOD = {
74
74
  path: string,
75
+ hash?: string,
75
76
  }
76
77
 
77
78
  /** This is the data structure we have in the NEEDLE_progressive extension */
@@ -524,7 +525,7 @@
524
525
  }
525
526
 
526
527
  /** the resolved LOD url */
527
- const lod_url = resolveUrl(source, unresolved_lod_url);
528
+ let lod_url = resolveUrl(source, unresolved_lod_url);
528
529
 
529
530
  // check if the requested file needs to be loaded via a GLTFLoader
530
531
  if (lod_url.endsWith(".glb") || lod_url.endsWith(".gltf")) {
@@ -578,7 +579,14 @@
578
579
  const loader = new GLTFLoader();
579
580
  addDracoAndKTX2Loaders(loader, context);
580
581
 
582
+ if (progressiveInfo && Array.isArray(progressiveInfo.lods)) {
583
+ const lodinfo = progressiveInfo.lods[level];
584
+ if (lodinfo.hash) {
585
+ lod_url += "?v=" + lodinfo.hash;
586
+ }
587
+ }
581
588
 
589
+
582
590
  if (debug) {
583
591
  await delay(Math.random() * 1000);
584
592
  if (debugverbose) console.warn("Start loading (delayed) " + lod_url, ext.guid);
src/engine-components/VideoPlayer.ts CHANGED
@@ -59,8 +59,20 @@
59
59
  @serializable()
60
60
  private source: VideoSource = VideoSource.Url;
61
61
  @serializable(URL)
62
- private url?: string | null = null;
62
+ get url() { return this._url }
63
+ set url(val: string | null) {
64
+ const prev = this._url;
65
+ const changed = prev !== val;
66
+ if (this.__didAwake) {
67
+ if (changed) {
68
+ this.setClipURL(val ?? "");
69
+ }
70
+ }
71
+ else this._url = val;
72
+ }
63
73
 
74
+ private _url: string | null = null;
75
+
64
76
  @serializable()
65
77
  private renderMode?: VideoRenderMode;
66
78
 
@@ -209,15 +221,15 @@
209
221
  }
210
222
 
211
223
  setClipURL(url: string) {
212
- if (this.url === url) return;
213
- this.url = url;
224
+ if (this._url === url) return;
225
+ this._url = url;
214
226
  this.source = VideoSource.Url;
215
227
 
216
228
  if (debug) console.log("set url", url);
217
229
  if (!this._videoElement) this.create(this.playOnAwake);
218
230
  else {
219
- if (url.endsWith(".m3u8")) {
220
- this.ensureM3U8CanBePlayed();
231
+ if (url.endsWith(".m3u8") || url.includes(".m3u")) {
232
+ this.ensureM3UCanBePlayed();
221
233
  }
222
234
  else {
223
235
  this._videoElement.src = url;
@@ -308,8 +320,8 @@
308
320
  if (!this._receivedInput) this._videoElement.muted = true;
309
321
  this.handleBeginPlaying(false);
310
322
 
311
- if(this.shouldUseM3U8){
312
- this.ensureM3U8CanBePlayed();
323
+ if (this.shouldUseM3U) {
324
+ this.ensureM3UCanBePlayed();
313
325
  return;
314
326
  }
315
327
 
@@ -487,8 +499,8 @@
487
499
  this.updateVideoElementSettings();
488
500
  this.updateVideoElementStyles();
489
501
  if (playAutomatically) {
490
- if (this.shouldUseM3U8) {
491
- this.ensureM3U8CanBePlayed();
502
+ if (this.shouldUseM3U) {
503
+ this.ensureM3UCanBePlayed();
492
504
  }
493
505
  this.play();
494
506
  }
@@ -576,10 +588,10 @@
576
588
 
577
589
 
578
590
 
579
- private get shouldUseM3U8(): boolean { return this.url != undefined && this.url.endsWith(".m3u8") && this.source === VideoSource.Url; }
591
+ private get shouldUseM3U(): boolean { return this.url != undefined && (this.url.endsWith(".m3u8") || this.url.endsWith(".m3u")) && this.source === VideoSource.Url; }
580
592
 
581
- private ensureM3U8CanBePlayed() {
582
- if (!this.shouldUseM3U8) return;
593
+ private ensureM3UCanBePlayed() {
594
+ if (!this.shouldUseM3U) return;
583
595
  let hls_script = document.head.querySelector("script[data-hls_library]") as HTMLScriptElement;
584
596
  if (!hls_script) {
585
597
  if (debug) console.log("HLS: load script");
@@ -599,7 +611,7 @@
599
611
  private _hls?: Hls;
600
612
  private onHlsAvailable = () => {
601
613
  if (debug) console.log("HLS: available", this.clip);
602
- if (!this.shouldUseM3U8 || !this.url) return;
614
+ if (!this.shouldUseM3U || !this.url) return;
603
615
  if (!this._hls)
604
616
  this._hls = new Hls();
605
617
  this.videoElement!.autoplay = true;
src/engine-components/webxr/WebARCameraBackground.ts CHANGED
@@ -116,6 +116,15 @@
116
116
  updateFromFrame(frame: XRFrame | null) {
117
117
  if (!frame) return;
118
118
 
119
+ // If camera feed is not present, then abort and hiden background
120
+ const enabledFeatures = frame.session.enabledFeatures;
121
+ if (enabledFeatures && !enabledFeatures.some(x => x === 'camera-access')) {
122
+ if (this.background) {
123
+ this.background.visible = false;
124
+ }
125
+ return;
126
+ }
127
+
119
128
  // https://chromium.googlesource.com/chromium/src/+/7c5ac3c0f95b97cf12be95a5c1c0a8ff163246d8/third_party/webxr_test_pages/webxr-samples/proposals/camera-access-barebones.html
120
129
  const pose = frame.getViewerPose(this.context.renderer.xr.getReferenceSpace()!);
121
130
  if (pose) {