@needle-tools/engine 4.4.0-ci.1 → 4.4.2

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 (147) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/assets/generateMeshBVH.worker-b7788939.js +25 -0
  3. package/dist/needle-engine.bundle.js +7043 -6766
  4. package/dist/needle-engine.bundle.light.js +7205 -6928
  5. package/dist/needle-engine.bundle.light.min.js +169 -139
  6. package/dist/needle-engine.bundle.light.umd.cjs +173 -143
  7. package/dist/needle-engine.bundle.min.js +169 -139
  8. package/dist/needle-engine.bundle.umd.cjs +172 -142
  9. package/dist/needle-engine.js +468 -467
  10. package/dist/needle-engine.light.js +468 -467
  11. package/dist/needle-engine.light.min.js +1 -1
  12. package/dist/needle-engine.light.umd.cjs +1 -1
  13. package/dist/needle-engine.min.js +1 -1
  14. package/dist/needle-engine.umd.cjs +1 -1
  15. package/dist/vendor.js +5108 -5122
  16. package/dist/vendor.light.js +5108 -5122
  17. package/dist/vendor.light.min.js +84 -84
  18. package/dist/vendor.light.umd.cjs +78 -78
  19. package/dist/vendor.min.js +84 -84
  20. package/dist/vendor.umd.cjs +78 -78
  21. package/lib/engine/codegen/register_types.js +2 -0
  22. package/lib/engine/codegen/register_types.js.map +1 -1
  23. package/lib/engine/engine_addressables.js.map +1 -1
  24. package/lib/engine/engine_components.js +3 -1
  25. package/lib/engine/engine_components.js.map +1 -1
  26. package/lib/engine/engine_context.d.ts +141 -14
  27. package/lib/engine/engine_context.js +164 -26
  28. package/lib/engine/engine_context.js.map +1 -1
  29. package/lib/engine/engine_element.js +12 -10
  30. package/lib/engine/engine_element.js.map +1 -1
  31. package/lib/engine/engine_gameobject.js +5 -0
  32. package/lib/engine/engine_gameobject.js.map +1 -1
  33. package/lib/engine/engine_license.d.ts +2 -0
  34. package/lib/engine/engine_license.js +103 -62
  35. package/lib/engine/engine_license.js.map +1 -1
  36. package/lib/engine/engine_networking_blob.js +40 -24
  37. package/lib/engine/engine_networking_blob.js.map +1 -1
  38. package/lib/engine/engine_physics_rapier.js +10 -9
  39. package/lib/engine/engine_physics_rapier.js.map +1 -1
  40. package/lib/engine/engine_serialization_core.js +6 -2
  41. package/lib/engine/engine_serialization_core.js.map +1 -1
  42. package/lib/engine/engine_utils_screenshot.js +1 -1
  43. package/lib/engine/engine_utils_screenshot.js.map +1 -1
  44. package/lib/engine/js-extensions/RGBAColor.d.ts +1 -0
  45. package/lib/engine/js-extensions/RGBAColor.js +56 -7
  46. package/lib/engine/js-extensions/RGBAColor.js.map +1 -1
  47. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +4 -3
  48. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  49. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +6 -6
  50. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  51. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -0
  52. package/lib/engine/webcomponents/needle menu/needle-menu.js +13 -2
  53. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  54. package/lib/engine/xr/NeedleXRController.js +2 -3
  55. package/lib/engine/xr/NeedleXRController.js.map +1 -1
  56. package/lib/engine/xr/NeedleXRSession.js +12 -12
  57. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  58. package/lib/engine-components/AudioSource.js +7 -0
  59. package/lib/engine-components/AudioSource.js.map +1 -1
  60. package/lib/engine-components/Camera.js +18 -12
  61. package/lib/engine-components/Camera.js.map +1 -1
  62. package/lib/engine-components/CameraUtils.js +8 -14
  63. package/lib/engine-components/CameraUtils.js.map +1 -1
  64. package/lib/engine-components/GroundProjection.js +1 -1
  65. package/lib/engine-components/GroundProjection.js.map +1 -1
  66. package/lib/engine-components/NeedleMenu.js +1 -1
  67. package/lib/engine-components/NeedleMenu.js.map +1 -1
  68. package/lib/engine-components/OrbitControls.js +3 -1
  69. package/lib/engine-components/OrbitControls.js.map +1 -1
  70. package/lib/engine-components/ReflectionProbe.d.ts +1 -0
  71. package/lib/engine-components/ReflectionProbe.js +16 -9
  72. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  73. package/lib/engine-components/SceneSwitcher.js +28 -7
  74. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  75. package/lib/engine-components/SyncedTransform.d.ts +6 -0
  76. package/lib/engine-components/SyncedTransform.js +10 -5
  77. package/lib/engine-components/SyncedTransform.js.map +1 -1
  78. package/lib/engine-components/codegen/components.d.ts +1 -0
  79. package/lib/engine-components/codegen/components.js +1 -0
  80. package/lib/engine-components/codegen/components.js.map +1 -1
  81. package/lib/engine-components/export/usdz/extensions/USDZUI.js +2 -1
  82. package/lib/engine-components/export/usdz/extensions/USDZUI.js.map +1 -1
  83. package/lib/engine-components/ui/BaseUIComponent.d.ts +0 -1
  84. package/lib/engine-components/ui/BaseUIComponent.js +1 -1
  85. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  86. package/lib/engine-components/ui/EventSystem.js +1 -1
  87. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  88. package/lib/engine-components/ui/Graphic.js +1 -0
  89. package/lib/engine-components/ui/Graphic.js.map +1 -1
  90. package/lib/engine-components/ui/RaycastUtils.js +1 -1
  91. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  92. package/lib/engine-components/ui/RectTransform.d.ts +2 -2
  93. package/lib/engine-components/ui/RectTransform.js +9 -6
  94. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  95. package/lib/engine-components/ui/Symbols.d.ts +1 -0
  96. package/lib/engine-components/ui/Symbols.js +2 -0
  97. package/lib/engine-components/ui/Symbols.js.map +1 -0
  98. package/lib/engine-components/ui/Utils.js +1 -1
  99. package/lib/engine-components/ui/Utils.js.map +1 -1
  100. package/lib/engine-components/utils/EnvironmentScene.d.ts +1 -1
  101. package/lib/engine-components/utils/EnvironmentScene.js +1 -1
  102. package/lib/engine-components/utils/EnvironmentScene.js.map +1 -1
  103. package/package.json +3 -3
  104. package/plugins/common/license.js +115 -27
  105. package/plugins/types/userconfig.d.ts +8 -0
  106. package/plugins/vite/build-pipeline.js +1 -1
  107. package/plugins/vite/defines.js +3 -1
  108. package/plugins/vite/dependencies.js +23 -4
  109. package/plugins/vite/facebook-instant-games.js +7 -4
  110. package/plugins/vite/index.js +1 -1
  111. package/plugins/vite/license.js +2 -1
  112. package/src/engine/codegen/register_types.ts +2 -0
  113. package/src/engine/engine_addressables.ts +3 -2
  114. package/src/engine/engine_components.ts +2 -1
  115. package/src/engine/engine_context.ts +169 -33
  116. package/src/engine/engine_element.ts +10 -9
  117. package/src/engine/engine_gameobject.ts +7 -0
  118. package/src/engine/engine_license.ts +116 -67
  119. package/src/engine/engine_networking_blob.ts +47 -26
  120. package/src/engine/engine_physics_rapier.ts +10 -12
  121. package/src/engine/engine_serialization_core.ts +6 -1
  122. package/src/engine/engine_utils_screenshot.ts +1 -1
  123. package/src/engine/js-extensions/RGBAColor.ts +59 -8
  124. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +6 -6
  125. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -6
  126. package/src/engine/webcomponents/needle menu/needle-menu.ts +13 -2
  127. package/src/engine/xr/NeedleXRController.ts +2 -3
  128. package/src/engine/xr/NeedleXRSession.ts +12 -12
  129. package/src/engine-components/AudioSource.ts +8 -3
  130. package/src/engine-components/Camera.ts +34 -22
  131. package/src/engine-components/CameraUtils.ts +8 -16
  132. package/src/engine-components/GroundProjection.ts +1 -1
  133. package/src/engine-components/NeedleMenu.ts +1 -1
  134. package/src/engine-components/OrbitControls.ts +2 -1
  135. package/src/engine-components/ReflectionProbe.ts +15 -8
  136. package/src/engine-components/SceneSwitcher.ts +28 -11
  137. package/src/engine-components/SyncedTransform.ts +11 -6
  138. package/src/engine-components/codegen/components.ts +1 -0
  139. package/src/engine-components/export/usdz/extensions/USDZUI.ts +2 -2
  140. package/src/engine-components/ui/BaseUIComponent.ts +1 -1
  141. package/src/engine-components/ui/EventSystem.ts +1 -1
  142. package/src/engine-components/ui/Graphic.ts +1 -1
  143. package/src/engine-components/ui/RaycastUtils.ts +1 -1
  144. package/src/engine-components/ui/RectTransform.ts +10 -7
  145. package/src/engine-components/ui/Symbols.ts +2 -0
  146. package/src/engine-components/ui/Utils.ts +2 -1
  147. package/src/engine-components/utils/EnvironmentScene.ts +1 -1
