@needle-tools/engine 3.2.15-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 (90) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/needle-engine.js +42120 -35732
  3. package/dist/needle-engine.min.js +687 -495
  4. package/dist/needle-engine.umd.cjs +689 -497
  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_math.d.ts +4 -0
  18. package/lib/engine/engine_math.js +6 -0
  19. package/lib/engine/engine_math.js.map +1 -1
  20. package/lib/engine-components/AnimatorController.js +7 -2
  21. package/lib/engine-components/AnimatorController.js.map +1 -1
  22. package/lib/engine-components/OrbitControls.js +13 -4
  23. package/lib/engine-components/OrbitControls.js.map +1 -1
  24. package/lib/engine-components/TransformGizmo.d.ts +8 -4
  25. package/lib/engine-components/TransformGizmo.js +62 -63
  26. package/lib/engine-components/TransformGizmo.js.map +1 -1
  27. package/lib/engine-components/codegen/components.d.ts +2 -1
  28. package/lib/engine-components/codegen/components.js +2 -1
  29. package/lib/engine-components/codegen/components.js.map +1 -1
  30. package/lib/engine-components/ui/Button.js +9 -5
  31. package/lib/engine-components/ui/Button.js.map +1 -1
  32. package/lib/engine-components/ui/Canvas.d.ts +13 -6
  33. package/lib/engine-components/ui/Canvas.js +101 -37
  34. package/lib/engine-components/ui/Canvas.js.map +1 -1
  35. package/lib/engine-components/ui/EventSystem.d.ts +6 -0
  36. package/lib/engine-components/ui/EventSystem.js +4 -4
  37. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  38. package/lib/engine-components/ui/Graphic.d.ts +5 -2
  39. package/lib/engine-components/ui/Graphic.js +38 -7
  40. package/lib/engine-components/ui/Graphic.js.map +1 -1
  41. package/lib/engine-components/ui/Image.d.ts +1 -0
  42. package/lib/engine-components/ui/Image.js +10 -1
  43. package/lib/engine-components/ui/Image.js.map +1 -1
  44. package/lib/engine-components/ui/InputField.d.ts +1 -0
  45. package/lib/engine-components/ui/InputField.js +8 -0
  46. package/lib/engine-components/ui/InputField.js.map +1 -1
  47. package/lib/engine-components/ui/Interfaces.d.ts +8 -0
  48. package/lib/engine-components/ui/Outline.d.ts +7 -0
  49. package/lib/engine-components/ui/Outline.js +21 -0
  50. package/lib/engine-components/ui/Outline.js.map +1 -0
  51. package/lib/engine-components/ui/RectTransform.d.ts +29 -11
  52. package/lib/engine-components/ui/RectTransform.js +178 -46
  53. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  54. package/lib/engine-components/ui/Text.d.ts +13 -10
  55. package/lib/engine-components/ui/Text.js +177 -246
  56. package/lib/engine-components/ui/Text.js.map +1 -1
  57. package/lib/engine-components/utils/LookAt.d.ts +7 -0
  58. package/lib/engine-components/utils/LookAt.js +29 -0
  59. package/lib/engine-components/utils/LookAt.js.map +1 -0
  60. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +8 -0
  61. package/lib/engine-components/webxr/WebXRImageTracking.js +79 -3
  62. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  63. package/lib/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +2 -2
  65. package/src/engine/codegen/register_types.js +4 -2
  66. package/src/engine/engine_addressables.ts +28 -10
  67. package/src/engine/engine_element.ts +1 -1
  68. package/src/engine/engine_gameobject.ts +18 -1
  69. package/src/engine/engine_input.ts +11 -0
  70. package/src/engine/engine_math.ts +10 -0
  71. package/src/engine-components/AnimatorController.ts +7 -1
  72. package/src/engine-components/OrbitControls.ts +14 -6
  73. package/src/engine-components/TransformGizmo.ts +64 -70
  74. package/src/engine-components/codegen/components.ts +2 -1
  75. package/src/engine-components/ui/Button.ts +14 -9
  76. package/src/engine-components/ui/Canvas.ts +104 -40
  77. package/src/engine-components/ui/EventSystem.ts +16 -9
  78. package/src/engine-components/ui/Graphic.ts +44 -8
  79. package/src/engine-components/ui/Image.ts +10 -1
  80. package/src/engine-components/ui/InputField.ts +9 -1
  81. package/src/engine-components/ui/Interfaces.ts +12 -0
  82. package/src/engine-components/ui/Outline.ts +13 -0
  83. package/src/engine-components/ui/RectTransform.ts +203 -60
  84. package/src/engine-components/ui/Text.ts +284 -265
  85. package/src/engine-components/utils/LookAt.ts +21 -0
  86. package/src/engine-components/webxr/WebXRImageTracking.ts +85 -10
  87. package/lib/engine-components/ui/Keyboard.d.ts +0 -31
  88. package/lib/engine-components/ui/Keyboard.js +0 -178
  89. package/lib/engine-components/ui/Keyboard.js.map +0 -1
  90. package/src/engine-components/ui/Keyboard.ts +0 -204
