@needle-tools/engine 2.53.0-pre → 2.54.2-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 (149) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/needle-engine.d.ts +2417 -2230
  3. package/dist/needle-engine.js +425 -425
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +49 -49
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/dist/needle-engine.tsbuildinfo +1 -0
  8. package/lib/engine/codegen/register_types.d.ts +1 -0
  9. package/lib/engine/codegen/register_types.js +346 -0
  10. package/lib/engine/codegen/register_types.js.map +1 -0
  11. package/lib/engine/debug/debug.d.ts +1 -0
  12. package/lib/engine/debug/debug.js +4 -0
  13. package/lib/engine/debug/debug.js.map +1 -1
  14. package/lib/engine/debug/debug_console.js +2 -1
  15. package/lib/engine/debug/debug_console.js.map +1 -1
  16. package/lib/engine/debug/debug_overlay.js +3 -1
  17. package/lib/engine/debug/debug_overlay.js.map +1 -1
  18. package/lib/engine/engine_default_parameters.d.ts +2 -2
  19. package/lib/engine/engine_element.js +4 -2
  20. package/lib/engine/engine_element.js.map +1 -1
  21. package/lib/engine/engine_element_loading.d.ts +1 -0
  22. package/lib/engine/engine_element_loading.js +17 -6
  23. package/lib/engine/engine_element_loading.js.map +1 -1
  24. package/lib/engine/engine_element_overlay.js +4 -2
  25. package/lib/engine/engine_element_overlay.js.map +1 -1
  26. package/lib/engine/engine_fileloader.d.ts +3 -0
  27. package/lib/engine/engine_fileloader.js +8 -0
  28. package/lib/engine/engine_fileloader.js.map +1 -0
  29. package/lib/engine/engine_generic_utils.d.ts +1 -0
  30. package/lib/engine/engine_generic_utils.js +14 -0
  31. package/lib/engine/engine_generic_utils.js.map +1 -0
  32. package/lib/engine/engine_input.js +4 -0
  33. package/lib/engine/engine_input.js.map +1 -1
  34. package/lib/engine/engine_networking_websocket.d.ts +1 -0
  35. package/lib/engine/engine_networking_websocket.js +1 -1
  36. package/lib/engine/engine_networking_websocket.js.map +1 -1
  37. package/lib/engine/engine_serialization_builtin_serializer.js +6 -3
  38. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  39. package/lib/engine/engine_utils.d.ts +1 -1
  40. package/lib/engine/engine_utils.js +3 -0
  41. package/lib/engine/engine_utils.js.map +1 -1
  42. package/lib/engine/extensions/NEEDLE_components.d.ts +1 -1
  43. package/lib/engine/extensions/NEEDLE_progressive.d.ts +22 -0
  44. package/lib/engine/extensions/NEEDLE_progressive.js +172 -44
  45. package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
  46. package/lib/engine-components/GroundProjection.d.ts +2 -1
  47. package/lib/engine-components/GroundProjection.js +19 -12
  48. package/lib/engine-components/GroundProjection.js.map +1 -1
  49. package/lib/engine-components/ParticleSystemModules.d.ts +1 -1
  50. package/lib/engine-components/ParticleSystemModules.js +48 -44
  51. package/lib/engine-components/ParticleSystemModules.js.map +1 -1
  52. package/lib/engine-components/ReflectionProbe.d.ts +1 -1
  53. package/lib/engine-components/Renderer.d.ts +1 -1
  54. package/lib/engine-components/Renderer.js +3 -8
  55. package/lib/engine-components/Renderer.js.map +1 -1
  56. package/lib/engine-components/Volume.js +8 -1
  57. package/lib/engine-components/Volume.js.map +1 -1
  58. package/lib/engine-components/WebXR.d.ts +2 -2
  59. package/lib/engine-components/WebXRAvatar.d.ts +1 -1
  60. package/lib/engine-components/codegen/components.d.ts +99 -99
  61. package/lib/engine-components/codegen/components.js +99 -99
  62. package/lib/engine-components/codegen/components.js.map +1 -1
  63. package/lib/engine-components/export/usdz/USDZExporter.d.ts +1 -0
  64. package/lib/engine-components/export/usdz/USDZExporter.js +17 -1
  65. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  66. package/lib/engine-components/ui/Button.js +30 -0
  67. package/lib/engine-components/ui/Button.js.map +1 -1
  68. package/lib/engine-components/ui/InputField.d.ts +2 -0
  69. package/lib/engine-components/ui/InputField.js +23 -1
  70. package/lib/engine-components/ui/InputField.js.map +1 -1
  71. package/lib/engine-components/ui/Utils.d.ts +2 -0
  72. package/lib/engine-components/ui/Utils.js +10 -9
  73. package/lib/engine-components/ui/Utils.js.map +1 -1
  74. package/lib/engine-components-experimental/Presentation.d.ts +6 -0
  75. package/lib/engine-components-experimental/Presentation.js +11 -0
  76. package/lib/engine-components-experimental/Presentation.js.map +1 -0
  77. package/lib/engine-components-experimental/annotation/LineDrawer.d.ts +18 -0
  78. package/lib/engine-components-experimental/annotation/LineDrawer.js +175 -0
  79. package/lib/engine-components-experimental/annotation/LineDrawer.js.map +1 -0
  80. package/lib/engine-components-experimental/annotation/LinesManager.d.ts +54 -0
  81. package/lib/engine-components-experimental/annotation/LinesManager.js +155 -0
  82. package/lib/engine-components-experimental/annotation/LinesManager.js.map +1 -0
  83. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +26 -0
  84. package/lib/engine-components-experimental/networking/PlayerSync.js +121 -0
  85. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -0
  86. package/lib/engine-schemes/vec2.d.ts +10 -0
  87. package/lib/engine-schemes/vec2.js +26 -0
  88. package/lib/engine-schemes/vec2.js.map +1 -0
  89. package/lib/include/three/ARButton.d.ts +3 -0
  90. package/lib/include/three/ARButton.js +158 -0
  91. package/lib/include/three/ARButton.js.map +1 -0
  92. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.d.ts +6 -0
  93. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js +46 -0
  94. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
  95. package/lib/include/three/VRButton.d.ts +5 -0
  96. package/lib/include/three/VRButton.js +122 -0
  97. package/lib/include/three/VRButton.js.map +1 -0
  98. package/lib/tsconfig.tsbuildinfo +1 -0
  99. package/package.json +1 -1
  100. package/src/engine/codegen/register_types.js +214 -214
  101. package/src/engine/debug/debug.ts +5 -0
  102. package/src/engine/debug/debug_console.ts +3 -2
  103. package/src/engine/debug/debug_overlay.ts +3 -1
  104. package/src/engine/engine_element.ts +4 -2
  105. package/src/engine/engine_element_loading.ts +13 -6
  106. package/src/engine/engine_element_overlay.ts +4 -2
  107. package/src/engine/engine_input.ts +4 -0
  108. package/src/engine/engine_networking_websocket.ts +3 -1
  109. package/src/engine/engine_serialization_builtin_serializer.ts +8 -5
  110. package/src/engine/engine_utils.ts +3 -1
  111. package/src/engine/extensions/NEEDLE_progressive.ts +185 -43
  112. package/src/engine-components/GroundProjection.ts +18 -12
  113. package/src/engine-components/ParticleSystemModules.ts +50 -47
  114. package/src/engine-components/Renderer.ts +5 -9
  115. package/src/engine-components/Volume.ts +8 -1
  116. package/src/engine-components/WebXR.ts +2 -2
  117. package/src/engine-components/codegen/components.ts +99 -99
  118. package/src/engine-components/export/usdz/USDZExporter.ts +19 -2
  119. package/src/engine-components/ui/Button.ts +8 -8
  120. package/src/engine-components/ui/InputField.ts +25 -2
  121. package/src/engine-components/ui/Utils.ts +12 -11
  122. package/src/engine-components-experimental/annotation/LineDrawer.ts +10 -7
  123. package/src/engine-components-experimental/annotation/LinesManager.ts +6 -6
  124. package/src/engine-components-experimental/networking/PlayerSync.ts +1 -1
  125. package/lib/engine/engine_caching.d.ts +0 -0
  126. package/lib/engine/engine_caching.js +0 -2
  127. package/lib/engine/engine_caching.js.map +0 -1
  128. package/lib/engine/tests/simulate_avatars.d.ts +0 -0
  129. package/lib/engine/tests/simulate_avatars.js +0 -3
  130. package/lib/engine/tests/simulate_avatars.js.map +0 -1
  131. package/lib/engine-components/NavMesh.d.ts +0 -0
  132. package/lib/engine-components/NavMesh.js +0 -101
  133. package/lib/engine-components/NavMesh.js.map +0 -1
  134. package/lib/engine-components/ParticleSystemBehaviours.d.ts +0 -0
  135. package/lib/engine-components/ParticleSystemBehaviours.js +0 -2
  136. package/lib/engine-components/ParticleSystemBehaviours.js.map +0 -1
  137. package/lib/engine-components/SpringJoint.d.ts +0 -0
  138. package/lib/engine-components/SpringJoint.js +0 -43
  139. package/lib/engine-components/SpringJoint.js.map +0 -1
  140. package/lib/engine-components/ui/CanvasScaler.d.ts +0 -0
  141. package/lib/engine-components/ui/CanvasScaler.js +0 -17
  142. package/lib/engine-components/ui/CanvasScaler.js.map +0 -1
  143. package/src/engine/dist/engine_three_utils.js +0 -279
  144. package/src/engine/engine_caching.ts +0 -0
  145. package/src/engine/tests/simulate_avatars.ts +0 -2
  146. package/src/engine-components/NavMesh.ts +0 -117
  147. package/src/engine-components/ParticleSystemBehaviours.ts +0 -0
  148. package/src/engine-components/SpringJoint.ts +0 -45
  149. package/src/engine-components/ui/CanvasScaler.ts +0 -21