@@ -278,26 +278,43 @@ export class Context implements IContext {
278
278
  if (this.isInAR) return window.innerHeight;
279
279
  return this.domElement.clientHeight;
280
280
  }
281
- /** the X position of the Needle Engine element on the website */
281
+ /** the X position of the `<needle-engine>` element on the website */
282
282
  get domX(): number {
283
283
  this.calculateBoundingClientRect();
284
284
  return this._domX;
285
285
  }
286
- /** the Y position of the Needlee Engine element on the website */
286
+ /** the Y position of the `<needle-engine>` element on the website */
287
287
  get domY(): number {
288
288
  this.calculateBoundingClientRect();
289
289
  return this._domY;
290
290
  }
291
+ /**
292
+ * Is a XR session currently active and presenting?
293
+ * @returns true if the xr renderer is currently presenting
294
+ */
291
295
  get isInXR() { return this.renderer?.xr?.isPresenting || false; }
292
296
  /** shorthand for `NeedleXRSession.active`
293
297
  * Automatically set by NeedleXRSession when a XR session is active
294
298
  * @returns the active XR session or null if no session is active
295
299
  * */
296
300
  xr: NeedleXRSession | null = null;
301
+ /**
302
+ * Shorthand for `this.xr?.mode`. AR or VR
303
+ * @returns the current XR session mode (immersive-vr or immersive-ar)
304
+ */
297
305
  get xrSessionMode() { return this.xr?.mode; }
