@needle-tools/engine 3.5.1-alpha → 3.5.3-alpha

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 (109) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/needle-engine.js +29554 -29281
  3. package/dist/needle-engine.min.js +357 -356
  4. package/dist/needle-engine.umd.cjs +354 -353
  5. package/lib/engine/codegen/register_types.js +4 -2
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/debug/debug_overlay.js +2 -1
  8. package/lib/engine/debug/debug_overlay.js.map +1 -1
  9. package/lib/engine/engine_components.js +2 -1
  10. package/lib/engine/engine_components.js.map +1 -1
  11. package/lib/engine/engine_element_loading.js +4 -4
  12. package/lib/engine/engine_element_loading.js.map +1 -1
  13. package/lib/engine/engine_gameobject.js +2 -0
  14. package/lib/engine/engine_gameobject.js.map +1 -1
  15. package/lib/engine/engine_input.js +4 -1
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_license.d.ts +2 -0
  18. package/lib/engine/engine_license.js +25 -4
  19. package/lib/engine/engine_license.js.map +1 -1
  20. package/lib/engine/engine_physics_rapier.js +2 -1
  21. package/lib/engine/engine_physics_rapier.js.map +1 -1
  22. package/lib/engine/engine_serialization_core.js +16 -1
  23. package/lib/engine/engine_serialization_core.js.map +1 -1
  24. package/lib/engine/extensions/NEEDLE_lighting_settings.js +10 -1
  25. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  26. package/lib/engine-components/Component.js +0 -3
  27. package/lib/engine-components/Component.js.map +1 -1
  28. package/lib/engine-components/SceneSwitcher.d.ts +9 -0
  29. package/lib/engine-components/SceneSwitcher.js +128 -0
  30. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  31. package/lib/engine-components/codegen/components.d.ts +2 -1
  32. package/lib/engine-components/codegen/components.js +2 -1
  33. package/lib/engine-components/codegen/components.js.map +1 -1
  34. package/lib/engine-components/export/usdz/Extension.d.ts +3 -2
  35. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +9 -5
  36. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +64 -20
  37. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  38. package/lib/engine-components/export/usdz/USDZExporter.d.ts +2 -0
  39. package/lib/engine-components/export/usdz/USDZExporter.js +44 -9
  40. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  41. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +9 -0
  42. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +48 -0
  43. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js.map +1 -0
  44. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +4 -5
  45. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +19 -10
  46. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
  47. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +9 -0
  48. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +75 -5
  49. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  50. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +20 -0
  51. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +45 -0
  52. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -1
  53. package/lib/engine-components/ui/BaseUIComponent.d.ts +1 -0
  54. package/lib/engine-components/ui/BaseUIComponent.js +8 -4
  55. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  56. package/lib/engine-components/ui/Canvas.js +1 -1
  57. package/lib/engine-components/ui/Canvas.js.map +1 -1
  58. package/lib/engine-components/ui/EventSystem.js +3 -0
  59. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  60. package/lib/engine-components/ui/Image.d.ts +3 -1
  61. package/lib/engine-components/ui/Image.js +15 -1
  62. package/lib/engine-components/ui/Image.js.map +1 -1
  63. package/lib/engine-components/ui/Interfaces.d.ts +1 -1
  64. package/lib/engine-components/ui/Layout.js +2 -0
  65. package/lib/engine-components/ui/Layout.js.map +1 -1
  66. package/lib/engine-components/ui/PointerEvents.d.ts +8 -1
  67. package/lib/engine-components/ui/PointerEvents.js +9 -1
  68. package/lib/engine-components/ui/PointerEvents.js.map +1 -1
  69. package/lib/engine-components/ui/RaycastUtils.js +5 -0
  70. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  71. package/lib/engine-components/ui/RectTransform.d.ts +2 -2
  72. package/lib/engine-components/ui/RectTransform.js +11 -12
  73. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  74. package/lib/engine-components/ui/Text.d.ts +0 -2
  75. package/lib/engine-components/ui/Text.js +0 -5
  76. package/lib/engine-components/ui/Text.js.map +1 -1
  77. package/lib/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +1 -1
  79. package/plugins/vite/license.js +2 -2
  80. package/src/engine/codegen/register_types.js +4 -2
  81. package/src/engine/debug/debug_overlay.ts +2 -1
  82. package/src/engine/engine_components.ts +2 -1
  83. package/src/engine/engine_element_loading.ts +4 -4
  84. package/src/engine/engine_gameobject.ts +3 -0
  85. package/src/engine/engine_input.ts +4 -1
  86. package/src/engine/engine_license.ts +25 -4
  87. package/src/engine/engine_physics_rapier.ts +2 -1
  88. package/src/engine/engine_serialization_core.ts +17 -1
  89. package/src/engine/extensions/NEEDLE_lighting_settings.ts +11 -1
  90. package/src/engine-components/Component.ts +1 -3
  91. package/src/engine-components/SceneSwitcher.ts +136 -1
  92. package/src/engine-components/codegen/components.ts +2 -1
  93. package/src/engine-components/export/usdz/Extension.ts +3 -2
  94. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +89 -21
  95. package/src/engine-components/export/usdz/USDZExporter.ts +48 -11
  96. package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +63 -0
  97. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +28 -16
  98. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +98 -21
  99. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +48 -2
  100. package/src/engine-components/ui/BaseUIComponent.ts +8 -3
  101. package/src/engine-components/ui/Canvas.ts +1 -1
  102. package/src/engine-components/ui/EventSystem.ts +5 -1
  103. package/src/engine-components/ui/Image.ts +16 -1
  104. package/src/engine-components/ui/Interfaces.ts +1 -1
  105. package/src/engine-components/ui/Layout.ts +2 -0
  106. package/src/engine-components/ui/PointerEvents.ts +16 -2
  107. package/src/engine-components/ui/RaycastUtils.ts +6 -1
  108. package/src/engine-components/ui/RectTransform.ts +11 -11
  109. package/src/engine-components/ui/Text.ts +1 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "3.5.1-alpha",