@@ -4,6 +4,7 @@ import { LoadingProgressArgs } from "./engine_setup";
4
4
  import { getParam } from "./engine_utils";
5
5
 
6
6
  const debug = getParam("debugloadingbar");
7
+ const debugRendering = getParam("debugloadingbarrendering");
7
8
 
8
9
  export class LoadingElementOptions {
9
10
  className?: string;
@@ -43,12 +44,14 @@ export function calculateProgress01(progress: LoadingProgressArgs) {
43
44
 
44
45
  export class EngineLoadingView implements ILoadingViewHandler {
45
46
 
47
+ static LoadingContainerClassName = "loading";
48
+
46
49
  // the raw progress
47
50
  loadingProgress: number = 0;
48
51
 
49
52
  private container: HTMLElement;
50
53
  private _progress: number = 0;
51
- private _allowCustomLoadingElement: boolean = false;
54
+ private _allowCustomLoadingElement: boolean = true;
52
55
  private _loadingElement?: HTMLElement;
53
56
  private _loadingTextContainer: HTMLElement | null = null;
54
57
  private _loadingBar: HTMLElement | null = null;
@@ -61,20 +64,21 @@ export class EngineLoadingView implements ILoadingViewHandler {
61
64
  }
62
65
 
63
66
  onLoadingBegin(message?: string) {
67
+ if(debug) console.log("Begin Loading")
64
68
  if (!this._loadingElement) {
65
69
  for (let i = 0; i < this.container.children.length; i++) {
66
70
  const el = this.container.children[i] as HTMLElement;
67
- if (el.classList.contains("loading")) {
71
+ if (el.classList.contains(EngineLoadingView.LoadingContainerClassName)) {
68
72
  if (!this._allowCustomLoadingElement) {
69
- console.warn("Custom loading container is not allowed")
73
+ if(debug) console.warn("Remove custom loading container")
70
74
  this.container.removeChild(el);
71
75
  continue;
72
76
  }
73
77
  this._loadingElement = this.createLoadingElement(el);
74
- return;
75
78
  }
76
79
  }
77
- this._loadingElement = this.createLoadingElement();
80
+ if (!this._loadingElement)
81
+ this._loadingElement = this.createLoadingElement();
78
82
  }
79
83
  this._progress = 0;
80
84
  this.loadingProgress = 0;
@@ -121,9 +125,11 @@ export class EngineLoadingView implements ILoadingViewHandler {
121
125
  if (this._progressLoop) return;
122
126
  let dt = 1 / 12;
123
127
  const max = 1 - .05;
128
+ if(debugRendering) dt = 1 / 500;
124
129
  this._progressLoop = setInterval(() => {
125
130
  if (this.loadingProgress >= 1 && this._progress >= max) {
126
131
  if (this._loadingElement) {
132
+ if(debug) console.log("Hiding loading element");
127
133
  this._loadingElement.style.display = "none";
128
134
  this._loadingElement.remove();
129
135
  }
@@ -148,6 +154,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
148
154
  }
149
155
 
150
156
  private createLoadingElement(existing?: HTMLElement): HTMLElement {
157
+ if(debug && !existing) console.log("Creating loading element");
151
158
  this._loadingElement = existing || document.createElement("div");
152
159
  if (!existing) {
153
160
  this._loadingElement.style.position = "fixed";
@@ -165,7 +172,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
165
172
  this._loadingElement.style.color = "white";
166
173
  }
167
174
 
168
- const className = this._loadingElementOptions?.className ?? "loading";
175
+ const className = this._loadingElementOptions?.className ?? EngineLoadingView.LoadingContainerClassName;
169
176
  this._loadingElement.classList.add(className);
170
177
  if (this._loadingElementOptions?.additionalClasses) {
171
178
  for (const c of this._loadingElementOptions.additionalClasses) {
@@ -1,6 +1,7 @@
1
1
  import { Context } from "./engine_setup";
2
- import { isMozillaXR } from "./engine_utils";
2
+ import { getParam, isMozillaXR } from "./engine_utils";
3
3
 
4
+ const debug = getParam("debugaroverlay");
4
5
  export const arContainerClassName = "ar";
5
6
  export const quitARClassName = "quit-ar";
6
7
 
@@ -110,7 +111,8 @@ export class AROverlayHandler {
110
111
  if (arElements && arElements.length > 0)
111
112
  return arElements[0] as HTMLElement;
112
113
 
113
- console.log("No overlay container found in document - generating new ony");
114
+ if (debug)
115
+ console.log("No overlay container found in document - generating new ony");
114
116
  const el = document.createElement("div");
115
117
  el.classList.add(arContainerClassName);
116
118
  el.style.position = "absolute";
@@ -384,6 +384,8 @@ export class Input extends EventTarget {
384
384
  for (let i = 0; i < evt.changedTouches.length; i++) {
385
385
  const touch = evt.changedTouches[i];
386
386
  const id = this.getPointerIndex(touch.identifier)
387
+ if (debug)
388
+ showBalloonMessage(`touch start #${id}, identifier:${touch.identifier}`);
387
389
  const args: PointerEventArgs = { button: id, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, source: evt };
388
390
  this.onDown(args);
389
391
  }
@@ -404,6 +406,8 @@ export class Input extends EventTarget {
404
406
  for (let i = 0; i < evt.changedTouches.length; i++) {
405
407
  const touch = evt.changedTouches[i];
406
408
  const id = this.getPointerIndex(touch.identifier)
409
+ if (debug)
410
+ showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
407
411
  const args: PointerEventArgs = { button: id, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, source: evt };
408
412
  this.onUp(args);
409
413
  }
@@ -1 +1,3 @@
1
- // TODO: move websocket specific networking code here!
1
+ // TODO: move websocket specific networking code here!
2
+
3
+ export {}
@@ -6,6 +6,7 @@ import { debugExtension } from "./engine_default_parameters";
6
6
  import { CallInfo, EventList } from "../engine-components/EventList";
7
7
  import { Color, Object3D, Texture, WebGLRenderTarget } from "three";
8
8
  import { RenderTexture } from "./engine_texture";
9
+ import { isDevEnvironment } from "../engine/debug/debug";
9
10
 
10
11
  // export class SourcePath {
11
12
  // src?:string
@@ -98,7 +99,8 @@ class ObjectSerializer extends TypeSerializer {
98
99
  res = GameObject.findByGuid(data.guid, context.context.scene);
99
100
  }
100
101
  if (!res) {
101
- console.warn("Could not resolve object reference", context.path, data, context.target, context.context.scene);
102
+ if (isDevEnvironment() || debugExtension)
103
+ console.warn("Could not resolve object reference", context.path, data, context.target, context.context.scene);
102
104
  data["could_not_resolve"] = true;
103
105
  }
104
106
  else if (debugExtension)
@@ -141,9 +143,10 @@ class ComponentSerializer extends TypeSerializer {
141
143
  // if not found within the gltf use the provided context scene
142
144
  // to find references outside
143
145
  res = this.findObjectForGuid(data.guid, context.context?.scene);
144
- if(res) return res;
146
+ if (res) return res;
145
147
  }
146
- console.warn("Could not resolve component reference", context.path, data, context.target);
148
+ if (isDevEnvironment() || debugExtension)
149
+ console.warn("Could not resolve component reference", context.path, data, context.target);
147
150
  data["could_not_resolve"] = true;
148
151
  return undefined;
149
152
  }
@@ -227,7 +230,7 @@ class EventListSerializer extends TypeSerializer {
227
230
  call.method = call.method.substring(4);
228
231
  if (target[call.method] !== undefined) foundMethod = true;
229
232
  }
230
- if (!foundMethod)
233
+ if (!foundMethod && (isDevEnvironment() || debugExtension))
231
234
  printWarningMethodNotFound();
232
235
  }
233
236
  }
@@ -302,7 +305,7 @@ export class RenderTextureSerializer extends TypeSerializer {
302
305
  }
303
306
 
304
307
  onDeserialize(data: any, context: SerializationContext) {
305
- if(data instanceof Texture && context.type === RenderTexture){
308
+ if (data instanceof Texture && context.type === RenderTexture) {
306
309
  const tex = data as Texture;
307
310
  const rt = new RenderTexture(tex.image.width, tex.image.height);
308
311
  rt.texture = tex;
@@ -52,7 +52,7 @@ export function getUrlParams() {
52
52
  return new URLSearchParams(window.location.search);
53
53
  }
54
54
 
55
- export function getParam(paramName: string): string | boolean {
55
+ export function getParam(paramName: string): string | boolean | number {
56
56
 
57
57
  if (saveParams && !requestedParams.includes(paramName))
58
58
  requestedParams.push(paramName);
@@ -60,6 +60,8 @@ export function getParam(paramName: string): string | boolean {
60
60
  if (urlParams.has(paramName)) {
61
61
  const val = urlParams.get(paramName);
62
62
  if (val) {
63
+ const num = Number(val);
64
+ if (!isNaN(num)) return num;
63
65
  return val;
64
66
  }
65
67
  else return true;
@@ -3,7 +3,7 @@ import { GLTF, GLTFLoader, GLTFLoaderPlugin, GLTFParser } from "three/examples/j
3
3
  import { SourceIdentifier } from "../engine_types";
4
4
  import { Context } from "../engine_setup";
5
5
  import { addDracoAndKTX2Loaders } from "../engine_loaders";
6
- import { getParam, getPath } from "../engine_utils";
6
+ import { delay, getParam, getPath } from "../engine_utils";
7
7
 
8
8
  export const EXTENSION_NAME = "NEEDLE_progressive";
9
9
 
@@ -14,6 +14,7 @@ declare type ProgressiveTextureSchema = {
14
14
  guid: string;
15
15
  }
16
16
 
17
+
17
18
  const debug_toggle_maps: Map<Material, { [key: string]: { original: Texture, lod0: Texture } }> = new Map();
18
19
  let show_lod0 = false;
19
20
  if (debug) {
@@ -71,7 +72,6 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
71
72
  }
72
73
  }
73
74
 
74
-
75
75
  get name(): string {
76
76
  return EXTENSION_NAME;
77
77
  }
@@ -125,6 +125,7 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
125
125
 
126
126
  private static cache = new Map<string, ProgressiveTextureSchema>();
127
127
  private static resolved: { [key: string]: Texture } = {};
128
+ private static currentlyLoading: { [key: string]: Promise<Texture | null> } = {};
128
129
 
129
130
  private static async getOrLoadTexture(context: Context, source: SourceIdentifier | undefined, material: Material, slot: string, current: Texture, _level: number): Promise<Texture | null> {
130
131
 
@@ -145,53 +146,82 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
145
146
  return this.resolved[resolveKey];
146
147
  }
147
148
 
148
- const loader = new GLTFLoader();
149
- addDracoAndKTX2Loaders(loader, context);
150
- if (debug) console.log("Load " + uri, material.name, slot, ext.guid);
151
- const gltf = await loader.loadAsync(uri);
152
- const parser = gltf.parser;
153
- if (debug) console.log("Loading finished " + uri, material.name, slot, ext.guid);
154
- let index = -1;
155
- let found = false;
156
- for (const tex of gltf.parser.json.textures) {
157
- index++;
158
- if (tex?.extensions) {
159
- const other: ProgressiveTextureSchema = tex?.extensions[EXTENSION_NAME];
160
- if (other?.guid) {
161
- if (other.guid === ext.guid) {
162
- found = true;
163
- break;
164
- // if (debug)
165
- // console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material);
166
- // this.resolved[resolveKey] = tex as Texture;
167
- // return tex;
149
+ const info = this.onProgressiveLoadStart(context, source, uri, material, slot);
150
+ try {
151
+ if(this.currentlyLoading[resolveKey] !== undefined) {
152
+ if(debug)
153
+ console.log("Already loading:", material.name + "." + slot, resolveKey);
154
+ const tex = await this.currentlyLoading[resolveKey];
155
+ return tex;
156
+ }
157
+ const request = new Promise<Texture | null>(async (resolve, _) => {
158
+ const loader = new GLTFLoader();
159
+ addDracoAndKTX2Loaders(loader, context);
160
+
161
+ if (debug) console.log("Load " + uri, material.name, slot, ext.guid);
162
+ if (debug) {
163
+ await delay(Math.random() * 1000);
164
+ }
165
+
166
+ const gltf = await loader.loadAsync(uri);
167
+ const parser = gltf.parser;
168
+ if (debug) console.log("Loading finished " + uri, material.name, slot, ext.guid);
169
+ let index = -1;
170
+ let found = false;
171
+ for (const tex of gltf.parser.json.textures) {
172
+ index++;
173
+ if (tex?.extensions) {
174
+ const other: ProgressiveTextureSchema = tex?.extensions[EXTENSION_NAME];
175
+ if (other?.guid) {
176
+ if (other.guid === ext.guid) {
177
+ found = true;
178
+ break;
179
+ }
180
+ }
168
181
  }
169
182
  }
170
- }
171
- }
172
- if (!found)
173
- return null;
183
+ if (!found)
184
+ return resolve(null);
174
185
 
175
- // const index = Number.parseInt(ext.pointer.substring("textures/".length));
176
- const tex = await parser.getDependency("texture", index);
177
- tex.encoding = current.encoding;
178
- if (tex) {
179
- tex.guid = ext.guid;
186
+ const tex = await parser.getDependency("texture", index);
187
+ tex.encoding = current.encoding;
188
+ if (tex) {
189
+ tex.guid = ext.guid;
190
+ }
191
+ this.resolved[resolveKey] = tex as Texture;
192
+ if (debug)
193
+ console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material, resolveKey);
194
+ resolve(tex);
195
+ });
196
+ this.currentlyLoading[resolveKey] = request;
197
+ const tex = await request;
198
+ return tex;
199
+ }
200
+ finally {
201
+ delete this.currentlyLoading[resolveKey];
202
+ this.onProgressiveLoadEnd(info);
180
203
  }
181
- this.resolved[resolveKey] = tex as Texture;
182
- if (debug)
183
- console.log(material.name, slot, "change \"" + current.name + "\" → \"" + tex.name + "\"", uri, index, tex, material, resolveKey);
184
- return tex;
185
204
  }
186
205
  else {
187
- if (debug) console.log("Load texture from uri: " + uri);
188
- const loader = new TextureLoader();
189
- const loaded = await loader.loadAsync(uri);
190
- if (loaded && debug) {
191
- console.log(ext, loaded);
206
+ const info = this.onProgressiveLoadStart(context, source, uri, material, slot);
207
+ try {
208
+ if (debug) console.log("Load texture from uri: " + uri);
209
+ const loader = new TextureLoader();
210
+ const tex = await loader.loadAsync(uri);
211
+ if (tex) {
212
+ (tex as any).guid = ext.guid;
213
+ tex.flipY = false;
214
+ tex.needsUpdate = true;
215
+ tex.encoding = current.encoding;
216
+ if (debug)
217
+ console.log(ext, tex);
218
+ }
219
+ else if (debug) console.warn("failed loading", uri);
220
+ return tex;
221
+ }
222
+ finally {
223
+ this.onProgressiveLoadEnd(info);
192
224
  }
193
- else console.warn("failed loading", uri);
194
- return loaded;
195
225
  }
196
226
  // loader.then((h: Texture) => {
197
227
  // // console.log(t, h);
@@ -212,4 +242,116 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
212
242
  }
213
243
  return null;
214
244
  }
215
- }
245
+
246
+
247
+ /** subscribe to events whenever a loading event starts, invoked for every single loading process that starts */
248
+ static beginListenStart(context: Context, evt: ProgressiveLoadingEvent) {
249
+ if (!this._progressiveEventListeners.has(context)) {
250
+ this._progressiveEventListeners.set(context, new ProgressiveLoadingEventHandler());
251
+ }
252
+ this._progressiveEventListeners.get(context)!.start.push(evt);
253
+ }
254
+ static stopListenStart(context: Context, evt: ProgressiveLoadingEvent) {
255
+ if (!this._progressiveEventListeners.has(context)) {
256
+ return;
257
+ }
258
+ const listeners = this._progressiveEventListeners.get(context)!.start;
259
+ const index = listeners.indexOf(evt);
260
+ if (index >= 0) {
261
+ listeners.splice(index, 1);
262
+ }
263
+ }
264
+
265
+ /** subscribe to loading event ended event */
266
+ static beginListenEnd(context: Context, evt: ProgressiveLoadingEvent) {
267
+ if (!this._progressiveEventListeners.has(context)) {
268
+ this._progressiveEventListeners.set(context, new ProgressiveLoadingEventHandler());
269
+ }
270
+ this._progressiveEventListeners.get(context)!.end.push(evt);
271
+ }
272
+ static stopListenEnd(context: Context, evt: ProgressiveLoadingEvent) {
273
+ if (!this._progressiveEventListeners.has(context)) {
274
+ return;
275
+ }
276
+ const listeners = this._progressiveEventListeners.get(context)!.end;
277
+ const index = listeners.indexOf(evt);
278
+ if (index >= 0) {
279
+ listeners.splice(index, 1);
280
+ }
281
+ }
282
+
283
+ /** event listeners per context */
284
+ private static _progressiveEventListeners: Map<Context, ProgressiveLoadingEventHandler> = new Map();
285
+ //** loading info per context, contains an array of urls that are currently being loaded */
286
+ private static _currentProgressiveLoadingInfo: Map<Context, ProgressiveLoadingInfo[]> = new Map();
287
+
288
+ // called whenever a progressive loading event starts
289
+ private static onProgressiveLoadStart(context: Context, source: SourceIdentifier | undefined, uri: string, material: Material, slot: string): ProgressiveLoadingInfo {
290
+ if (!this._currentProgressiveLoadingInfo.has(context)) {
291
+ this._currentProgressiveLoadingInfo.set(context, []);
292
+ }
293
+ const info = new ProgressiveLoadingInfo(context, source, uri, material, slot);
294
+ const current = this._currentProgressiveLoadingInfo.get(context)!;
295
+ const listener = this._progressiveEventListeners.get(context);
296
+ if (listener) listener.onStart(info);
297
+
298
+ current.push(info);
299
+ return info;
300
+ }
301
+
302
+ private static onProgressiveLoadEnd(info: ProgressiveLoadingInfo) {
303
+ if (!info) return;
304
+ const context = info.context;
305
+ if (!this._currentProgressiveLoadingInfo.has(context)) {
306
+ return;
307
+ }
308
+ const current = this._currentProgressiveLoadingInfo.get(context)!;
309
+ const index = current.indexOf(info);
310
+ if (index < 0) {
311
+ return;
312
+ }
313
+ current.splice(index, 1);
314
+ const listener = this._progressiveEventListeners.get(context);
315
+ if (listener) listener.onEnd(info);
316
+ }
317
+ }
318
+
319
+
320
+ /** info object that holds information about a file that is currently being loaded */
321
+ export class ProgressiveLoadingInfo {
322
+ readonly context: Context;
323
+ readonly source: SourceIdentifier | undefined;
324
+ readonly uri: string;
325
+ readonly material?: Material;
326
+ readonly slot?: string;
327
+ // TODO: can contain information if the event is a background process / preloading or if the object is currently visible
328
+
329
+ constructor(context: Context, source: SourceIdentifier | undefined, uri: string, material?: Material, slot?: string) {
330
+ this.context = context;
331
+ this.source = source;
332
+ this.uri = uri;
333
+ this.material = material;
334
+ this.slot = slot;
335
+ }
336
+ };
337
+
338
+
339
+ export declare type ProgressiveLoadingEvent = (info: ProgressiveLoadingInfo) => void;
340
+
341
+ /** progressive loading event handler implementation */
342
+ class ProgressiveLoadingEventHandler {
343
+ start: ProgressiveLoadingEvent[] = [];
344
+ end: ProgressiveLoadingEvent[] = [];
345
+
346
+ onStart(listener: ProgressiveLoadingInfo) {
347
+ for (const l of this.start) {
348
+ l(listener);
349
+ }
350
+ }
351
+
352
+ onEnd(listener: ProgressiveLoadingInfo) {
353
+ for (const l of this.end) {
354
+ l(listener);
355
+ }
356
+ }
357
+ }
@@ -7,6 +7,9 @@ import { Texture } from "three";
7
7
 
8
8
  export class GroundProjectedEnv extends Behaviour {
9
9
 
10
+ @serializable()
11
+ applyOnAwake: boolean = false;
12
+
10
13
  @serializable()
11
14
  set scale(val: number) {
12
15
  this._scale = val;
@@ -40,14 +43,23 @@ export class GroundProjectedEnv extends Behaviour {
40
43
  private _watcher?: Watch;
41
44
 
42
45
 
43
- onEnable() {
44
- // TODO: if we do this in the first frame we can not disable it again. Something buggy with the watch?!
45
- if (this.context.time.frameCount > 0)
46
+ awake() {
47
+ if (this.applyOnAwake)
46
48
  this.updateAndCreate();
47
49
  }
48
50
 
49
- start() {
50
- this.updateAndCreate();
51
+ onEnable() {
52
+ // TODO: if we do this in the first frame we can not disable it again. Something buggy with the watch?!
53
+ if (this.context.time.frameCount > 0) {
54
+ if (this.applyOnAwake)
55
+ this.updateAndCreate();
56
+ }
57
+ if (!this._watcher) {
58
+ this._watcher = new Watch(this.context.scene, "environment");
59
+ this._watcher.subscribeWrite(_ => {
60
+ this.updateProjection();
61
+ });
62
+ }
51
63
  }
52
64
 
53
65
  onDisable() {
@@ -57,13 +69,7 @@ export class GroundProjectedEnv extends Behaviour {
57
69
 
58
70
  private updateAndCreate() {
59
71
  this.updateProjection();
60
- if (!this._watcher) {
61
- this._watcher = new Watch(this.context.scene, "environment");
62
- this._watcher.subscribeWrite(_ => {
63
- this.updateProjection();
64
- });
65
- }
66
- this._watcher.apply();
72
+ this._watcher?.apply();
67
73
  }
68
74
 
69
75
  updateProjection() {
@@ -517,9 +517,9 @@ export class ShapeModule implements EmitterShape {
517
517
  private readonly _worldSpaceMatrixInverse: Matrix4 = new Matrix4();
518
518
 
519
519
 
520
- constructor() {
521
- // console.log(this);
522
- }
520
+ // constructor() {
521
+ // console.log(this);
522
+ // }
523
523
 
524
524
  update(system: IParticleSystem, _context: Context, simulationSpace: ParticleSystemSimulationSpace, obj: Object3D) {
525
525
  this.system = system;
@@ -534,6 +534,16 @@ export class ShapeModule implements EmitterShape {
534
534
  }
535
535
  }
536
536
 
537
+ private updateRotation() {
538
+ const isRotated = this.rotation.x !== 0 || this.rotation.y !== 0 || this.rotation.z !== 0;
539
+ if (isRotated) {
540
+ this._rotation.x = Mathf.toRadians(this.rotation.x);
541
+ this._rotation.y = -Mathf.toRadians(this.rotation.y);
542
+ this._rotation.z = -Mathf.toRadians(this.rotation.z);
543
+ }
544
+ return isRotated;
545
+ }
546
+
537
547
  /** nebula implementations: */
538
548
 
539
549
  /** initializer implementation */
@@ -544,61 +554,52 @@ export class ShapeModule implements EmitterShape {
544
554
  return this._vector;
545
555
  }
546
556
  getPosition(): void {
547
- if (!this.enabled) {
548
- this._vector.set(0, 0, 0);
549
- return;
550
- }
557
+ this._vector.set(0, 0, 0);
551
558
  const pos = this._temp.copy(this.position);
552
559
  const isWorldSpace = this._space === ParticleSystemSimulationSpace.World;
553
560
  if (isWorldSpace) {
554
561
  pos.applyQuaternion(this.system.worldQuaternion);
555
562
  }
556
-
557
- const isRotated = this.rotation.x !== 0 || this.rotation.y !== 0 || this.rotation.z !== 0;
558
- if (isRotated) {
559
- this._rotation.x = Mathf.toRadians(this.rotation.x);
560
- this._rotation.y = -Mathf.toRadians(this.rotation.y);
561
- this._rotation.z = -Mathf.toRadians(this.rotation.z);
562
- }
563
-
564
563
  let radius = this.radius;
565
564
  if (isWorldSpace) radius *= this.system.worldScale.x;
566
- switch (this.shapeType) {
567
- case ParticleSystemShapeType.Box:
568
- this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
569
- this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
570
- this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
571
- this._vector.add(pos);
572
- break;
573
- case ParticleSystemShapeType.Cone:
574
- this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
575
- break;
576
- case ParticleSystemShapeType.Sphere:
577
- this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
578
- this._vector.x *= this.scale.x;
579
- this._vector.y *= this.scale.y;
580
- this._vector.z *= this.scale.z;
581
- break;
582
- case ParticleSystemShapeType.Circle:
583
- this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
584
- this._vector.x *= this.scale.x;
585
- this._vector.y *= this.scale.y;
586
- this._vector.z *= 0;
587
- break;
588
- default:
589
- this._vector.set(0, 0, 0);
590
- break;
591
- // case ParticleSystemShapeType.Hemisphere:
592
- // randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
593
- // break;
594
- }
565
+ if (this.enabled) {
566
+ switch (this.shapeType) {
567
+ case ParticleSystemShapeType.Box:
568
+ this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
569
+ this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
570
+ this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
571
+ this._vector.add(pos);
572
+ break;
573
+ case ParticleSystemShapeType.Cone:
574
+ this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
575
+ break;
576
+ case ParticleSystemShapeType.Sphere:
577
+ this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
578
+ this._vector.x *= this.scale.x;
579
+ this._vector.y *= this.scale.y;
580
+ this._vector.z *= this.scale.z;
581
+ break;
582
+ case ParticleSystemShapeType.Circle:
583
+ this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
584
+ this._vector.x *= this.scale.x;
585
+ this._vector.y *= this.scale.y;
586
+ this._vector.z *= 0;
587
+ break;
588
+ default:
589
+ this._vector.set(0, 0, 0);
590
+ break;
591
+ // case ParticleSystemShapeType.Hemisphere:
592
+ // randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
593
+ // break;
594
+ }
595
595
 
596
- this.randomizePosition(this._vector, this.randomPositionAmount);
596
+ this.randomizePosition(this._vector, this.randomPositionAmount);
597
+ }
597
598
 
598
- if (isRotated)
599
+ if (this.updateRotation())
599
600
  this._vector.applyEuler(this._rotation);
600
601
 
601
- if (this._space === ParticleSystemSimulationSpace.World) {
602
+ if (isWorldSpace) {
602
603
  this._vector.add(this.system.worldPos);
603
604
  }
604
605
  }
@@ -634,6 +635,8 @@ export class ShapeModule implements EmitterShape {
634
635
  if (this._space === ParticleSystemSimulationSpace.World) {
635
636
  this._dir.applyMatrix4(this._worldSpaceMatrixInverse);
636
637
  }
638
+ if (this.updateRotation())
639
+ this._dir.applyEuler(this._rotation);
637
640
  this._dir.normalize();
638
641
  this.spherizeDirection(this._dir, this.sphericalDirectionAmount);
639
642
  this.randomizeDirection(this._dir, this.randomDirectionAmount);