@needle-tools/engine 2.62.1-pre → 2.62.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 (85) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/needle-engine.js +9270 -9157
  3. package/dist/needle-engine.umd.cjs +223 -223
  4. package/lib/engine/api.d.ts +1 -0
  5. package/lib/engine/api.js +1 -0
  6. package/lib/engine/api.js.map +1 -1
  7. package/lib/engine/engine.js +0 -2
  8. package/lib/engine/engine.js.map +1 -1
  9. package/lib/engine/engine_camera.d.ts +4 -0
  10. package/lib/engine/engine_camera.js +13 -0
  11. package/lib/engine/engine_camera.js.map +1 -0
  12. package/lib/engine/engine_element.d.ts +1 -0
  13. package/lib/engine/engine_element.js +17 -1
  14. package/lib/engine/engine_element.js.map +1 -1
  15. package/lib/engine/engine_input.d.ts +2 -0
  16. package/lib/engine/engine_input.js +14 -0
  17. package/lib/engine/engine_input.js.map +1 -1
  18. package/lib/engine/engine_setup.js +1 -1
  19. package/lib/engine/engine_setup.js.map +1 -1
  20. package/lib/engine/engine_types.d.ts +4 -0
  21. package/lib/engine/engine_types.js.map +1 -1
  22. package/lib/engine/extensions/extension_utils.js +1 -1
  23. package/lib/engine/extensions/extension_utils.js.map +1 -1
  24. package/lib/engine/js-extensions/Camera.d.ts +1 -0
  25. package/lib/engine/js-extensions/Camera.js +37 -0
  26. package/lib/engine/js-extensions/Camera.js.map +1 -0
  27. package/lib/engine/js-extensions/Layers.js +1 -0
  28. package/lib/engine/js-extensions/Layers.js.map +1 -1
  29. package/lib/engine/js-extensions/index.d.ts +2 -0
  30. package/lib/engine/js-extensions/index.js +3 -0
  31. package/lib/engine/js-extensions/index.js.map +1 -0
  32. package/lib/engine-components/CameraUtils.d.ts +1 -3
  33. package/lib/engine-components/CameraUtils.js +34 -17
  34. package/lib/engine-components/CameraUtils.js.map +1 -1
  35. package/lib/engine-components/Light.d.ts +4 -1
  36. package/lib/engine-components/Light.js +17 -2
  37. package/lib/engine-components/Light.js.map +1 -1
  38. package/lib/engine-components/OrbitControls.d.ts +4 -1
  39. package/lib/engine-components/OrbitControls.js +13 -2
  40. package/lib/engine-components/OrbitControls.js.map +1 -1
  41. package/lib/engine-components/timeline/TimelineTracks.js +7 -1
  42. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  43. package/lib/engine-components/ui/BaseUIComponent.d.ts +3 -0
  44. package/lib/engine-components/ui/BaseUIComponent.js +20 -10
  45. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  46. package/lib/engine-components/ui/Button.js +7 -3
  47. package/lib/engine-components/ui/Button.js.map +1 -1
  48. package/lib/engine-components/ui/EventSystem.js +21 -42
  49. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  50. package/lib/engine-components/ui/Graphic.d.ts +1 -0
  51. package/lib/engine-components/ui/Graphic.js +7 -0
  52. package/lib/engine-components/ui/Graphic.js.map +1 -1
  53. package/lib/engine-components/ui/Interfaces.d.ts +2 -1
  54. package/lib/engine-components/ui/RaycastUtils.js +2 -0
  55. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  56. package/lib/engine-components/ui/Text.js +10 -1
  57. package/lib/engine-components/ui/Text.js.map +1 -1
  58. package/lib/needle-engine.d.ts +1 -0
  59. package/lib/needle-engine.js +1 -2
  60. package/lib/needle-engine.js.map +1 -1
  61. package/lib/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +1 -1
  63. package/src/engine/api.ts +2 -1
  64. package/src/engine/engine.ts +0 -3
  65. package/src/engine/engine_camera.ts +18 -0
  66. package/src/engine/engine_element.ts +17 -1
  67. package/src/engine/engine_input.ts +17 -5
  68. package/src/engine/engine_setup.ts +2 -1
  69. package/src/engine/engine_types.ts +9 -4
  70. package/src/engine/extensions/extension_utils.ts +1 -1
  71. package/src/engine/js-extensions/Camera.ts +35 -0
  72. package/src/engine/js-extensions/Layers.ts +2 -1
  73. package/src/engine/js-extensions/index.ts +2 -0
  74. package/src/engine-components/CameraUtils.ts +42 -20
  75. package/src/engine-components/Light.ts +16 -3
  76. package/src/engine-components/OrbitControls.ts +18 -6
  77. package/src/engine-components/timeline/TimelineTracks.ts +9 -3
  78. package/src/engine-components/ui/BaseUIComponent.ts +21 -11
  79. package/src/engine-components/ui/Button.ts +7 -3
  80. package/src/engine-components/ui/EventSystem.ts +20 -42
  81. package/src/engine-components/ui/Graphic.ts +7 -0
  82. package/src/engine-components/ui/Interfaces.ts +3 -1
  83. package/src/engine-components/ui/RaycastUtils.ts +2 -1
  84. package/src/engine-components/ui/Text.ts +11 -2
  85. package/src/needle-engine.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.62.1-pre",
