@needle-tools/engine 2.28.0-pre → 2.30.0-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.
Files changed (130) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/needle-engine.d.ts +232 -139
  3. package/dist/needle-engine.js +349 -345
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +24 -20
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine.d.ts +1 -0
  8. package/lib/engine/engine_input.d.ts +13 -1
  9. package/lib/engine/engine_input.js +47 -16
  10. package/lib/engine/engine_input.js.map +1 -1
  11. package/lib/engine/engine_physics.d.ts +1 -0
  12. package/lib/engine/engine_physics.js +2 -1
  13. package/lib/engine/engine_physics.js.map +1 -1
  14. package/lib/engine/engine_playerview.d.ts +26 -0
  15. package/lib/engine/engine_playerview.js +65 -0
  16. package/lib/engine/engine_playerview.js.map +1 -0
  17. package/lib/engine/engine_serialization.d.ts +1 -0
  18. package/lib/engine/engine_serialization.js +1 -0
  19. package/lib/engine/engine_serialization.js.map +1 -1
  20. package/lib/engine/engine_serialization_core.js +5 -0
  21. package/lib/engine/engine_serialization_core.js.map +1 -1
  22. package/lib/engine/engine_setup.d.ts +8 -0
  23. package/lib/engine/engine_setup.js +23 -0
  24. package/lib/engine/engine_setup.js.map +1 -1
  25. package/lib/engine/engine_utils.d.ts +1 -1
  26. package/lib/engine/engine_utils.js +25 -8
  27. package/lib/engine/engine_utils.js.map +1 -1
  28. package/lib/engine/extensions/NEEDLE_deferred_texture.d.ts +1 -1
  29. package/lib/engine/extensions/NEEDLE_deferred_texture.js +26 -14
  30. package/lib/engine/extensions/NEEDLE_deferred_texture.js.map +1 -1
  31. package/lib/engine/extensions/NEEDLE_lighting_settings.js +6 -2
  32. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  33. package/lib/engine/extensions/extension_utils.js +24 -13
  34. package/lib/engine/extensions/extension_utils.js.map +1 -1
  35. package/lib/engine/extensions/extensions.js +3 -1
  36. package/lib/engine/extensions/extensions.js.map +1 -1
  37. package/lib/engine-components/Camera.js +7 -0
  38. package/lib/engine-components/Camera.js.map +1 -1
  39. package/lib/engine-components/Component.d.ts +1 -1
  40. package/lib/engine-components/Component.js.map +1 -1
  41. package/lib/engine-components/Light.js +1 -0
  42. package/lib/engine-components/Light.js.map +1 -1
  43. package/lib/engine-components/OrbitControls.js +3 -3
  44. package/lib/engine-components/OrbitControls.js.map +1 -1
  45. package/lib/engine-components/ParticleSystem.d.ts +0 -1
  46. package/lib/engine-components/ParticleSystem.js +24 -27
  47. package/lib/engine-components/ParticleSystem.js.map +1 -1
  48. package/lib/engine-components/PlayerColor.js +1 -2
  49. package/lib/engine-components/PlayerColor.js.map +1 -1
  50. package/lib/engine-components/Renderer.d.ts +1 -0
  51. package/lib/engine-components/Renderer.js +10 -3
  52. package/lib/engine-components/Renderer.js.map +1 -1
  53. package/lib/engine-components/ScreenCapture.d.ts +1 -0
  54. package/lib/engine-components/ScreenCapture.js +265 -1
  55. package/lib/engine-components/ScreenCapture.js.map +1 -1
  56. package/lib/engine-components/SpectatorCamera.d.ts +24 -17
  57. package/lib/engine-components/SpectatorCamera.js +435 -182
  58. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  59. package/lib/engine-components/SyncedCamera.d.ts +8 -4
  60. package/lib/engine-components/SyncedCamera.js +15 -18
  61. package/lib/engine-components/SyncedCamera.js.map +1 -1
  62. package/lib/engine-components/SyncedRoom.js +2 -0
  63. package/lib/engine-components/SyncedRoom.js.map +1 -1
  64. package/lib/engine-components/VideoPlayer.d.ts +10 -1
  65. package/lib/engine-components/VideoPlayer.js +64 -15
  66. package/lib/engine-components/VideoPlayer.js.map +1 -1
  67. package/lib/engine-components/Volume.d.ts +4 -0
  68. package/lib/engine-components/Volume.js +44 -3
  69. package/lib/engine-components/Volume.js.map +1 -1
  70. package/lib/engine-components/WebARSessionRoot.d.ts +9 -2
  71. package/lib/engine-components/WebARSessionRoot.js +69 -24
  72. package/lib/engine-components/WebARSessionRoot.js.map +1 -1
  73. package/lib/engine-components/WebXR.d.ts +6 -3
  74. package/lib/engine-components/WebXR.js +43 -7
  75. package/lib/engine-components/WebXR.js.map +1 -1
  76. package/lib/engine-components/WebXRAvatar.d.ts +3 -0
  77. package/lib/engine-components/WebXRAvatar.js +20 -0
  78. package/lib/engine-components/WebXRAvatar.js.map +1 -1
  79. package/lib/engine-components/WebXRController.js +14 -8
  80. package/lib/engine-components/WebXRController.js.map +1 -1
  81. package/lib/engine-components/WebXRSync.js +3 -3
  82. package/lib/engine-components/WebXRSync.js.map +1 -1
  83. package/lib/engine-components/XRFlag.d.ts +2 -1
  84. package/lib/engine-components/XRFlag.js +1 -0
  85. package/lib/engine-components/XRFlag.js.map +1 -1
  86. package/lib/engine-components/ui/CanvasGroup.d.ts +1 -0
  87. package/lib/engine-components/ui/CanvasGroup.js +1 -0
  88. package/lib/engine-components/ui/CanvasGroup.js.map +1 -1
  89. package/lib/engine-components/ui/EventSystem.js +13 -4
  90. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  91. package/lib/engine-components/ui/Graphic.d.ts +1 -0
  92. package/lib/engine-components/ui/Graphic.js +2 -0
  93. package/lib/engine-components/ui/Graphic.js.map +1 -1
  94. package/lib/engine-components/ui/Interfaces.d.ts +2 -0
  95. package/package.json +2 -2
  96. package/src/engine/engine_components.js +16 -0
  97. package/src/engine/engine_input.ts +62 -20
  98. package/src/engine/engine_physics.ts +2 -1
  99. package/src/engine/engine_playerview.ts +80 -0
  100. package/src/engine/engine_serialization.ts +3 -1
  101. package/src/engine/engine_serialization_core.ts +8 -0
  102. package/src/engine/engine_setup.ts +24 -0
  103. package/src/engine/engine_utils.ts +34 -8
  104. package/src/engine/extensions/NEEDLE_deferred_texture.ts +25 -19
  105. package/src/engine/extensions/NEEDLE_lighting_settings.ts +4 -2
  106. package/src/engine/extensions/extension_utils.ts +24 -12
  107. package/src/engine/extensions/extensions.ts +3 -2
  108. package/src/engine-components/Camera.ts +9 -1
  109. package/src/engine-components/Component.ts +1 -1
  110. package/src/engine-components/Light.ts +3 -0
  111. package/src/engine-components/OrbitControls.ts +3 -3
  112. package/src/engine-components/ParticleSystem.ts +25 -26
  113. package/src/engine-components/PlayerColor.ts +1 -1
  114. package/src/engine-components/Renderer.ts +11 -3
  115. package/src/engine-components/ScreenCapture.ts +312 -2
  116. package/src/engine-components/SpectatorCamera.ts +490 -195
  117. package/src/engine-components/SyncedCamera.ts +23 -22
  118. package/src/engine-components/SyncedRoom.ts +1 -0
  119. package/src/engine-components/VideoPlayer.ts +97 -21
  120. package/src/engine-components/Volume.ts +47 -4
  121. package/src/engine-components/WebARSessionRoot.ts +78 -28
  122. package/src/engine-components/WebXR.ts +51 -15
  123. package/src/engine-components/WebXRAvatar.ts +27 -2
  124. package/src/engine-components/WebXRController.ts +21 -15
  125. package/src/engine-components/WebXRSync.ts +3 -3
  126. package/src/engine-components/XRFlag.ts +1 -0
  127. package/src/engine-components/ui/CanvasGroup.ts +2 -0
  128. package/src/engine-components/ui/EventSystem.ts +21 -15
  129. package/src/engine-components/ui/Graphic.ts +3 -0
  130. package/src/engine-components/ui/Interfaces.ts +2 -0
