@@ -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
|
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
|
|
@@ -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
|
-
|
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);
|
@@ -59,8 +59,20 @@
|
|
59
59
|
@serializable()
|
60
60
|
private source: VideoSource = VideoSource.Url;
|
61
61
|
@serializable(URL)
|
62
|
-
|
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.
|
213
|
-
this.
|
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.
|
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.
|
312
|
-
this.
|
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.
|
491
|
-
this.
|
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
|
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
|
582
|
-
if (!this.
|
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.
|
614
|
+
if (!this.shouldUseM3U || !this.url) return;
|
603
615
|
if (!this._hls)
|
604
616
|
this._hls = new Hls();
|
605
617
|
this.videoElement!.autoplay = true;
|
@@ -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) {
|