@@ -1,13 +1,12 @@
1
1
  import { Graphic } from './Graphic';
2
2
  import * as ThreeMeshUI from 'three-mesh-ui'
3
- import { $shadowDomOwner, includesDir } from "./BaseUIComponent";
4
- import { RectTransform } from './RectTransform';
3
+ import { DocumentedOptions as ThreeMeshUIEveryOptions } from "three-mesh-ui/build/types/core/elements/MeshUIBaseElement";
5
4
  import { Color } from 'three';
6
- import { FrameEvent } from '../../engine/engine_setup';
7
5
  import { updateRenderSettings } from './Utils';
8
6
  import { Canvas } from './Canvas';
9
7
  import { serializable } from '../../engine/engine_serialization_decorator';
10
8
  import { getParam, resolveUrl } from '../../engine/engine_utils';
9
+ import { ICanvas } from './Interfaces';
11
10
 
12
11
  const debug = getParam("debugtext");
13
12
 
@@ -32,7 +31,7 @@ enum HorizontalWrapMode {
32
31
  Overflow = 1,
33
32
  }
34
33
 
35
- enum FontStyle {
34
+ export enum FontStyle {
36
35
  Normal = 0,
37
36
  Bold = 1,
38
37
  Italic = 2,
@@ -53,7 +52,7 @@ export class Text extends Graphic {
53
52
  lineSpacing: number = 1;
54
53
  @serializable()
55
54
  supportRichText: boolean = false;
56
- @serializable()
55
+ @serializable(URL)
57
56
  font?: string;
58
57
  @serializable()
59
58
  fontStyle: FontStyle = FontStyle.Normal;
@@ -62,94 +61,108 @@ export class Text extends Graphic {
62
61
  get text(): string {
63
62
  return this._text;
64
63
  }
64
+
65
65
  set text(val: string) {
66
+
67
+
66
68
  this._text = val;
67
- if (!this._textMeshUi && this._text.length > 0 && this.context.time.frame > 0) {
68
- this.createText(val, this.getTextOpts(), this.supportRichText);
69
- }
70
- if (this._textMeshUi) {
71
- if (this._textMeshUi.length > 1) {
72
- this.requestRebuild();
73
- return;
74
- }
75
- //@ts-ignore
76
- this._textMeshUi[0].set({ content: val });
77
- this.markDirty();
78
- }
69
+ this.feedText(this.text, this.supportRichText);
79
70
  }
71
+
80
72
  private set_text(val: string) {
81
73
  this.text = val;
82
74
  }
83
75
 
84
76
  @serializable()
85
- get fontSize(): number { return this._fontSize; }
77
+ get fontSize(): number {
78
+ return this._fontSize;
79
+ }
80
+
86
81
  set fontSize(val: number) {
82
+
83
+ // Setting that kind of property in a parent, would cascade to each 'non-overrided' children.
87
84
  this._fontSize = val;
88
- if (this._textMeshUi) {
89
- if (this._textMeshUi.length > 1) {
90
- this.requestRebuild();
91
- return;
92
- }
93
- //@ts-ignore
94
- this._textMeshUi[0].set({ fontSize: val });
95
- this.markDirty();
96
- }
85
+ this.uiObject?.set({ fontSize: val });
86
+
97
87
  }
98
88
 
89
+
99
90
  protected onColorChanged(): void {
100
- if (this._textMeshUi) {
101
- if (this._textMeshUi.length > 1) {
102
- this.requestRebuild();
103
- return;
104
- }
105
- const col = this.color;
106
- //@ts-ignore
107
- this._textMeshUi[0].set({ fontColor: col, fontOpacity: col.alpha });
108
- this.markDirty();
91
+ this.uiObject?.set({ color: this.color, fontOpacity: this.color.alpha });
92
+ }
93
+
94
+ onParentRectTransformChanged(): void {
95
+ super.onParentRectTransformChanged();
96
+ if (this.uiObject) {
97
+ this.updateOverflow();
109
98
  }
110
99
  }
111
100
 
112
- private _isWaitingForRebuild: boolean = false;
113
- private requestRebuild() {
114
- if (this._isWaitingForRebuild) return;
115
- this._isWaitingForRebuild = true,
116
- this.startCoroutine(this.rebuildDelayedRoutine(), FrameEvent.EarlyUpdate);
101
+ onBeforeRender(): void {
102
+ if (this.uiObject && (this.Root as any as ICanvas).screenspace) {
103
+ this.updateOverflow();
104
+ }
117
105
  }
118
- private *rebuildDelayedRoutine() {
119
- this._isWaitingForRebuild = false;
120
- if (this._textMeshUi) {
121
- for (const text of this._textMeshUi) {
122
- text.removeFromParent();
123
- }
124
- this._textMeshUi.length = 0;
106
+
107
+ private updateOverflow() {
108
+ // HACK: force the text overflow to update
109
+ const overflow = (this.uiObject as any)?._overflow;
110
+ if (overflow) {
111
+ overflow._needsUpdate = true;
112
+ this.markDirty();
125
113
  }
126
- this.createText(this.text, this.getTextOpts(), this.supportRichText);
127
- this.markDirty();
128
114
  }
129
115
 
130
116
  protected onCreate(_opts: any): void {
131
117
  if (debug) console.log(this);
132
- const hideOverflow = this.verticalOverflow == VerticalWrapMode.Truncate && this.horizontalOverflow == HorizontalWrapMode.Wrap;
133
- if (hideOverflow)
118
+
119
+ if (this.horizontalOverflow == HorizontalWrapMode.Overflow) {
120
+ // Only line characters in the textContent (\n,\r\t) would be able to multiline the text
121
+ _opts.whiteSpace = 'pre';
122
+ }
123
+
124
+ if (this.verticalOverflow == VerticalWrapMode.Truncate) {
134
125
  this.context.renderer.localClippingEnabled = true;
126
+ _opts.overflow = 'hidden';
127
+ }
128
+
129
+
130
+ // @marwie : this combination is currently KO. See sample "Overflow Overview"
131
+ if (this.horizontalOverflow == HorizontalWrapMode.Overflow && this.verticalOverflow == VerticalWrapMode.Truncate) {
132
+ // This could fix this combination, but would require anchors updates to replace element
133
+ // _opts.width = 'auto';
134
+ }
135
+
136
+
137
+ _opts.lineHeight = this.lineSpacing;
138
+
139
+ // @marwie : Should be fixed. Currently _opts are always fed with :
140
+ // backgroundOpacity : color.opacity
141
+ // backgroundColor : color
142
+ delete _opts.backgroundOpacity;
143
+ delete _opts.backgroundColor;
144
+
145
+ // helper to show bounds of text element
146
+ if (debug) {
147
+ _opts.backgroundColor = 0xff9900;
148
+ _opts.backgroundOpacity = 0.5;
149
+ }
135
150
 
136
151
  const rt = this.rectTransform;
137
- // this._container = this._textMeshUi;
138
- // every mesh ui component must be inside a block
139
- // images emit nothing but blocks
140
- // this code should probably be moved somewhere else and also handle raw image / anything that emits block (sprite?)
141
- // so we only add extra blocks if the parent doesnt have one yet
142
- // maybe we can just ask the component the text will be added to to not rely on our unity components?
143
- // this can hopefully be removed once this is fixed/improved: https://github.com/felixmariotto/three-mesh-ui/issues/168
144
- this._textContainer = this.uiObject = this.createBlock(rt, hideOverflow, null, true);
145
152
 
153
+ // Texts now support both options, block and inline, and inline has all default to inherit
154
+ _opts = { ..._opts, ...this.getTextOpts() };
146
155
 
147
- this.createText(this.text, this.getTextOpts(), this.supportRichText);
148
- if (this.uiObject) {
149
- //@ts-ignore
150
- // this._container.width += this.fontSize * .333; // avoid word wrapping
156
+ this.getAlignment(_opts);
157
+
158
+ if (debug) {
159
+ _opts.backgroundColor = Math.random() * 0xffffff;
160
+ _opts.backgroundOpacity = 0.1;
151
161
  }
152
- this.uiObject = this.createBlock(rt, hideOverflow, this.uiObject, false);
162
+
163
+ this.uiObject = rt.createNewText(_opts);
164
+ this.feedText(this.text, this.supportRichText);
165
+
153
166
  }
154
167
 
155
168
  onAfterAddedToScene() {
@@ -159,8 +172,9 @@ export class Text extends Graphic {
159
172
 
160
173
  private _text: string = "";
161
174
  private _fontSize: number = 12;
162
- private _textMeshUi: Array<ThreeMeshUI.Text> | null = null;
163
- private _textContainer: ThreeMeshUI.Block | null = null;
175
+
176
+ private _textMeshUi: Array<ThreeMeshUI.Inline> | null = null;
177
+
164
178
 
165
179
  private getTextOpts(): object {
166
180
  let fontSize = this.fontSize;
@@ -168,15 +182,15 @@ export class Text extends Graphic {
168
182
  // fontSize /= this.canvas?.scaleFactor;
169
183
  // }
170
184
 
171
- // @TODO : THH check for changes. fontColor => color?
185
+
172
186
  const textOpts = {
173
- content: this.text,
174
- fontColor: this.color,
187
+ color: this.color,
175
188
  fontOpacity: this.color.alpha,
176
189
  fontSize: fontSize,
177
190
  fontKerning: "normal",
191
+
178
192
  };
179
- this.setFont(textOpts, this.fontStyle);
193
+ this.setFont(textOpts as ThreeMeshUIEveryOptions, this.fontStyle);
180
194
  return textOpts;
181
195
  }
182
196
 
@@ -186,190 +200,128 @@ export class Text extends Graphic {
186
200
  this._didHandleTextRenderOnTop = false;
187
201
  if (this.uiObject) {
188
202
  // @ts-ignore
189
- this.uiObject.onAfterUpdate = () => {
190
- this.updateWidth();
203
+
204
+ // @TODO : Evaluate the need of keeping it anonymous.
205
+ // From v7.x afterUpdate can be removed but requires a reference
206
+ this.uiObject.addAfterUpdate(() => {
191
207
  // We need to update the shadow owner when the text updates
192
208
  // because once the font has loaded we get new children (a new mesh)
193
- // which is the text, it needs to be linked back to this component
209
+ // which is the text, it needs to be linked back to this component
194
210
  // to be properly handled by the EventSystem
195
211
  // since the EventSystem looks for shadow component owners to handle events
196
212
  this.setShadowComponentOwner(this.uiObject);
197
213
  this.markDirty();
198
- };
214
+ });
199
215
  }
200
216
 
201
- setTimeout(()=> this.markDirty(), 10);
217
+ setTimeout(() => this.markDirty(), 10);
202
218
  }
203
219
 
204
- private createBlock(rt: RectTransform, hideOverflow: boolean, content: THREE.Object3D | Array<THREE.Object3D> | null, isTextIntermediate: boolean = false): ThreeMeshUI.Block | null {
205
- //@ts-ignore
206
- const opts: ThreeMeshUI.BlockOptions = {};
207
-
208
- // @TODO : THH would require update for 7.x
209
- opts.hiddenOverflow = hideOverflow;
210
-
211
- // @TODO : THH would require update for 7.x
212
- opts.interLine = (this.lineSpacing - 1) * this.fontSize * 1.333;
213
-
214
- this.getAlignment(opts, isTextIntermediate);
215
- const block = rt.createNewBlock(opts);
216
- // The text block should never write depth to avoid z-fighting
217
- const mat = block["backgroundMaterial"];
218
- if(mat) mat.depthWrite = false;
219
- if (content) {
220
- if (Array.isArray(content)) {
221
- block.add(...content);
222
- } else {
223
- block.add(content);
224
- }
225
- }
226
- return block;
227
- }
220
+ private getAlignment(opts: ThreeMeshUIEveryOptions): ThreeMeshUIEveryOptions {
228
221
 
229
-
230
- private getAlignment(opts: ThreeMeshUI.BlockOptions | any, isTextIntermediate: boolean = false): ThreeMeshUI.BlockOptions {
231
- if (!isTextIntermediate) {
232
- opts.contentDirection = "row";
233
-
234
- switch (this.alignment) {
235
- case TextAnchor.UpperLeft:
236
- case TextAnchor.MiddleLeft:
237
- case TextAnchor.LowerLeft:
238
- opts.textAlign = "left";
239
- break;
240
- case TextAnchor.UpperCenter:
241
- case TextAnchor.MiddleCenter:
242
- case TextAnchor.LowerCenter:
243
- opts.textAlign = "center";
244
-
245
- break;
246
- case TextAnchor.UpperRight:
247
- case TextAnchor.MiddleRight:
248
- case TextAnchor.LowerRight:
249
- opts.textAlign = "right";
250
- break;
251
- }
252
- }
222
+ opts.flexDirection = "column";
253
223
 
254
224
  switch (this.alignment) {
255
- // @info ThreeMeshUI remaining alignment : space-between|space-around|"stretch(experimental)"
256
- default:
257
225
  case TextAnchor.UpperLeft:
258
- case TextAnchor.UpperCenter:
259
- case TextAnchor.UpperRight:
260
- opts.justifyContent = "start";
261
- break;
262
226
  case TextAnchor.MiddleLeft:
263
- case TextAnchor.MiddleCenter:
264
- case TextAnchor.MiddleRight:
265
- opts.justifyContent = "center";
266
- break;
267
227
  case TextAnchor.LowerLeft:
228
+ opts.textAlign = "left";
229
+ break;
230
+ case TextAnchor.UpperCenter:
231
+ case TextAnchor.MiddleCenter:
268
232
  case TextAnchor.LowerCenter:
233
+ opts.textAlign = "center";
234
+
235
+ break;
236
+ case TextAnchor.UpperRight:
237
+ case TextAnchor.MiddleRight:
269
238
  case TextAnchor.LowerRight:
270
- opts.justifyContent = "end";
239
+ opts.textAlign = "right";
271
240
  break;
272
241
  }
273
242
 
274
- // @TODO : THH evaluate this is still useful. In case of texts, horizontal alignments are made with textAlign
243
+
275
244
  switch (this.alignment) {
245
+ default:
276
246
  case TextAnchor.UpperLeft:
277
- case TextAnchor.MiddleLeft:
278
- case TextAnchor.LowerLeft:
247
+ case TextAnchor.UpperCenter:
248
+ case TextAnchor.UpperRight:
279
249
  opts.alignItems = "start";
280
250
  break;
281
- case TextAnchor.UpperCenter:
251
+ case TextAnchor.MiddleLeft:
282
252
  case TextAnchor.MiddleCenter:
283
- case TextAnchor.LowerCenter:
253
+ case TextAnchor.MiddleRight:
284
254
  opts.alignItems = "center";
285
-
286
255
  break;
287
- case TextAnchor.UpperRight:
288
- case TextAnchor.MiddleRight:
256
+ case TextAnchor.LowerLeft:
257
+ case TextAnchor.LowerCenter:
289
258
  case TextAnchor.LowerRight:
290
259
  opts.alignItems = "end";
291
260
  break;
292
261
  }
293
- return opts;
294
- }
295
262
 
296
- private updateWidth() {
297
- if (this.horizontalOverflow === HorizontalWrapMode.Overflow) {
298
- setTimeout(() => {
299
- if (!this._textMeshUi) return;
300
- const container = this._textMeshUi[0].parent;
301
- if (!container) return;
302
- //@ts-ignore
303
- if (container.lines) {
304
- //@ts-ignore
305
- let newWidth = container.lines.reduce((accu, line) => { return accu + line.width }, 0);
306
- //@ts-ignore
307
- newWidth += container.getFontSize() * 5;
308
- //@ts-ignore
309
- newWidth += (container.padding * 2 || 0);
310
- newWidth += this.fontSize * 1.5;
311
- // TODO: handle alignment!
312
- // const pos = container.position;
313
- // pos.x = this.gameObject.position.x * -.01 + newWidth * .5 - this.rect.sizeDelta.x * .005;
314
- // this._textMeshUi.set({ position: pos });
315
- //@ts-ignore
316
- container.set({ width: newWidth });
317
- this.ensureShadowComponentOwner();
318
- }
319
- }, 1);
320
- }
263
+ return opts;
321
264
  }
322
265
 
323
- private ensureShadowComponentOwner() {
324
- if (this.shadowComponent) {
325
- this.shadowComponent.traverse(c => {
326
- if (c[$shadowDomOwner] === undefined)
327
- c[$shadowDomOwner] = this;
328
- });
329
- }
330
- }
266
+ private feedText(text: string, richText: boolean) {
267
+ // if (!text || text.length <= 0) return;
268
+ // if (!text ) return;
331
269
 
332
- private createText(text: string, opts: any, richText: boolean) {
333
- if (!text || text.length <= 0) return;
270
+ if (!this.uiObject) return null;
334
271
  if (!this._textMeshUi)
335
272
  this._textMeshUi = [];
336
- if (!richText) {
337
- // console.log(text)
338
- const opt = { ...opts };
339
- opt.content = text;
340
- const element = new ThreeMeshUI.Text(opt);
341
- this._textMeshUi.push(element);
342
- if (this._textContainer) {
343
- this._textContainer.add(element);
344
- }
345
- }
346
- else {
273
+
274
+ // this.uiObject.textContent = text;
275
+ // return ;
276
+
277
+ if (!richText || text.length === 0) {
278
+ //@TODO: @swingingtom how would the text content be set?
279
+ //@ts-ignore
280
+ this.uiObject.textContent = text;
281
+ } else {
282
+
283
+
347
284
  let currentTag: TagInfo | null = this.getNextTag(text);
348
285
  if (!currentTag) {
349
- return this.createText(text, opts, false);
350
- }
351
- else if (currentTag.startIndex > 0) {
352
- this.createText(text.substring(0, currentTag.startIndex), opts, false);
286
+ //@TODO: @swingingtom how would the text content be set?
287
+ //@ts-ignore
288
+ return this.uiObject.textContent = text;
289
+ } else if (currentTag.startIndex > 0) {
290
+ this.uiObject.add(new ThreeMeshUI.Inline({ textContent: text.substring(0, currentTag.startIndex), color: 'inherit' }))
353
291
  }
354
292
  const stackArray: Array<TagStackEntry> = [];
355
293
  while (currentTag) {
356
294
  const next = this.getNextTag(text, currentTag.endIndex);
295
+
296
+ const opts = {
297
+ fontFamily: this.uiObject?.get('fontFamily'),
298
+ color: 'inherit',
299
+ textContent: ""
300
+ }
301
+
357
302
  if (next) {
358
- const content = this.getText(text, currentTag, next);
303
+
304
+ opts.textContent = this.getText(text, currentTag, next);
305
+
359
306
  this.handleTag(currentTag, opts, stackArray);
360
- this.createText(content, opts, false);
361
- }
362
- else {
363
- const content = text.substring(currentTag.endIndex);
307
+ this.uiObject?.add(new ThreeMeshUI.Inline(opts))
308
+
309
+ } else {
310
+
311
+ opts.textContent = text.substring(currentTag.endIndex);
312
+
364
313
  this.handleTag(currentTag, opts, stackArray);
365
- this.createText(content, opts, false);
314
+ this.uiObject?.add(new ThreeMeshUI.Inline(opts))
366
315
  }
367
316
  currentTag = next;
368
317
  }
369
318
  }
319
+
320
+ return null;
370
321
  }
371
322
 
372
323
  private _didHandleTextRenderOnTop: boolean = false;
324
+
373
325
  private handleTextRenderOnTop() {
374
326
  if (this._didHandleTextRenderOnTop) return;
375
327
  this._didHandleTextRenderOnTop = true;
@@ -377,11 +329,17 @@ export class Text extends Graphic {
377
329
  }
378
330
 
379
331
  // waits for all the text objects to be ready to set the render on top setting
380
- private *renderOnTopCoroutine() {
332
+ // @THH : this isn't true anymore. We can set mesh and material properties before their counterparts are created.
333
+ // Values would automatically be passed when created. Not sure for depthWrite but it can be added;
334
+ private * renderOnTopCoroutine() {
381
335
  if (!this.canvas) return;
382
336
  const updatedRendering: boolean[] = [];
383
337
  const canvas = this.canvas;
384
- const settings = { renderOnTop: canvas.renderOnTop, depthWrite: canvas.depthWrite, doubleSided: canvas.doubleSided };
338
+ const settings = {
339
+ renderOnTop: canvas.renderOnTop,
340
+ depthWrite: canvas.depthWrite,
341
+ doubleSided: canvas.doubleSided
342
+ };
385
343
  while (true) {
386
344
  let isWaitingForElementToUpdate = false;
387
345
  if (this._textMeshUi) {
@@ -407,36 +365,31 @@ export class Text extends Graphic {
407
365
  // console.log(tag);
408
366
  if (!tag.isEndTag) {
409
367
  if (tag.type.includes("color")) {
410
- const stackEntry = new TagStackEntry(tag, { fontColor: opts.fontColor });
368
+ const stackEntry = new TagStackEntry(tag, { color: opts.color });
411
369
  stackArray.push(stackEntry);
412
370
  if (tag.type.length > 6) // color=
413
371
  {
414
- const col = tag.type.substring(6);
415
- opts.fontColor = new Color(col);
416
- }
417
- else {
372
+ const col = parseInt("0x" + tag.type.substring(7));
373
+ opts.color = col;
374
+ } else {
418
375
  // if it does not contain a color it is white
419
- opts.fontColor = new Color(1, 1, 1);
376
+ opts.color = new Color(1, 1, 1);
420
377
  }
421
- }
422
- else if (tag.type == "b") {
378
+ } else if (tag.type == "b") {
379
+ this.setFont(opts, FontStyle.Bold);
423
380
  const stackEntry = new TagStackEntry(tag, {
424
- fontFamily: opts.fontFamily,
425
- fontTexture: opts.fontTexture,
381
+ fontWeight: 700,
426
382
  });
427
383
  stackArray.push(stackEntry);
428
- this.setFont(opts, FontStyle.Bold);
429
- }
430
- else if (tag.type == "i") {
384
+ } else if (tag.type == "i") {
385
+ this.setFont(opts, FontStyle.Italic);
431
386
  const stackEntry = new TagStackEntry(tag, {
432
- fontFamily: opts.fontFamily,
433
- fontTexture: opts.fontTexture,
387
+ fontStyle: 'italic'
434
388
  });
435
389
  stackArray.push(stackEntry);
436
- this.setFont(opts, FontStyle.Italic);
390
+
437
391
  }
438
- }
439
- else {
392
+ } else {
440
393
  if (stackArray.length > 0) {
441
394
  const last = stackArray.pop();
442
395
  if (last) {
@@ -464,60 +417,126 @@ export class Text extends Graphic {
464
417
  return null;
465
418
  }
466
419
 
467
- private setFont(opts: any, fontStyle: FontStyle) {
468
- const name = this.getFontStyleName(fontStyle);
469
- let family = name;
470
- if (!family?.endsWith("-msdf.json")) family += "-msdf.json";
471
- opts.fontFamily = family;
472
-
473
- let texture = name;
474
- if (!texture?.endsWith(".png")) texture += ".png";
475
- opts.fontTexture = texture;
476
- }
477
-
478
- private getFontStyleName(style: FontStyle): string | null {
479
- if (!this.font) return null;
480
- let fontName = this.font;
481
-
482
- // if a font path has a known suffix we remove it
483
- const fontNameLower = fontName.toLowerCase();
484
- if (fontNameLower.endsWith("-regular")) {
485
- if (style === FontStyle.Normal) return resolveUrl(this.sourceId, fontName);
486
- fontName = fontName.substring(0, fontName.length - "-regular".length);
487
- }
488
- else if (fontNameLower.endsWith("-bold")) {
489
- if (style === FontStyle.Bold)return resolveUrl(this.sourceId, fontName);
490
- fontName = fontName.substring(0, fontName.length - "-bold".length);
491
- }
492
- else if (fontNameLower.endsWith("-italic")) {
493
- if (style === FontStyle.Italic)return resolveUrl(this.sourceId, fontName);
494
- fontName = fontName.substring(0, fontName.length - "-italic".length);
495
- }
496
- else if (fontNameLower.endsWith("-bolditalic")) {
497
- if (style === FontStyle.BoldAndItalic)return resolveUrl(this.sourceId, fontName);
498
- fontName = fontName.substring(0, fontName.length - "-bolditalic".length);
499
- }
500
- else
501
- // If a font does not have a specific style suffic we dont support getting the correct font style
502
- return resolveUrl(this.sourceId, fontName);
420
+ /**
421
+ * Update provided opts to have a proper fontDefinition : family+weight+style
422
+ * Ensure Family and Variant are registered in FontLibrary
423
+ *
424
+ * @param opts
425
+ * @param fontStyle
426
+ * @private
427
+ */
428
+ private setFont(opts: ThreeMeshUIEveryOptions, fontStyle: FontStyle) {
429
+
430
+ // @TODO : THH could be useful to uniformize font family name :
431
+ // This would ease possible html/vr matching
432
+ // - Arial instead of assets/arial
433
+ // - Arial should stay Arial instead of arial
434
+ if (!this.font) return;
435
+ let familyName = this.font;
436
+
437
+ // ensure a font family is register under this name
438
+ let fontFamily = ThreeMeshUI.FontLibrary.getFontFamily(familyName as string);
439
+ if (!fontFamily)
440
+ fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(familyName as string);
441
+
442
+ // @TODO: @swingingtom how should the font be set?
443
+ //@ts-ignore
444
+ opts.fontFamily = fontFamily;
445
+ const lowerFamilyName = familyName.toLowerCase();
503
446
 
504
- switch (style) {
447
+ switch (fontStyle) {
448
+ default:
505
449
  case FontStyle.Normal:
506
- fontName += "-regular";
507
- break;
450
+ opts.fontWeight = 400;
451
+ opts.fontStyle = "normal";
452
+ break
453
+
508
454
  case FontStyle.Bold:
509
- fontName += "-bold";
455
+ opts.fontWeight = 700;
456
+ opts.fontStyle = "normal";
457
+ if (!lowerFamilyName.includes("-bold"))
458
+ familyName += "-bold";
510
459
  break;
460
+
511
461
  case FontStyle.Italic:
512
- fontName += "-italic";
462
+ opts.fontWeight = 400;
463
+ opts.fontStyle = "italic"
464
+ if (!lowerFamilyName.includes("-italic"))
465
+ familyName += "-italic";
513
466
  break;
467
+
514
468
  case FontStyle.BoldAndItalic:
515
- fontName += "-bolditalic";
516
- break;
469
+ opts.fontStyle = 'italic';
470
+ opts.fontWeight = 400;
471
+ if (!lowerFamilyName.includes("-bold"))
472
+ familyName += "-bold";
473
+ if (!lowerFamilyName.includes("-italic"))
474
+ familyName += "-italic";
475
+ }
476
+
477
+
478
+ // Ensure a fontVariant is registered
479
+ //@TODO: @swingingtom add type for fontWeight
480
+ let fontVariant = fontFamily.getVariant(opts.fontWeight as any as string, opts.fontStyle);
481
+ if (!fontVariant) {
482
+ let jsonPath = familyName;
483
+ if (!jsonPath?.endsWith("-msdf.json")) jsonPath += "-msdf.json";
484
+ let texturePath = familyName;
485
+ if (!texturePath?.endsWith(".png")) texturePath += ".png";
486
+
487
+ //@TODO: @swingingtom add type for fontWeight
488
+ //@TODO: @swingingtom addVariant return type is wrong (should be FontVariant)
489
+ fontVariant = fontFamily.addVariant(opts.fontWeight as any as string, opts.fontStyle, jsonPath, texturePath as string) as any as ThreeMeshUI.FontVariant;
490
+ fontVariant?.addEventListener('ready', () => {
491
+ this.markDirty();
492
+ });
517
493
  }
518
494
 
519
- return resolveUrl(this.sourceId, fontName);
520
495
  }
496
+
497
+ // private getFontStyleName(style: FontStyle): string | null {
498
+ // if (!this.font) return null;
499
+ // let fontName = this.font;
500
+
501
+ // // if a font path has a known suffix we remove it
502
+ // const fontNameLower = fontName.toLowerCase();
503
+ // if (fontNameLower.endsWith("-regular")) {
504
+ // if (style === FontStyle.Normal) return resolveUrl(this.sourceId, fontName);
505
+ // fontName = fontName.substring(0, fontName.length - "-regular".length);
506
+ // }
507
+ // else if (fontNameLower.endsWith("-bold")) {
508
+ // if (style === FontStyle.Bold) return resolveUrl(this.sourceId, fontName);
509
+ // fontName = fontName.substring(0, fontName.length - "-bold".length);
510
+ // }
511
+ // else if (fontNameLower.endsWith("-italic")) {
512
+ // if (style === FontStyle.Italic) return resolveUrl(this.sourceId, fontName);
513
+ // fontName = fontName.substring(0, fontName.length - "-italic".length);
514
+ // }
515
+ // else if (fontNameLower.endsWith("-bolditalic")) {
516
+ // if (style === FontStyle.BoldAndItalic) return resolveUrl(this.sourceId, fontName);
517
+ // fontName = fontName.substring(0, fontName.length - "-bolditalic".length);
518
+ // }
519
+ // else
520
+ // // If a font does not have a specific style suffic we dont support getting the correct font style
521
+ // return resolveUrl(this.sourceId, fontName);
522
+
523
+ // switch (style) {
524
+ // case FontStyle.Normal:
525
+ // fontName += "-regular";
526
+ // break;
527
+ // case FontStyle.Bold:
528
+ // fontName += "-bold";
529
+ // break;
530
+ // case FontStyle.Italic:
531
+ // fontName += "-italic";
532
+ // break;
533
+ // case FontStyle.BoldAndItalic:
534
+ // fontName += "-bolditalic";
535
+ // break;
536
+ // }
537
+
538
+ // return resolveUrl(this.sourceId, fontName);
539
+ // }
521
540
  }
522
541
 
523
542
  class TagStackEntry {