@@ -6,6 +6,14 @@ import { getParam } from './engine_utils';
6
6
 
7
7
  const debug = getParam("debuggetcomponent");
8
8
 
9
+ function tryGetObject(obj) {
10
+ if(obj === null || obj === undefined) return obj;
11
+ if(obj.isObject3D) return obj;
12
+ // handle threejs intersection object
13
+ if (obj.object && obj.object.isObject3D) return obj.object;
14
+ return obj;
15
+ }
16
+
9
17
  export function registerComponent(script, context) {
10
18
  if (!script) return;
11
19
  const new_scripts = context?.new_scripts ?? Context.Current.new_scripts;
@@ -134,6 +142,14 @@ const getComponentsInChildren = function (componentType, obj, arr) {
134
142
 
135
143
  const getComponentInParent = function (componentType, obj) {
136
144
  if (!obj) return null;
145
+ if (Array.isArray(obj)) {
146
+ for (let i = 0; i < obj.length; i++) {
147
+ const o = tryGetObject(obj[i]);
148
+ const res = getComponentInParent(componentType, o);
149
+ if (res) return res;
150
+ }
151
+ return null;
152
+ }
137
153
  // console.log(obj);
138
154
  const res = getComponent(componentType, obj);
139
155
  if (res) return res;
@@ -1,10 +1,34 @@
1
1
  import * as THREE from 'three';
2
+ import { assign } from './engine_serialization_core';
2
3
  import { Context } from './engine_setup';
3
4
 
4
- export declare type PointerEventArgs = { button: number; clientX: number; clientY: number; pointerId?: number, pointerType: string, source?: Event };
5
+ export declare type PointerEventArgs = {
6
+ button: number;
7
+ clientX: number;
8
+ clientY: number;
9
+ pointerId?: number,
10
+ pointerType: string,
11
+ source?: Event
12
+ };
13
+
14
+ export class KeyEventArgs {
15
+ key: string;
16
+ keyType: string;
17
+ source?: Event;
18
+ constructor(evt : KeyboardEvent){
19
+ this.key = evt.key;
20
+ this.keyType = evt.type;
21
+ this.source = evt;
22
+ }
23
+ }
5
24
 
6
25
  export enum InputEvents {
7
- PointerDown = "pointerDown",
26
+ PointerDown = "pointerdown",
27
+ PointerUp = "pointerup",
28
+ PointerMove = "pointermove",
29
+ KeyDown = "keydown",
30
+ KeyUp = "keyup",
31
+ KeyPressed = "keypress"
8
32
  }
9
33
 
10
34
  // export class InputEvent extends Event {
@@ -244,29 +268,32 @@ export class Input extends EventTarget {
244
268
 
245
269
  private keysPressed: { [key: number]: { pressed: boolean, frame: number, startFrame: number, key: string } } = {};
246
270
 
247
- private onKeyDown(evt) {
271
+ private onKeyDown(evt: KeyboardEvent) {
248
272
  if (!this.context.application.hasFocus)
249
273
  return;
250
- const ex = this.keysPressed[evt.keyCode];
274
+ const ex = this.keysPressed[evt.key];
251
275
  if (ex && ex.pressed) return;
252
- this.keysPressed[evt.keyCode] = { pressed: true, frame: this.context.time.frameCount + 1, startFrame: this.context.time.frameCount + 1, key: evt.key };
276
+ this.keysPressed[evt.key] = { pressed: true, frame: this.context.time.frameCount + 1, startFrame: this.context.time.frameCount + 1, key: evt.key };
277
+ this.onDispatchEvent(InputEvents.KeyDown, new KeyEventArgs(evt));
253
278
  }
254
- private onKeyPressed(evt) {
279
+ private onKeyPressed(evt: KeyboardEvent) {
255
280
  if (!this.context.application.hasFocus)
256
281
  return;
257
- const p = this.keysPressed[evt.keyCode];
282
+ const p = this.keysPressed[evt.key];
258
283
  if (!p) return;
259
284
  p.pressed = true;
260
285
  p.frame = this.context.time.frameCount + 1;
286
+ this.onDispatchEvent(InputEvents.KeyPressed, new KeyEventArgs(evt));
287
+
261
288
  }
262
- private onKeyUp(evt) {
289
+ private onKeyUp(evt: KeyboardEvent) {
263
290
  if (!this.context.application.hasFocus)
264
291
  return;
265
- const p = this.keysPressed[evt.keyCode];
292
+ const p = this.keysPressed[evt.key];
266
293
  if (!p) return;
267
294
  p.pressed = false;
268
295
  p.frame = this.context.time.frameCount + 1;
269
- // console.log("UP", evt.key, evt.keyCode, this.keysPressed);
296
+ this.onDispatchEvent(InputEvents.KeyUp, new KeyEventArgs(evt));
270
297
  }
271
298
 
272
299
  private onMouseWheel(_evt) {
@@ -297,8 +324,8 @@ export class Input extends EventTarget {
297
324
  private onPointerDown(evt: PointerEvent) {
298
325
  if (evt.defaultPrevented) return;
299
326
  if (evt.pointerType === undefined && evt.pointerId === undefined) return;
300
- if (evt.preventDefault)
301
- evt.preventDefault();
327
+ // if (evt.preventDefault)
328
+ // evt.preventDefault();
302
329
  let i = evt.pointerType === "mouse" ? evt.button : this.getPointerIndex(evt.pointerId);
303
330
  if (evt.pointerId === undefined && evt.button !== undefined) {
304
331
  i = evt.button;
@@ -308,12 +335,12 @@ export class Input extends EventTarget {
308
335
  // evt.stopPropagation();
309
336
  }
310
337
 
311
- private onPointerMove(evt: PointerEvent, suppressPreventDefault: boolean | undefined = undefined) {
338
+ private onPointerMove(evt: PointerEvent) {
312
339
  // console.log(evt);
313
340
  if (evt.defaultPrevented) return;
314
341
  if (evt.pointerType === undefined && evt.pointerId === undefined) return;
315
- if (suppressPreventDefault !== true)
316
- evt.preventDefault();
342
+ // if (suppressPreventDefault !== true)
343
+ // evt.preventDefault();
317
344
  let i = evt.pointerType === "mouse" ? evt.button : this.getPointerIndex(evt.pointerId);
318
345
  if (evt.pointerId === undefined && evt.button !== undefined) {
319
346
  i = evt.button;
@@ -321,13 +348,14 @@ export class Input extends EventTarget {
321
348
  if (i < 0) i = 0;
322
349
  const args = { button: i, clientX: evt.clientX, clientY: evt.clientY, pointerType: evt.pointerType, source: evt };
323
350
  this.onMove(args);
351
+ this.onDispatchEvent(InputEvents.PointerMove, evt);
324
352
  }
325
353
 
326
- private onPointerUp(evt: PointerEvent, suppressPreventDefault: boolean | undefined = undefined) {
354
+ private onPointerUp(evt: PointerEvent) {
327
355
  if (evt.defaultPrevented) return;
328
356
  if (evt.pointerType === undefined && evt.pointerId === undefined) return;
329
- if (suppressPreventDefault !== true)
330
- evt.preventDefault();
357
+ // if (suppressPreventDefault !== true)
358
+ // evt.preventDefault();
331
359
  // console.log(evt, this._pointerIds);
332
360
  let i = evt.pointerType === "mouse" ? evt.button : this.getPointerIndex(evt.pointerId);
333
361
  // console.log("INDEX", i);
@@ -338,6 +366,7 @@ export class Input extends EventTarget {
338
366
  this.onUp({ button: i, clientX: evt.clientX, clientY: evt.clientY, pointerType: evt.pointerType, source: evt });
339
367
  // console.log(this.getPointerPressedCount());
340
368
  // evt.stopPropagation();
369
+ this.onDispatchEvent(InputEvents.PointerUp, evt);
341
370
  }
342
371
 
343
372
  // private onMouseDown(evt){
@@ -385,8 +414,7 @@ export class Input extends EventTarget {
385
414
  this.updatePointerPosition(evt);
386
415
  // console.log("DOWN", this._pointerDown, this.mousePositionRC);
387
416
 
388
- Context.Current = this.context;
389
- this.dispatchEvent(new Event(InputEvents.PointerDown));
417
+ this.onDispatchEvent(InputEvents.PointerDown, evt);
390
418
  }
391
419
  // moveEvent?: Event;
392
420
  private onMove(evt: PointerEventArgs) {
@@ -474,6 +502,20 @@ export class Input extends EventTarget {
474
502
  while (arr.length <= index) arr.push(null as any);
475
503
  arr[index] = value;
476
504
  }
505
+
506
+ private onDispatchEvent(name: InputEvents, args: PointerEventArgs | KeyEventArgs) {
507
+ const prevContext = Context.Current;
508
+ try {
509
+
510
+ Context.Current = this.context;
511
+ const e = new Event(name);
512
+ assign(e, args);
513
+ this.dispatchEvent(e);
514
+ }
515
+ finally {
516
+ Context.Current = prevContext;
517
+ }
518
+ }
477
519
  }
478
520
 
479
521
 
@@ -71,6 +71,7 @@ class CannonCollision {
71
71
 
72
72
  export class RaycastOptions {
73
73
  ray: THREE.Ray | undefined = undefined;
74
+ cam : THREE.Camera | undefined | null = undefined;
74
75
  screenPoint: THREE.Vector2 | undefined = undefined;
75
76
  raycaster: THREE.Raycaster | undefined = undefined;
76
77
  results: Array<THREE.Intersection> | undefined = undefined;
@@ -193,7 +194,7 @@ export class Physics {
193
194
  rc.ray.copy(options.ray);
194
195
  }
195
196
  else {
196
- const cam = this.context.mainCamera;
197
+ const cam = options.cam ?? this.context.mainCamera;
197
198
  if (!cam) {
198
199
  console.error("Can not perform raycast - no main camera found");
199
200
  if (this.defaultRaycastOptions.results) this.defaultRaycastOptions.results.length = 0;
@@ -0,0 +1,80 @@
1
+ import { getParam } from "./engine_utils";
2
+ import { Object3D } from "three";
3
+ import { Context } from "./engine_setup";
4
+
5
+ const debug = getParam("debugplayerview");
6
+
7
+ export enum ViewDevice {
8
+ Browser = "browser",
9
+ Headset = "headset",
10
+ Handheld = "handheld",
11
+ }
12
+
13
+ export class PlayerView {
14
+ readonly userId: string;
15
+ readonly context: Context;
16
+
17
+ viewDevice: ViewDevice = ViewDevice.Browser;
18
+
19
+ get currentObject(): Object3D | undefined | null {
20
+ return this._object;
21
+ }
22
+ set currentObject(obj: Object3D | undefined | null) {
23
+ this._object = obj;
24
+ }
25
+
26
+ get isConnected(): boolean {
27
+ return this.context.connection.userIsInRoom(this.userId);
28
+ }
29
+
30
+ removed: boolean = false;
31
+
32
+ private _object: undefined | Object3D | null;
33
+
34
+ constructor(userId: string, context: Context) {
35
+ this.userId = userId;
36
+ this.context = context;
37
+ }
38
+ }
39
+
40
+ export class PlayerViewManager {
41
+
42
+ private readonly context: Context;
43
+ private readonly playerViews = new Map<string, PlayerView>();
44
+
45
+ constructor(context: Context) {
46
+ this.context = context;
47
+ }
48
+
49
+ setPlayerView(id: string, obj: Object3D | undefined | null, device: ViewDevice) {
50
+ let view = this.playerViews.get(id);
51
+ if (!view) {
52
+ view = new PlayerView(id, this.context);
53
+ this.playerViews.set(id, view);
54
+ }
55
+ view.viewDevice = device;
56
+ view.currentObject = obj;
57
+ view.removed = false;
58
+ }
59
+
60
+ getPlayerView(id: string | null | undefined): PlayerView | undefined {
61
+ if (!id) return undefined;
62
+ if (!this.context.connection.userIsInRoom(id)) {
63
+ this.playerViews.delete(id);
64
+ return undefined;
65
+ }
66
+ const view = this.playerViews.get(id);
67
+ return view;
68
+ }
69
+
70
+ removePlayerView(id: string, device: ViewDevice) {
71
+ const view = this.playerViews.get(id);
72
+ if (view?.viewDevice === device) {
73
+ if (debug)
74
+ console.log("REMOVE", id);
75
+ view.removed = true;
76
+ this.playerViews.delete(id);
77
+ }
78
+ }
79
+
80
+ }
@@ -1,4 +1,6 @@
1
1
  import { serializeObject, deserializeObject } from "./engine_serialization_core";
2
2
  import * as builtin from "./engine_serialization_builtin_serializer";
3
3
  // export builtin so it will be called and registered
4
- export { serializeObject, deserializeObject, builtin };
4
+ export { serializeObject, deserializeObject, builtin };
5
+
6
+ export { serializeable } from "./engine_serialization_decorator"
@@ -248,6 +248,14 @@ export function deserializeObject(obj: ISerializable, serializedData: object, co
248
248
  for (const key in typeInfo) {
249
249
  const serializedEntryInfo = typeInfo[key];
250
250
  const data = serializedData[key];
251
+
252
+
253
+ if (obj[key] !== undefined && data === undefined) {
254
+ // if a field is marked as serialized and has some default value
255
+ // but no data was serialized do not override the default value with undefined
256
+ continue;
257
+ }
258
+
251
259
  context.path = key;
252
260
 
253
261
  if (serializedEntryInfo === null) {
@@ -26,6 +26,7 @@ import { RendererData } from './engine_rendererdata';
26
26
  import { Addressables } from './engine_addressables';
27
27
  import { Application } from './engine_application';
28
28
  import { LightDataRegistry, ILightDataRegistry } from './engine_lightdata';
29
+ import { PlayerViewManager } from './engine_playerview';
29
30
 
30
31
 
31
32
  const debug = utils.getParam("debugSetup");
@@ -71,6 +72,11 @@ export enum FrameEvent {
71
72
  PhysicsStep = 10,
72
73
  }
73
74
 
75
+ export enum XRSessionMode {
76
+ ImmersiveVR = "immersive-vr",
77
+ ImmersiveAR = "immersive-ar",
78
+ }
79
+
74
80
  export declare type OnBeforeRenderCallback = (renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, geometry: THREE.BufferGeometry, material: THREE.Material, group: THREE.Group) => void
75
81
 
76
82
  export class Context {
@@ -108,6 +114,7 @@ export class Context {
108
114
  get domX(): number { return this.domElement.offsetLeft; }
109
115
  get domY(): number { return this.domElement.offsetTop; }
110
116
  get isInXR() { return this.renderer.xr?.isPresenting || false; }
117
+ xrSessionMode : XRSessionMode | undefined = undefined;
111
118
  get xrSession() { return this.renderer.xr?.getSession(); }
112
119
  get arOverlayElement(): HTMLElement {
113
120
  const el = this.domElement as any;
@@ -165,6 +172,7 @@ export class Context {
165
172
  rendererData: RendererData;
166
173
  addressables: Addressables;
167
174
  lightmaps: ILightDataRegistry;
175
+ players : PlayerViewManager;
168
176
 
169
177
  private _sizeChanged: boolean = false;
170
178
  private _isCreated: boolean = false;
@@ -212,6 +220,7 @@ export class Context {
212
220
  this.rendererData = new RendererData(this);
213
221
  this.addressables = new Addressables(this);
214
222
  this.lightmaps = new LightDataRegistry(this);
223
+ this.players = new PlayerViewManager(this);
215
224
 
216
225
  window.addEventListener('resize', this.updateSize.bind(this));
217
226
  const ro = new ResizeObserver(_ => this._sizeChanged = true);
@@ -310,6 +319,21 @@ export class Context {
310
319
  (this.mainCameraComponent as Camera)?.applyClearFlagsIfIsActiveCamera();
311
320
  }
312
321
 
322
+ removeCamera(cam?: Camera | null) {
323
+ if(!cam) return;
324
+ const index = this._cameraStack.indexOf(cam);
325
+ if (index >= 0) this._cameraStack.splice(index, 1);
326
+
327
+ if (this.mainCameraComponent === cam) {
328
+ this.mainCameraComponent = undefined;
329
+
330
+ if (this._cameraStack.length > 0) {
331
+ const last = this._cameraStack[this._cameraStack.length - 1];
332
+ this.setCurrentCamera(last);
333
+ }
334
+ }
335
+ }
336
+
313
337
  private _onBeforeRenderListeners: { [key: string]: OnBeforeRenderCallback[] } = {};
314
338
 
315
339
  /** use this to subscribe to onBeforeRender events on threejs objects */
@@ -181,17 +181,43 @@ export function delay(milliseconds: number): Promise<void> {
181
181
  // if a timeline is exported via menu item the audio clip path is relative to the glb (same folder)
182
182
  // we need to detect that here and build the new audio source path relative to the new glb location
183
183
  // the same is/might be true for any file that is/will be exported via menu item
184
- export function getPath(glbLocation: SourceIdentifier | undefined, path: string) {
185
- if (path && glbLocation && !path.includes("/")) {
186
- // get directory of glb and prepend it to the audio file path
187
- const pathIndex = glbLocation.lastIndexOf("/");
188
- if (pathIndex >= 0) {
189
- const newPath = glbLocation.substring(0, pathIndex + 1) + path;
190
- return newPath;
184
+ const debugGetPath = getParam("debugsourcepath");
185
+ export function getPath(source: SourceIdentifier | undefined, uri: string): string {
186
+ if (source === undefined) {
187
+ if (debugGetPath) console.warn("getPath: source is undefined, returning uri", uri);
188
+ return uri;
189
+ }
190
+ if (uri.startsWith("http")) {
191
+ if (debugGetPath) console.warn("getPath: uri is absolute, returning uri", uri);
192
+ return uri;
193
+ }
194
+ const pathIndex = source.lastIndexOf("/");
195
+ if (pathIndex >= 0) {
196
+ let newUri = source.substring(0, pathIndex + 1);
197
+
198
+ const uriDirectoryIndex = uri.lastIndexOf("/");
199
+ if (uriDirectoryIndex >= 0) {
200
+ newUri += uri.substring(uriDirectoryIndex + 1);
201
+ } else {
202
+ newUri += uri;
191
203
  }
204
+ if (debugGetPath) console.log("getPath:", source, " - changed uri from\n", uri, "\n→ ", newUri);
205
+ return newUri;
192
206
  }
193
- return path;
207
+ return uri;
194
208
  }
209
+ // export function getPath(glbLocation: SourceIdentifier | undefined, path: string) {
210
+ // if (path && glbLocation && !path.includes("/")) {
211
+ // // get directory of glb and prepend it to the audio file path
212
+ // const pathIndex = glbLocation.lastIndexOf("/");
213
+ // if (pathIndex >= 0) {
214
+ // const newPath = glbLocation.substring(0, pathIndex + 1) + path;
215
+ // return newPath;
216
+ // }
217
+ // }
218
+ // return path;
219
+ // }
220
+
195
221
 
196
222
  export type WriteCallback = (data: any) => void;
197
223
 
@@ -3,7 +3,7 @@ import { GLTF, GLTFLoader, GLTFLoaderPlugin, GLTFParser } from "three/examples/j
3
3
  import { SourceIdentifier } from "../engine_gltf";
4
4
  import { Context } from "../engine_setup";
5
5
  import { addDracoAndKTX2Loaders } from "../engine_loaders";
6
- import { getParam } from "../engine_utils";
6
+ import { getParam, getPath } from "../engine_utils";
7
7
 
8
8
  export const EXTENSION_NAME = "NEEDLE_deferred_texture";
9
9
 
@@ -11,24 +11,27 @@ const debug = getParam("debugdeferredtextures")
11
11
 
12
12
  declare type DeferredTextureModel = {
13
13
  uri: string;
14
- guid?: string;
14
+ guid: string;
15
15
  usage?: string,
16
16
  }
17
17
 
18
18
  export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
19
19
 
20
- static assignTextureLOD(context: Context, material: Material, level: number = 0) {
20
+ static assignTextureLOD(context: Context, source : SourceIdentifier | undefined, material: Material, level: number = 0) {
21
21
  if (!material) return;
22
22
  for (const slot of Object.keys(material)) {
23
23
  const val = material[slot];
24
24
  if (val?.isTexture === true) {
25
25
  if (debug)
26
- console.log("FIND", slot, val?.name, val?.userData, val);
27
- NEEDLE_deferred_texture.getOrLoadTexture(context, material, slot, val, level).then(t => {
26
+ console.log("-----------\n", "FIND", material.name, slot, val?.name, val?.userData, val);
27
+ NEEDLE_deferred_texture.getOrLoadTexture(context, source, material, slot, val, level).then(t => {
28
28
  if (t?.isTexture === true) {
29
29
  t.needsUpdate = true;
30
+ if (debug)
31
+ console.log("Assign LOD", material.name, slot, t.name, t["guid"], material, "Prev:", val, "Now:", t, "\n--------------");
30
32
  material[slot] = t;
31
33
  material.needsUpdate = true;
34
+
32
35
  }
33
36
  });
34
37
  }
@@ -67,9 +70,8 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
67
70
  // }
68
71
 
69
72
  afterRoot(gltf: GLTF): null {
70
-
71
73
  if (debug)
72
- console.log("AFTER", gltf);
74
+ console.log("AFTER", this.sourceId, gltf);
73
75
  this.parser.json.textures?.forEach((textureInfo, index) => {
74
76
  if (textureInfo?.extensions) {
75
77
  const ext: DeferredTextureModel = textureInfo?.extensions[EXTENSION_NAME];
@@ -91,29 +93,31 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
91
93
  private static cache = new Map<string, DeferredTextureModel>();
92
94
  private static resolved: { [key: string]: Texture } = {};
93
95
 
94
- private static async getOrLoadTexture(context: Context, material: Material, slot: string, current: Texture, _level: number): Promise<Texture | null> {
96
+ private static async getOrLoadTexture(context: Context, source : SourceIdentifier | undefined, material: Material, slot: string, current: Texture, _level: number): Promise<Texture | null> {
95
97
 
96
98
  const key = current.uuid;
97
- const ext: DeferredTextureModel = NEEDLE_deferred_texture.cache.get(key) || current.userData.deferred;
99
+ const ext: DeferredTextureModel | undefined = NEEDLE_deferred_texture.cache.get(key);// || current.userData.deferred;
98
100
  if (ext) {
99
101
  if (debug)
100
102
  console.log(key, ext.uri, ext.guid);
101
- const uri = ext.uri;
103
+ const uri = getPath(source, ext.uri);
102
104
  if (uri.endsWith(".glb") || uri.endsWith(".gltf")) {
103
105
  if (!ext.guid) {
104
106
  console.warn("missing pointer for glb/gltf texture", ext);
105
107
  return null;
106
108
  }
107
- const resolveKey = ext.uri + "_" + ext.guid;
108
- if (this.resolved[resolveKey])
109
+ const resolveKey = uri + "_" + ext.guid;
110
+ if (this.resolved[resolveKey]) {
111
+ if (debug) console.log("Texture has already been loaded: " + resolveKey, material.name, slot, current.name);
109
112
  return this.resolved[resolveKey];
113
+ }
110
114
 
111
115
  const loader = new GLTFLoader();
112
116
  addDracoAndKTX2Loaders(loader, context);
113
- if (debug) console.log("Load " + uri, material.name, slot);
117
+ if (debug) console.log("Load " + uri, material.name, slot, ext.guid);
114
118
  const gltf = await loader.loadAsync(uri);
115
119
  const parser = gltf.parser;
116
-
120
+ if (debug) console.log("Loading finished " + uri, material.name, slot, ext.guid);
117
121
  let index = -1;
118
122
  let found = false;
119
123
  for (const tex of gltf.parser.json.textures) {
@@ -137,20 +141,22 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
137
141
 
138
142
  // const index = Number.parseInt(ext.pointer.substring("textures/".length));
139
143
  const tex = await parser.getDependency("texture", index);
144
+ if (tex) {
145
+ tex.guid = ext.guid;
146
+ }
140
147
  this.resolved[resolveKey] = tex as Texture;
141
-
142
148
  if (debug)
143
- console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material);
149
+ console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material, resolveKey);
144
150
  return tex;
145
151
  }
146
152
  else {
147
-
153
+ if (debug) console.log("Load texture from uri: " + uri);
148
154
  const loader = new TextureLoader();
149
- const loaded = await loader.loadAsync(ext.uri);
155
+ const loaded = await loader.loadAsync(uri);
150
156
  if (loaded && debug) {
151
157
  console.log(ext, loaded);
152
158
  }
153
- else console.warn("failed loading", ext.uri);
159
+ else console.warn("failed loading", uri);
154
160
  return loaded;
155
161
  }
156
162
  // loader.then((h: Texture) => {
@@ -99,13 +99,14 @@ export class SceneLightSettings extends Behaviour {
99
99
  onEnable() {
100
100
  const isActive = this.context.mainCameraComponent?.sourceId === this.sourceId;
101
101
  if (debug)
102
- console.log("enable", this.sourceId, isActive, this, this.context.mainCameraComponent?.sourceId);
102
+ console.log("Enable scene lighting", this.sourceId, isActive, this, this.context.mainCameraComponent?.sourceId);
103
103
  if (!isActive) {
104
+ if(debug) console.warn("This is no active?!", this.context.mainCameraComponent?.sourceId)
104
105
  // this.enabled = false;
105
106
  // return;
106
107
  }
107
108
  if (this.ambientMode == AmbientMode.Flat) {
108
- if (this.ambientLight) {
109
+ if (this.ambientLight && !this._ambientLightObj) {
109
110
  this._ambientLightObj = new AmbientLight(this.ambientLight, Math.PI * this.ambientIntensity);
110
111
  }
111
112
  if (this._ambientLightObj) {
@@ -155,6 +156,7 @@ export class SceneLightSettings extends Behaviour {
155
156
  if (debug)
156
157
  console.log("disable", this.sourceId, this);
157
158
  if (this._lightProbeObj) this._lightProbeObj.removeFromParent();
159
+ if(this._ambientLightObj) this._ambientLightObj.removeFromParent();
158
160
  if (this.sourceId)
159
161
  this.context.rendererData.disableReflection();
160
162
  }
@@ -10,13 +10,20 @@ declare type DependencyInfo = {
10
10
  dependencyName: string,
11
11
  }
12
12
 
13
- const rootExtensionPrefix = "/extensions/";
13
+ const rootExtensionPrefix = ["/extensions/", "extensions/"];
14
14
  const defaultDependencies = [
15
15
  { prefix: "/nodes/", dependencyName: "node" },
16
16
  { prefix: "/meshes/", dependencyName: "mesh" },
17
17
  { prefix: "/materials/", dependencyName: "material" },
18
18
  { prefix: "/textures/", dependencyName: "texture" },
19
- { prefix: "/animations/", dependencyName: "animation" }
19
+ { prefix: "/animations/", dependencyName: "animation" },
20
+
21
+ // legacy support
22
+ { prefix: "nodes/", dependencyName: "node" },
23
+ { prefix: "meshes/", dependencyName: "mesh" },
24
+ { prefix: "materials/", dependencyName: "material" },
25
+ { prefix: "textures/", dependencyName: "texture" },
26
+ { prefix: "animations/", dependencyName: "animation" },
20
27
  ]
21
28
 
22
29
  export async function resolveReferences(parser: GLTFParser, obj) {
@@ -95,16 +102,21 @@ function internalResolve(paths: DependencyInfo[], parser: GLTFParser, obj, promi
95
102
 
96
103
 
97
104
  function resolveExtension(parser: GLTFParser, str): Promise<void> | null {
98
- if (parser && parser.plugins && typeof str === "string" && str.startsWith(rootExtensionPrefix)) {
99
- let name = str.substring(rootExtensionPrefix.length);
100
- const endIndex = name.indexOf("/");
101
- if (endIndex >= 0) name = name.substring(0, endIndex);
102
- const ext = parser.plugins[name] as IExtensionReferenceResolver;
103
- if (debugExtension)
104
- console.log(name, ext);
105
- if (typeof ext?.resolve === "function") {
106
- const path = str.substring(rootExtensionPrefix.length + name.length + 1);
107
- return ext.resolve(parser, path);
105
+ if (parser && parser.plugins && typeof str === "string") {
106
+ for (const prefix of rootExtensionPrefix) {
107
+ if (str.startsWith(prefix)) {
108
+ let name = str.substring(prefix.length);
109
+ const endIndex = name.indexOf("/");
110
+ if (endIndex >= 0) name = name.substring(0, endIndex);
111
+ const ext = parser.plugins[name] as IExtensionReferenceResolver;
112
+ if (debugExtension)
113
+ console.log(name, ext);
114
+ if (typeof ext?.resolve === "function") {
115
+ const path = str.substring(prefix.length + name.length + 1);
116
+ return ext.resolve(parser, path);
117
+ }
118
+ break;
119
+ }
108
120
  }
109
121
  }
110
122
  return null;
@@ -26,8 +26,9 @@ export function registerComponentExtension(loader: GLTFLoader): NEEDLE_component
26
26
  class PointerResolver {
27
27
  resolvePath(path: string) {
28
28
  if (path.includes('/extensions/builtin_components/'))
29
- path = path.replace('/extensions/builtin_components/', '/userData/components/');
30
-
29
+ return path.replace('/extensions/builtin_components/', '/userData/components/');
30
+ if (path.includes('extensions/builtin_components/'))
31
+ return path.replace('extensions/builtin_components/', '/userData/components/');
31
32
  return path;
32
33
  }
33
34
  }