@needle-tools/engine 3.2.14-alpha → 3.3.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 (101) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/needle-engine.js +42139 -35749
  3. package/dist/needle-engine.min.js +694 -516
  4. package/dist/needle-engine.umd.cjs +696 -518
  5. package/lib/engine/codegen/register_types.js +4 -2
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/engine_addressables.d.ts +3 -3
  8. package/lib/engine/engine_addressables.js +30 -9
  9. package/lib/engine/engine_addressables.js.map +1 -1
  10. package/lib/engine/engine_element.js +1 -1
  11. package/lib/engine/engine_element.js.map +1 -1
  12. package/lib/engine/engine_gameobject.d.ts +2 -1
  13. package/lib/engine/engine_gameobject.js +17 -0
  14. package/lib/engine/engine_gameobject.js.map +1 -1
  15. package/lib/engine/engine_input.js +10 -0
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_license.js +11 -24
  18. package/lib/engine/engine_license.js.map +1 -1
  19. package/lib/engine/engine_math.d.ts +4 -0
  20. package/lib/engine/engine_math.js +6 -0
  21. package/lib/engine/engine_math.js.map +1 -1
  22. package/lib/engine-components/AnimatorController.js +7 -2
  23. package/lib/engine-components/AnimatorController.js.map +1 -1
  24. package/lib/engine-components/OrbitControls.js +13 -4
  25. package/lib/engine-components/OrbitControls.js.map +1 -1
  26. package/lib/engine-components/SceneSwitcher.d.ts +1 -1
  27. package/lib/engine-components/SceneSwitcher.js +25 -1
  28. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  29. package/lib/engine-components/TransformGizmo.d.ts +8 -4
  30. package/lib/engine-components/TransformGizmo.js +62 -63
  31. package/lib/engine-components/TransformGizmo.js.map +1 -1
  32. package/lib/engine-components/codegen/components.d.ts +2 -1
  33. package/lib/engine-components/codegen/components.js +2 -1
  34. package/lib/engine-components/codegen/components.js.map +1 -1
  35. package/lib/engine-components/export/usdz/USDZExporter.d.ts +1 -0
  36. package/lib/engine-components/export/usdz/USDZExporter.js +10 -2
  37. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  38. package/lib/engine-components/ui/Button.js +9 -5
  39. package/lib/engine-components/ui/Button.js.map +1 -1
  40. package/lib/engine-components/ui/Canvas.d.ts +13 -6
  41. package/lib/engine-components/ui/Canvas.js +101 -37
  42. package/lib/engine-components/ui/Canvas.js.map +1 -1
  43. package/lib/engine-components/ui/EventSystem.d.ts +6 -0
  44. package/lib/engine-components/ui/EventSystem.js +4 -4
  45. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  46. package/lib/engine-components/ui/Graphic.d.ts +5 -2
  47. package/lib/engine-components/ui/Graphic.js +38 -7
  48. package/lib/engine-components/ui/Graphic.js.map +1 -1
  49. package/lib/engine-components/ui/Image.d.ts +1 -0
  50. package/lib/engine-components/ui/Image.js +10 -1
  51. package/lib/engine-components/ui/Image.js.map +1 -1
  52. package/lib/engine-components/ui/InputField.d.ts +1 -0
  53. package/lib/engine-components/ui/InputField.js +8 -0
  54. package/lib/engine-components/ui/InputField.js.map +1 -1
  55. package/lib/engine-components/ui/Interfaces.d.ts +8 -0
  56. package/lib/engine-components/ui/Outline.d.ts +7 -0
  57. package/lib/engine-components/ui/Outline.js +21 -0
  58. package/lib/engine-components/ui/Outline.js.map +1 -0
  59. package/lib/engine-components/ui/RectTransform.d.ts +29 -11
  60. package/lib/engine-components/ui/RectTransform.js +178 -46
  61. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  62. package/lib/engine-components/ui/Text.d.ts +13 -10
  63. package/lib/engine-components/ui/Text.js +177 -246
  64. package/lib/engine-components/ui/Text.js.map +1 -1
  65. package/lib/engine-components/utils/LookAt.d.ts +7 -0
  66. package/lib/engine-components/utils/LookAt.js +29 -0
  67. package/lib/engine-components/utils/LookAt.js.map +1 -0
  68. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +8 -0
  69. package/lib/engine-components/webxr/WebXRImageTracking.js +79 -3
  70. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  71. package/lib/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +2 -2
  73. package/src/engine/codegen/register_types.js +4 -2
  74. package/src/engine/engine_addressables.ts +28 -10
  75. package/src/engine/engine_element.ts +1 -1
  76. package/src/engine/engine_gameobject.ts +18 -1
  77. package/src/engine/engine_input.ts +11 -0
  78. package/src/engine/engine_license.ts +12 -25
  79. package/src/engine/engine_math.ts +10 -0
  80. package/src/engine-components/AnimatorController.ts +7 -1
  81. package/src/engine-components/OrbitControls.ts +14 -6
  82. package/src/engine-components/SceneSwitcher.ts +28 -3
  83. package/src/engine-components/TransformGizmo.ts +64 -70
  84. package/src/engine-components/codegen/components.ts +2 -1
  85. package/src/engine-components/export/usdz/USDZExporter.ts +7 -2
  86. package/src/engine-components/ui/Button.ts +14 -9
  87. package/src/engine-components/ui/Canvas.ts +104 -40
  88. package/src/engine-components/ui/EventSystem.ts +16 -9
  89. package/src/engine-components/ui/Graphic.ts +44 -8
  90. package/src/engine-components/ui/Image.ts +10 -1
  91. package/src/engine-components/ui/InputField.ts +9 -1
  92. package/src/engine-components/ui/Interfaces.ts +12 -0
  93. package/src/engine-components/ui/Outline.ts +13 -0
  94. package/src/engine-components/ui/RectTransform.ts +203 -60
  95. package/src/engine-components/ui/Text.ts +284 -265
  96. package/src/engine-components/utils/LookAt.ts +21 -0
  97. package/src/engine-components/webxr/WebXRImageTracking.ts +85 -10
  98. package/lib/engine-components/ui/Keyboard.d.ts +0 -31
  99. package/lib/engine-components/ui/Keyboard.js +0 -178
  100. package/lib/engine-components/ui/Keyboard.js.map +0 -1
  101. package/src/engine-components/ui/Keyboard.ts +0 -204