3
+ "version": "3.5.3-alpha",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "type": "module",
@@ -13,8 +13,8 @@ export const needleLicense = (command, config, userSettings) => {
13
13
  if (isNeedleEngineFile || isViteChunkFile) {
14
14
  const needleConfig = await loadConfig();
15
15
  if (needleConfig) {
16
- if (needleConfig.hasProLicense === true) {
17
- src = src.replace("NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = false;", "NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = " + needleConfig.hasProLicense + ";");
16
+ if (typeof needleConfig.license === "string") {
17
+ src = src.replace("const NEEDLE_ENGINE_LICENSE_TYPE: string = \"\";", "const NEEDLE_ENGINE_LICENSE_TYPE: string = \"" + needleConfig.license + "\";");
18
18
  return { code: src, map: null }
19
19
  }
20
20
  }
@@ -14,6 +14,7 @@ import { Animator } from "../../engine-components/Animator";
14
14
  import { AnimatorController } from "../../engine-components/AnimatorController";
15
15
  import { Antialiasing } from "../../engine-components/postprocessing/Effects/Antialiasing";
16
16
  import { AttachedObject } from "../../engine-components/webxr/WebXRController";
17
+ import { AudioExtension } from "../../engine-components/export/usdz/extensions/behavior/AudioExtension";
17
18
  import { AudioListener } from "../../engine-components/AudioListener";
18
19
  import { AudioSource } from "../../engine-components/AudioSource";
19
20
  import { AudioTrackHandler } from "../../engine-components/timeline/TimelineTracks";
@@ -116,6 +117,7 @@ import { ParticleSystemRenderer } from "../../engine-components/ParticleSystem";
116
117
  import { PixelationEffect } from "../../engine-components/postprocessing/Effects/Pixelation";
117
118
  import { PlayableDirector } from "../../engine-components/timeline/PlayableDirector";
118
119
  import { PlayAnimationOnClick } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
120
+ import { PlayAudioOnClick } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
119
121
  import { PlayerColor } from "../../engine-components/PlayerColor";
120
122
  import { PlayerState } from "../../engine-components-experimental/networking/PlayerSync";
121
123
  import { PlayerSync } from "../../engine-components-experimental/networking/PlayerSync";
@@ -184,7 +186,6 @@ import { TriggerModel } from "../../engine-components/export/usdz/extensions/beh
184
186
  import { UIRaycastUtils } from "../../engine-components/ui/RaycastUtils";
185
187
  import { UIRootComponent } from "../../engine-components/ui/BaseUIComponent";
186
188
  import { UsageMarker } from "../../engine-components/Interactable";
187
- import { USDZBehaviours } from "../../engine-components/export/usdz/extensions/behavior/Behaviour";
188
189
  import { USDZExporter } from "../../engine-components/export/usdz/USDZExporter";
189
190
  import { USDZText } from "../../engine-components/export/usdz/extensions/USDZText";
190
191
  import { VariantAction } from "../../engine-components/export/usdz/extensions/behavior/Actions";
@@ -229,6 +230,7 @@ TypeStore.add("Animator", Animator);
229
230
  TypeStore.add("AnimatorController", AnimatorController);
230
231
  TypeStore.add("Antialiasing", Antialiasing);
231
232
  TypeStore.add("AttachedObject", AttachedObject);
233
+ TypeStore.add("AudioExtension", AudioExtension);
232
234
  TypeStore.add("AudioListener", AudioListener);
233
235
  TypeStore.add("AudioSource", AudioSource);
234
236
  TypeStore.add("AudioTrackHandler", AudioTrackHandler);
@@ -331,6 +333,7 @@ TypeStore.add("ParticleSystemRenderer", ParticleSystemRenderer);
331
333
  TypeStore.add("PixelationEffect", PixelationEffect);
332
334
  TypeStore.add("PlayableDirector", PlayableDirector);
333
335
  TypeStore.add("PlayAnimationOnClick", PlayAnimationOnClick);
336
+ TypeStore.add("PlayAudioOnClick", PlayAudioOnClick);
334
337
  TypeStore.add("PlayerColor", PlayerColor);
335
338
  TypeStore.add("PlayerState", PlayerState);
336
339
  TypeStore.add("PlayerSync", PlayerSync);
@@ -399,7 +402,6 @@ TypeStore.add("TriggerModel", TriggerModel);
399
402
  TypeStore.add("UIRaycastUtils", UIRaycastUtils);
400
403
  TypeStore.add("UIRootComponent", UIRootComponent);
401
404
  TypeStore.add("UsageMarker", UsageMarker);
402
- TypeStore.add("USDZBehaviours", USDZBehaviours);
403
405
  TypeStore.add("USDZExporter", USDZExporter);
404
406
  TypeStore.add("USDZText", USDZText);
405
407
  TypeStore.add("VariantAction", VariantAction);
@@ -107,7 +107,8 @@ const currentMessages = new Set<string>();
107
107
  function showMessage(type: LogType, element: HTMLElement, msg: string) {
108
108
  const container = getLogsContainer(element);
109
109
  if (container.childElementCount >= 20) {
110
- return;
110
+ const last = container.lastElementChild;
111
+ returnMessageContainer(last as HTMLElement);
111
112
  }
112
113
  // truncate long messages before they go into the cache/set
113
114
  if(msg.length > 300) msg = msg.substring(0, 300) + "...";
@@ -160,7 +160,8 @@ export function getComponent<T>(obj: Object3D, componentType: Constructor<T>) {
160
160
 
161
161
  export function getComponents<T>(obj: Object3D, componentType: Constructor<T>, arr?: T[] | null): T[] {
162
162
  if (!arr) arr = [];
163
- return onGetComponent(obj, componentType, arr);
163
+ onGetComponent(obj, componentType, arr);
164
+ return arr;
164
165
  }
165
166
 
166
167
  export function getComponentInChildren<T>(obj: Object3D, componentType: Constructor<T>, includeInactive?: boolean) {
@@ -3,7 +3,7 @@ import { Mathf } from "./engine_math";
3
3
  import { LoadingProgressArgs } from "./engine_setup";
4
4
  import { getParam } from "./engine_utils";
5
5
  import { logoSVG } from "./assets"
6
- import { hasProLicense } from "./engine_license";
6
+ import { hasCommercialLicense, hasProLicense } from "./engine_license";
7
7
 
8
8
  const debug = getParam("debugloadingbar");
9
9
  const debugRendering = getParam("debugloadingbarrendering");
@@ -186,7 +186,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
186
186
  if (loadingStyle === "light")
187
187
  this._loadingElement.style.backgroundColor = "#ddd";
188
188
  else
189
- this._loadingElement.style.backgroundColor = "#000";
189
+ this._loadingElement.style.backgroundColor = "#222";
190
190
  this._loadingElement.style.display = "flex";
191
191
  this._loadingElement.style.alignItems = "center";
192
192
  this._loadingElement.style.justifyContent = "center";
@@ -289,7 +289,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
289
289
  messageContainer.style.fontSize = ".8em";
290
290
  messageContainer.style.paddingTop = ".5em";
291
291
  messageContainer.style.fontWeight = "200";
292
- messageContainer.style.fontFamily = "Roboto, sans-serif";
292
+ messageContainer.style.fontFamily = "Roboto, sans-serif, Arial";
293
293
  // messageContainer.style.border = "1px solid rgba(255,255,255,.1)";
294
294
  messageContainer.style.justifyContent = "center";
295
295
  this._loadingElement.appendChild(messageContainer);
@@ -301,7 +301,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
301
301
  }
302
302
  }
303
303
 
304
- if (!hasLicense) {
304
+ if (!hasCommercialLicense()) {
305
305
  const nonCommercialContainer = document.createElement("div");
306
306
  nonCommercialContainer.style.paddingTop = ".6em";
307
307
  nonCommercialContainer.style.fontSize = ".8em";
@@ -113,6 +113,9 @@ export function destroy(instance: Object3D | Component, recursive: boolean = tru
113
113
  }
114
114
 
115
115
  function internalDestroy(instance: Object3D | Component, recursive: boolean = true, dispose: boolean = false, isRoot: boolean = true) {
116
+ if (instance === null || instance === undefined)
117
+ return;
118
+
116
119
  const comp = instance as Component;
117
120
  if (comp.isComponent) {
118
121
  comp.__internalDisable();
@@ -611,9 +611,12 @@ export class Input extends EventTarget implements IInput {
611
611
 
612
612
  const lf = this._pointerPositionsLastFrame[evt.button];
613
613
  lf.copy(this._pointerPositions[evt.button]);
614
+ // accumulate delta (it's reset in end of frame), if we just write it here it's not correct when the browser console is open
615
+ const delta = this._pointerPositionsDelta[evt.button];
614
616
  const dx = evt.clientX - lf.x;
615
617
  const dy = evt.clientY - lf.y;
616
- this._pointerPositionsDelta[evt.button].set(dx, dy);
618
+ delta.x += dx;
619
+ delta.y += dy;
617
620
 
618
621
  this._pointerPositions[evt.button].x = evt.clientX;
619
622
  this._pointerPositions[evt.button].y = evt.clientY;
@@ -7,10 +7,28 @@ const debug = getParam("debuglicense");
7
7
 
8
8
  // This is modified by a bundler (e.g. vite)
9
9
  // Do not edit manually
10
- const NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE = false;
10
+ const NEEDLE_ENGINE_LICENSE_TYPE: string = "";
11
+ if (debug) console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)
11
12
 
12
13
  export function hasProLicense() {
13
- return NEEDLE_ENGINE_COMMERCIAL_USE_LICENSE;
14
+ switch (NEEDLE_ENGINE_LICENSE_TYPE) {
15
+ case "pro":
16
+ case "enterprise":
17
+ return true;
18
+ };
19
+ return false;
20
+ }
21
+
22
+ export function hasIndieLicense() {
23
+ switch (NEEDLE_ENGINE_LICENSE_TYPE) {
24
+ case "indie":
25
+ return true;
26
+ }
27
+ return false;
28
+ }
29
+
30
+ export function hasCommercialLicense() {
31
+ return hasProLicense() || hasIndieLicense();
14
32
  }
15
33
 
16
34
 
@@ -52,7 +70,8 @@ function insertNonCommercialUseHint(ctx: IContext) {
52
70
  }
53
71
  }, 100);
54
72
 
55
- logNonCommercialUse();
73
+ if (!hasCommercialLicense())
74
+ logNonCommercialUse();
56
75
 
57
76
  let svg = `<img class="logo" src="${logoSVG}" style="width: 40px; height: 40px; margin-right: 2px; vertical-align: middle; margin-bottom: 2px;"/>`;
58
77
  const logoElement = document.createElement("div");
@@ -66,7 +85,9 @@ function insertNonCommercialUseHint(ctx: IContext) {
66
85
  // textElement.innerHTML = "Needle Engine<br/><span class=\"non-commercial\">Non Commercial</span>";
67
86
  licenseElement.appendChild(textElement);
68
87
 
69
- licenseElement.title = "Needle Engine — non commercial version";
88
+ licenseElement.title = "Needle Engine";
89
+ if (!hasCommercialLicense())
90
+ licenseElement.title += " non commercial version";
70
91
  licenseElement.addEventListener("click", () => {
71
92
  globalThis.open("https://needle.tools", "_blank");
72
93
  });
@@ -207,7 +207,8 @@ export class RapierPhysics implements IPhysicsEngine {
207
207
  private async internalInitialization() {
208
208
  // NEEDLE_PHYSICS_INIT_START
209
209
  // use .env file with VITE_NEEDLE_USE_RAPIER=false to treeshape rapier
210
- if (import.meta.env.VITE_NEEDLE_USE_RAPIER === "false") {
210
+ // @ts-ignore
211
+ if ("env" in import.meta && import.meta.env.VITE_NEEDLE_USE_RAPIER === "false") {
211
212
  return false;
212
213
  }
213
214
  // Can be transformed during build time to disable rapier
@@ -434,7 +434,14 @@ function implictlyAssignPrimitiveTypes(obj: any, serializedData: any) {
434
434
  if (targetMember !== undefined) continue;
435
435
  // resolve serialized primitive types
436
436
  if (isPrimitiveType(data[key]) && !isPrimitiveType(member)) {
437
- // console.log("ASSIGN", key, member, member[key], targetMember, data[key]);
437
+
438
+ const prop = tryFindPropertyDescriptor(member, key);
439
+ if (!prop?.writable === false || (prop && prop.set === undefined)) {
440
+ if (debug)
441
+ console.warn("Property is not writable \"" + key + "\"", member, prop, data[key], member[key]);
442
+ continue;
443
+ }
444
+ // console.log("ASSIGN", key, member, member[key], targetMember, data[key], prop);
438
445
  member[key] = data[key];
439
446
  }
440
447
  }
@@ -442,6 +449,15 @@ function implictlyAssignPrimitiveTypes(obj: any, serializedData: any) {
442
449
  }
443
450
  }
444
451
 
452
+ function tryFindPropertyDescriptor(obj: object, key: string) : PropertyDescriptor | undefined {
453
+ while(obj){
454
+ const desc = Object.getOwnPropertyDescriptor(obj, key);
455
+ if(desc) return desc;
456
+ obj = Object.getPrototypeOf(obj);
457
+ }
458
+ return undefined;
459
+ }
460
+
445
461
  function isPrimitiveType(val): boolean {
446
462
  switch (typeof val) {
447
463
  case "number":
@@ -48,8 +48,9 @@ export class NEEDLE_lighting_settings implements GLTFLoaderPlugin {
48
48
  let settings: SceneLightSettings | undefined = undefined;
49
49
  // If the result scene has only one child we add the LightingSettingsComponent to that child
50
50
  if (_result.scene.children.length === 1) {
51
+ const obj = _result.scene.children[0];
51
52
  // add a component to the root of the scene
52
- settings = GameObject.addNewComponent(_result.scene.children[0], SceneLightSettings, false);
53
+ settings = GameObject.addNewComponent(obj, SceneLightSettings, false);
53
54
  }
54
55
  // if the scene already has multiple children we add it as a new object
55
56
  else {
@@ -117,6 +118,15 @@ export class SceneLightSettings extends Behaviour {
117
118
  }
118
119
  });
119
120
  }
121
+
122
+ // make sure the component is in the end of the list
123
+ // (e.g. if we have an animation on the first component from an instance and add the scenelightingcomponent the animation binding will break)
124
+ const comps = this.gameObject.userData?.components;
125
+ if (comps) {
126
+ const index = comps.indexOf(this);
127
+ comps.splice(index, 1);
128
+ comps.push(this);
129
+ }
120
130
  }
121
131
 
122
132
  onDestroy(): void {
@@ -195,9 +195,6 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
195
195
  * @param instance component to move to the GO
196
196
  */
197
197
  public static moveComponent(go: IGameObject, instance: Component): void {
198
- if (instance.gameObject == null) {
199
- throw new Error("Did you mean to create a new component? Use addNewComponent");
200
- }
201
198
  moveComponentInstance(go, instance as any);
202
199
  }
203
200
 
@@ -277,6 +274,7 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
277
274
  // these are implemented via threejs object extensions
278
275
  abstract activeSelf: boolean;
279
276
  abstract addNewComponent<T>(type: Constructor<T>): T | null;
277
+ // TODO: add method for addExisting component
280
278
  abstract removeComponent(comp: Component): Component;
281
279
  abstract getOrAddComponent<T>(typeName: Constructor<T> | null): T;
282
280
  abstract getComponent<T>(type: Constructor<T>): T | null;
@@ -56,11 +56,27 @@ export class SceneSwitcher extends Behaviour {
56
56
  @serializable()
57
57
  useSceneLighting: boolean = true;
58
58
 
59
+ /** how many scenes after the currently active scene should be preloaded */
60
+ @serializable()
61
+ preloadNext: number = 1;
62
+
63
+ /** how many scenes before the currently active scene should be preloaded */
64
+ @serializable()
65
+ preloadPrevious: number = 1;
66
+
67
+ /** how many scenes can be loaded in parallel */
68
+ @serializable()
69
+ preloadConcurrent: number = 2;
70
+
71
+
72
+ get currentIndex(): number { return this._currentIndex; }
59
73
 
60
74
  private _currentIndex: number = -1;
61
75
  private _currentScene: AssetReference | undefined = undefined;
62
76
  private _engineElementOverserver: MutationObserver | undefined = undefined;
63
77
 
78
+ private _preloadScheduler?: PreLoadScheduler;
79
+
64
80
  async start() {
65
81
  if (this._currentIndex === -1 && !await this.tryLoadFromQueryParam()) {
66
82
  const value = this.context.domElement.getAttribute(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
@@ -100,6 +116,13 @@ export class SceneSwitcher extends Behaviour {
100
116
  this._engineElementOverserver.observe(this.context.domElement, {
101
117
  attributes: true
102
118
  });
119
+
120
+ if (!this._preloadScheduler)
121
+ this._preloadScheduler = new PreLoadScheduler(this);
122
+ this._preloadScheduler.maxLoadAhead = this.preloadNext;
123
+ this._preloadScheduler.maxLoadBehind = this.preloadPrevious;
124
+ this._preloadScheduler.maxConcurrent = this.preloadConcurrent;
125
+ this._preloadScheduler.begin();
103
126
  }
104
127
 
105
128
  onDisable(): void {
@@ -107,6 +130,7 @@ export class SceneSwitcher extends Behaviour {
107
130
  this.context.input.removeEventListener(InputEvents.KeyDown, this.onKeyDown);
108
131
  this.context.input.removeEventListener(InputEvents.PointerMove, this.onPointerMove);
109
132
  this.context.input.removeEventListener(InputEvents.PointerUp, this.onPointerUp);
133
+ this._preloadScheduler?.stop();
110
134
  }
111
135
 
112
136
  private onPopState = async (_state: PopStateEvent) => {
@@ -189,7 +213,7 @@ export class SceneSwitcher extends Behaviour {
189
213
  select(index: number | string): Promise<boolean> {
190
214
  if (debug) console.log("select", index);
191
215
 
192
- if(typeof index === "object"){
216
+ if (typeof index === "object") {
193
217
  // If a user tries to reference a scene object in a UnityEvent and invoke select(obj)
194
218
  // Then the object will be serialized as a object { guid : ... } or with the index json pointer
195
219
  // This case is not supported right now. Object references in the editor must not be scene references
@@ -271,6 +295,15 @@ export class SceneSwitcher extends Behaviour {
271
295
  return false;
272
296
  }
273
297
 
298
+ preload(index: number) {
299
+ if (index >= 0 && index < this.scenes.length) {
300
+ const scene = this.scenes[index];
301
+ if(scene instanceof AssetReference)
302
+ return scene.preload();
303
+ }
304
+ return couldNotLoadScenePromise;
305
+ }
306
+
274
307
  private tryLoadFromQueryParam() {
275
308
  if (!this.queryParameterName?.length) return couldNotLoadScenePromise;
276
309
  // try restore the scene from the url
@@ -308,3 +341,105 @@ export class SceneSwitcher extends Behaviour {
308
341
  return couldNotLoadScenePromise;
309
342
  }
310
343
  }
344
+
345
+
346
+
347
+
348
+ class PreLoadScheduler {
349
+ maxLoadAhead: number;
350
+ maxLoadBehind: number;
351
+ maxConcurrent: number;
352
+
353
+ private _isRunning: boolean = false;
354
+ private _rooms: SceneSwitcher;
355
+ private _roomTasks: LoadTask[] = [];
356
+ private _maxConcurrentLoads: number = 1;
357
+
358
+ constructor(rooms: SceneSwitcher, ahead: number = 1, behind: number = 1, maxConcurrent: number = 2) {
359
+ this._rooms = rooms;
360
+ this.maxLoadAhead = ahead;
361
+ this.maxLoadBehind = behind;
362
+ this.maxConcurrent = maxConcurrent;
363
+ }
364
+
365
+ begin() {
366
+ if (this._isRunning) return;
367
+ if (debug) console.log("Preload begin")
368
+ this._isRunning = true;
369
+ let lastRoom: number = -1;
370
+ let searchDistance: number;
371
+ let searchCall: number;
372
+ const array = this._rooms.scenes;
373
+ let interval = setInterval(() => {
374
+ if (this.allLoaded()) {
375
+ if (debug)
376
+ console.log("All scenes loaded");
377
+ this.stop();
378
+ }
379
+ if (!this._isRunning) {
380
+ clearInterval(interval);
381
+ return;
382
+ }
383
+ if (this.canLoadNewScene() === false) return;
384
+ if (lastRoom !== this._rooms.currentIndex) {
385
+ lastRoom = this._rooms.currentIndex;
386
+ searchCall = 0;
387
+ searchDistance = 0;
388
+ }
389
+ const searchForward = searchCall % 2 === 0;
390
+ if (searchForward) searchDistance += 1;
391
+ searchCall += 1;
392
+ const maxSearchDistance = searchForward ? this.maxLoadAhead : this.maxLoadBehind;
393
+ if (searchDistance > maxSearchDistance) return;
394
+ let roomIndex = searchForward ? lastRoom + searchDistance : lastRoom - searchDistance;
395
+ if (roomIndex < 0) return;
396
+ // if (roomIndex < 0) roomIndex = array.length + roomIndex;
397
+ if (roomIndex < 0 || roomIndex >= array.length) return;
398
+ const scene = array[roomIndex];
399
+ new LoadTask(roomIndex, scene, this._roomTasks);
400
+ }, 200);
401
+ }
402
+
403
+ stop() {
404
+ this._isRunning = false;
405
+ }
406
+
407
+ canLoadNewScene(): boolean {
408
+ return this._roomTasks.length < this._maxConcurrentLoads;
409
+ }
410
+
411
+ allLoaded(): boolean {
412
+ for (const room of this._rooms.scenes) {
413
+ if (room.isLoaded() === false) return false;
414
+ }
415
+ return true;
416
+ }
417
+ }
418
+
419
+ class LoadTask {
420
+
421
+ index: number;
422
+ asset: AssetReference;
423
+ tasks: LoadTask[];
424
+
425
+ constructor(index: number, asset: AssetReference, tasks: LoadTask[]) {
426
+ this.index = index;
427
+ this.asset = asset;
428
+ this.tasks = tasks;
429
+ tasks.push(this);
430
+ this.awaitLoading();
431
+ }
432
+
433
+ private async awaitLoading() {
434
+ if (!this.asset.isLoaded()) {
435
+ if (debug)
436
+ console.log("Preload start: " + this.asset.uri, this.index);
437
+ await this.asset.preload();
438
+ if (debug)
439
+ console.log("Preload finished: " + this.asset.uri, this.index);
440
+ }
441
+
442
+ const i = this.tasks.indexOf(this);
443
+ if (i >= 0) this.tasks.splice(i, 1);
444
+ }
445
+ }
@@ -12,6 +12,7 @@ export { Animator } from "../Animator";
12
12
  export { AnimatorController } from "../AnimatorController";
13
13
  export { Antialiasing } from "../postprocessing/Effects/Antialiasing";
14
14
  export { AttachedObject } from "../webxr/WebXRController";
15
+ export { AudioExtension } from "../export/usdz/extensions/behavior/AudioExtension";
15
16
  export { AudioListener } from "../AudioListener";
16
17
  export { AudioSource } from "../AudioSource";
17
18
  export { AudioTrackHandler } from "../timeline/TimelineTracks";
@@ -114,6 +115,7 @@ export { ParticleSystemRenderer } from "../ParticleSystem";
114
115
  export { PixelationEffect } from "../postprocessing/Effects/Pixelation";
115
116
  export { PlayableDirector } from "../timeline/PlayableDirector";
116
117
  export { PlayAnimationOnClick } from "../export/usdz/extensions/behavior/BehaviourComponents";
118
+ export { PlayAudioOnClick } from "../export/usdz/extensions/behavior/BehaviourComponents";
117
119
  export { PlayerColor } from "../PlayerColor";
118
120
  export { PointerEventData } from "../ui/PointerEvents";
119
121
  export { PostProcessingHandler } from "../postprocessing/PostProcessingHandler";
@@ -179,7 +181,6 @@ export { TriggerModel } from "../export/usdz/extensions/behavior/BehavioursBuild
179
181
  export { UIRaycastUtils } from "../ui/RaycastUtils";
180
182
  export { UIRootComponent } from "../ui/BaseUIComponent";
181
183
  export { UsageMarker } from "../Interactable";
182
- export { USDZBehaviours } from "../export/usdz/extensions/behavior/Behaviour";
183
184
  export { USDZExporter } from "../export/usdz/USDZExporter";
184
185
  export { USDZText } from "../export/usdz/extensions/USDZText";
185
186
  export { VariantAction } from "../export/usdz/extensions/behavior/Actions";
@@ -1,11 +1,12 @@
1
- import { USDObject } from "./ThreeUSDZExporter";
1
+ import { USDObject, USDZExporterContext } from "./ThreeUSDZExporter";
2
+ import { Object3D } from "three";
2
3
 
3
4
  export interface IUSDExporterExtension {
4
5
 
5
6
  get extensionName(): string;
6
7
  onBeforeBuildDocument?(context);
7
8
  onAfterBuildDocument?(context);
8
- onExportObject?(object, model : USDObject, context);
9
+ onExportObject?(object: Object3D, model : USDObject, context: USDZExporterContext);
9
10
  onAfterSerialize?(context);
10
11
  onAfterHierarchy?(context, writer : any);
11
12
  }