3
+ "version": "2.62.2-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "module": "dist/needle-engine.js",
package/src/engine/api.ts CHANGED
@@ -8,4 +8,5 @@ export * from "./debug/debug";
8
8
  export { validate } from "./engine_util_decorator"
9
9
  export { Gizmos } from "./engine_gizmos"
10
10
  export * from "./engine_scenetools";
11
- export * from "./engine_math"
11
+ export * from "./engine_math"
12
+ export * from "./js-extensions"
@@ -1,8 +1,5 @@
1
1
  import "./engine_hot_reload"
2
2
 
3
- import * as layers from "./js-extensions/Layers";
4
- layers.patchLayers();
5
-
6
3
  import * as engine_setup from "./engine_setup";
7
4
  import * as engine_scenetools from "./engine_scenetools";
8
5
  import "./tests/test_utils";
@@ -0,0 +1,18 @@
1
+ import { ICameraController } from "./engine_types";
2
+ import { Camera } from "three";
3
+
4
+
5
+ const $cameraController = Symbol("cameraController");
6
+
7
+ export function getCameraController(cam: Camera): ICameraController | null {
8
+ return cam[$cameraController];
9
+ }
10
+
11
+ export function setCameraController(cam: Camera, cameraController: ICameraController, active: boolean) {
12
+ if (active)
13
+ cam[$cameraController] = cameraController;
14
+ else{
15
+ if(cam[$cameraController] === cameraController)
16
+ cam[$cameraController] = null;
17
+ }
18
+ }
@@ -79,6 +79,12 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
79
79
  public get loadingProgress01(): number { return this._loadingProgress01; }
80
80
  public get loadingFinished(): boolean { return this.loadingProgress01 > .999; }
81
81
 