306
+ /** Shorthand for `this.xrSessionMode === "immersive-vr"`
307
+ * @returns true if a webxr VR session is currently active.
308
+ */
298
309
  get isInVR() { return this.xrSessionMode === "immersive-vr"; }
310
+ /**
311
+ * Shorthand for `this.xrSessionMode === "immersive-ar"`
312
+ * @returns true if a webxr AR session is currently active.
313
+ */
299
314
  get isInAR() { return this.xrSessionMode === "immersive-ar"; }
300
- /** If a XR session is active and in pass through mode (immersive-ar on e.g. Quest) */
315
+ /** If a XR session is active and in pass through mode (immersive-ar on e.g. Quest)
316
+ * @returns true if the XR session is in pass through mode
317
+ */
301
318
  get isInPassThrough() { return this.xr ? this.xr.isPassThrough : false; }
302
319
  /** access the raw `XRSession` object (shorthand for `context.renderer.xr.getSession()`). For more control use `NeedleXRSession.active` */
303
320
  get xrSession() { return this.renderer?.xr?.getSession(); }
@@ -308,34 +325,79 @@ export class Context implements IContext {
308
325
  /** @returns the current WebXR camera while the WebXRManager is active (shorthand for `context.renderer.xr.getCamera()`) */
309
326
  get xrCamera(): WebXRArrayCamera | undefined { return this.renderer.xr.isPresenting ? this.renderer?.xr?.getCamera() : undefined }
310
327
  private _xrFrame: XRFrame | null = null;
328
+ /**
329
+ * The AR overlay element is used to display 2D HTML elements while a AR session is active.
330
+ */
311
331
  get arOverlayElement(): HTMLElement {
312
332
  const el = this.domElement as any;
313
333
  if (typeof el.getAROverlayContainer === "function")
314
334
  return el.getAROverlayContainer();
315
335
  return this.domElement;
316
336
  }
317
- /** Current event of the update cycle */
337
+ /**
338
+ * Current event of the update cycle (e.g. `FrameEvent.EarlyUpdate` or `FrameEvent.OnBeforeRender`)
339
+ */
318
340
  get currentFrameEvent(): FrameEvent {
319
341
  return this._currentFrameEvent;
320
342
  }
321
343
  private _currentFrameEvent: FrameEvent = FrameEvent.Undefined;
322
344
 
345
+ /**
346
+ * The scene contains all objects in the hierarchy and is automatically rendered by the context every frane.
347
+ */
323
348
  scene: Scene;
349
+ /**
350
+ * The renderer is used to render the scene. It is automatically created when the context is created.
351
+ */
324
352
  renderer!: WebGLRenderer;
353
+ /**
354
+ * The effect composer can be used to render postprocessing effects. If assigned then it will automatically render the scene every frame.
355
+ */
325
356
  composer: EffectComposer | ThreeEffectComposer | null = null;
326
357
 
327
- // all scripts
358
+ /**
359
+ * @internal All known components. Don't use directly
360
+ */
328
361
  readonly scripts: IComponent[] = [];
362
+ /**
363
+ * @internal All paused components. Don't use directly
364
+ */
329
365
  readonly scripts_pausedChanged: IComponent[] = [];
330
- // scripts with update event
366
+ /**
367
+ * @internal All components that have a early update event. Don't use directly
368
+ */
331
369
  readonly scripts_earlyUpdate: IComponent[] = [];
370
+ /**
371
+ * @internal All components that have a update event. Don't use directly
372
+ */
332
373
  readonly scripts_update: IComponent[] = [];
374
+ /**
375
+ * @internal All components that have a late update event. Don't use directly
376
+ */
333
377
  readonly scripts_lateUpdate: IComponent[] = [];
378
+ /**
379
+ * @internal All components that have a onBeforeRender event. Don't use directly
380
+ */
334
381
  readonly scripts_onBeforeRender: IComponent[] = [];
382
+ /**
383
+ * @internal All components that have a onAfterRender event. Don't use directly
384
+ */
335
385
  readonly scripts_onAfterRender: IComponent[] = [];
386
+ /**
387
+ * @internal All components that have coroutines. Don't use directly
388
+ */
336
389
  readonly scripts_WithCorroutines: IComponent[] = [];
390
+ /**
391
+ * @internal Components with immersive-vr event methods. Don't use directly
392
+ */
337
393
  readonly scripts_immersive_vr: INeedleXRSessionEventReceiver[] = [];
394
+ /**
395
+ * @internal Components with immersive-ar event methods. Don't use directly
396
+ */
338
397
  readonly scripts_immersive_ar: INeedleXRSessionEventReceiver[] = [];
398
+ /**
399
+ * @internal Coroutine data
400
+ */
339
401
  readonly coroutines: { [FrameEvent: number]: Array<CoroutineData> } = {}
340
402
 
341
403
  /** callbacks called once after the context has been created */
@@ -346,20 +408,30 @@ export class Context implements IContext {
346
408
  readonly pre_render_callbacks: Array<(frame: XRFrame | null) => void> = [];
347
409
  /** called every frame after rendering (after all component events) */
348
410
  readonly post_render_callbacks: Function[] = [];
349
-
350
411
  /** called every frame befroe update (this list is emptied every frame) */
351
412
  readonly pre_update_oneshot_callbacks: Function[] = [];
352
413
 
414
+ /** @internal */
353
415
  readonly new_scripts: IComponent[] = [];
416
+ /** @internal */
354
417
  readonly new_script_start: IComponent[] = [];
418
+ /** @internal */
355
419
  readonly new_scripts_pre_setup_callbacks: Function[] = [];
420
+ /** @internal */
356
421
  readonly new_scripts_post_setup_callbacks: Function[] = [];
422
+ /** @internal */
357
423
  readonly new_scripts_xr: INeedleXRSessionEventReceiver[] = [];
358
424
 
359
- /** The main camera component of the scene - this camera is used for rendering */
425
+ /**
426
+ * The **main camera component** of the scene - this camera is used for rendering.
427
+ * Use `setCurrentCamera` for updating the main camera.
428
+ */
360
429
  mainCameraComponent: ICamera | undefined = undefined;
361
430
 
362
- /** The main camera of the scene - this camera is used for rendering */
431
+ /**
432
+ * The main camera of the scene - this camera is used for rendering
433
+ * Use `setCurrentCamera` for updating the main camera.
434
+ */
363
435
  get mainCamera(): Camera {
364
436
  if (this._mainCamera) {
365
437
  return this._mainCamera;
@@ -382,11 +454,13 @@ export class Context implements IContext {
382
454
  private _mainCamera: Camera | null = null;
383
455
  private _fallbackCamera: PerspectiveCamera | null = null;
384
456
 
457
+ /** access application state (e.g. if all audio should be muted) */
385
458
  application: Application;
386
459
  /** access animation mixer used by components in the scene */
387
460
  animations: AnimationsRegistry;
388
461
  /** access timings (current frame number, deltaTime, timeScale, ...) */
389
462
  time: Time;
463
+ /** access input data (e.g. click or touch events) */
390
464
  input: Input;
391
465
  /** access physics related methods (e.g. raycasting). To access the phyiscs engine use `context.physics.engine` */
392
466
  physics: Physics;
@@ -396,6 +470,7 @@ export class Context implements IContext {
396
470
  * @deprecated AssetDataBase is deprecated
397
471
  */
398
472
  assets: AssetDatabase;
473
+ /** The main light in the scene */
399
474
  mainLight: ILight | null = null;
400
475
  /** @deprecated Use sceneLighting */
401
476
  get rendererData() { return this.sceneLighting }
@@ -404,11 +479,13 @@ export class Context implements IContext {
404
479
  lightmaps: ILightDataRegistry;
405
480
  players: PlayerViewManager;
406
481
  readonly lodsManager: LODsManager;
482
+ /** Access the needle menu to add or remove buttons to the menu element */
407
483
  readonly menu: NeedleMenu;
408
484
 
485
+ /** @returns true if the context is fully created and ready */
409
486
  get isCreated() { return this._isCreated; }
410
487
 
411
- private _sizeChanged: boolean = false;
488
+ private _needsUpdateSize: boolean = false;
412
489
  private _isCreated: boolean = false;
413
490
  private _isCreating: boolean = false;
414
491
  private _isVisible: boolean = false;
@@ -446,11 +523,11 @@ export class Context implements IContext {
446
523
  this.animations = new AnimationsRegistry(this);
447
524
 
448
525
 
449
- const resizeCallback = () => this._sizeChanged = true;
526
+ const resizeCallback = () => this._needsUpdateSize = true;
450
527
  window.addEventListener('resize', resizeCallback);
451
528
  this._disposeCallbacks.push(() => window.removeEventListener('resize', resizeCallback));
452
529
 
453
- const resizeObserver = new ResizeObserver(_ => this._sizeChanged = true);
530
+ const resizeObserver = new ResizeObserver(_ => this._needsUpdateSize = true);
454
531
  resizeObserver.observe(this.domElement);
455
532
  this._disposeCallbacks.push(() => resizeObserver.disconnect());
456
533
 
@@ -462,7 +539,12 @@ export class Context implements IContext {
462
539
  ContextRegistry.register(this);
463
540
  }
464
541
 
465
- /** calling this function will dispose the current renderer and create a new one */
542
+ /**
543
+ * Calling this function will dispose the current renderer and create a new one which will then be assigned to the context. It can be used to create a new renderer with custom WebGLRendererParameters.
544
+ * **Note**: Instead you can also modify the static `Context.DefaultWebGlRendererParameters` before the context is created.
545
+ * **Note**: This method is recommended because it re-uses an potentially already existing canvas element. This is necessary to keep input event handlers from working (e.g. components like OrbitControls subscribe to input events on the canvas)
546
+ * @returns {WebGLRenderer} the newly created renderer
547
+ */
466
548
  createNewRenderer(params?: WebGLRendererParameters) {
467
549
  this.renderer?.dispose();
468
550
 
@@ -506,6 +588,8 @@ export class Context implements IContext {
506
588
  this.lodsManager.setRenderer(this.renderer);
507
589
 
508
590
  this.input.bindEvents();
591
+
592
+ return this.renderer;
509
593
  }
510
594
 
511
595
 
@@ -519,15 +603,32 @@ export class Context implements IContext {
519
603
 
520
604
 
521
605
  /** will request a renderer size update the next render call (will call updateSize the next update) */
522
- requestSizeUpdate() { this._sizeChanged = true; }
606
+ requestSizeUpdate() { this._needsUpdateSize = true; }
523
607
 
524
608
  /** Clamps the renderer max resolution. If undefined the max resolution is not clamped. Default is undefined */
525
609
  maxRenderResolution?: Vec2;
526
610
 
527
- /** update the renderer and canvas size */
611
+ /** Control the renderer devicePixelRatio.
612
+ * **Options**
613
+ * - `auto` - Needle Engine automatically sets the pixel ratio to the current window.devicePixelRatio.
614
+ * - `manual` - Needle Engine will not change the renderer pixel ratio. You can set it manually.
615
+ * - `number` - Needle Engine will set the pixel ratio to the given number. The change will be applied to the renderer and the composer (if used) at the end of the current frame.
616
+ */
617
+ get devicePixelRatio() { return this._devicePixelRatio; }
618
+ set devicePixelRatio(val: "auto" | "manual" | number) {
619
+ if (val !== this._devicePixelRatio) {
620
+ this._devicePixelRatio = val;
621
+ this._needsUpdateSize = true;
622
+ }
623
+ }
624
+ private _devicePixelRatio: "auto" | "manual" | number = "auto";
625
+
626
+ /**
627
+ * Update the renderer and canvas size. This is also automatically called when a DOM size change is detected.
628
+ */
528
629
  updateSize(force: boolean = false) {
529
630
  if (force || (!this.isManagedExternally && this.renderer.xr?.isPresenting === false)) {
530
- this._sizeChanged = false;
631
+ this._needsUpdateSize = false;
531
632
  const scaleFactor = this.resolutionScaleFactor;
532
633
  let width = this.domWidth * scaleFactor;
533
634
  let height = this.domHeight * scaleFactor;
@@ -540,20 +641,32 @@ export class Context implements IContext {
540
641
  const camera = this.mainCamera as PerspectiveCamera;
541
642
  this.updateAspect(camera);
542
643
  this.renderer.setSize(width, height, true);
543
- this.renderer.setPixelRatio(window.devicePixelRatio);
544
644
  // avoid setting pixel values here since this can cause pingpong updates
545
645
  // e.g. when system scale is set to 125%
546
646
  // https://github.com/needle-tools/needle-engine-support/issues/69
547
647
  this.renderer.domElement.style.width = "100%";
548
648
  this.renderer.domElement.style.height = "100%";
649
+
650
+ const devicePixelRatio = typeof this.devicePixelRatio === "number"
651
+ ? this.devicePixelRatio
652
+ : this.devicePixelRatio === "auto"
653
+ ? window.devicePixelRatio
654
+ : undefined;
655
+ if (devicePixelRatio !== undefined) {
656
+ this.renderer.setPixelRatio(devicePixelRatio);
657
+ }
658
+
549
659
  if (this.composer) {
550
660
  this.composer.setSize?.call(this.composer, width, height);
551
- if ("setPixelRatio" in this.composer && typeof this.composer.setPixelRatio === "function")
661
+ if (devicePixelRatio !== undefined && "setPixelRatio" in this.composer && typeof this.composer.setPixelRatio === "function")
552
662
  this.composer.setPixelRatio?.call(this.composer, window.devicePixelRatio);
553
663
  }
554
664
  }
555
665
  }
556
666
 
667
+ /**
668
+ * Update the camera aspect ratio or orthorgraphic camera size. This is automatically called when a DOM size change is detected.
669
+ */
557
670
  updateAspect(camera: PerspectiveCamera | OrthographicCamera, width?: number, height?: number) {
558
671
  if (!camera) return;
559
672
  if (width === undefined)
@@ -601,6 +714,7 @@ export class Context implements IContext {
601
714
  async onCreate(opts?: ContextCreateArgs) {
602
715
  return this.create(opts);
603
716
  }
717
+ /** @internal */
604
718
  async create(opts?: ContextCreateArgs) {
605
719
  try {
606
720
  this._isCreating = true;
@@ -626,7 +740,11 @@ export class Context implements IContext {
626
740
  this.domElement.dispatchEvent(new CustomEvent("error", { detail: error }));
627
741
  }
628
742
 
629
- /** Will destroy all scenes and objects in the scene
743
+ /**
744
+ * Clears the context and destroys all scenes and objects in the scene.
745
+ * The ContextCleared event is called at the end.
746
+ * This is automatically called when e.g. the `src` attribute changes on `<needle-engine>`
747
+ * or when the web component is removed from the DOM
630
748
  */
631
749
  clear() {
632
750
  ContextRegistry.dispatchCallback(ContextEvent.ContextClearing, this);
@@ -652,12 +770,16 @@ export class Context implements IContext {
652
770
  ContextRegistry.dispatchCallback(ContextEvent.ContextCleared, this);
653
771
  }
654
772
 
773
+ /**
774
+ * Dispose all allocated resources and clears the scene. This is automatically called e.g. when the `<needle-engine>` component is removed from the DOM.
775
+ */
655
776
  dispose() {
656
777
  this.internalOnDestroy();
657
778
  }
658
779
 
659
780
  /**@deprecated use dispose() */
660
781
  onDestroy() { this.internalOnDestroy(); }
782
+
661
783
  private internalOnDestroy() {
662
784
  Context.Current = this;
663
785
  ContextRegistry.dispatchCallback(ContextEvent.ContextDestroying, this);
@@ -698,6 +820,7 @@ export class Context implements IContext {
698
820
  }
699
821
  }
700
822
 
823
+ /** @internal Automatically called by components when you call `startCoroutine`. Use `startCoroutine` instead */
701
824
  registerCoroutineUpdate(script: IComponent, coroutine: Generator, evt: FrameEvent): Generator {
702
825
  if (typeof coroutine?.next !== "function") {
703
826
  console.error("Registered invalid coroutine function from " + script.name + "\nCoroutine functions must be generators: \"*myCoroutine() {...}\"\nStart a coroutine from a component by calling \"this.startCoroutine(myCoroutine())\"")
@@ -708,12 +831,14 @@ export class Context implements IContext {
708
831
  return coroutine;
709
832
  }
710
833
 
834
+ /** @internal Automatically called by components. */
711
835
  unregisterCoroutineUpdate(coroutine: Generator, evt: FrameEvent): void {
712
836
  if (!this.coroutines[evt]) return;
713
837
  const idx = this.coroutines[evt].findIndex(c => c.main === coroutine);
714
838
  if (idx >= 0) this.coroutines[evt].splice(idx, 1);
715
839
  }
716
840
 
841
+ /** @internal Automatically called */
717
842
  stopAllCoroutinesFrom(script: IComponent) {
718
843
  for (const evt in this.coroutines) {
719
844
  const rout: CoroutineData[] = this.coroutines[evt];
@@ -728,6 +853,7 @@ export class Context implements IContext {
728
853
 
729
854
  private _cameraStack: ICamera[] = [];
730
855
 
856
+ /** Change the main camera */
731
857
  setCurrentCamera(cam: ICamera) {
732
858
  if (!cam) return;
733
859
  if (!cam.threeCamera) cam.buildCamera(); // < to build camera
@@ -745,6 +871,9 @@ export class Context implements IContext {
745
871
  (this.mainCameraComponent as ICamera)?.applyClearFlagsIfIsActiveCamera();
746
872
  }
747
873
 
874
+ /**
875
+ * Remove the camera from the mainCamera stack (if it has been set before with `setCurrentCamera`)
876
+ */
748
877
  removeCamera(cam?: ICamera | null) {
749
878
  if (!cam) return;
750
879
  const index = this._cameraStack.indexOf(cam);
@@ -760,12 +889,12 @@ export class Context implements IContext {
760
889
  }
761
890
  }
762
891
 
763
-
764
-
765
892
  private _onBeforeRenderListeners = new Map<string, OnRenderCallback[]>();
766
893
  private _onAfterRenderListeners = new Map<string, OnRenderCallback[]>();
767
894
 
768
- /** use this to subscribe to onBeforeRender events on threejs objects */
895
+ /** Use to subscribe to onBeforeRender events on threejs objects.
896
+ * @link https://threejs.org/docs/#api/en/core/Object3D.onBeforeRender
897
+ */
769
898
  addBeforeRenderListener(target: Object3D, callback: OnRenderCallback) {
770
899
  if (!this._onBeforeRenderListeners.has(target.uuid)) {
771
900
  this._onBeforeRenderListeners.set(target.uuid, []);
@@ -773,6 +902,9 @@ export class Context implements IContext {
773
902
  }
774
903
  this._onBeforeRenderListeners.get(target.uuid)!.push(callback);
775
904
  }
905
+ /** Remove callback from three `onBeforeRender` event (if it has been added with `addBeforeRenderListener(...)`)
906
+ * @link https://threejs.org/docs/#api/en/core/Object3D.onBeforeRender
907
+ */
776
908
  removeBeforeRenderListener(target: Object3D, callback: OnRenderCallback) {
777
909
  if (this._onBeforeRenderListeners.has(target.uuid)) {
778
910
  const arr = this._onBeforeRenderListeners.get(target.uuid)!;
@@ -781,7 +913,10 @@ export class Context implements IContext {
781
913
  }
782
914
  }
783
915
 
784
- /** use this to subscribe to onAfterRender events on threejs objects */
916
+ /**
917
+ * Subscribe to onAfterRender events on threejs objects
918
+ * @link https://threejs.org/docs/#api/en/core/Object3D.onAfterRender
919
+ */
785
920
  addAfterRenderListener(target: Object3D, callback: OnRenderCallback) {
786
921
  if (!this._onAfterRenderListeners.has(target.uuid)) {
787
922
  this._onAfterRenderListeners.set(target.uuid, []);
@@ -789,6 +924,10 @@ export class Context implements IContext {
789
924
  }
790
925
  this._onAfterRenderListeners.get(target.uuid)?.push(callback);
791
926
  }
927
+ /**
928
+ * Remove from onAfterRender events on threejs objects
929
+ * @link https://threejs.org/docs/#api/en/core/Object3D.onAfterRender
930
+ */
792
931
  removeAfterRenderListener(target: Object3D, callback: OnRenderCallback) {
793
932
  if (this._onAfterRenderListeners.has(target.uuid)) {
794
933
  const arr = this._onAfterRenderListeners.get(target.uuid)!;
@@ -816,25 +955,23 @@ export class Context implements IContext {
816
955
  private _renderTarget?: WebGLRenderTarget;
817
956
  private _isRendering: boolean = false;
818
957
 
958
+ /** @returns true while the WebGL renderer is rendering (between onBeforeRender and onAfterRender events) */
819
959
  get isRendering() { return this._isRendering; }
820
960
 
821
961
  setRequireDepth(val: boolean) {
822
962
  this._requireDepthTexture = val;
823
963
  }
824
-
825
964
  setRequireColor(val: boolean) {
826
965
  this._requireColorTexture = val;
827
966
  }
828
-
829
967
  get depthTexture(): DepthTexture | null {
830
968
  return this._renderTarget?.depthTexture || null;
831
969
  }
832
-
833
970
  get opaqueColorTexture(): Texture | null {
834
971
  return this._renderTarget?.texture || null;
835
972
  }
836
973
 
837
- /** returns true if the dom element is visible on screen */
974
+ /** @returns true if the `<needle-engine>` DOM element is visible on screen (`context.domElement`) */
838
975
  get isVisibleToUser() {
839
976
  if (this.isInXR) return true;
840
977
  if (!this._isVisible) return false;
@@ -842,7 +979,6 @@ export class Context implements IContext {
842
979
  return style.visibility !== "hidden" && style.display !== "none" && style.opacity !== "0";
843
980
  }
844
981
 
845
-
846
982
  private _createId: number = 0;
847
983
  private async internalOnCreate(opts?: ContextCreateArgs): Promise<boolean> {
848
984
  const createId = ++this._createId;
@@ -1016,7 +1152,7 @@ export class Context implements IContext {
1016
1152
  // this.composer.setSize(this.domWidth, this.domHeight);
1017
1153
  }
1018
1154
 
1019
- this._sizeChanged = true;
1155
+ this._needsUpdateSize = true;
1020
1156
 
1021
1157
  if (this._stats) {
1022
1158
  this._stats.showPanel(0);
@@ -1186,11 +1322,11 @@ export class Context implements IContext {
1186
1322
  if (frame === undefined) frame = null;
1187
1323
  if (isDevEnvironment() || debug || looputils.hasNewScripts()) {
1188
1324
  try {
1189
- performance.mark('update.start');
1325
+ //performance.mark('update.start');
1190
1326
  this.internalStep(timestamp, frame);
1191
1327
  this._renderlooperrors = 0;
1192
- performance.mark('update.end');
1193
- performance.measure('NE Frame', 'update.start', 'update.end');
1328
+ //performance.mark('update.end');
1329
+ //performance.measure('NE Frame', 'update.start', 'update.end');
1194
1330
  }
1195
1331
  catch (err) {
1196
1332
  this._renderlooperrors += 1;
@@ -1361,7 +1497,7 @@ export class Context implements IContext {
1361
1497
  this.executeCoroutines(FrameEvent.OnBeforeRender);
1362
1498
  invokeLifecycleFunctions(this, FrameEvent.OnBeforeRender);
1363
1499
 
1364
- if (this._sizeChanged)
1500
+ if (this._needsUpdateSize)
1365
1501
  this.updateSize();
1366
1502
 
1367
1503
  if (this.pre_render_callbacks) {
@@ -12,6 +12,7 @@ import { NeedleLoader } from "./engine_scenetools.js";
12
12
  import { Context, ContextCreateArgs } from "./engine_setup.js";
13
13
  import { type INeedleEngineComponent, type LoadedModel } from "./engine_types.js";
14
14
  import { getParam } from "./engine_utils.js";
15
+ import { RGBAColor } from "./js-extensions/RGBAColor.js";
15
16
  import { ensureFonts } from "./webcomponents/fonts.js";
16
17
 
17
18
  //
@@ -177,6 +178,9 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
177
178
  -webkit-touch-callout: none;
178
179
  -webkit-user-drag: none;
179
180
  -webkit-user-modify: none;
181
+
182
+ left: 0;
183
+ top: 0;
180
184
  }
181
185
  :host .content {
182
186
  position: absolute;
@@ -517,7 +521,7 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
517
521
 
518
522
  private applyAttributes() {
519
523
  // set tonemapping if configured
520
- if (this._context.renderer) {
524
+ if (this._context?.renderer) {
521
525
  const attribute = this.getAttribute("tonemapping") || this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
522
526
  switch (attribute?.toLowerCase()) {
523
527
  case "none":
@@ -555,15 +559,12 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
555
559
  }
556
560
 
557
561
  const backgroundColor = this.getAttribute("background-color");
558
- if (this._context) {
562
+ if (this._context?.renderer) {
559
563
  if (typeof backgroundColor === "string" && backgroundColor.length > 0) {
560
- const currentBackground = this._context.scene.background;
561
- if (currentBackground instanceof Color) {
562
- currentBackground.set(backgroundColor);
563
- }
564
- else {
565
- this._context.scene.background = new Color(backgroundColor);
566
- }
564
+ const rgbaColor = RGBAColor.fromColorRepresentation(backgroundColor);
565
+ if(debug) console.debug("<needle-engine> background-color changed, str:", backgroundColor, "→", rgbaColor)
566
+ this._context.renderer.setClearColor(rgbaColor, rgbaColor.alpha);
567
+ this.context.scene.background = null;
567
568
  }
568
569
  }
569
570
  }
@@ -1,5 +1,6 @@
1
1
  import { Bone, Object3D, Quaternion, SkinnedMesh, Vector3 } from "three";
2
2
 
3
+ import { $shadowDomOwner } from "../engine-components/ui/Symbols.js";
3
4
  import { type AssetReference } from "./engine_addressables.js";
4
5
  import { __internalNotifyObjectDestroyed as __internalRemoveReferences, disposeObjectResources } from "./engine_assetdatabase.js";
5
6
  import { ComponentLifecycleEvents } from "./engine_components_internal.js";
@@ -380,6 +381,12 @@ function internalInstantiate(
380
381
  )
381
382
  : GameObject | Object3D | null {
382
383
  if (!instance) return null;
384
+
385
+ // Don't clone UI shadow objects
386
+ if (instance[$shadowDomOwner]) {
387
+ return null;
388
+ }
389
+
383
390
  // prepare, remove things that dont work out of the box
384
391
  // e.g. user data we want to manually clone
385
392
  // also children throw errors (e.g. recursive toJson with nested meshes)