@@ -12,6 +12,7 @@ import { serializable } from "../../../engine/engine_serialization";
12
12
  import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/debug";
13
13
  import { Context } from "../../../engine/engine_setup";
14
14
  import { WebARSessionRoot } from "../../webxr/WebARSessionRoot";
15
+ import { hasProLicense } from "../../../engine/engine_license";
15
16
 
16
17
  const debug = getParam("debugusdz");
17
18
 
@@ -36,6 +37,9 @@ export class USDZExporter extends Behaviour {
36
37
  @serializable()
37
38
  autoExportAnimations: boolean = false;
38
39
 
40
+ @serializable()
41
+ exportFileName?: string;
42
+
39
43
  @serializable(QuickLookOverlay)
40
44
  overlay?: QuickLookOverlay;
41
45
 
@@ -130,8 +134,9 @@ export class USDZExporter extends Behaviour {
130
134
  const eventArgs = { self: this, exporter: exporter, extensions: extensions, object: this.objectToExport };
131
135
  this.dispatchEvent(new CustomEvent("before-export", { detail: eventArgs }))
132
136
 
133
- let name = "needle";
137
+ let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
134
138
  if (debug) name += "-" + getFormattedDate();
139
+ else if (!hasProLicense()) name = name + " - Made with Needle";
135
140
 
136
141
  //@ts-ignore
137
142
  exporter.debug = debug;
@@ -151,7 +156,7 @@ export class USDZExporter extends Behaviour {
151
156
 
152
157
  // see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
153
158
  const overlay = this.buildQuicklookOverlay();
154
- console.log(overlay);
159
+ if(debug) console.log(overlay);
155
160
  const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
156
161
  const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
157
162
  const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
@@ -69,9 +69,9 @@ export class Button extends Behaviour implements IPointerEventHandler {
69
69
  console.log("Button Enter", this.animationTriggers?.highlightedTrigger, this.animator);
70
70
  this._isHovered = true;
71
71
  if (this.transition == Transition.Animation && this.animationTriggers && this.animator) {
72
- this.animator.SetTrigger(this.animationTriggers.highlightedTrigger);
72
+ this.animator.setTrigger(this.animationTriggers.highlightedTrigger);
73
73
  }
74
- else if(this.transition === Transition.ColorTint && this.colors) {
74
+ else if (this.transition === Transition.ColorTint && this.colors) {
75
75
  this._image?.setState("hovered");
76
76
  }
77
77
  this.context.input.setCursorPointer();
@@ -82,9 +82,9 @@ export class Button extends Behaviour implements IPointerEventHandler {
82
82
  console.log("Button Exit", this.animationTriggers?.highlightedTrigger, this.animator);
83
83
  this._isHovered = false;
84
84
  if (this.transition == Transition.Animation && this.animationTriggers && this.animator) {
85
- this.animator.SetTrigger(this.animationTriggers.normalTrigger);
85
+ this.animator.setTrigger(this.animationTriggers.normalTrigger);
86
86
  }
87
- else if(this.transition === Transition.ColorTint && this.colors) {
87
+ else if (this.transition === Transition.ColorTint && this.colors) {
88
88
  this._image?.setState("normal");
89
89
  }
90
90
  this.context.input.setCursorNormal();
@@ -94,9 +94,9 @@ export class Button extends Behaviour implements IPointerEventHandler {
94
94
  if (debug)
95
95
  console.log("Button Down", this.animationTriggers?.highlightedTrigger, this.animator);
96
96
  if (this.transition == Transition.Animation && this.animationTriggers && this.animator) {
97
- this.animator.SetTrigger(this.animationTriggers.pressedTrigger);
97
+ this.animator.setTrigger(this.animationTriggers.pressedTrigger);
98
98
  }
99
- else if(this.transition === Transition.ColorTint && this.colors) {
99
+ else if (this.transition === Transition.ColorTint && this.colors) {
100
100
  this._image?.setState("pressed");
101
101
  }
102
102
  }
@@ -105,9 +105,9 @@ export class Button extends Behaviour implements IPointerEventHandler {
105
105
  if (debug)
106
106
  console.warn("Button Up", this.animationTriggers?.highlightedTrigger, this.animator, this._isHovered);
107
107
  if (this.transition == Transition.Animation && this.animationTriggers && this.animator) {
108
- this.animator.SetTrigger(this._isHovered ? this.animationTriggers.highlightedTrigger : this.animationTriggers.normalTrigger);
108
+ this.animator.setTrigger(this._isHovered ? this.animationTriggers.highlightedTrigger : this.animationTriggers.normalTrigger);
109
109
  }
110
- else if(this.transition === Transition.ColorTint && this.colors) {
110
+ else if (this.transition === Transition.ColorTint && this.colors) {
111
111
  this._image?.setState(this._isHovered ? "hovered" : "normal");
112
112
  }
113
113
  }
@@ -197,6 +197,10 @@ export class Button extends Behaviour implements IPointerEventHandler {
197
197
  private stateSetup(image: Image) {
198
198
  image.setInteractable(this.interactable);
199
199
 
200
+ // @marwie : If this piece of code could be moved to the SimpleStateBehavior instanciation location,
201
+ // Its setup could be eased :
202
+ // @see https://github.com/felixmariotto/three-mesh-ui/blob/7.1.x/examples/ex__keyboard.js#L407
203
+
200
204
  const normal = this.getFinalColor(image.color, this.colors?.normalColor);
201
205
  const normalState = {
202
206
  state: "normal",
@@ -242,7 +246,8 @@ export class Button extends Behaviour implements IPointerEventHandler {
242
246
  state: "disabled",
243
247
  attributes: {
244
248
  backgroundColor: disabled,
245
- backgroundOpacity: disabled.alpha,
249
+ // @marwie, this disabled alpha property doesn't seem to have the opacity requested in unity
250
+ backgroundOpacity: disabled.alpha
246
251
  }
247
252
  };
248
253
  image.setupState(disabledState);
@@ -2,13 +2,13 @@ import { onChange, updateRenderSettings as updateRenderSettingsRecursive } from
2
2
  import { serializable } from "../../engine/engine_serialization_decorator";
3
3
  import { FrameEvent } from "../../engine/engine_setup";
4
4
  import { BaseUIComponent, UIRootComponent } from "./BaseUIComponent";
5
- import { Mathf } from "../../engine/engine_math";
6
- import * as THREE from "three";
7
- import { getComponentsInChildren } from "../../engine/engine_components";
8
- import { IComponent } from "../../engine/engine_types";
9
5
  import { GameObject } from "../Component";
10
- import { showBalloonMessage, showBalloonWarning } from "../../engine/debug";
11
6
  import { Object3D } from "three";
7
+ import { RectTransform } from "./RectTransform";
8
+ import { ICanvas } from "./Interfaces";
9
+ import { Camera } from "../Camera";
10
+ import { EventSystem } from "./EventSystem";
11
+ import * as ThreeMeshUI from 'three-mesh-ui'
12
12
 
13
13
  export enum RenderMode {
14
14
  ScreenSpaceOverlay = 0,
@@ -17,7 +17,11 @@ export enum RenderMode {
17
17
  Undefined = -1,
18
18
  }
19
19
 
20
- export class Canvas extends UIRootComponent {
20
+ export class Canvas extends UIRootComponent implements ICanvas {
21
+
22
+ get screenspace(): any {
23
+ return this.renderMode !== RenderMode.WorldSpace;
24
+ }
21
25
 
22
26
  @serializable()
23
27
  set renderOnTop(val: boolean) {
@@ -27,8 +31,15 @@ export class Canvas extends UIRootComponent {
27
31
  this._renderOnTop = val;
28
32
  this.onRenderSettingsChanged();
29
33
  }
30
- get renderOnTop() { return this._renderOnTop; }
31
- private _renderOnTop: boolean = false;
34
+ get renderOnTop() {
35
+ if (this._renderOnTop !== undefined) return this._renderOnTop;
36
+ if (this.screenspace) {
37
+ // Render ScreenSpaceOverlay always on top
38
+ if (this._renderMode === RenderMode.ScreenSpaceOverlay) return true;
39
+ }
40
+ return false;
41
+ }
42
+ private _renderOnTop: boolean | undefined;
32
43
 
33
44
  @serializable()
34
45
  set depthWrite(val: boolean) {
@@ -99,7 +110,14 @@ export class Canvas extends UIRootComponent {
99
110
  this._scaleFactor = val;
100
111
  }
101
112
 
113
+ @serializable(Camera)
114
+ worldCamera?: Camera;
115
+
116
+ @serializable()
117
+ planeDistance: number = -1;
118
+
102
119
  awake() {
120
+ //@ts-ignore
103
121
  this.shadowComponent = this.gameObject;
104
122
  super.awake();
105
123
  }
@@ -108,34 +126,59 @@ export class Canvas extends UIRootComponent {
108
126
  super.onEnable();
109
127
  this._updateRenderSettingsRoutine = undefined;
110
128
  this.onRenderSettingsChanged();
129
+ document.addEventListener("resize", this._boundRenderSettingsChanged);
130
+ // We want to run AFTER all regular onBeforeRender callbacks
131
+ this.context.pre_render_callbacks.push(this.onBeforeRenderRoutine);
132
+ this.context.post_render_callbacks.push(this.onAfterRenderRoutine);
133
+ }
134
+
135
+ onDisable(): void {
136
+ super.onDisable();
137
+ document.removeEventListener("resize", this._boundRenderSettingsChanged);
138
+ // Remove callbacks
139
+ const preRenderIndex = this.context.pre_render_callbacks.indexOf(this.onBeforeRenderRoutine);
140
+ if (preRenderIndex !== -1) {
141
+ this.context.pre_render_callbacks.splice(preRenderIndex, 1);
142
+ }
143
+ const postRenderIndex = this.context.post_render_callbacks.indexOf(this.onAfterRenderRoutine);
144
+ if (postRenderIndex !== -1) {
145
+ this.context.post_render_callbacks.splice(postRenderIndex, 1);
146
+ }
111
147
  }
112
148
 
113
- private previousAspect: number = -1;
149
+ private _boundRenderSettingsChanged = this.onRenderSettingsChanged.bind(this);
150
+
114
151
  private previousParent: Object3D | null = null;
115
152
 
116
- onBeforeRender() {
117
- if (this.isScreenSpace && this.context.mainCameraComponent && this.context.mainCameraComponent.aspect !== this.previousAspect) {
118
- this.previousAspect = this.context.mainCameraComponent.aspect;
119
- this.updateRenderMode();
120
- }
121
- else if(this.renderOnTop){
153
+ onBeforeRenderRoutine = () => {
154
+ if (this.renderOnTop) {
122
155
  // This is just a test but in reality it should be combined with all world canvases with render on top in one render pass
123
156
  this.previousParent = this.gameObject.parent;
124
157
  this.gameObject.removeFromParent();
125
158
  }
159
+ else {
160
+ this.onUpdateRenderMode();
161
+ // TODO: we might need to optimize this. This is here to make sure the TMUI text clipping matrices are correct. Ideally the text does use onBeforeRender and apply the clipping matrix there so we dont have to force update all the matrices here
162
+ this.shadowComponent?.updateMatrixWorld(true);
163
+ this.shadowComponent?.updateWorldMatrix(true, true);
164
+ EventSystem.ensureUpdateMeshUI(ThreeMeshUI, this.context);
165
+ }
126
166
  }
127
167
 
128
- onAfterRender(): void {
168
+ onAfterRenderRoutine = () => {
129
169
  if (this.renderOnTop && this.previousParent && this.context.mainCamera) {
130
170
  this.previousParent.add(this.gameObject);
131
171
  this.context.renderer.autoClear = false;
132
172
  this.context.renderer.clearDepth();
173
+ this.onUpdateRenderMode(true);
174
+ this.shadowComponent?.updateMatrixWorld(true);
175
+ EventSystem.ensureUpdateMeshUI(ThreeMeshUI, this.context);
133
176
  this.context.renderer.render(this.gameObject, this.context.mainCamera);
134
177
  this.context.renderer.autoClear = true;
135
178
  }
136
179
  }
137
180
 
138
- applyRenderSettings(){
181
+ applyRenderSettings() {
139
182
  this.onRenderSettingsChanged();
140
183
  }
141
184
 
@@ -149,7 +192,7 @@ export class Canvas extends UIRootComponent {
149
192
  yield;
150
193
  this._updateRenderSettingsRoutine = undefined;
151
194
  if (this.shadowComponent) {
152
- this.updateRenderMode();
195
+ this.onUpdateRenderMode();
153
196
  // this.onWillUpdateRenderSettings();
154
197
  updateRenderSettingsRecursive(this.shadowComponent, this);
155
198
  for (const ch of GameObject.getComponentsInChildren(this.gameObject, BaseUIComponent)) {
@@ -159,44 +202,65 @@ export class Canvas extends UIRootComponent {
159
202
  }
160
203
 
161
204
  private _activeRenderMode: RenderMode = -1;
205
+ private _lastWidth: number = -1;
206
+ private _lastHeight: number = -1;
162
207
 
163
- private get isScreenSpace(): boolean {
164
- return this._activeRenderMode === RenderMode.ScreenSpaceCamera || this._activeRenderMode === RenderMode.ScreenSpaceOverlay;
165
- }
208
+ private onUpdateRenderMode(force: boolean = false) {
209
+ if (!force) {
210
+ if (this._renderMode === this._activeRenderMode && this._lastWidth === this.context.domWidth && this._lastHeight === this.context.domHeight) {
211
+ return;
212
+ }
213
+ }
214
+ this._activeRenderMode = this._renderMode;
215
+ let camera = this.context.mainCameraComponent;
216
+ let planeDistance: number = camera?.farClipPlane ?? 100;
217
+ if (this._renderMode === RenderMode.ScreenSpaceCamera) {
218
+ if (this.worldCamera)
219
+ camera = this.worldCamera as Camera;
220
+ if (this.planeDistance > 0)
221
+ planeDistance = this.planeDistance;
222
+ }
166
223
 
167
- private updateRenderMode() {
168
- if (this.renderMode === this._activeRenderMode) return;
169
- switch (this.renderMode) {
224
+ switch (this._renderMode) {
170
225
  case RenderMode.ScreenSpaceOverlay:
171
226
  case RenderMode.ScreenSpaceCamera:
172
- showBalloonWarning("Screenspace Canvas is not supported yet. Please use worldspace");
173
- const camera = this.context.mainCameraComponent;
227
+ this._lastWidth = this.context.domWidth;
228
+ this._lastHeight = this.context.domHeight;
229
+
230
+ // showBalloonWarning("Screenspace Canvas is not supported yet. Please use worldspace");
174
231
  if (!camera) return;
232
+
175
233
  const canvas = this.gameObject;
176
234
  const camObj = camera.gameObject;
177
235
  camObj?.add(canvas);
178
- const pos = camera.farClipPlane;
236
+ // we move the plane SLIGHTLY closer to be sure not to cull the canvas
237
+ const plane = planeDistance - .1;
179
238
  canvas.position.x = 0;
180
239
  canvas.position.y = 0;
181
- canvas.position.z = -pos;
240
+ canvas.position.z = -plane;
241
+ canvas.quaternion.identity();
182
242
 
183
- // console.log(this.shadowComponent)
243
+ const rect = this.gameObject.getComponent(RectTransform)!;
184
244
 
185
- if (camera.fieldOfView) {
186
- const w = Math.tan(Mathf.toRadians(camera.fieldOfView) * pos) * (camera.aspect * 1.333333);
187
- const h = w * (this.context.domHeight / this.context.domWidth);
188
- canvas.scale.x = -w;
189
- canvas.scale.y = h;
245
+ const vFOV = camera.fieldOfView! * Math.PI / 180;
246
+ const h = 2 * Math.tan(vFOV / 2) * Math.abs(plane);
247
+ canvas.scale.x = h / this.context.domHeight;
248
+ canvas.scale.y = h / this.context.domHeight;
249
+ // Set scale.z, otherwise small offsets in screenspace mode have different visual results based on export scale and other settings
250
+ canvas.scale.z = .01;
251
+ rect.sizeDelta.x = this.context.domWidth;
252
+ rect.sizeDelta.y = this.context.domHeight;
253
+ rect?.markDirty();
190
254
 
191
- }
192
- // const rects = this.gameObject.getComponentsInChildren(BaseUIComponent);
193
- // for (const rect of rects) {
194
- // rect.set({ width: this.context.domWidth * .5, height: 100 })
195
- // }
255
+
256
+ // this.context.scene.add(this.gameObject)
257
+ // this.gameObject.scale.multiplyScalar(.01);
258
+ // this.gameObject.position.set(0,0,0);
196
259
 
197
260
  break;
198
261
  case RenderMode.WorldSpace:
199
-
262
+ this._lastWidth = -1;
263
+ this._lastHeight = -1;
200
264
  break;
201
265
  }
202
266
  }
@@ -7,7 +7,7 @@ import { Context } from "../../engine/engine_setup";
7
7
  import { OrbitControls } from "../OrbitControls";
8
8
  import { IPointerEventHandler, PointerEventData } from "./PointerEvents";
9
9
  import { ObjectRaycaster, Raycaster } from "./Raycaster";
10
- import { InputEvents } from "../../engine/engine_input";
10
+ import { InputEvents, PointerEventArgs } from "../../engine/engine_input";
11
11
  import { Object3D } from "three";
12
12
  import { ICanvasGroup, IGraphic } from "./Interfaces";
13
13
  import { getParam } from "../../engine/engine_utils";
@@ -22,6 +22,12 @@ export enum EventSystemEvents {
22
22
  AfterHandleInput = "AfterHandleInput",
23
23
  }
24
24
 
25
+ export declare type AfterHandleInputEvent = {
26
+ sender: EventSystem,
27
+ args: PointerEventData,
28
+ hasActiveUI: boolean
29
+ }
30
+
25
31
  export class EventSystem extends Behaviour {
26
32
 
27
33
 
@@ -249,16 +255,16 @@ export class EventSystem extends Behaviour {
249
255
  if (!hits) return;
250
256
  this.lastPointerEvent = args;
251
257
 
252
- const evt = {
258
+ const evt : AfterHandleInputEvent = {
253
259
  sender: this,
254
260
  args: args,
255
261
  hasActiveUI: this.currentActiveMeshUIComponents.length > 0,
256
262
  }
257
- if(debug && args.isClicked)
258
- showBalloonMessage("EventSystem: " + args.pointerId + " - " + this.context.time.frame + " - Up:" + args.isUp + ", Down:" + args.isDown)
263
+ if (debug && args.isClicked)
264
+ showBalloonMessage("EventSystem: " + args.pointerId + " - " + this.context.time.frame + " - Up:" + args.isUp + ", Down:" + args.isDown)
259
265
  this.dispatchEvent(new CustomEvent(EventSystemEvents.BeforeHandleInput, { detail: evt }))
260
266
  this.handleIntersections(hits, args);
261
- this.dispatchEvent(new CustomEvent(EventSystemEvents.AfterHandleInput, { detail: evt }))
267
+ this.dispatchEvent(new CustomEvent<AfterHandleInputEvent>(EventSystemEvents.AfterHandleInput, { detail: evt }))
262
268
  }
263
269
 
264
270
  private _tempComponentsArray: Behaviour[] = [];
@@ -518,8 +524,8 @@ class MeshUIHelper {
518
524
  if (currentFrame === lu.frame) return;
519
525
  lu.frame = currentFrame;
520
526
  let shouldUpdate = this.needsUpdate || currentFrame < 1;
521
- if(lu.nextUpdate === context.time.frameCount) shouldUpdate = true;
522
- if(this.needsUpdate) lu.nextUpdate = currentFrame + 3;
527
+ if (lu.nextUpdate === context.time.frameCount) shouldUpdate = true;
528
+ // if(this.needsUpdate) lu.nextUpdate = currentFrame + 3;
523
529
  if (shouldUpdate) {
524
530
  if (debug)
525
531
  console.log("Update threemeshui");
@@ -564,8 +570,9 @@ class MeshUIHelper {
564
570
  static findBlockInParent(elem: any): ThreeMeshUI.Block | null {
565
571
  if (!elem) return null;
566
572
  if (elem.isBlock) {
567
- if (Object.keys(elem.states).length > 0)
568
- return elem;
573
+ // @TODO : Replace states managements
574
+ // if (Object.keys(elem.states).length > 0)
575
+ return elem;
569
576
  }
570
577
  return this.findBlockInParent(elem.parent);
571
578
  }
@@ -1,4 +1,4 @@
1
- import { IGraphic } from './Interfaces';
1
+ import { IGraphic, IRectTransformChangedReceiver } from './Interfaces';
2
2
  import * as ThreeMeshUI from 'three-mesh-ui'
3
3
  import { RGBAColor } from "../js-extensions/RGBAColor"
4
4
  import { BaseUIComponent } from "./BaseUIComponent";
@@ -7,9 +7,15 @@ import { Color, LinearEncoding, sRGBEncoding, Texture } from 'three';
7
7
  import { RectTransform } from './RectTransform';
8
8
  import { onChange, scheduleAction } from "./Utils"
9
9
  import { GameObject } from '../Component';
10
+ import SimpleStateBehavior from "three-mesh-ui/examples/behaviors/states/SimpleStateBehavior"
11
+ import { Outline } from './Outline';
10
12
 
13
+ const _colorStateObject: { backgroundColor: Color, backgroundOpacity: number } = {
14
+ backgroundColor: new Color(1, 1, 1),
15
+ backgroundOpacity: 1,
16
+ };
11
17
 
12
- export class Graphic extends BaseUIComponent implements IGraphic {
18
+ export class Graphic extends BaseUIComponent implements IGraphic, IRectTransformChangedReceiver {
13
19
 
14
20
  get isGraphic() { return true; }
15
21
 
@@ -26,9 +32,11 @@ export class Graphic extends BaseUIComponent implements IGraphic {
26
32
  }
27
33
  this._color.copy(col);
28
34
  }
35
+
29
36
  protected onColorChanged() {
30
- const newcolor = this.color;
31
- this.setOptions({ backgroundColor: newcolor, backgroundOpacity: newcolor.alpha, borderOpacity: newcolor.alpha });
37
+ _colorStateObject.backgroundColor = this._color;
38
+ _colorStateObject.backgroundOpacity = this._color.alpha;
39
+ this.uiObject?.set(_colorStateObject);
32
40
  }
33
41
 
34
42
  // used via animations
@@ -44,6 +52,9 @@ export class Graphic extends BaseUIComponent implements IGraphic {
44
52
 
45
53
 
46
54
  private _rect: RectTransform | null = null;
55
+
56
+ private _stateManager : SimpleStateBehavior | null = null;
57
+
47
58
  protected get rectTransform(): RectTransform {
48
59
  if (!this._rect) {
49
60
  this._rect = GameObject.getComponent(this.gameObject, RectTransform);
@@ -51,6 +62,11 @@ export class Graphic extends BaseUIComponent implements IGraphic {
51
62
  return this._rect!;
52
63
  }
53
64
 
65
+ onParentRectTransformChanged() {
66
+ this.uiObject?.set({ width: this.rectTransform.width, height:this.rectTransform.height })
67
+ this.markDirty();
68
+ }
69
+
54
70
  __internalNewInstanceCreated(): void {
55
71
  super.__internalNewInstanceCreated();
56
72
  this._rect = null;
@@ -63,14 +79,21 @@ export class Graphic extends BaseUIComponent implements IGraphic {
63
79
  if (this.uiObject) {
64
80
  //@ts-ignore
65
81
  this.uiObject.setState(state);
82
+ this?.markDirty();
66
83
  }
67
84
  }
68
85
 
69
86
  setupState(state: object) {
70
87
  this.makePanel();
71
88
  if (this.uiObject) {
89
+
90
+ // @marwie : v7.x now have a concurrent state management in core mimicking html/css
91
+ // ie : (::firstChild::hover::disabled) where firstchild, hover and disabled are all on different channels
92
+ // In order to keep needle Raycaster and EventSystem intact, I added in v7 a SimpleStateBehavior, which acts as previously
93
+
94
+ if( !this._stateManager ) this._stateManager = new SimpleStateBehavior(this.uiObject);
72
95
  //@ts-ignore
73
- this.uiObject.setupState(state);
96
+ this.uiObject.setupState(state.state, state.attributes);
74
97
  }
75
98
  }
76
99
 
@@ -79,8 +102,8 @@ export class Graphic extends BaseUIComponent implements IGraphic {
79
102
  if (this.uiObject) {
80
103
  //@ts-ignore
81
104
  this.uiObject.set(opts);
82
- if (opts["backgroundColor"] !== undefined || opts["backgroundOpacity"] !== undefined)
83
- this.uiObject["updateBackgroundMaterial"]?.call(this.uiObject);
105
+ // if (opts["backgroundColor"] !== undefined || opts["backgroundOpacity"] !== undefined)
106
+ // this.uiObject["updateBackgroundMaterial"]?.call(this.uiObject);
84
107
  }
85
108
  }
86
109
 
@@ -119,6 +142,7 @@ export class Graphic extends BaseUIComponent implements IGraphic {
119
142
  offset: 1, // without a tiny offset we get z fighting
120
143
  };
121
144
  this.onBeforeCreate(opts);
145
+ this.applyEffects(opts);
122
146
  this.onCreate(opts);
123
147
  this.controlsChildLayout = false;
124
148
  this._currentlyCreatingPanel = false;
@@ -133,6 +157,17 @@ export class Graphic extends BaseUIComponent implements IGraphic {
133
157
  }
134
158
  protected onAfterCreated() { }
135
159
 
160
+ private applyEffects(opts){
161
+ const outline = this.gameObject?.getComponent(Outline);
162
+ if (outline) {
163
+ if (outline.effectDistance) opts.borderWidth = Math.max(Math.abs(outline.effectDistance.x), Math.abs(outline.effectDistance.y));
164
+ if (outline.effectColor) {
165
+ opts.borderColor = outline.effectColor;
166
+ opts.borderOpacity = outline.effectColor.alpha;
167
+ }
168
+ }
169
+ }
170
+
136
171
  /** used internally to ensure textures assigned to UI use linear encoding */
137
172
  static textureCache: Map<Texture, Texture> = new Map();
138
173
 
@@ -151,13 +186,14 @@ export class Graphic extends BaseUIComponent implements IGraphic {
151
186
  tex = clone;
152
187
  }
153
188
  }
154
- this.setOptions({ backgroundTexture: tex, borderRadius: 0, backgroundOpacity: this.color.alpha, backgroundSize: "stretch" });
189
+ this.setOptions({ backgroundImage: tex, borderRadius: 0, backgroundOpacity: this.color.alpha, backgroundSize: "stretch" });
155
190
  }
156
191
  }
157
192
 
158
193
  protected onAfterAddedToScene(): void {
159
194
  super.onAfterAddedToScene();
160
195
  if (this.shadowComponent) {
196
+ // @TODO: I think we dont even need this anymore and this leads to the offset being applied twice
161
197
  //@ts-ignore
162
198
  this.shadowComponent.offset = this.shadowComponent.position.z;
163
199
 
@@ -24,11 +24,15 @@ export class Image extends MaskableGraphic {
24
24
 
25
25
  private _sprite?: Sprite;
26
26
 
27
+ @serializable()
28
+ private pixelsPerUnitMultiplier: number = 1;
29
+
27
30
  private isBuiltinSprite() {
28
31
  switch (this.sprite?.texture?.name) {
29
32
  case "InputFieldBackground":
30
33
  case "UISprite":
31
34
  case "Background":
35
+ case "Knob":
32
36
  return true;
33
37
  }
34
38
  // this is a hack/workaround for production builds where the name of the sprite is missing
@@ -39,12 +43,17 @@ export class Image extends MaskableGraphic {
39
43
  }
40
44
 
41
45
  protected onBeforeCreate(opts: any): void {
46
+ super.onBeforeCreate(opts);
42
47
  if (this.isBuiltinSprite()) {
43
- opts.borderRadius = 5;
48
+ opts.borderRadius = 5 / this.pixelsPerUnitMultiplier;
49
+ if(this.sprite?.texture?.name === "Knob") {
50
+ opts.borderRadius = 999;
51
+ }
44
52
  opts.borderColor = new Color(.4, .4, .4);
45
53
  opts.borderOpacity = this.color.alpha;
46
54
  opts.borderWidth = .3;
47
55
  }
56
+
48
57
  }
49
58
 
50
59
  protected onAfterCreated(): void {
@@ -13,7 +13,7 @@ const debug = getParam("debuginputfield");
13
13
 
14
14
  export class InputField extends Behaviour implements IPointerEventHandler {
15
15
 
16
- get text() : string {
16
+ get text(): string {
17
17
  return this.textComponent?.text ?? "";
18
18
  }
19
19
 
@@ -148,6 +148,14 @@ export class InputField extends Behaviour implements IPointerEventHandler {
148
148
  this.onEndEdit?.invoke(InputField.htmlField.value);
149
149
  }
150
150
 
151
+ // @Marwie, I can provide this fix. But the issue seems to comes from Raycaster+EventSystem
152
+ // As we rollout InputField, and no others elements is behind raycast,
153
+ // ThreeMeshUI.update is not called.
154
+ update() {
155
+ if (InputField.active === this) {
156
+ this.textComponent?.markDirty();
157
+ }
158
+ }
151
159
 
152
160
  private onInput(evt: KeyboardEvent) {
153
161
  if (InputField.active !== this) return;
@@ -1,5 +1,9 @@
1
1
  import { IComponent } from "../../engine/engine_types";
2
2
 
3
+ export interface ICanvas {
4
+ get screenspace() : boolean;
5
+ }
6
+
3
7
  export interface ICanvasGroup {
4
8
  get isCanvasGroup() : boolean;
5
9
  blocksRaycasts: boolean;
@@ -9,4 +13,12 @@ export interface ICanvasGroup {
9
13
  export interface IGraphic extends IComponent {
10
14
  get isGraphic() : boolean;
11
15
  raycastTarget: boolean;
16
+ }
17
+
18
+ export interface IRectTransform extends IComponent {
19
+
20
+ }
21
+
22
+ export interface IRectTransformChangedReceiver {
23
+ onParentRectTransformChanged(comp : IRectTransform) : void;
12
24
  }
@@ -0,0 +1,13 @@
1
+ import { RGBAColor } from "../js-extensions";
2
+ import { serializable } from "../../engine/engine_serialization";
3
+ import { Behaviour } from "../Component";
4
+ import { Color, Vector2 } from "three"
5
+
6
+ export class Outline extends Behaviour {
7
+
8
+ @serializable(RGBAColor)
9
+ effectColor?: RGBAColor;
10
+
11
+ @serializable(Vector2)
12
+ effectDistance?: Vector2;
13
+ }