82
+ public get cameraControls(): boolean {
83
+ const attr = this.getAttribute("camera-controls");
84
+ if(attr === null || attr === "False" || attr === "false" || attr === "0") return false;
85
+ return true;
86
+ }
87
+
82
88
  public getContext(): Promise<Context> {
83
89
  return new Promise((res, _rej) => {
84
90
  if (this._context && this.loadingFinished) {
@@ -237,7 +243,17 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
237
243
  }
238
244
 
239
245
  static get observedAttributes() {
240
- return ["hash", "src", "loadstart", "progress", "loadfinished", "dracoDecoderPath", "dracoDecoderType", "ktx2DecoderPath"];
246
+ return [
247
+ "hash",
248
+ "src",
249
+ "camera-controls",
250
+ "loadstart",
251
+ "progress",
252
+ "loadfinished",
253
+ "dracoDecoderPath",
254
+ "dracoDecoderType",
255
+ "ktx2DecoderPath"
256
+ ];
241
257
  }
242
258
 
243
259
  attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
@@ -220,6 +220,7 @@ export class Input extends EventTarget {
220
220
  private _pointerPositionDown: THREE.Vector2[] = [new THREE.Vector2()];
221
221
  private _pointerDownTime: number[] = [];
222
222
  private _pointerUpTime: number[] = [];
223
+ private _pointerUpTimestamp: number[] = [];
223
224
  private _pointerIds: number[] = [];
224
225
  private _pointerTypes: string[] = [""];
225
226
  private _mouseWheelChanged: boolean[] = [false];
@@ -411,13 +412,15 @@ export class Input extends EventTarget {
411
412
  }
412
413
  }
413
414
 
414
- private onTouchUp(evt) {
415
+ private onTouchUp(evt: TouchEvent) {
415
416
  if (evt.changedTouches.length <= 0) return;
416
417
  for (let i = 0; i < evt.changedTouches.length; i++) {
417
418
  const touch = evt.changedTouches[i];
418
- const id = this.getPointerIndex(touch.identifier)
419
- if (debug)
420
- showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
419
+ const id = this.getPointerIndex(touch.identifier);
420
+
421
+ if (!this.isNewEvent(evt.timeStamp, id, this._pointerUpTimestamp)) continue;
422
+
423
+ if (debug) showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
421
424
  const args: PointerEventArgs = { button: id, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, source: evt };
422
425
  this.onUp(args);
423
426
  }
@@ -440,11 +443,20 @@ export class Input extends EventTarget {
440
443
  private onMouseUp(evt: MouseEvent) {
441
444
  if (evt.defaultPrevented) return;
442
445
  let i = evt.button;
446
+ if (!this.isNewEvent(evt.timeStamp, i, this._pointerUpTimestamp)) return;
443
447
  this.onUp({ button: i, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, source: evt });
444
448
  }
445
449
 
450
+ // Prevent the same event being handled twice (e.g. on touch we get a mouseUp and touchUp evt with the same timestamp)
451
+ private isNewEvent(timestamp: number, index: number, arr: number[]): boolean {
452
+ while (arr.length <= index) arr.push(-1);
453
+ if (timestamp === arr[index]) return false;
454
+ arr[index] = timestamp;
455
+ return true;
456
+ }
457
+
446
458
  private isInRect(e: { clientX: number, clientY: number }): boolean {
447
- if(this.context.isInXR) return true;
459
+ if (this.context.isInXR) return true;
448
460
  const rect = this.context.domElement.getBoundingClientRect();
449
461
  const px = e.clientX;
450
462
  const py = e.clientY;
@@ -504,7 +504,6 @@ export class Context implements IContext {
504
504
  this.domElement.prepend(this.renderer.domElement);
505
505
 
506
506
  Context._current = this;
507
- ContextRegistry.dispatchCallback(ContextEvent.ContextCreated, this);
508
507
 
509
508
  // Setup
510
509
  Context._current = this;
@@ -591,6 +590,8 @@ export class Context implements IContext {
591
590
 
592
591
  if (debug)
593
592
  logHierarchy(this.scene, true);
593
+
594
+ ContextRegistry.dispatchCallback(ContextEvent.ContextCreated, this);
594
595
  }
595
596
 
596
597
  private render(_, frame) {
@@ -30,10 +30,11 @@ export declare type CoroutineData = {
30
30
  export interface IContext {
31
31
  alias?: string | null;
32
32
 
33
- scene : Scene;
34
- renderer : WebGLRenderer;
35
- mainCamera : Camera | null;
36
- domElement : HTMLElement;
33
+ scene: Scene;
34
+ renderer: WebGLRenderer;
35
+ mainCamera: Camera | null;
36
+ mainCameraComponent: ICamera | undefined;
37
+ domElement: HTMLElement;
37
38
 
38
39
  scripts: IComponent[];
39
40
  scripts_pausedChanged: IComponent[];
@@ -158,6 +159,10 @@ export declare interface ICamera extends IComponent {
158
159
  screenPointToRay(x: number, y: number, ray?: Ray): Ray;
159
160
  }
160
161
 
162
+ export declare interface ICameraController {
163
+ get isCameraController(): boolean;
164
+ }
165
+
161
166
  export declare interface ILight extends IComponent {
162
167
  intensity: number;
163
168
  color: Color;
@@ -43,7 +43,7 @@ function internalResolve(paths: DependencyInfo[], parser: GLTFParser, obj, promi
43
43
  // handle json pointer in string variable
44
44
  if (typeof val === "string") {
45
45
  const ext = resolveExtension(parser, val);
46
- if (ext !== null) {
46
+ if (ext !== null && ext !== undefined) {
47
47
  if (typeof ext.then === "function")
48
48
  promises.push(ext.then(res => obj[key] = res));
49
49
  else obj[key] = ext;
@@ -0,0 +1,35 @@
1
+ import { PerspectiveCamera } from "three";
2
+
3
+ // Wrap camera FOV to allow animation of fov
4
+ Object.defineProperty(PerspectiveCamera.prototype, "fov", {
5
+ get: function () {
6
+ return this._fov;;
7
+ },
8
+ set: function (val) {
9
+ const changed = val !== this._fov;
10
+ this._fov = val;
11
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
12
+ }
13
+ });
14
+
15
+ Object.defineProperty(PerspectiveCamera.prototype, "near", {
16
+ get: function () {
17
+ return this._near;
18
+ },
19
+ set: function (val) {
20
+ const changed = val !== this._near;
21
+ this._near = val;
22
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
23
+ }
24
+ });
25
+
26
+ Object.defineProperty(PerspectiveCamera.prototype, "far", {
27
+ get: function () {
28
+ return this._far;
29
+ },
30
+ set: function (val) {
31
+ const changed = val !== this._far;
32
+ this._far = val;
33
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
34
+ }
35
+ });
@@ -16,4 +16,5 @@ export function patchLayers() {
16
16
  if(this[$customVisibilityFlag] === false) return false;
17
17
  return origTest.call(this, layer);
18
18
  };
19
- }
19
+ }
20
+ patchLayers();
@@ -0,0 +1,2 @@
1
+ import "./Layers"
2
+ import "./Camera"
@@ -1,32 +1,54 @@
1
1
  import { OrbitControls } from "./OrbitControls";
2
- import { Camera } from "./Camera";
3
2
  import { addNewComponent } from "../engine/engine_components";
4
- import { Color, Object3D, Scene, Vector3 } from "three";
5
- import { ICamera, SourceIdentifier } from "../engine/engine_types";
6
- import { lookAtInverse } from "../engine/engine_three_utils";
3
+ import { Object3D } from "three";
4
+ import { ICamera } from "../engine/engine_types";
7
5
  import { RGBAColor } from "./js-extensions/RGBAColor";
8
6
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry";
9
-
7
+ import { getCameraController } from "../engine/engine_camera";
8
+ import { Camera } from "./Camera";
9
+ import { EngineElement } from "../engine/engine_element";
10
10
 
11
11
  ContextRegistry.registerCallback(ContextEvent.MissingCamera, (evt) => {
12
- createCameraWithOrbitControl(evt.context.scene, "unknown");
13
- });
12
+ const scene = evt.context.scene;
13
+ const srcId = "unknown";
14
14
 
15
+ const cameraObject = new Object3D();
16
+ scene.add(cameraObject);
15
17
 
16
- export function createCameraWithOrbitControl(scene: Scene, source: SourceIdentifier): ICamera {
17
- const srcId = source;
18
- const go = new Object3D();
19
- scene.add(go);
20
18
  const camInstance = new Camera();
21
- const cam = addNewComponent(go, camInstance, true) as ICamera
19
+ const cam = addNewComponent(cameraObject, camInstance, true) as ICamera
22
20
  cam.sourceId = srcId;
23
21
  cam.clearFlags = 2;
24
22
  cam.backgroundColor = new RGBAColor(0.5, 0.5, 0.5, 1);
25
- const orbit = addNewComponent(go, new OrbitControls(), false) as OrbitControls;
26
- orbit.sourceId = srcId;
27
- go.position.x = -2;
28
- go.position.y = 2;
29
- go.position.z = 2;
30
- lookAtInverse(go, new Vector3(0, 0, 0));
31
- return cam as Camera;
32
- }
23
+
24
+ cameraObject.position.x = -2;
25
+ cameraObject.position.y = 2;
26
+ cameraObject.position.z = 2;
27
+ return cam;
28
+ });
29
+
30
+ ContextRegistry.registerCallback(ContextEvent.ContextCreated, (evt) => {
31
+ if (!evt.context.mainCamera) return;
32
+
33
+ // check if the <needle-engine controls> is not set to false
34
+ const engineElement = evt.context.domElement as EngineElement
35
+
36
+ if (engineElement?.cameraControls === true) {
37
+
38
+ // Check if something else already acts as a camera controller
39
+ const existing = getCameraController(evt.context.mainCamera);
40
+ if (existing?.isCameraController === true) {
41
+ return;
42
+ }
43
+
44
+ const cam = evt.context.mainCameraComponent;
45
+ const cameraObject = cam?.gameObject;
46
+ if (cameraObject) {
47
+ const orbit = addNewComponent(cameraObject, new OrbitControls(), false) as OrbitControls;
48
+ orbit.sourceId = "unknown";
49
+ }
50
+ else {
51
+ console.warn("Missing camera object, can not add orbit controls")
52
+ }
53
+ }
54
+ })
@@ -95,7 +95,17 @@ export class Light extends Behaviour implements ILight {
95
95
  public innerSpotAngle: number = 1;
96
96
 
97
97
  @serializable(Color)
98
- public color: THREE.Color = new THREE.Color(0xffffff);
98
+ set color(val: Color) {
99
+ this._color = val;
100
+ if (this.light !== undefined) {
101
+ this.light.color = val;
102
+ }
103
+ }
104
+ get color(): Color {
105
+ if (this.light) return this.light.color;
106
+ return this._color;
107
+ }
108
+ public _color: THREE.Color = new THREE.Color(0xffffff);
99
109
 
100
110
  @serializable()
101
111
  set shadowNearPlane(val: number) {
@@ -242,13 +252,14 @@ export class Light extends Behaviour implements ILight {
242
252
 
243
253
  awake() {
244
254
  this.color = new THREE.Color(this.color ?? 0xffffff);
245
- if(debug) console.log(this.name, this);
255
+ if (debug) console.log(this.name, this);
246
256
  }
247
257
 
248
258
  onEnable(): void {
249
259
  this.createLight();
250
260
  if (this.isBaked) return;
251
261
  else if (this.light) {
262
+ this.light.visible = true;
252
263
  if (this.selfIsLight) {
253
264
  // nothing to do
254
265
  }
@@ -257,12 +268,14 @@ export class Light extends Behaviour implements ILight {
257
268
  }
258
269
  if (this.type === LightType.Directional)
259
270
  this.startCoroutine(this.updateMainLightRoutine(), FrameEvent.LateUpdate);
260
-
261
271
  this._webXRStartedListener = WebXR.addEventListener(WebXREvent.XRStarted, this.onWebXRStarted.bind(this));
262
272
  this._webXREndedListener = WebXR.addEventListener(WebXREvent.XRStopped, this.onWebXREnded.bind(this));
263
273
  }
264
274
 
265
275
  onDisable() {
276
+ if (this.light) {
277
+ this.light.visible = false;
278
+ }
266
279
  WebXR.removeEventListener(WebXREvent.XRStarted, this._webXRStartedListener);
267
280
  WebXR.removeEventListener(WebXREvent.XRStopped, this._webXREndedListener);
268
281
  }
@@ -6,17 +6,23 @@ import { RaycastOptions } from "../engine/engine_physics";
6
6
  import { serializable } from "../engine/engine_serialization_decorator";
7
7
  import { getParam, isMobileDevice } from "../engine/engine_utils";
8
8
 
9
- import { Box3, Object3D, PerspectiveCamera, Vector2, Vector3 } from "three";
9
+ import { Camera as ThreeCamera, Box3, Object3D, PerspectiveCamera, Vector2, Vector3 } from "three";
10
10
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls";
11
11
  import { EventSystem, EventSystemEvents } from "./ui/EventSystem";
12
- import { transformWithEsbuild } from "vite";
12
+ import { ICameraController } from "../engine/engine_types";
13
+ import { setCameraController } from "../engine/engine_camera";
13
14
 
14
15
  const freeCam = getParam("freecam");
15
16
 
16
17
  const disabledKeys = { LEFT: "", UP: "", RIGHT: "", BOTTOM: "" };
17
18
  let defaultKeys: any = undefined;
18
19
 
19
- export class OrbitControls extends Behaviour {
20
+ export class OrbitControls extends Behaviour implements ICameraController {
21
+
22
+ get isCameraController(): boolean {
23
+ return true;
24
+ }
25
+
20
26
  public get controls() {
21
27
  return this._controls;
22
28
  }
@@ -64,6 +70,7 @@ export class OrbitControls extends Behaviour {
64
70
 
65
71
  private _eventSystem?: EventSystem;
66
72
  private _afterHandleInputFn?: any;
73
+ private _camera : Camera | null = null;
67
74
 
68
75
  targetElement: HTMLElement | null = null;
69
76
 
@@ -98,8 +105,10 @@ export class OrbitControls extends Behaviour {
98
105
 
99
106
  onEnable() {
100
107
  this._enableTime = this.context.time.time;
101
- const camGo = GameObject.getComponent(this.gameObject, Camera);
102
- const cam = camGo?.cam;
108
+ const cameraComponent = GameObject.getComponent(this.gameObject, Camera);
109
+ this._camera = cameraComponent;
110
+ const cam = cameraComponent?.cam;
111
+ if (cam) setCameraController(cam, this, true);
103
112
  if (!this._controls) {
104
113
  console.assert(cam !== null && cam !== undefined, "Missing camera", this);
105
114
  if (cam)
@@ -153,6 +162,9 @@ export class OrbitControls extends Behaviour {
153
162
  }
154
163
 
155
164
  onDisable() {
165
+ if (this._camera?.cam) {
166
+ setCameraController(this._camera.cam, this, false);
167
+ }
156
168
  if (this._controls) {
157
169
  this._controls.enabled = false;
158
170
  this._controls.autoRotate = false;
@@ -160,7 +172,7 @@ export class OrbitControls extends Behaviour {
160
172
  }
161
173
  }
162
174
 
163
- private _shouldDisable : boolean = false;
175
+ private _shouldDisable: boolean = false;
164
176
  private afterHandleInput() {
165
177
  if (this._controls && this._eventSystem) {
166
178
  this._shouldDisable = this._eventSystem.hasActiveUI;
@@ -276,7 +276,7 @@ export class AnimationTrackHandler extends TrackHandler {
276
276
  const isInTimeRange = time >= model.start && time <= model.end;
277
277
  const postExtrapolation: Models.ClipExtrapolation = model.postExtrapolationMode;
278
278
  let isActive = isInTimeRange;
279
- if (!isActive && !didPostExtrapolate && model.end < time && model.postExtrapolationMode !== Models.ClipExtrapolation.None) {
279
+ if (!isActive && !didPostExtrapolate && model.end < time && postExtrapolation !== Models.ClipExtrapolation.None) {
280
280
  const nextClip = i < this.clips.length - 1 ? this.models[i + 1] : null;
281
281
  // use post extrapolate if its the last clip of the next clip has not yet started
282
282
  if (!nextClip || nextClip.start > time) {
@@ -284,6 +284,12 @@ export class AnimationTrackHandler extends TrackHandler {
284
284
  didPostExtrapolate = true;
285
285
  }
286
286
  }
287
+ const preExtrapolation: Models.ClipExtrapolation = model.preExtrapolationMode;
288
+ if (i == 0 && !isActive && !didPostExtrapolate && model.start > time && preExtrapolation !== Models.ClipExtrapolation.None) {
289
+ isActive = true;
290
+ if (preExtrapolation !== Models.ClipExtrapolation.Hold)
291
+ time += model.start;
292
+ }
287
293
  if (isActive) {
288
294
  // const clip = this.clips[i];
289
295
  let weight = 1;
@@ -316,9 +322,9 @@ export class AnimationTrackHandler extends TrackHandler {
316
322
  }
317
323
  }
318
324
 
319
- if(model.reversed === true) action.time = action.getClip().duration - t;
325
+ if (model.reversed === true) action.time = action.getClip().duration - t;
320
326
  else action.time = t;
321
-
327
+
322
328
  action.timeScale = 0;
323
329
  const effectiveWeight = weight * this.director.weight;
324
330
  action.weight = effectiveWeight;
@@ -53,6 +53,13 @@ export class BaseUIComponent extends Behaviour {
53
53
  // private _intermediate?: Object3D;
54
54
  protected _parentComponent?: BaseUIComponent | null = undefined;
55
55
 
56
+ __internalNewInstanceCreated() {
57
+ super.__internalNewInstanceCreated();
58
+ this.shadowComponent = null;
59
+ this._root = undefined;
60
+ this._parentComponent = undefined;
61
+ }
62
+
56
63
  onEnable() {
57
64
  super.onEnable();
58
65
  }
@@ -79,17 +86,7 @@ export class BaseUIComponent extends Behaviour {
79
86
  // this.gameObject.layers.set(2)
80
87
 
81
88
 
82
- // TODO: only traverse our own hierarchy, we can stop if we find another owner
83
- container.traverse(c => {
84
- if (c[$shadowDomOwner] === undefined) {
85
- c[$shadowDomOwner] = this;
86
- }
87
-
88
- // // this makes text not render anymore when enabled again
89
- // if (raycastTarget === false) {
90
- // c.layers.set(2);
91
- // }
92
- });
89
+ this.setShadowComponentOwner(container);
93
90
 
94
91
  let needsUpdate = false;
95
92
 
@@ -136,6 +133,19 @@ export class BaseUIComponent extends Behaviour {
136
133
  // })
137
134
  }
138
135
 
136
+ protected setShadowComponentOwner(current: Object3D | null | undefined) {
137
+ if (!current) return;
138
+ // TODO: only traverse our own hierarchy, we can stop if we find another owner
139
+ if (current[$shadowDomOwner] === undefined || current[$shadowDomOwner] === this) {
140
+ current[$shadowDomOwner] = this;
141
+ if (current.children) {
142
+ for (const ch of current.children) {
143
+ this.setShadowComponentOwner(ch);
144
+ }
145
+ }
146
+ }
147
+ }
148
+
139
149
  private traverseOwnedShadowComponents(current: Object3D, owner: any, callback: (obj: any) => void) {
140
150
  if (!current) return;
141
151
  if (current[$shadowDomOwner] === owner) {
@@ -6,6 +6,7 @@ import { RGBAColor } from "../js-extensions/RGBAColor";
6
6
  import { serializable } from "../../engine/engine_serialization_decorator";
7
7
  import { Animator } from "../Animator";
8
8
  import { getParam } from "../../engine/engine_utils";
9
+ import { showBalloonMessage } from "../../engine/debug";
9
10
 
10
11
  const debug = getParam("debugbutton");
11
12
 
@@ -102,7 +103,7 @@ export class Button extends Behaviour implements IPointerEventHandler {
102
103
 
103
104
  onPointerUp(_) {
104
105
  if (debug)
105
- console.log("Button Down", this.animationTriggers?.highlightedTrigger, this.animator);
106
+ console.warn("Button Up", this.animationTriggers?.highlightedTrigger, this.animator, this._isHovered);
106
107
  if (this.transition == Transition.Animation && this.animationTriggers && this.animator) {
107
108
  this.animator.SetTrigger(this._isHovered ? this.animationTriggers.highlightedTrigger : this.animationTriggers.normalTrigger);
108
109
  }
@@ -112,8 +113,10 @@ export class Button extends Behaviour implements IPointerEventHandler {
112
113
  }
113
114
 
114
115
  onPointerClick(_args: PointerEventData) {
115
- if (debug)
116
- console.trace("Button Click", this.onClick);
116
+ if (debug) {
117
+ console.warn("Button Click", this.onClick);
118
+ showBalloonMessage("CLICKED button " + this.name + " at " + this.context.time.frameCount);
119
+ }
117
120
  this.onClick?.invoke();
118
121
  }
119
122
 
@@ -153,6 +156,7 @@ export class Button extends Behaviour implements IPointerEventHandler {
153
156
  super.awake();
154
157
  if (debug)
155
158
  console.log(this);
159
+ this._isInit = false;
156
160
  this.init();
157
161
  }
158
162