@needle-tools/engine 3.0.1-alpha.4 → 3.1.0-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 (146) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/needle-engine.js +13110 -12429
  3. package/dist/needle-engine.min.js +378 -365
  4. package/dist/needle-engine.umd.cjs +366 -353
  5. package/lib/engine/api.d.ts +2 -1
  6. package/lib/engine/api.js +2 -1
  7. package/lib/engine/api.js.map +1 -1
  8. package/lib/engine/codegen/register_types.js +8 -0
  9. package/lib/engine/codegen/register_types.js.map +1 -1
  10. package/lib/engine/debug/debug_overlay.js +3 -0
  11. package/lib/engine/debug/debug_overlay.js.map +1 -1
  12. package/lib/engine/engine_addressables.d.ts +21 -1
  13. package/lib/engine/engine_addressables.js +83 -7
  14. package/lib/engine/engine_addressables.js.map +1 -1
  15. package/lib/engine/engine_context.d.ts +2 -0
  16. package/lib/engine/engine_context.js +3 -0
  17. package/lib/engine/engine_context.js.map +1 -1
  18. package/lib/engine/engine_element.d.ts +1 -1
  19. package/lib/engine/engine_element.js +2 -2
  20. package/lib/engine/engine_element.js.map +1 -1
  21. package/lib/engine/engine_element_loading.d.ts +7 -2
  22. package/lib/engine/engine_element_loading.js +67 -22
  23. package/lib/engine/engine_element_loading.js.map +1 -1
  24. package/lib/engine/engine_license.d.ts +1 -1
  25. package/lib/engine/engine_license.js +6 -19
  26. package/lib/engine/engine_license.js.map +1 -1
  27. package/lib/engine/engine_networking.d.ts +1 -0
  28. package/lib/engine/engine_networking.js +3 -0
  29. package/lib/engine/engine_networking.js.map +1 -1
  30. package/lib/engine/engine_serialization.d.ts +2 -2
  31. package/lib/engine/engine_serialization.js +2 -3
  32. package/lib/engine/engine_serialization.js.map +1 -1
  33. package/lib/engine/engine_serialization_builtin_serializer.d.ts +5 -0
  34. package/lib/engine/engine_serialization_builtin_serializer.js +16 -0
  35. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  36. package/lib/engine/engine_serialization_core.d.ts +1 -0
  37. package/lib/engine/engine_serialization_core.js +26 -20
  38. package/lib/engine/engine_serialization_core.js.map +1 -1
  39. package/lib/engine/engine_utils.d.ts +9 -0
  40. package/lib/engine/engine_utils.js +33 -13
  41. package/lib/engine/engine_utils.js.map +1 -1
  42. package/lib/engine/extensions/NEEDLE_animator_controller_model.d.ts +1 -0
  43. package/lib/engine/extensions/NEEDLE_animator_controller_model.js.map +1 -1
  44. package/lib/engine/extensions/NEEDLE_progressive.js +2 -2
  45. package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
  46. package/lib/engine-components/AnimatorController.js +22 -3
  47. package/lib/engine-components/AnimatorController.js.map +1 -1
  48. package/lib/engine-components/AudioSource.d.ts +2 -3
  49. package/lib/engine-components/AudioSource.js +28 -32
  50. package/lib/engine-components/AudioSource.js.map +1 -1
  51. package/lib/engine-components/CameraUtils.js.map +1 -1
  52. package/lib/engine-components/Component.js.map +1 -1
  53. package/lib/engine-components/ParticleSystem.d.ts +5 -2
  54. package/lib/engine-components/ParticleSystem.js +49 -10
  55. package/lib/engine-components/ParticleSystem.js.map +1 -1
  56. package/lib/engine-components/ParticleSystemModules.d.ts +2 -0
  57. package/lib/engine-components/ParticleSystemModules.js +23 -12
  58. package/lib/engine-components/ParticleSystemModules.js.map +1 -1
  59. package/lib/engine-components/ScreenCapture.d.ts +1 -0
  60. package/lib/engine-components/ScreenCapture.js +145 -46
  61. package/lib/engine-components/ScreenCapture.js.map +1 -1
  62. package/lib/engine-components/Skybox.js +2 -2
  63. package/lib/engine-components/Skybox.js.map +1 -1
  64. package/lib/engine-components/SyncedRoom.js +1 -2
  65. package/lib/engine-components/SyncedRoom.js.map +1 -1
  66. package/lib/engine-components/VideoPlayer.d.ts +4 -2
  67. package/lib/engine-components/VideoPlayer.js +35 -12
  68. package/lib/engine-components/VideoPlayer.js.map +1 -1
  69. package/lib/engine-components/WebXR.d.ts +4 -1
  70. package/lib/engine-components/WebXR.js +10 -2
  71. package/lib/engine-components/WebXR.js.map +1 -1
  72. package/lib/engine-components/WebXRController.js +2 -2
  73. package/lib/engine-components/WebXRController.js.map +1 -1
  74. package/lib/engine-components/WebXRImageTracking.d.ts +39 -0
  75. package/lib/engine-components/WebXRImageTracking.js +173 -0
  76. package/lib/engine-components/WebXRImageTracking.js.map +1 -0
  77. package/lib/engine-components/codegen/components.d.ts +4 -0
  78. package/lib/engine-components/codegen/components.js +4 -0
  79. package/lib/engine-components/codegen/components.js.map +1 -1
  80. package/lib/engine-components/postprocessing/Effects/DepthOfField.d.ts +2 -0
  81. package/lib/engine-components/postprocessing/Effects/DepthOfField.js +19 -1
  82. package/lib/engine-components/postprocessing/Effects/DepthOfField.js.map +1 -1
  83. package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.d.ts +13 -0
  84. package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js +63 -0
  85. package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js.map +1 -0
  86. package/lib/engine-components/postprocessing/VolumeParameter.d.ts +1 -0
  87. package/lib/engine-components/postprocessing/VolumeParameter.js +4 -0
  88. package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
  89. package/lib/engine-components/timeline/PlayableDirector.d.ts +3 -1
  90. package/lib/engine-components/timeline/PlayableDirector.js +40 -2
  91. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  92. package/lib/engine-components/timeline/TimelineTracks.d.ts +2 -2
  93. package/lib/engine-components/timeline/TimelineTracks.js +14 -16
  94. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  95. package/lib/engine-components/ui/Text.js +7 -8
  96. package/lib/engine-components/ui/Text.js.map +1 -1
  97. package/lib/include/three/ARButton.d.ts +1 -1
  98. package/lib/include/three/ARButton.js +2 -1
  99. package/lib/include/three/ARButton.js.map +1 -1
  100. package/lib/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +3 -4
  102. package/plugins/vite/config.js +8 -0
  103. package/plugins/vite/copyfiles.js +12 -3
  104. package/plugins/vite/drop.js +1 -0
  105. package/plugins/vite/index.js +4 -0
  106. package/plugins/vite/license.js +24 -0
  107. package/plugins/vite/transform-codegen.js +45 -0
  108. package/src/engine/api.ts +2 -2
  109. package/src/engine/codegen/register_types.js +10 -2
  110. package/src/engine/debug/debug_overlay.ts +2 -0
  111. package/src/engine/engine_addressables.ts +102 -8
  112. package/src/engine/engine_context.ts +4 -1
  113. package/src/engine/engine_element.ts +2 -2
  114. package/src/engine/engine_element_loading.ts +68 -22
  115. package/src/engine/engine_license.ts +6 -17
  116. package/src/engine/engine_networking.ts +4 -0
  117. package/src/engine/engine_serialization.ts +5 -4
  118. package/src/engine/engine_serialization_builtin_serializer.ts +23 -3
  119. package/src/engine/engine_serialization_core.ts +27 -20
  120. package/src/engine/engine_utils.ts +35 -14
  121. package/src/engine/extensions/NEEDLE_animator_controller_model.ts +1 -0
  122. package/src/engine/extensions/NEEDLE_progressive.ts +2 -2
  123. package/src/engine-components/AnimatorController.ts +18 -3
  124. package/src/engine-components/AudioSource.ts +29 -38
  125. package/src/engine-components/CameraUtils.ts +2 -2
  126. package/src/engine-components/Component.ts +1 -1
  127. package/src/engine-components/ParticleSystem.ts +52 -10
  128. package/src/engine-components/ParticleSystemModules.ts +24 -12
  129. package/src/engine-components/ScreenCapture.ts +149 -49
  130. package/src/engine-components/Skybox.ts +2 -2
  131. package/src/engine-components/SyncedRoom.ts +1 -2
  132. package/src/engine-components/VideoPlayer.ts +33 -11
  133. package/src/engine-components/WebXR.ts +11 -2
  134. package/src/engine-components/WebXRController.ts +2 -2
  135. package/src/engine-components/WebXRImageTracking.ts +192 -0
  136. package/src/engine-components/codegen/components.ts +4 -0
  137. package/src/engine-components/postprocessing/Effects/DepthOfField.ts +21 -6
  138. package/src/engine-components/postprocessing/Effects/TiltShiftEffect.ts +56 -0
  139. package/src/engine-components/postprocessing/VolumeParameter.ts +5 -0
  140. package/src/engine-components/timeline/PlayableDirector.ts +38 -2
  141. package/src/engine-components/timeline/TimelineTracks.ts +15 -18
  142. package/src/engine-components/ui/Text.ts +7 -8
  143. package/src/include/three/ARButton.js +2 -2
  144. package/lib/engine/codegen/license.json +0 -1
  145. package/license-2447137e.js +0 -4
  146. package/src/engine/codegen/license.json +0 -1
