@@ -30,7 +30,9 @@
|
|
30
30
|
import { ContextEvent, ContextRegistry } from './engine_context_registry.js';
|
31
31
|
import { delay, getParam } from './engine_utils.js';
|
32
32
|
import { VERSION } from './engine_constants.js';
|
33
|
-
import { isDevEnvironment, LogType, showBalloonMessage } from './debug/index.js';
|
33
|
+
import { isDevEnvironment, LogType, showBalloonMessage, showBalloonWarning } from './debug/index.js';
|
34
|
+
import { getLoader } from './engine_gltf.js';
|
35
|
+
import { isLocalNetwork } from './engine_networking_utils.js';
|
34
36
|
|
35
37
|
|
36
38
|
const debug = utils.getParam("debugcontext");
|
@@ -43,26 +45,45 @@
|
|
43
45
|
// those will be accessed from our custom html element to load them into their context
|
44
46
|
export const build_scene_functions: { [name: string]: (context: Context) => Promise<void> } = {};
|
45
47
|
|
48
|
+
|
46
49
|
export declare class LoadingProgressArgs {
|
50
|
+
/** the name or URL of the loaded file */
|
47
51
|
name: string;
|
52
|
+
/** the loading progress event from the loader */
|
48
53
|
progress: ProgressEvent;
|
54
|
+
/** the index of the loaded file */
|
49
55
|
index: number;
|
56
|
+
/** the total number of files to load */
|
50
57
|
count: number;
|
51
58
|
}
|
52
|
-
export declare class
|
53
|
-
|
59
|
+
export declare class ContextCreateArgs {
|
60
|
+
/** list of glTF or GLB files to load */
|
61
|
+
files: Array<string>;
|
62
|
+
/** called when loading a provided glTF file started */
|
63
|
+
onLoadingStart?: (index: number, file: string) => void;
|
64
|
+
/** called on update for each loaded glTF file */
|
65
|
+
onLoadingProgress?: (args: LoadingProgressArgs) => void;
|
66
|
+
/** Called after a gLTF file has finished loading */
|
67
|
+
onLoadingFinished?: (index: number, file: string, glTF: GLTF | null) => void;
|
54
68
|
}
|
55
69
|
|
56
70
|
export class ContextArgs {
|
57
71
|
name?: string;
|
72
|
+
/** for debugging only */
|
58
73
|
alias?: string;
|
59
|
-
|
60
|
-
renderer?: WebGLRenderer = undefined;
|
74
|
+
/** the hash is used as a seed when initially loading the scene files */
|
61
75
|
hash?: string;
|
62
76
|
|
63
|
-
|
64
|
-
|
65
|
-
|
77
|
+
/** when true the context will not check if it's visible in the viewport and always update and render */
|
78
|
+
runInBackground?: boolean;
|
79
|
+
/** the DOM element the context belongs to or is inside of (this does not have to be the canvas. use renderer.domElement if you want to access the dom canvas) */
|
80
|
+
domElement?: HTMLElement | null;
|
81
|
+
/** externally owned renderer */
|
82
|
+
renderer?: WebGLRenderer;
|
83
|
+
/** externally owned camera */
|
84
|
+
camera?: Camera;
|
85
|
+
/** externally owned scene */
|
86
|
+
scene?: Scene;
|
66
87
|
}
|
67
88
|
|
68
89
|
export enum FrameEvent {
|
@@ -81,6 +102,7 @@
|
|
81
102
|
ImmersiveAR = "immersive-ar",
|
82
103
|
}
|
83
104
|
|
105
|
+
/** threejs callback event signature */
|
84
106
|
export declare type OnRenderCallback = (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void
|
85
107
|
|
86
108
|
|
@@ -202,7 +224,7 @@
|
|
202
224
|
get isInAR() { return this.xrSessionMode === XRSessionMode.ImmersiveAR; }
|
203
225
|
get xrSession() { return this.renderer.xr?.getSession(); }
|
204
226
|
get xrFrame() { return this._xrFrame }
|
205
|
-
get xrCamera()
|
227
|
+
get xrCamera(): WebXRArrayCamera | undefined { return this.renderer.xr?.getCamera(); }
|
206
228
|
private _xrFrame: XRFrame | null = null;
|
207
229
|
get arOverlayElement(): HTMLElement {
|
208
230
|
const el = this.domElement as any;
|
@@ -232,7 +254,13 @@
|
|
232
254
|
scripts_WithCorroutines: IComponent[] = [];
|
233
255
|
coroutines: { [FrameEvent: number]: Array<CoroutineData> } = {}
|
234
256
|
|
257
|
+
mainCameraComponent: ICamera | undefined;
|
258
|
+
|
259
|
+
private _camera: Camera | null = null;
|
235
260
|
get mainCamera(): Camera | null {
|
261
|
+
if (this._camera) {
|
262
|
+
return this._camera;
|
263
|
+
}
|
236
264
|
if (this.mainCameraComponent) {
|
237
265
|
const cam = this.mainCameraComponent as ICamera;
|
238
266
|
if (!cam.cam)
|
@@ -241,7 +269,9 @@
|
|
241
269
|
}
|
242
270
|
return null;
|
243
271
|
}
|
244
|
-
|
272
|
+
set mainCamera(cam: Camera | null) {
|
273
|
+
this._camera = cam;
|
274
|
+
}
|
245
275
|
|
246
276
|
post_setup_callbacks: Function[] = [];
|
247
277
|
pre_update_callbacks: Function[] = [];
|
@@ -284,6 +314,7 @@
|
|
284
314
|
this.alias = args?.alias;
|
285
315
|
this.domElement = args?.domElement || document.body;
|
286
316
|
this.hash = args?.hash;
|
317
|
+
|
287
318
|
if (args?.renderer) {
|
288
319
|
this.renderer = args.renderer;
|
289
320
|
this.isManagedExternally = true;
|
@@ -292,10 +323,11 @@
|
|
292
323
|
this.createRenderer();
|
293
324
|
}
|
294
325
|
|
295
|
-
this.
|
326
|
+
if (args?.runInBackground !== undefined) this.runInBackground = args.runInBackground;
|
327
|
+
if (args?.scene) this.scene = args.scene;
|
328
|
+
else this.scene = new Scene();
|
329
|
+
if (args?.camera) this._camera = args.camera;
|
296
330
|
|
297
|
-
ContextRegistry.register(this);
|
298
|
-
|
299
331
|
this.application = new Application(this);
|
300
332
|
this.time = new Time();
|
301
333
|
this.input = new Input(this);
|
@@ -307,6 +339,7 @@
|
|
307
339
|
this.lightmaps = new LightDataRegistry(this);
|
308
340
|
this.players = new PlayerViewManager(this);
|
309
341
|
|
342
|
+
|
310
343
|
const resizeCallback = () => this._sizeChanged = true;
|
311
344
|
window.addEventListener('resize', resizeCallback);
|
312
345
|
this._disposeCallbacks.push(() => window.removeEventListener('resize', resizeCallback));
|
@@ -319,6 +352,8 @@
|
|
319
352
|
this._isVisible = entries[0].isIntersecting;
|
320
353
|
});
|
321
354
|
this._disposeCallbacks.push(() => this._intersectionObserver?.disconnect());
|
355
|
+
|
356
|
+
ContextRegistry.register(this);
|
322
357
|
}
|
323
358
|
|
324
359
|
private createRenderer() {
|
@@ -402,10 +437,14 @@
|
|
402
437
|
camera.updateProjectionMatrix();
|
403
438
|
}
|
404
439
|
|
405
|
-
|
440
|
+
/** @deprecated use create. This method will be removed in a future version */
|
441
|
+
async onCreate(opts?: ContextCreateArgs) {
|
442
|
+
return this.create(opts);
|
443
|
+
}
|
444
|
+
async create(opts?: ContextCreateArgs) {
|
406
445
|
try {
|
407
446
|
this._isCreating = true;
|
408
|
-
const res = await this.internalOnCreate(
|
447
|
+
const res = await this.internalOnCreate(opts);
|
409
448
|
return res;
|
410
449
|
}
|
411
450
|
finally {
|
@@ -610,12 +649,15 @@
|
|
610
649
|
}
|
611
650
|
|
612
651
|
|
613
|
-
private
|
652
|
+
private _createId: number = 0;
|
653
|
+
private async internalOnCreate(opts?: ContextCreateArgs) {
|
654
|
+
const createId = ++this._createId;
|
614
655
|
|
615
656
|
this.clear();
|
616
657
|
// stop the animation loop if its running during creation
|
617
658
|
// since we do not want to start enabling scripts etc before they are deserialized
|
618
|
-
this.
|
659
|
+
if (this.isManagedExternally === false)
|
660
|
+
this.renderer?.setAnimationLoop(null);
|
619
661
|
|
620
662
|
await delay(1);
|
621
663
|
|
@@ -624,26 +666,25 @@
|
|
624
666
|
|
625
667
|
// load and create scene
|
626
668
|
let prepare_succeeded = true;
|
627
|
-
let loadedFiles
|
669
|
+
let loadedFiles!: Array<GLTF | null>;
|
628
670
|
try {
|
629
671
|
Context.Current = this;
|
630
|
-
if (
|
631
|
-
loadedFiles = await
|
632
|
-
// if the files are null the loading was cancelled
|
633
|
-
// this happens when the src attribute changes during loading and we want to load other files
|
634
|
-
if(loadedFiles === null) return false;
|
672
|
+
if (opts) {
|
673
|
+
loadedFiles = await this.internalLoadInitialContent(createId, opts);
|
635
674
|
}
|
675
|
+
else loadedFiles = [];
|
636
676
|
}
|
637
677
|
catch (err) {
|
638
678
|
console.error(err);
|
639
679
|
prepare_succeeded = false;
|
640
680
|
}
|
641
681
|
if (!prepare_succeeded) return false;
|
682
|
+
if (createId !== this._createId) return false;
|
642
683
|
|
643
684
|
this.internalOnUpdateVisible();
|
644
685
|
|
645
686
|
if (!this.renderer) {
|
646
|
-
if(debug) console.warn("Context has no renderer (perhaps it was disconnected?", this.domElement.isConnected);
|
687
|
+
if (debug) console.warn("Context has no renderer (perhaps it was disconnected?", this.domElement.isConnected);
|
647
688
|
return false;
|
648
689
|
}
|
649
690
|
|
@@ -746,18 +787,87 @@
|
|
746
787
|
// the _defaultTargetFramerate is intentionally an object so it can be changed at any time if not explictly set by the user
|
747
788
|
this.targetFrameRate = Context._defaultTargetFramerate;
|
748
789
|
}
|
749
|
-
else if(debug) console.log("Target framerate set to", this.targetFrameRate);
|
790
|
+
else if (debug) console.log("Target framerate set to", this.targetFrameRate);
|
750
791
|
|
751
792
|
this._isCreating = false;
|
752
|
-
this.
|
793
|
+
if (!this.isManagedExternally)
|
794
|
+
this.restartRenderLoop();
|
753
795
|
this._dispatchReadyAfterFrame = true;
|
754
796
|
return ContextRegistry.dispatchCallback(ContextEvent.ContextCreated, this, { files: loadedFiles });
|
755
797
|
}
|
756
798
|
|
757
|
-
private
|
758
|
-
|
759
|
-
|
799
|
+
private async internalLoadInitialContent(createId: number, args: ContextCreateArgs): Promise<Array<GLTF>> {
|
800
|
+
const results = new Array<GLTF>();
|
801
|
+
// early out if we dont have any files to load
|
802
|
+
if (args.files.length === 0) return results;
|
760
803
|
|
804
|
+
|
805
|
+
const files = [...args.files];
|
806
|
+
const progressArg: LoadingProgressArgs = {
|
807
|
+
name: "",
|
808
|
+
progress: null!,
|
809
|
+
index: 0,
|
810
|
+
count: files.length
|
811
|
+
}
|
812
|
+
|
813
|
+
const loader = getLoader();
|
814
|
+
let loadingHash = 0;
|
815
|
+
if (this.hash) loadingHash = Number.parseInt(this.hash) ?? 0;
|
816
|
+
for (let i = 0; i < files.length; i++) {
|
817
|
+
// abort loading if the create id has changed
|
818
|
+
if (createId !== this._createId) {
|
819
|
+
if (debug) console.log("Aborting loading because create id changed", createId, this._createId);
|
820
|
+
break;
|
821
|
+
}
|
822
|
+
const file = files[i];
|
823
|
+
|
824
|
+
if (!file.includes(".glb") && !file.includes(".gltf")) {
|
825
|
+
const warning = `Needle Engine: found suspicious src "${file}"`;
|
826
|
+
console.warn(warning);
|
827
|
+
if (isLocalNetwork()) showBalloonWarning(warning);
|
828
|
+
}
|
829
|
+
|
830
|
+
args?.onLoadingStart?.call(this, i, file);
|
831
|
+
const res = await loader.loadSync(this, file, file, loadingHash, prog => {
|
832
|
+
progressArg.name = file;
|
833
|
+
progressArg.progress = prog;
|
834
|
+
progressArg.index = i;
|
835
|
+
progressArg.count = files.length;
|
836
|
+
args.onLoadingProgress?.call(this, progressArg);
|
837
|
+
});
|
838
|
+
args?.onLoadingFinished?.call(this, i, file, res ?? null);
|
839
|
+
if (res) {
|
840
|
+
results.push(res);
|
841
|
+
}
|
842
|
+
else {
|
843
|
+
// a file could not be loaded
|
844
|
+
console.warn("Could not load file: " + file);
|
845
|
+
}
|
846
|
+
}
|
847
|
+
|
848
|
+
// if the id was changed while still loading
|
849
|
+
// then we want to cleanup/destroy previously loaded files
|
850
|
+
if (createId !== this._createId) {
|
851
|
+
for (const res of results) {
|
852
|
+
if (res) {
|
853
|
+
for (const scene of res.scenes)
|
854
|
+
destroy(scene, true, true);
|
855
|
+
}
|
856
|
+
}
|
857
|
+
}
|
858
|
+
// otherwise we want to add the loaded files to the current scene
|
859
|
+
else {
|
860
|
+
for (const res of results) {
|
861
|
+
if (res) {
|
862
|
+
this.scene.add(res.scene);
|
863
|
+
}
|
864
|
+
}
|
865
|
+
}
|
866
|
+
|
867
|
+
return results;
|
868
|
+
}
|
869
|
+
|
870
|
+
|
761
871
|
/** Sets the animation loop.
|
762
872
|
* Can not be done while creating the context or when disposed
|
763
873
|
**/
|
@@ -770,15 +880,15 @@
|
|
770
880
|
console.warn("Can not start render loop while creating context");
|
771
881
|
return false;
|
772
882
|
}
|
773
|
-
|
774
|
-
this.renderer.setAnimationLoop(renderMethod);
|
883
|
+
this.renderer.setAnimationLoop((timestamp, frame: XRFrame | null) => this.update(timestamp, frame));
|
775
884
|
return true;
|
776
885
|
}
|
777
886
|
|
778
|
-
|
887
|
+
public update(timestamp: DOMHighResTimeStamp, frame?: XRFrame | null) {
|
888
|
+
if (frame === undefined) frame = null;
|
779
889
|
if (isDevEnvironment() || debug || looputils.hasNewScripts()) {
|
780
890
|
try {
|
781
|
-
this.internalRender(
|
891
|
+
this.internalRender(timestamp, frame);
|
782
892
|
}
|
783
893
|
catch (err) {
|
784
894
|
if ((isDevEnvironment() || debug) && err instanceof Error)
|
@@ -790,19 +900,22 @@
|
|
790
900
|
}
|
791
901
|
}
|
792
902
|
else {
|
793
|
-
this.internalRender(
|
903
|
+
this.internalRender(timestamp, frame);
|
794
904
|
}
|
795
905
|
}
|
796
906
|
|
797
907
|
private _lastTimestamp = 0;
|
798
|
-
private
|
908
|
+
private _accumulatedTime = 0;
|
909
|
+
private _dispatchReadyAfterFrame = false;
|
910
|
+
|
911
|
+
private internalRender(timestamp: DOMHighResTimeStamp, frame: XRFrame | null) {
|
799
912
|
this._xrFrame = frame;
|
800
913
|
|
801
914
|
this._currentFrameEvent = FrameEvent.Undefined;
|
802
|
-
|
915
|
+
|
803
916
|
if (this.isInXR === false && this.targetFrameRate !== undefined) {
|
804
|
-
if(this._lastTimestamp === 0) this._lastTimestamp = timestamp;
|
805
|
-
this._accumulatedTime += (timestamp - this._lastTimestamp) /1000;
|
917
|
+
if (this._lastTimestamp === 0) this._lastTimestamp = timestamp;
|
918
|
+
this._accumulatedTime += (timestamp - this._lastTimestamp) / 1000;
|
806
919
|
this._lastTimestamp = timestamp;
|
807
920
|
let targetFrameRate = this.targetFrameRate;
|
808
921
|
if (typeof targetFrameRate === "object") targetFrameRate = targetFrameRate.value!;
|
@@ -896,7 +1009,7 @@
|
|
896
1009
|
|
897
1010
|
if (this.onHandlePaused()) return;
|
898
1011
|
|
899
|
-
if (this.isVisibleToUser) {
|
1012
|
+
if (this.isVisibleToUser || this.runInBackground) {
|
900
1013
|
|
901
1014
|
this._currentFrameEvent = FrameEvent.OnBeforeRender;
|
902
1015
|
|
@@ -953,7 +1066,11 @@
|
|
953
1066
|
|
954
1067
|
this.connection.sendBufferedMessagesNow();
|
955
1068
|
|
956
|
-
this._stats
|
1069
|
+
if (this._stats) {
|
1070
|
+
this._stats.end();
|
1071
|
+
if (this.time.frameCount % 150 === 0)
|
1072
|
+
console.log({ ...this.renderer.info.memory }, { ...this.renderer.info.render });
|
1073
|
+
}
|
957
1074
|
|
958
1075
|
if (this._dispatchReadyAfterFrame) {
|
959
1076
|
this._dispatchReadyAfterFrame = false;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Context, LoadingProgressArgs } from "./engine_setup.js";
|
1
|
+
import { Context, ContextCreateArgs, LoadingProgressArgs } from "./engine_setup.js";
|
2
2
|
import { AROverlayHandler, arContainerClassName } from "./engine_element_overlay.js";
|
3
3
|
import { GameObject } from "../engine-components/Component.js";
|
4
4
|
import { calculateProgress01, EngineLoadingView, ILoadingViewHandler } from "./engine_element_loading.js";
|
@@ -118,7 +118,10 @@
|
|
118
118
|
:host canvas {
|
119
119
|
position: absolute;
|
120
120
|
user-select: none;
|
121
|
-
touch
|
121
|
+
/** allow touch panning but no pinch zoom **/
|
122
|
+
/** but this doesnt work yet:
|
123
|
+
* touch-action: pan-x, pan-y;
|
124
|
+
**/
|
122
125
|
}
|
123
126
|
:host .content {
|
124
127
|
position: absolute;
|
@@ -148,7 +151,7 @@
|
|
148
151
|
<slot class="overlay-content"></slot>
|
149
152
|
</div>
|
150
153
|
`;
|
151
|
-
|
154
|
+
|
152
155
|
if (this.shadowRoot)
|
153
156
|
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
154
157
|
|
@@ -306,82 +309,45 @@
|
|
306
309
|
if (debug) console.warn("--------------", loadId, "Needle Engine: Begin loading", alias ?? "", filesToLoad);
|
307
310
|
this.onBeforeBeginLoading();
|
308
311
|
|
309
|
-
type LoadFunctionResult = (ctx: Context) => Promise<LoadedGLTF[] | null>;
|
310
|
-
let loadFunction: null | LoadFunctionResult = null;
|
311
312
|
const loadedFiles: Array<LoadedGLTF> = [];
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
progress: progress.progress,
|
339
|
-
index: i,
|
340
|
-
count: filesToLoad.length,
|
341
|
-
totalProgress01: this._loadingProgress01
|
342
|
-
}
|
343
|
-
}
|
344
|
-
const res = await loader.loadSync(ctx, url, url, hash, prog => {
|
345
|
-
if(this._loadId !== loadId) return;
|
346
|
-
// Calc progress
|
347
|
-
progress.progress = prog;
|
348
|
-
this._loadingProgress01 = calculateProgress01(progress);
|
349
|
-
// Update overlay if we use it
|
350
|
-
if (useDefaultLoading) this._loadingView?.onLoadingUpdate(progress);
|
351
|
-
// Dispatch event
|
352
|
-
progressEventArgs.detail.progress = prog;
|
353
|
-
progressEventArgs.detail.totalProgress01 = this._loadingProgress01;
|
354
|
-
this.dispatchEvent(new CustomEvent("progress", progressEventArgs));
|
313
|
+
const progressEventDetail = {
|
314
|
+
context: this._context,
|
315
|
+
name: "",
|
316
|
+
progress: {} as ProgressEvent,
|
317
|
+
index: 0,
|
318
|
+
count: filesToLoad.length,
|
319
|
+
totalProgress01: this._loadingProgress01
|
320
|
+
};
|
321
|
+
const progressEvent = new CustomEvent("progress", { detail: progressEventDetail });
|
322
|
+
const args: ContextCreateArgs = {
|
323
|
+
files: filesToLoad,
|
324
|
+
onLoadingProgress: evt => {
|
325
|
+
evt.name = getNameFromUrl(evt.name);
|
326
|
+
console.log({...evt})
|
327
|
+
if (useDefaultLoading) this._loadingView?.onLoadingUpdate(evt);
|
328
|
+
progressEventDetail.name = evt.name;
|
329
|
+
progressEventDetail.progress = evt.progress;
|
330
|
+
this._loadingProgress01 = calculateProgress01(evt);
|
331
|
+
progressEventDetail.totalProgress01 = this._loadingProgress01;
|
332
|
+
this.dispatchEvent(progressEvent);
|
333
|
+
},
|
334
|
+
onLoadingFinished: (_index, file, glTF) => {
|
335
|
+
if (glTF) {
|
336
|
+
loadedFiles.push({
|
337
|
+
src: file,
|
338
|
+
file: glTF
|
355
339
|
});
|
356
|
-
if (res) {
|
357
|
-
loadedFiles.push({
|
358
|
-
src: url,
|
359
|
-
file: res as GLTF
|
360
|
-
});
|
361
|
-
const obj = res.scene;
|
362
|
-
if (obj) {
|
363
|
-
// if the loading ID changed but the scene has already been loaded we want to dispose it
|
364
|
-
if (this._loadId !== loadId) {
|
365
|
-
destroy(obj, true, true)
|
366
|
-
return null;
|
367
|
-
}
|
368
|
-
GameObject.add(obj, ctx.scene, ctx);
|
369
|
-
}
|
370
|
-
}
|
371
340
|
}
|
372
|
-
|
373
|
-
return loadedFiles;
|
374
|
-
};
|
341
|
+
}
|
375
342
|
}
|
376
343
|
|
377
344
|
const currentHash = this.getAttribute("hash");
|
378
345
|
if (currentHash !== null && currentHash !== undefined)
|
379
346
|
this._context.hash = currentHash;
|
380
347
|
this._context.alias = alias;
|
381
|
-
await this._context.
|
348
|
+
await this._context.create(args);
|
382
349
|
if (this._loadId !== loadId) return;
|
383
|
-
if (debug)
|
384
|
-
console.warn("--------------", loadId, "Needle Engine: finished loading", alias ?? "", filesToLoad);
|
350
|
+
if (debug) console.warn("--------------", loadId, "Needle Engine: finished loading", alias ?? "", filesToLoad);
|
385
351
|
this._loadingProgress01 = 1;
|
386
352
|
if (useDefaultLoading) {
|
387
353
|
this._loadingView?.onLoadingUpdate(1, "creating scene");
|
@@ -505,7 +471,7 @@
|
|
505
471
|
this.classList.add(arSessionActiveClassName);
|
506
472
|
this.classList.remove(desktopSessionActiveClassName);
|
507
473
|
const arContainer = this.getAROverlayContainer();
|
508
|
-
if(debug) console.warn("onSetupAR:", arContainer)
|
474
|
+
if (debug) console.warn("onSetupAR:", arContainer)
|
509
475
|
if (arContainer) {
|
510
476
|
arContainer.classList.add(arSessionActiveClassName);
|
511
477
|
arContainer.classList.remove(desktopSessionActiveClassName);
|
@@ -566,7 +566,7 @@
|
|
566
566
|
positions = this._meshCache.get(key)!;
|
567
567
|
}
|
568
568
|
else {
|
569
|
-
console.warn("Your model is using scaled mesh colliders which is not optimal for performance", mesh.name, Object.assign({}, scale)
|
569
|
+
console.warn("Your model is using scaled mesh colliders which is not optimal for performance", mesh.name, Object.assign({}, scale));
|
570
570
|
// showBalloonWarning("Your model is using scaled mesh colliders which is not optimal for performance: " + mesh.name + ", consider using unscaled objects");
|
571
571
|
const scaledPositions = new Float32Array(positions.length);
|
572
572
|
for (let i = 0; i < positions.length; i += 3) {
|
@@ -347,8 +347,16 @@
|
|
347
347
|
onDeserialize(data: any, context: SerializationContext) {
|
348
348
|
if (data instanceof Texture && context.type === RenderTexture) {
|
349
349
|
const tex = data as Texture;
|
350
|
-
const rt = new RenderTexture(tex.image.width, tex.image.height
|
350
|
+
const rt = new RenderTexture(tex.image.width, tex.image.height, {
|
351
|
+
colorSpace: THREE.LinearSRGBColorSpace,
|
352
|
+
});
|
351
353
|
rt.texture = tex;
|
354
|
+
|
355
|
+
tex.isRenderTargetTexture = true;
|
356
|
+
tex.flipY = true;
|
357
|
+
tex.offset.y = 1;
|
358
|
+
tex.repeat.y = -1;
|
359
|
+
tex.needsUpdate = true;
|
352
360
|
|
353
361
|
if (tex instanceof CompressedTexture) {
|
354
362
|
//@ts-ignore
|
@@ -357,10 +365,6 @@
|
|
357
365
|
tex.format = THREE.RGBAFormat;
|
358
366
|
}
|
359
367
|
|
360
|
-
rt.texture.flipY = true;
|
361
|
-
rt.texture.offset.y = 1;
|
362
|
-
rt.texture.repeat.y = -1;
|
363
|
-
|
364
368
|
return rt;
|
365
369
|
}
|
366
370
|
return undefined;
|
@@ -3,15 +3,16 @@
|
|
3
3
|
import { RGBAColor } from "../engine-components/js-extensions/RGBAColor.js";
|
4
4
|
import { CollisionDetectionMode, PhysicsMaterial, RigidbodyConstraints } from "./engine_physics.types.js";
|
5
5
|
import { CircularBuffer } from "./engine_utils.js";
|
6
|
+
import { GLTF as GLTF3 } from "three/examples/jsm/loaders/GLTFLoader.js";
|
6
7
|
|
7
|
-
export type GLTF = {
|
8
|
-
asset: { generator: string, version: string }
|
9
|
-
animations: AnimationClip[];
|
10
|
-
cameras: Camera[];
|
11
|
-
scene: Object3D;
|
12
|
-
scenes: Object3D[];
|
13
|
-
userData: object;
|
14
|
-
parser: any;
|
8
|
+
export type GLTF = GLTF3 & {
|
9
|
+
// asset: { generator: string, version: string }
|
10
|
+
// animations: AnimationClip[];
|
11
|
+
// cameras: Camera[];
|
12
|
+
// scene: Object3D;
|
13
|
+
// scenes: Object3D[];
|
14
|
+
// userData: object;
|
15
|
+
// parser: any;
|
15
16
|
}
|
16
17
|
|
17
18
|
export type LoadedGLTF = {
|
@@ -263,7 +263,6 @@
|
|
263
263
|
return
|
264
264
|
}
|
265
265
|
|
266
|
-
if(debug) console.log("EventSystem: raycast");
|
267
266
|
const hits = this.performRaycast(null);
|
268
267
|
if (!hits) return;
|
269
268
|
this.lastPointerEvent = args;
|
@@ -195,10 +195,18 @@
|
|
195
195
|
if (Graphic.textureCache.has(tex)) {
|
196
196
|
tex = Graphic.textureCache.get(tex)!;
|
197
197
|
} else {
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
198
|
+
if (tex.isRenderTargetTexture) {
|
199
|
+
// we can not clone the texture if it's a render target
|
200
|
+
// otherwise it won't be updated anymore in the UI
|
201
|
+
// TODO: below maskable graphic is flipped but settings a rendertexture results in the texture being upside down.
|
202
|
+
// we should remove the flip below (scale.y *= -1) but this needs to be tested with all UI components
|
203
|
+
}
|
204
|
+
else {
|
205
|
+
const clone = tex.clone();
|
206
|
+
clone.colorSpace = LinearSRGBColorSpace;
|
207
|
+
Graphic.textureCache.set(tex, clone);
|
208
|
+
tex = clone;
|
209
|
+
}
|
202
210
|
}
|
203
211
|
}
|
204
212
|
this.setOptions({ backgroundImage: tex, borderRadius: 0, backgroundOpacity: this.color.alpha, backgroundSize: "stretch" });
|
@@ -588,16 +588,31 @@
|
|
588
588
|
// this way all properties of custom shaders are also accessible via material._MyProperty
|
589
589
|
function createUniformProperties(material: CustomShader) {
|
590
590
|
if (material.uniforms) {
|
591
|
+
if (debug)
|
592
|
+
console.log("Uniforms:", material.uniforms);
|
591
593
|
for (const key in material.uniforms) {
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
594
|
+
defineProperty(key, key);
|
595
|
+
|
596
|
+
// see NE-3396
|
597
|
+
switch (key) {
|
598
|
+
case "_Color":
|
599
|
+
defineProperty("color", key);
|
600
|
+
break;
|
601
|
+
// case "_Metallic":
|
602
|
+
// defineProperty("metalness", key);
|
603
|
+
// break;
|
600
604
|
}
|
601
605
|
}
|
602
606
|
}
|
607
|
+
function defineProperty(key: string, uniformsKey: string) {
|
608
|
+
if (!Object.getOwnPropertyDescriptor(material, key)) {
|
609
|
+
Object.defineProperty(material, key, {
|
610
|
+
get: () => material.uniforms[uniformsKey].value,
|
611
|
+
set: (value) => {
|
612
|
+
material.uniforms[uniformsKey].value = value
|
613
|
+
material.needsUpdate = true;
|
614
|
+
}
|
615
|
+
});
|
616
|
+
}
|
617
|
+
}
|
603
618
|
}
|
@@ -9,8 +9,13 @@
|
|
9
9
|
export * from "./engine-schemes/api.js";
|
10
10
|
|
11
11
|
// make accessible for external javascript
|
12
|
-
import { Context } from "./engine/
|
13
|
-
const Needle = {
|
12
|
+
import { Context, loadSync } from "./engine/api.js";
|
13
|
+
const Needle = {
|
14
|
+
Context: Context,
|
15
|
+
glTF: {
|
16
|
+
loadFromURL: loadSync,
|
17
|
+
}
|
18
|
+
};
|
14
19
|
if (globalThis["Needle"] !== undefined) {
|
15
20
|
console.warn("Needle Engine is already imported");
|
16
21
|
}
|