@@ -5,21 +5,12 @@ import { logoSVG } from "./assets";
5
5
 
6
6
  const debug = getParam("debuglicense");
7
7
 
8
+ // This is modified by a bundler (e.g. vite)
9
+ // Do not edit manually
10
+ const HAS_LICENSE = false;
8
11
 
9
- // import licenseInformation from "./codegen/license.json";
10
- let licenseInformation: any = null;
11
- let licenseImportPromise: Promise<any> | null = null;
12
- try {
13
- licenseImportPromise = import("./codegen/license.json");
14
- licenseImportPromise.then(res => {
15
- licenseInformation = res.default;
16
- licenseImportPromise = null;
17
- if (debug) console.log("License imported via dynamic import", licenseInformation)
18
- }).catch(err => { if (debug) console.error("Error on dynamic license import", err) });
19
- }
20
- catch (err) {
21
- if (debug)
22
- console.log("Importing license failed", err)
12
+ export function hasProLicense() {
13
+ return HAS_LICENSE;
23
14
  }
24
15
 
25
16
 
@@ -29,9 +20,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreated, evt => {
29
20
 
30
21
  async function showLicenseInfo(ctx: IContext) {
31
22
  try {
32
- // if(licenseImportPromise) await licenseImportPromise;
33
- //@ts-ignore
34
- if (!licenseInformation || licenseInformation.hasLicense !== true || debug) return onNonCommercialVersionDetected(ctx);
23
+ if (hasProLicense() !== true) return onNonCommercialVersionDetected(ctx);
35
24
  }
36
25
  catch (err) {
37
26
  if (debug) console.log("License check failed", err)
@@ -274,6 +274,10 @@ export class NetworkConnection implements INetworkConnection {
274
274
  return this._currentDelay;
275
275
  }
276
276
 
277
+ public sendPing() {
278
+ this.send("ping", { time: this.context.time.time });
279
+ }
280
+
277
281
  public userIsInRoom(id: string): boolean {
278
282
  return this._currentInRoom.indexOf(id) !== -1;
279
283
  }
@@ -1,6 +1,7 @@
1
1
  import { serializeObject, deserializeObject } from "./engine_serialization_core";
2
- import * as builtin from "./engine_serialization_builtin_serializer";
3
- // export builtin so it will be called and registered
4
- export { serializeObject, deserializeObject, builtin };
5
2
 
6
- export { serializable, serializeable } from "./engine_serialization_decorator"
3
+ export { serializeObject, deserializeObject };
4
+
5
+ export { serializable, serializeable } from "./engine_serialization_decorator"
6
+
7
+ export * from "./engine_serialization_builtin_serializer";
@@ -7,6 +7,7 @@ import { CallInfo, EventList } from "../engine-components/EventList";
7
7
  import { Color, Object3D, Texture, WebGLRenderTarget } from "three";
8
8
  import { RenderTexture } from "./engine_texture";
9
9
  import { isDevEnvironment } from "../engine/debug/debug";
10
+ import { resolveUrl } from "./engine_utils";
10
11
 
11
12
  // export class SourcePath {
12
13
  // src?:string
@@ -300,8 +301,8 @@ class EventListSerializer extends TypeSerializer {
300
301
  return undefined;
301
302
  }
302
303
 
303
- private createEventMethod(target : object, methodName: string, args?: any) : Function | undefined {
304
-
304
+ private createEventMethod(target: object, methodName: string, args?: any): Function | undefined {
305
+
305
306
  return (...forwardedArgs) => {
306
307
  const method = target[methodName];
307
308
  if (typeof method === "function") {
@@ -339,4 +340,23 @@ export class RenderTextureSerializer extends TypeSerializer {
339
340
  return undefined;
340
341
  }
341
342
  }
342
- new RenderTextureSerializer();
343
+ new RenderTextureSerializer();
344
+
345
+
346
+ export class UriSerializer extends TypeSerializer {
347
+ constructor() {
348
+ super([URL]);
349
+ }
350
+
351
+ onSerialize(_data: string, _context: SerializationContext) {
352
+ return null;
353
+ }
354
+
355
+ onDeserialize(data: string, _context: SerializationContext) {
356
+ if (typeof data === "string") {
357
+ return resolveUrl(_context.gltfId, data);
358
+ }
359
+ return undefined;
360
+ }
361
+ }
362
+ new UriSerializer();
@@ -173,6 +173,7 @@ export class SerializationContext {
173
173
  root: THREE.Object3D;
174
174
 
175
175
  gltf?: GLTF;
176
+ /** the url of the glb that is currently being loaded */
176
177
  gltfId?: SourceIdentifier;
177
178
  object!: THREE.Object3D;
178
179
  target?: object;
@@ -290,16 +291,21 @@ export function deserializeObject(obj: ISerializable, serializedData: object, co
290
291
  context.type = undefined;
291
292
  context.path = key;
292
293
 
294
+ if (obj.onBeforeDeserializeMember !== undefined) {
295
+ // callback to the instance, if it returns true assume it's done all the things itself
296
+ if (obj.onBeforeDeserializeMember(key, data, context) === true) continue;
297
+ }
298
+
293
299
  if (serializedEntryInfo === null) {
294
300
  obj[key] = data;
301
+ // if(typeof data === "string"){
302
+ // const serializer = helper.getSerializerForConstructor(String);
303
+ // const res = serializer?.onDeserialize(data, context);
304
+ // if(res !== undefined) obj[key] = res;
305
+ // }
295
306
  }
296
307
  else {
297
308
 
298
- if (obj.onBeforeDeserializeMember !== undefined) {
299
- // callback to the instance, if it returns true assume it's done all the things itself
300
- if (obj.onBeforeDeserializeMember(key, data, context) === true) continue;
301
- }
302
-
303
309
  if (Array.isArray(serializedEntryInfo)) {
304
310
  for (let i = 0; i < serializedEntryInfo.length; i++) {
305
311
  const typeInfoOrConstructor = serializedEntryInfo[i];
@@ -314,24 +320,25 @@ export function deserializeObject(obj: ISerializable, serializedData: object, co
314
320
  obj[key] = tryResolve(serializedEntryInfo);
315
321
  }
316
322
 
317
- function tryResolve(typeInfoOrConstructor) {
318
- const typeInformationOrConstructor = typeInfoOrConstructor as ITypeInformation;
319
- // if the entry does specify an object of type ITypeInformation and has the type field set
320
- const type = typeInformationOrConstructor.type;
321
- if (type) {
322
- return deserializeObjectWithType(data, type, context, undefined, obj[key]);
323
- }
324
- // it can also just contain a constructor
325
- else {
326
- const constructor = typeInfoOrConstructor as Constructor<any>;
327
- return deserializeObjectWithType(data, constructor, context, undefined, obj[key]);
328
- }
329
- }
330
323
 
331
324
  buffer.length = 0;
325
+ }
326
+
327
+ if (obj.onAfterDeserializeMember !== undefined) {
328
+ obj.onAfterDeserializeMember(key, data, context);
329
+ }
332
330
 
333
- if (obj.onAfterDeserializeMember !== undefined) {
334
- obj.onAfterDeserializeMember(key, data, context);
331
+ function tryResolve(typeInfoOrConstructor) {
332
+ const typeInformationOrConstructor = typeInfoOrConstructor as ITypeInformation;
333
+ // if the entry does specify an object of type ITypeInformation and has the type field set
334
+ const type = typeInformationOrConstructor.type;
335
+ if (type) {
336
+ return deserializeObjectWithType(data, type, context, undefined, obj[key]);
337
+ }
338
+ // it can also just contain a constructor
339
+ else {
340
+ const constructor = typeInfoOrConstructor as Constructor<any>;
341
+ return deserializeObjectWithType(data, constructor, context, undefined, obj[key]);
335
342
  }
336
343
  }
337
344
  }
@@ -203,30 +203,51 @@ export function delay(milliseconds: number): Promise<void> {
203
203
  });
204
204
  }
205
205
 
206
- // if a timeline is exported via menu item the audio clip path is relative to the glb (same folder)
206
+ // 1) if a timeline is exported via menu item the audio clip path is relative to the glb (same folder)
207
207
  // we need to detect that here and build the new audio source path relative to the new glb location
208
208
  // the same is/might be true for any file that is/will be exported via menu item
209
- const debugGetPath = getParam("debugsourcepath");
210
- export function getPath(source: SourceIdentifier | undefined, uri: string): string {
211
- if (source === undefined) {
212
- if (debugGetPath) console.warn("getPath: source is undefined, returning uri", uri);
209
+ // 2) if the needle.config assetDirectory is modified (from e.g. /assets to /needle/assets) when building a distributable our vite transform and copy plugin will move the files to dist/assets hence we cannot use project-relative paths (because the path changes). What we do instead if make all paths serialized in a glb relative to the glb. The rel: prefix is used to detect urls that need to be resolved.
210
+ const debugGetPath = getParam("debugresolveurl");
211
+
212
+ export const relativePathPrefix = "rel:";
213
+
214
+ /** @deprecated use resolveUrl instead */
215
+ export function getPath(source:SourceIdentifier|undefined, uri:string) : string {
216
+ return resolveUrl(source, uri);
217
+ }
218
+ /**
219
+ * Use to resolve a url serialized in a glTF file
220
+ * @param source The uri of the loading file
221
+ * @param uri The uri of the file to resolve, can be absolute or relative
222
+ * @returns The resolved uri
223
+ */
224
+ export function resolveUrl(source: SourceIdentifier | undefined, uri: string): string {
225
+ if (uri === undefined) {
226
+ if (debugGetPath) console.warn("getPath: uri is undefined, returning uri", uri);
227
+ return uri;
228
+ }
229
+ if(uri.startsWith("./")) {
213
230
  return uri;
214
231
  }
215
232
  if (uri.startsWith("http")) {
216
233
  if (debugGetPath) console.warn("getPath: uri is absolute, returning uri", uri);
217
234
  return uri;
218
235
  }
236
+ if (source === undefined) {
237
+ if (debugGetPath) console.warn("getPath: source is undefined, returning uri", uri);
238
+ return uri;
239
+ }
240
+ if(uri.startsWith(relativePathPrefix)){
241
+ uri = uri.substring(4);
242
+ }
219
243
  const pathIndex = source.lastIndexOf("/");
220
244
  if (pathIndex >= 0) {
221
- let newUri = source.substring(0, pathIndex + 1);
222
-
223
- const uriDirectoryIndex = uri.lastIndexOf("/");
224
- if (uriDirectoryIndex >= 0) {
225
- newUri += uri.substring(uriDirectoryIndex + 1);
226
- } else {
227
- newUri += uri;
228
- }
229
- if (debugGetPath) console.log("getPath:", source, " - changed uri from\n", uri, "\n→ ", newUri);
245
+ // Take the source uri as the base path
246
+ const basePath = source.substring(0, pathIndex + 1);
247
+ // Append the relative uri
248
+ let newUri = basePath + uri;
249
+ // newUri = new URL(newUri, globalThis.location.href).href;
250
+ if (debugGetPath) console.log("source:", source, "- changed uri \nfrom", uri, "\n→ ", newUri, "\n" + basePath);
230
251
  return newUri;
231
252
  }
232
253
  return uri;
@@ -32,6 +32,7 @@ export declare type StateMachine = {
32
32
  export declare type State = {
33
33
  name: string,
34
34
  hash: number;
35
+ speed?: number;
35
36
  motion: Motion,
36
37
  transitions: Transition[],
37
38
  behaviours: StateMachineBehaviourModel[],
@@ -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 { delay, getParam, getPath } from "../engine_utils";
6
+ import { delay, getParam, resolveUrl } from "../engine_utils";
7
7
 
8
8
  export const EXTENSION_NAME = "NEEDLE_progressive";
9
9
 
@@ -130,7 +130,7 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
130
130
  if (progressiveInfo) {
131
131
  if (debug)
132
132
  console.log(key, progressiveInfo.uri, progressiveInfo.guid);
133
- const uri = getPath(source, progressiveInfo.uri);
133
+ const uri = resolveUrl(source, progressiveInfo.uri);
134
134
  if (uri.endsWith(".glb") || uri.endsWith(".gltf")) {
135
135
  if (!progressiveInfo.guid) {
136
136
  console.warn("missing pointer for glb/gltf texture", progressiveInfo);
@@ -249,9 +249,16 @@ export class AnimatorController {
249
249
 
250
250
  if (action) {
251
251
  const dur = state.motion.clip!.duration;
252
- const normalizedTime = dur <= 0 ? 1 : action.time / dur;
253
- const makeTransition = transition.hasExitTime ? normalizedTime >= transition.exitTime : true;
254
- // console.log(state.name, makeTransition, transition.hasExitTime, normalizedTime, transition.exitTime)
252
+ const normalizedTime = dur <= 0 ? 1 : Math.abs(action.time / dur);
253
+ let makeTransition = false;
254
+ if (transition.hasExitTime) {
255
+ if (action.timeScale > 0) makeTransition = normalizedTime >= transition.exitTime;
256
+ // When the animation is playing backwards we need to check exit time inverted
257
+ else if(action.timeScale < 0) makeTransition = 1 - normalizedTime >= transition.exitTime;
258
+ }
259
+ else {
260
+ makeTransition = true;
261
+ }
255
262
  if (makeTransition) {
256
263
  // if (transition.hasExitTime && transition.exitTime >= .9999)
257
264
  action.clampWhenFinished = true;
@@ -283,6 +290,12 @@ export class AnimatorController {
283
290
  action.time = 0;
284
291
  action.play();
285
292
  }
293
+ else if (action.time <= 0 && action.timeScale < 0) {
294
+ didTriggerLooping = true;
295
+ action.reset();
296
+ action.time = action.getClip().duration;
297
+ action.play();
298
+ }
286
299
  }
287
300
 
288
301
  // call update state behaviours:
@@ -367,9 +380,11 @@ export class AnimatorController {
367
380
  action.stop();
368
381
  action.reset();
369
382
  action.timeScale = this._speed;
383
+ if (state.speed !== undefined) action.timeScale *= state.speed;
370
384
  action.enabled = true;
371
385
  const duration = state.motion.clip!.duration;
372
386
  action.time = offsetNormalized * duration;
387
+ if(action.timeScale < 0) action.time = duration - action.time;
373
388
  action.clampWhenFinished = true;
374
389
  action.setLoop(LoopOnce, 0);
375
390
  if (durationInSec > 0)
@@ -82,7 +82,7 @@ export class AudioSource extends Behaviour {
82
82
  document.addEventListener('touchstart', fn);
83
83
  }
84
84
 
85
- @serializable()
85
+ @serializable(URL)
86
86
  clip: string = "";
87
87
 
88
88
  @serializable()
@@ -168,48 +168,11 @@ export class AudioSource extends Behaviour {
168
168
  public get ShouldPlay(): boolean { return this.shouldPlay; }
169
169
 
170
170
 
171
- private _focusCallback: any;
172
- private _muteChangedCallback: any;
173
-
174
171
  awake() {
175
172
  this.audioLoader = new THREE.AudioLoader();
176
173
  if (this.playOnAwake) this.shouldPlay = true;
177
-
178
- window.addEventListener('visibilitychange', _evt => {
179
- switch (document.visibilityState) {
180
- case "hidden":
181
- this.wasPlaying = this.isPlaying;
182
- this.pause();
183
- break;
184
- case "visible":
185
- if (this.wasPlaying) this.play();
186
- break;
187
- }
188
- });
189
-
190
- this._focusCallback = () => {
191
- if (this.enabled && this.playOnAwake && !this.isPlaying && AudioSource._userInteractionRegistered) {
192
- this.play();
193
- }
194
- }
195
-
196
- this._muteChangedCallback = () => {
197
- if (this.context.application.muted)
198
- this.sound?.setVolume(0);
199
- else
200
- this.sound?.setVolume(this.volume);
201
- }
202
-
203
- this.context.application.addEventListener(ApplicationEvents.Visible, this._focusCallback);
204
- this.context.application.addEventListener(ApplicationEvents.MuteChanged, this._muteChangedCallback);
205
174
  }
206
175
 
207
- onDestroy() {
208
- this.context.application.removeEventListener(ApplicationEvents.Visible, this._focusCallback);
209
- this.context.application.removeEventListener(ApplicationEvents.MuteChanged, this._muteChangedCallback);
210
- }
211
-
212
-
213
176
  onEnable(): void {
214
177
  if (!AudioSource._userInteractionRegistered) {
215
178
  AudioSource._beginWaitForUserInteraction(() => {
@@ -220,12 +183,40 @@ export class AudioSource extends Behaviour {
220
183
  else if (this.playOnAwake && this.context.application.isVisible) {
221
184
  this.play();
222
185
  }
186
+ globalThis.addEventListener('visibilitychange', this.onVisibilityChanged);
187
+ this.context.application.addEventListener(ApplicationEvents.MuteChanged, this.onApplicationMuteChanged);
223
188
  }
224
189
 
225
190
  onDisable() {
191
+ globalThis.removeEventListener('visibilitychange', this.onVisibilityChanged);
192
+ this.context.application.removeEventListener(ApplicationEvents.MuteChanged, this.onApplicationMuteChanged);
226
193
  this.stop();
227
194
  }
228
195
 
196
+ private onVisibilityChanged = () => {
197
+ switch (document.visibilityState) {
198
+ case "hidden":
199
+ this.wasPlaying = this.isPlaying;
200
+ if (this.isPlaying) {
201
+ this.pause();
202
+ }
203
+ break;
204
+ case "visible":
205
+ if (debug) console.log("visible", this.enabled, this.playOnAwake, !this.isPlaying, AudioSource._userInteractionRegistered, this.wasPlaying);
206
+ if (this.enabled && this.playOnAwake && !this.isPlaying && AudioSource._userInteractionRegistered && this.wasPlaying) {
207
+ this.play();
208
+ }
209
+ break;
210
+ }
211
+ }
212
+
213
+ private onApplicationMuteChanged = () => {
214
+ if (this.context.application.muted)
215
+ this.sound?.setVolume(0);
216
+ else
217
+ this.sound?.setVolume(this.volume);
218
+ }
219
+
229
220
  private lerp = (x, y, a) => x * (1 - a) + y * a;
230
221
 
231
222
  private onLoaded(buffer) {
@@ -6,7 +6,7 @@ import { RGBAColor } from "./js-extensions/RGBAColor";
6
6
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry";
7
7
  import { getCameraController } from "../engine/engine_camera";
8
8
  import { Camera } from "./Camera";
9
- import { EngineElement } from "../engine/engine_element";
9
+ import { NeedleEngineHTMLElement } from "../engine/engine_element";
10
10
 
11
11
  ContextRegistry.registerCallback(ContextEvent.MissingCamera, (evt) => {
12
12
  const scene = evt.context.scene;
@@ -31,7 +31,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreated, (evt) => {
31
31
  if (!evt.context.mainCamera) return;
32
32
 
33
33
  // check if the <needle-engine controls> is not set to false
34
- const engineElement = evt.context.domElement as EngineElement
34
+ const engineElement = evt.context.domElement as NeedleEngineHTMLElement
35
35
 
36
36
  if (engineElement?.cameraControls === true) {
37
37
 
@@ -9,6 +9,7 @@ import { syncDestroy, syncInstantiate } from "../engine/engine_networking_instan
9
9
  import { ConstructorConcrete, SourceIdentifier, IComponent, IGameObject, Constructor, GuidsMap, UIDProvider, Collision, ICollider } from "../engine/engine_types";
10
10
  import { addNewComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, moveComponentInstance, removeComponent } from "../engine/engine_components";
11
11
  import { findByGuid, destroy, InstantiateOptions, instantiate, HideFlags, foreachComponent, markAsInstancedRendered, isActiveInHierarchy, isActiveSelf, isUsingInstancing, setActive, isDestroyed } from "../engine/engine_gameobject";
12
+ import { resolveUrl } from "../engine/engine_utils";
12
13
 
13
14
 
14
15
  // export interface ISerializationCallbackReceiver {
@@ -641,7 +642,6 @@ class Component implements IComponent, EventTarget {
641
642
 
642
643
  return false;
643
644
  }
644
-
645
645
  }
646
646
 
647
647
  class Behaviour extends Component {
@@ -52,6 +52,7 @@ export class ParticleSystemRenderer extends Behaviour {
52
52
  @serializable()
53
53
  minParticleSize!: number;
54
54
 
55
+
55
56
  start() {
56
57
  if (this.maxParticleSize !== .5 && this.minParticleSize !== 0) {
57
58
  if (isDevEnvironment()) {
@@ -192,6 +193,7 @@ class ParticleSystemEmissionOverTime extends BaseValueGenerator {
192
193
  }
193
194
 
194
195
  genValue(): number {
196
+ if (!this.system.isPlaying) return 0;
195
197
  if (!this.system.emission.enabled) return 0;
196
198
  if (this.system.currentParticles >= this.system.maxParticles) return 0;
197
199
  // emission over time
@@ -219,6 +221,7 @@ class ParticleSystemEmissionOverTime extends BaseValueGenerator {
219
221
  class ParticleSystemEmissionOverDistance extends BaseValueGenerator {
220
222
 
221
223
  genValue(): number {
224
+ if (!this.system.isPlaying) return 0;
222
225
  // this seems not be called yet
223
226
  return 0;
224
227
  // if (this.system.currentParticles >= this.system.maxParticles) return 0;
@@ -250,14 +253,19 @@ abstract class ParticleSystemBaseBehaviour implements Behavior {
250
253
  clone(): Behavior { throw new Error("Method not implemented."); }
251
254
  }
252
255
 
256
+ const $startFrame = Symbol("startFrame")
253
257
  class TextureSheetAnimationBehaviour extends ParticleSystemBaseBehaviour {
254
258
  type: string = "NeedleTextureSheet"
255
259
 
260
+ // initialize(_particle: Particle): void {
261
+ // _particle[$startFrame] = this.system.textureSheetAnimation.getStartIndex();
262
+ // }
263
+
256
264
  update(particle: Particle, _delta: number) {
257
265
  const sheet = this.system.textureSheetAnimation;
258
266
  if (sheet.enabled) {
259
267
  const t01 = particle.age / particle.life;
260
- const index = sheet.evaluate(t01);;
268
+ const index = sheet.evaluate(t01);
261
269
  if (index !== undefined)
262
270
  particle.uvTile = index;
263
271
  }
@@ -325,8 +333,9 @@ class SizeBehaviour extends ParticleSystemBaseBehaviour {
325
333
  size *= this.system.sizeOverLifetime.evaluate(age01, undefined, particle[$sizeLerpFactor]).x;
326
334
  const scaleFactor = this.system.worldScale.x / this.system.cameraScale;
327
335
  particle.size = particle.startSize * size * scaleFactor;
328
- // in Unity this is viewport size
329
- particle.size = Mathf.clamp(particle.size, this._minSize, this._maxSize);
336
+ // in Unity this is viewport size, we don't really support this yet (and the renderer is logging a warning)
337
+ // so for now it's disabled again
338
+ // particle.size = Mathf.clamp(particle.size, this._minSize, this._maxSize);
330
339
  }
331
340
  }
332
341
  }
@@ -378,6 +387,7 @@ class TrailBehaviour extends ParticleSystemBaseBehaviour {
378
387
 
379
388
  const $startVelocity = Symbol("startVelocity");
380
389
  const $gravityFactor = Symbol("gravityModifier");
390
+ const $gravitySpeed = Symbol("gravitySpeed");
381
391
  const $velocityLerpFactor = Symbol("velocity lerp factor");
382
392
  const temp3 = new Vector3();
383
393
  const temp4 = new Quaternion();
@@ -401,6 +411,7 @@ class VelocityBehaviour extends ParticleSystemBaseBehaviour {
401
411
 
402
412
  const gravityFactor = this.system.main.gravityModifier.evaluate(Math.random(), Math.random()) / (9.81 * 2);
403
413
  particle[$gravityFactor] = gravityFactor * simulationSpeed;
414
+ particle[$gravitySpeed] = 1;
404
415
 
405
416
  particle[$velocityLerpFactor] = Math.random();
406
417
 
@@ -417,7 +428,8 @@ class VelocityBehaviour extends ParticleSystemBaseBehaviour {
417
428
  let gravityFactor = particle[$gravityFactor];
418
429
  if (gravityFactor !== 0) {
419
430
  // gravityFactor *= -1;
420
- temp3.copy(this._gravityDirection).multiplyScalar(gravityFactor);
431
+ temp3.copy(this._gravityDirection).multiplyScalar(gravityFactor * particle[$gravitySpeed]);
432
+ particle[$gravitySpeed] += delta;
421
433
  if (debug) Gizmos.DrawDirection(particle.position, temp3, 0x0000ff, 0, false, 10);
422
434
  baseVelocity.add(temp3);
423
435
  }
@@ -537,7 +549,7 @@ class ParticleSystemInterface implements ParticleSystemParameters {
537
549
  }
538
550
  switch (this.system.renderer.renderMode) {
539
551
  case ParticleSystemRenderMode.Billboard: return RenderMode.BillBoard;
540
- // case ParticleSystemRenderMode.Stretch: return RenderMode.Stretch;
552
+ case ParticleSystemRenderMode.Stretch: return RenderMode.StretchedBillBoard;
541
553
  case ParticleSystemRenderMode.HorizontalBillboard: return RenderMode.LocalSpace;
542
554
  case ParticleSystemRenderMode.VerticalBillboard: return RenderMode.LocalSpace;
543
555
  case ParticleSystemRenderMode.Mesh: return RenderMode.LocalSpace;
@@ -587,8 +599,8 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
587
599
  }
588
600
  }, true)
589
601
  }
602
+
590
603
  this._isPlaying = true;
591
- this._time = 0;
592
604
 
593
605
  // https://github.com/Alchemist0823/three.quarks/pull/35
594
606
  if (this._particleSystem) {
@@ -598,12 +610,40 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
598
610
  this.emission?.reset();
599
611
  }
600
612
 
601
- pause() {
613
+ pause(includeChildren = true) {
614
+ if (includeChildren) {
615
+ GameObject.foreachComponent(this.gameObject, comp => {
616
+ if (comp instanceof ParticleSystem && comp !== this) {
617
+ comp.pause(false);
618
+ }
619
+ }, true)
620
+ }
602
621
  this._isPlaying = false;
603
622
  }
604
- stop() {
623
+
624
+ /** clear=true removes all emitted particles */
625
+ stop(includeChildren = true, clear: boolean = false) {
626
+ if (includeChildren) {
627
+ GameObject.foreachComponent(this.gameObject, comp => {
628
+ if (comp instanceof ParticleSystem && comp !== this) {
629
+ comp.stop(false, clear);
630
+ }
631
+ }, true)
632
+ }
605
633
  this._isPlaying = false;
606
634
  this._time = 0;
635
+ if (clear) this.reset();
636
+ }
637
+
638
+ /** remove emitted particles and reset time */
639
+ reset() {
640
+ this._time = 0;
641
+ if (this._particleSystem) {
642
+ this._particleSystem.particleNum = 0;
643
+ this._particleSystem["emissionState"].time = 0;
644
+ this._particleSystem["emitEnded"] = false;
645
+ this.emission?.reset();
646
+ }
607
647
  }
608
648
 
609
649
  private _state?: ParticlesEmissionState;
@@ -872,8 +912,10 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
872
912
  console.log(`Particles ${this.name} - Prewarm for ${framesToSimulate} frames (${timeToSimulate} sec). Duration: ${duration}, Lifetime: ${lifetime}`);
873
913
  for (let i = 0; i < framesToSimulate; i++) {
874
914
  if (this.currentParticles >= this.maxParticles) break;
875
- if (Date.now() - startTime > 2) {
876
- console.warn(`Particles ${this.name} - Prewarm took too long. Aborting.`);
915
+ const timePassed = Date.now() - startTime;
916
+ if (timePassed > 2000) {
917
+ console.warn(`Particles ${this.name} - Prewarm took too long. Aborting: ${timePassed}`);
918
+ break;
877
919
  }
878
920
  this.onUpdate();
879
921
  this.onSimulate(dt);