@needle-tools/engine 3.8.0-alpha → 3.9.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 (62) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/needle-engine.js +222 -221
  3. package/dist/needle-engine.light.js +222 -221
  4. package/dist/needle-engine.light.min.js +1 -1
  5. package/dist/needle-engine.light.umd.cjs +1 -1
  6. package/dist/needle-engine.min.js +1 -1
  7. package/dist/needle-engine.umd.cjs +1 -1
  8. package/lib/engine/codegen/register_types.js +2 -0
  9. package/lib/engine/codegen/register_types.js.map +1 -1
  10. package/lib/engine/engine_context.js +1 -1
  11. package/lib/engine/engine_context.js.map +1 -1
  12. package/lib/engine/engine_element.js +15 -8
  13. package/lib/engine/engine_element.js.map +1 -1
  14. package/lib/engine/engine_element_overlay.d.ts +2 -1
  15. package/lib/engine/engine_element_overlay.js +38 -55
  16. package/lib/engine/engine_element_overlay.js.map +1 -1
  17. package/lib/engine/extensions/extensions.d.ts +1 -1
  18. package/lib/engine-components/Renderer.d.ts +1 -1
  19. package/lib/engine-components/Renderer.js +14 -10
  20. package/lib/engine-components/Renderer.js.map +1 -1
  21. package/lib/engine-components/RendererLightmap.d.ts +4 -6
  22. package/lib/engine-components/RendererLightmap.js +35 -25
  23. package/lib/engine-components/RendererLightmap.js.map +1 -1
  24. package/lib/engine-components/codegen/components.d.ts +1 -0
  25. package/lib/engine-components/codegen/components.js +1 -0
  26. package/lib/engine-components/codegen/components.js.map +1 -1
  27. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +34 -30
  28. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  29. package/lib/engine-components/export/usdz/USDZExporter.js +2 -0
  30. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  31. package/lib/engine-components/export/usdz/extensions/USDZText.d.ts +2 -1
  32. package/lib/engine-components/export/usdz/extensions/USDZText.js +40 -3
  33. package/lib/engine-components/export/usdz/extensions/USDZText.js.map +1 -1
  34. package/lib/engine-components/export/usdz/extensions/USDZUI.d.ts +8 -0
  35. package/lib/engine-components/export/usdz/extensions/USDZUI.js +101 -0
  36. package/lib/engine-components/export/usdz/extensions/USDZUI.js.map +1 -0
  37. package/lib/engine-components/ui/Button.d.ts +1 -1
  38. package/lib/engine-components/ui/Button.js +4 -1
  39. package/lib/engine-components/ui/Button.js.map +1 -1
  40. package/lib/engine-components/ui/EventSystem.js +2 -0
  41. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  42. package/lib/engine-components/ui/Graphic.js +3 -3
  43. package/lib/engine-components/ui/Graphic.js.map +1 -1
  44. package/lib/engine-components/webxr/WebXR.js +1 -1
  45. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/engine/codegen/register_types.js +2 -0
  48. package/src/engine/engine_context.ts +1 -1
  49. package/src/engine/engine_element.ts +15 -8
  50. package/src/engine/engine_element_overlay.ts +41 -52
  51. package/src/engine/extensions/extensions.ts +1 -1
  52. package/src/engine-components/Renderer.ts +19 -14
  53. package/src/engine-components/RendererLightmap.ts +36 -31
  54. package/src/engine-components/codegen/components.ts +1 -0
  55. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +43 -37
  56. package/src/engine-components/export/usdz/USDZExporter.ts +2 -0
  57. package/src/engine-components/export/usdz/extensions/USDZText.ts +41 -1
  58. package/src/engine-components/export/usdz/extensions/USDZUI.ts +120 -0
  59. package/src/engine-components/ui/Button.ts +3 -1
  60. package/src/engine-components/ui/EventSystem.ts +3 -1
  61. package/src/engine-components/ui/Graphic.ts +3 -3
  62. package/src/engine-components/webxr/WebXR.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Context } from "./engine_setup";
2
- import { getParam, isMozillaXR } from "./engine_utils";
2
+ import { getParam, isMobileDevice, isMozillaXR } from "./engine_utils";
3
3
 
4
4
  const debug = getParam("debugaroverlay");
5
5
  export const arContainerClassName = "ar";
@@ -21,6 +21,7 @@ export class AROverlayHandler {
21
21
 
22
22
  private _createdAROnlyElements: Array<any> = [];
23
23
  private _reparentedObjects: Array<{ el: Element, previousParent: HTMLElement | null }> = [];
24
+ private contentElement: HTMLElement | null = null;
24
25
 
25
26
  requestEndAR() {
26
27
  this.onRequestedEndAR();
@@ -30,13 +31,16 @@ export class AROverlayHandler {
30
31
  this.currentSession = session;
31
32
  this.arContainer = overlayContainer;
32
33
 
33
- const arElements = context.domElement.shadowRoot!.querySelectorAll(`.${arContainerClassName}`);
34
- arElements.forEach(el => {
35
- if (!el) return;
36
- if (el === this.arContainer) return;
37
- this._reparentedObjects.push({ el: el, previousParent: el.parentElement });
38
- this.arContainer?.appendChild(el);
39
- });
34
+ if (isMozillaXR()) {
35
+ const arElements = context.domElement!.children;
36
+ for (let i = 0; i < arElements?.length; i++) {
37
+ const el = arElements[i];
38
+ if (!el) return;
39
+ if (el === this.arContainer) return;
40
+ this._reparentedObjects.push({ el: el, previousParent: el.parentElement });
41
+ this.arContainer?.appendChild(el);
42
+ }
43
+ }
40
44
 
41
45
  const quit_Elements = overlayContainer.getElementsByClassName(quitARClassName);
42
46
  if (!quit_Elements || quit_Elements.length <= 0) {
@@ -91,46 +95,20 @@ export class AROverlayHandler {
91
95
  }
92
96
  }
93
97
 
94
- findOrCreateARContainer(element: HTMLElement): HTMLElement {
95
- if(debug) console.log("findOrCreateARContainer");
96
- // search in the needle-engine element
97
- if (element.classList.contains(arContainerClassName)) {
98
- if(debug) console.log("Found overlay container in needle-engine element");
99
- return element;
100
- }
101
- if (element.shadowRoot) {
102
- const el = element.shadowRoot!.querySelector(`.${arContainerClassName}`);
103
- if (el) {
104
- if(debug) console.log("Found overlay container in needle-engine element");
105
- return el as HTMLElement;
106
- };
107
- }
108
98
 
109
- // search in document as well; "ar" element could live outside needle-engine element
110
- const arElements = document.getElementsByClassName(arContainerClassName);
111
- if (arElements && arElements.length > 0){
112
- if(debug) console.log("Found overlay container in document");
113
- return arElements[0] as HTMLElement;
114
- }
99
+ createOverlayContainer(needleEngineElement: HTMLElement): HTMLElement {
100
+ if (this.contentElement) return this.contentElement;
115
101
 
116
102
  if (debug)
117
- console.log("No overlay container found in document - generating new ony");
118
- const el = document.createElement("div");
119
- el.classList.add(arContainerClassName);
120
- el.style.cssText = `
121
- position: fixed;
122
- top: 0;
123
- left: 0;
124
- width: 100%;
125
- height: 100%;
126
- display: flex;
127
- visibility: visible;
128
- z-index: 9999;
129
- pointer-events: none;
130
- // background: rgba(0,0,0,1);
131
- `;
132
- if(debug) this.createFallbackCloseARButton(element);
133
- return this.appendElement(el, element) as HTMLElement;
103
+ console.log("Setup overlay container");
104
+
105
+ const contentElement = needleEngineElement.shadowRoot!.querySelector(".content") as HTMLElement;
106
+ this.contentElement = contentElement;
107
+
108
+ const overlaySlot = needleEngineElement.shadowRoot!.querySelector(".overlay-content");
109
+ if (overlaySlot) contentElement.appendChild(overlaySlot);
110
+ if (debug && !isMobileDevice()) this.createFallbackCloseARButton(contentElement);
111
+ return contentElement;
134
112
  }
135
113
 
136
114
  private onRequestedEndAR() {
@@ -147,30 +125,41 @@ export class AROverlayHandler {
147
125
  private createFallbackCloseARButton(element: HTMLElement) {
148
126
  const quitARSlot = document.createElement("slot");
149
127
  quitARSlot.setAttribute("name", "quit-ar");
150
- this.appendElement(quitARSlot, element);
151
- if(debug) quitARSlot.addEventListener('click', () => console.log("Clicked fallback close button"));
152
- quitARSlot.addEventListener('click', this.closeARCallback);
128
+ this.appendElement(quitARSlot, element);
129
+ if (debug) quitARSlot.addEventListener('click', () => console.log("Clicked fallback close button"));
130
+ quitARSlot.addEventListener('click', this.closeARCallback);
153
131
  this._createdAROnlyElements.push(quitARSlot);
154
132
  // for mozilla XR reparenting we have to make sure the close button is clickable so we set it on the element directly
155
133
  // it's in general perhaps more safe to set it on the element to ensure it's clickable
156
134
  quitARSlot.style.pointerEvents = "auto";
157
135
 
136
+ // we need another container to make sure the button is always on top
137
+ const fixedButtonContainer = document.createElement("div");
138
+ fixedButtonContainer.style.cssText = `
139
+ position: fixed;
140
+ top: 0;
141
+ right: 0;
142
+ z-index: 600;
143
+ pointer-events: all;
144
+ `;
145
+ this.appendElement(fixedButtonContainer, quitARSlot);
146
+
158
147
  var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
159
148
  svg.classList.add("quit-ar-button");
160
149
  svg.setAttribute('width', "38px");
161
150
  svg.setAttribute('height', "38px");
162
- quitARSlot.appendChild(svg);
151
+ fixedButtonContainer.appendChild(svg);
163
152
 
164
153
  var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
165
154
  path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28');
166
- path.setAttribute('stroke', '#ddd');
155
+ path.setAttribute('stroke', '#aaa');
167
156
  path.setAttribute('stroke-width', "3px");
168
157
  svg.appendChild(path);
169
- if(debug) console.log("Created fallback close button", svg, element);
158
+ if (debug) console.log("Created fallback close button", svg, element);
170
159
  }
171
160
 
172
161
  private appendElement(element: Element, parent: HTMLElement) {
173
- if(parent.shadowRoot) return parent.shadowRoot.appendChild(element);
162
+ if (parent.shadowRoot) return parent.shadowRoot.appendChild(element);
174
163
  return parent.appendChild(element);
175
164
  }
176
165
 
@@ -15,7 +15,7 @@ import { NEEDLE_render_objects } from "./NEEDLE_render_objects";
15
15
  import { NEEDLE_progressive } from "./NEEDLE_progressive";
16
16
  import { InternalUsageTrackerPlugin } from "./usage_tracker";
17
17
  import { isUsageTrackingEnabled } from "../engine_assetdatabase";
18
- import { GLTFLoaderPlugin } from "three/examples/jsm/loaders/GLTFLoader";
18
+ import { GLTFLoaderPlugin } from "three/examples/jsm/loaders/GLTFLoader.js";
19
19
  // import { GLTFAnimationPointerExtension } from "three/examples/jsm/loaders/GLTFLoaderAnimationPointer";
20
20
 
21
21
  // lazily import the GLTFAnimationPointerExtension in case it doesnt exist (e.g. using vanilla three)
@@ -352,7 +352,7 @@ export class Renderer extends Behaviour implements IRenderer {
352
352
 
353
353
  if (this.isMultiMaterialObject(this.gameObject)) {
354
354
  for (const child of this.gameObject.children) {
355
- this.context.addBeforeRenderListener(child, this.onBeforeRenderThree.bind(this));
355
+ this.context.addBeforeRenderListener(child, this.onBeforeRenderThree);
356
356
  child.layers.mask = this.gameObject.layers.mask;
357
357
  }
358
358
 
@@ -376,14 +376,13 @@ export class Renderer extends Behaviour implements IRenderer {
376
376
  }
377
377
  // TODO: custom shader with sub materials
378
378
  else if (this.isMeshOrSkinnedMesh(this.gameObject)) {
379
-
380
- this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree.bind(this));
379
+ this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
381
380
 
382
381
  if (this.renderOrder !== undefined && this.renderOrder.length > 0)
383
382
  this.gameObject.renderOrder = this.renderOrder[0];
384
383
  }
385
384
  else {
386
- this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree.bind(this));
385
+ this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
387
386
  }
388
387
 
389
388
  this.applyLightmapping();
@@ -415,7 +414,7 @@ export class Renderer extends Behaviour implements IRenderer {
415
414
  const mat = this.gameObject["material"];
416
415
  if (!mat?.isMeshBasicMaterial) {
417
416
  if (this._lightmaps.length <= 0) {
418
- const rm = new RendererLightmap(this.gameObject, this.context);
417
+ const rm = new RendererLightmap(this.gameObject as any as Mesh, this.context);
419
418
  this._lightmaps.push(rm);
420
419
  }
421
420
  const rm = this._lightmaps[0];
@@ -434,16 +433,12 @@ export class Renderer extends Behaviour implements IRenderer {
434
433
  if (!child["material"]?.isMeshBasicMaterial) {
435
434
  let rm: RendererLightmap | undefined = undefined;
436
435
  if (i >= this._lightmaps.length) {
437
- rm = new RendererLightmap(child as GameObject, this.context);
436
+ rm = new RendererLightmap(child as Mesh, this.context);
438
437
  this._lightmaps.push(rm);
439
438
  }
440
439
  else
441
440
  rm = this._lightmaps[i];
442
441
  rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
443
- // onBeforeRender is not called when the renderer is on a group
444
- // this is an issue we probably also need to handle for custom shaders
445
- // and need a better solution, but for now this fixes lightmaps for multimaterial objects
446
- rm.bindOnBeforeRender();
447
442
  }
448
443
  }
449
444
  }
@@ -529,7 +524,6 @@ export class Renderer extends Behaviour implements IRenderer {
529
524
  }
530
525
 
531
526
  this.updateReflectionProbe();
532
-
533
527
  }
534
528
 
535
529
  onDisable() {
@@ -542,13 +536,22 @@ export class Renderer extends Behaviour implements IRenderer {
542
536
 
543
537
  onDestroy(): void {
544
538
  this.handles = null;
539
+
540
+ if (this.isMultiMaterialObject(this.gameObject)) {
541
+ for (const child of this.gameObject.children) {
542
+ this.context.removeBeforeRenderListener(child, this.onBeforeRenderThree);
543
+ }
544
+ }
545
+ else {
546
+ this.context.removeBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
547
+ }
548
+
545
549
  }
546
550
 
547
551
  applyStencil() {
548
552
  NEEDLE_render_objects.applyStencil(this);
549
553
  }
550
554
 
551
-
552
555
  onBeforeRender() {
553
556
  if (!this.gameObject) {
554
557
  return;
@@ -611,7 +614,9 @@ export class Renderer extends Behaviour implements IRenderer {
611
614
  }
612
615
 
613
616
  }
614
- onBeforeRenderThree(_renderer, _scene, _camera, _geometry, material, _group) {
617
+
618
+
619
+ private onBeforeRenderThree = (_renderer, _scene, _camera, _geometry, material, _group) => {
615
620
 
616
621
  this.loadProgressiveTextures(material);
617
622
 
@@ -656,7 +661,7 @@ export class Renderer extends Behaviour implements IRenderer {
656
661
 
657
662
  if (this._lightmaps) {
658
663
  for (const lm of this._lightmaps) {
659
- lm.onBeforeRenderThree(material);
664
+ lm.updateLightmapUniforms(material);
660
665
  }
661
666
  }
662
667
  }
@@ -1,5 +1,5 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
- import { Material, Mesh, ShaderMaterial, Texture, Vector4 } from "three";
2
+ import { Material, Mesh, Shader, ShaderMaterial, Texture, Vector4 } from "three";
3
3
  import { Context, OnRenderCallback } from "../engine/engine_setup";
4
4
  import { getParam } from "../engine/engine_utils";
5
5
 
@@ -24,12 +24,12 @@ export class RendererLightmap {
24
24
  lightmapScaleOffset: THREE.Vector4 = new Vector4(1, 1, 0, 0);
25
25
 
26
26
  private context: Context;
27
- private gameObject: GameObject;
27
+ private gameObject: Mesh;
28
28
  private lightmapTexture: Texture | null = null;
29
29
  private lightmapScaleOffsetUniform = { value: new Vector4(1, 1, 0, 0) };
30
30
  private lightmapUniform: { value: Texture | null } = { value: null };
31
31
 
32
- constructor(gameObject: GameObject, context: Context) {
32
+ constructor(gameObject: Mesh, context: Context) {
33
33
  this.gameObject = gameObject;
34
34
  this.context = context;
35
35
  }
@@ -46,19 +46,21 @@ export class RendererLightmap {
46
46
  this.applyLightmap();
47
47
  }
48
48
 
49
- bindOnBeforeRender() {
50
- this.context.removeBeforeRenderListener(this.gameObject, this.onBeforeRenderThreeComplete);
51
- this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThreeComplete);
49
+ updateLightmapUniforms(material: Material) {
50
+ const uniforms = material["uniforms"];
51
+ if (uniforms && uniforms.lightmap) {
52
+ this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
53
+ this.lightmapUniform.value = this.lightmapTexture;
54
+ uniforms.lightmap = this.lightmapUniform;
55
+ uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
56
+ }
52
57
  }
53
58
 
54
- private onBeforeRenderThreeComplete = (_renderer, _scene, _camera, _geometry, material, _group) => {
55
- this.onBeforeRenderThree(material);
56
- }
57
59
 
58
60
  private applyLightmap() {
59
-
60
61
  if (this.gameObject.type === "Object3D") {
61
- // console.warn("Can not add lightmap. Is this object missing a renderer?");
62
+ if (debug)
63
+ console.warn("Can not add lightmap. Is this object missing a renderer?", this.gameObject.name);
62
64
  return;
63
65
  }
64
66
 
@@ -70,23 +72,27 @@ export class RendererLightmap {
70
72
  console.assert(this.gameObject.type === "Mesh", "Lightmap only works on meshes", this);
71
73
 
72
74
  const mesh = this.gameObject as unknown as Mesh;
73
- // TODO: ensure uv2 exists
74
75
  if (!mesh.geometry.getAttribute("uv1"))
75
76
  mesh.geometry.setAttribute("uv1", mesh.geometry.getAttribute("uv"));
76
77
 
77
- const mat: Material = this.gameObject["material"].clone();
78
- this.gameObject["material"] = mat;
79
-
80
- this.gameObject["material"].onBeforeCompile = (shader, _) => {
81
- shader.lightMapUv = "uv1";
82
- shader.uniforms.lightmap = this.lightmapUniform;
83
- shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
84
- };
85
-
78
+ if (Array.isArray(this.gameObject.material)) {
79
+ const mats: Material[] = this.gameObject.material;
80
+ for (let i = 0; i < mats.length; i++) {
81
+ const mat = mats[i];
82
+ const cloned = mat.clone();
83
+ mats[i] = cloned;
84
+ cloned.onBeforeCompile = this.onBeforeCompile;
85
+ }
86
+ }
87
+ else {
88
+ const mat: Material = this.gameObject.material.clone();
89
+ this.gameObject.material = mat;
90
+ this.gameObject.material.onBeforeCompile = this.onBeforeCompile;
91
+ }
86
92
 
87
93
  if (this.lightmapIndex >= 0) {
88
94
  const lightmap = this.lightmapTexture;
89
- const mat = this.gameObject["material"];
95
+ const mat = this.gameObject.material as any;
90
96
  if (mat && lightmap) {
91
97
  if (!mat.uniforms) mat.uniforms = {};
92
98
  mat.lightMap = lightmap;
@@ -95,15 +101,14 @@ export class RendererLightmap {
95
101
  }
96
102
  }
97
103
 
98
- onBeforeRenderThree(material: THREE.Material) {
99
-
100
- const uniforms = material["uniforms"];
101
- if (uniforms && uniforms.lightmap) {
102
- this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
103
- this.lightmapUniform.value = this.lightmapTexture;
104
- uniforms.lightmap = this.lightmapUniform;
105
- uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
106
- }
104
+ private onBeforeCompile = (shader: Shader, _) => {
105
+ if(debug) console.log("Lightmaps, before compile")
106
+ //@ts-ignore
107
+ shader.lightMapUv = "uv1";
108
+ this.lightmapScaleOffsetUniform.value = this.lightmapScaleOffset;
109
+ this.lightmapUniform.value = this.lightmapTexture;
110
+ shader.uniforms.lightmap = this.lightmapUniform;
111
+ shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
107
112
  }
108
113
 
109
114
  private setLightmapDebugMaterial() {
@@ -184,6 +184,7 @@ export { UIRootComponent } from "../ui/BaseUIComponent";
184
184
  export { UsageMarker } from "../Interactable";
185
185
  export { USDZExporter } from "../export/usdz/USDZExporter";
186
186
  export { USDZText } from "../export/usdz/extensions/USDZText";
187
+ export { USDZUIExtension } from "../export/usdz/extensions/USDZUI";
187
188
  export { VariantAction } from "../export/usdz/extensions/behavior/Actions";
188
189
  export { VelocityOverLifetimeModule } from "../ParticleSystemModules";
189
190
  export { VerticalLayoutGroup } from "../ui/Layout";
@@ -657,7 +657,7 @@ function addResources( object, context: USDZExporterContext ) {
657
657
 
658
658
  if ( geometry ) {
659
659
 
660
- if ( material.isMeshStandardMaterial ) { // || material.isMeshBasicMaterial // TODO convert unlit to lit+emissive
660
+ if ( material.isMeshStandardMaterial || material.isMeshBasicMaterial ) { // TODO convert unlit to lit+emissive
661
661
 
662
662
  const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
663
663
 
@@ -1124,7 +1124,7 @@ ${array.join( '' )}
1124
1124
 
1125
1125
  }
1126
1126
 
1127
- function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompatible = false ) {
1127
+ function buildMaterial( material: MeshBasicMaterial, textures, quickLookCompatible = false ) {
1128
1128
 
1129
1129
  // https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
1130
1130
 
@@ -1182,7 +1182,9 @@ function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompa
1182
1182
 
1183
1183
  const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
1184
1184
  const needsNormalScaleAndBias = mapType === 'normal';
1185
- const normalScaleValueString = (material.normalScale ? material.normalScale.x * 2 : 2).toFixed( PRECISION );
1185
+ const normalScaleValueString = material instanceof MeshStandardMaterial
1186
+ ? (material.normalScale ? material.normalScale.x * 2 : 2).toFixed( PRECISION )
1187
+ : "1";
1186
1188
 
1187
1189
  return `
1188
1190
  ${needsTextureTransform ? `def Shader "Transform2d_${mapType}" (
@@ -1254,72 +1256,76 @@ function buildMaterial( material: MeshStandardMaterial, textures, quickLookCompa
1254
1256
 
1255
1257
  }
1256
1258
 
1257
- if ( material.emissiveMap ) {
1259
+ if ( material.aoMap ) {
1258
1260
 
1259
- inputs.push( `${pad}color3f inputs:emissiveColor.connect = </Materials/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>` );
1261
+ inputs.push( `${pad}float inputs:occlusion.connect = </Materials/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>` );
1260
1262
 
1261
- samplers.push( buildTexture( material.emissiveMap, 'emissive' ) );
1263
+ samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
1264
+
1265
+ }
1262
1266
 
1263
- } else if ( material.emissive?.getHex() > 0 ) {
1267
+ if ( material.alphaMap ) {
1268
+
1269
+ inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
1270
+ inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
1264
1271
 
1265
- inputs.push( `${pad}color3f inputs:emissiveColor = ${buildColor( material.emissive )}` );
1272
+ samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
1266
1273
 
1267
1274
  } else {
1268
-
1269
- inputs.push( `${pad}color3f inputs:emissiveColor = (0, 0, 0)` );
1275
+
1276
+ inputs.push( `${pad}float inputs:opacity = ${effectiveOpacity}` );
1270
1277
 
1271
1278
  }
1272
1279
 
1273
- if ( material.normalMap ) {
1280
+ if ( material instanceof MeshStandardMaterial ) {
1274
1281
 
1275
- inputs.push( `${pad}normal3f inputs:normal.connect = </Materials/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>` );
1282
+ if ( material.emissiveMap ) {
1276
1283
 
1277
- samplers.push( buildTexture( material.normalMap, 'normal' ) );
1284
+ inputs.push( `${pad}color3f inputs:emissiveColor.connect = </Materials/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>` );
1278
1285
 
1279
- }
1286
+ samplers.push( buildTexture( material.emissiveMap, 'emissive' ) );
1280
1287
 
1281
- if ( material.aoMap ) {
1282
-
1283
- inputs.push( `${pad}float inputs:occlusion.connect = </Materials/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>` );
1288
+ } else if ( material.emissive?.getHex() > 0 ) {
1284
1289
 
1285
- samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
1290
+ inputs.push( `${pad}color3f inputs:emissiveColor = ${buildColor( material.emissive )}` );
1286
1291
 
1287
- }
1292
+ } else {
1293
+
1294
+ inputs.push( `${pad}color3f inputs:emissiveColor = (0, 0, 0)` );
1288
1295
 
1289
- if ( material.roughnessMap && material.roughness === 1 ) {
1296
+ }
1290
1297
 
1291
- inputs.push( `${pad}float inputs:roughness.connect = </Materials/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>` );
1298
+ if ( material.normalMap ) {
1292
1299
 
1293
- samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
1300
+ inputs.push( `${pad}normal3f inputs:normal.connect = </Materials/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>` );
1294
1301
 
1295
- } else {
1302
+ samplers.push( buildTexture( material.normalMap, 'normal' ) );
1296
1303
 
1297
- inputs.push( `${pad}float inputs:roughness = ${material.roughness !== undefined ? material.roughness : 1 }` );
1304
+ }
1298
1305
 
1299
- }
1306
+ if ( material.roughnessMap && material.roughness === 1 ) {
1300
1307
 
1301
- if ( material.metalnessMap && material.metalness === 1 ) {
1308
+ inputs.push( `${pad}float inputs:roughness.connect = </Materials/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>` );
1302
1309
 
1303
- inputs.push( `${pad}float inputs:metallic.connect = </Materials/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>` );
1310
+ samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
1304
1311
 
1305
- samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
1312
+ } else {
1306
1313
 
1307
- } else {
1314
+ inputs.push( `${pad}float inputs:roughness = ${material.roughness !== undefined ? material.roughness : 1 }` );
1308
1315
 
1309
- inputs.push( `${pad}float inputs:metallic = ${material.metalness !== undefined ? material.metalness : 0 }` );
1316
+ }
1310
1317
 
1311
- }
1318
+ if ( material.metalnessMap && material.metalness === 1 ) {
1312
1319
 
1313
- if ( material.alphaMap ) {
1320
+ inputs.push( `${pad}float inputs:metallic.connect = </Materials/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>` );
1314
1321
 
1315
- inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
1316
- inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
1322
+ samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
1317
1323
 
1318
- samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
1324
+ } else {
1319
1325
 
1320
- } else {
1326
+ inputs.push( `${pad}float inputs:metallic = ${material.metalness !== undefined ? material.metalness : 0 }` );
1321
1327
 
1322
- inputs.push( `${pad}float inputs:opacity = ${effectiveOpacity}` );
1328
+ }
1323
1329
 
1324
1330
  }
1325
1331
 
@@ -16,6 +16,7 @@ import { hasProLicense } from "../../../engine/engine_license";
16
16
  import { BehaviorExtension } from "./extensions/behavior/Behaviour";
17
17
  import { AudioExtension } from "./extensions/behavior/AudioExtension";
18
18
  import { TextExtension } from "./extensions/USDZText";
19
+ import { USDZUIExtension } from "./extensions/USDZUI";
19
20
  import { Renderer } from "../../Renderer"
20
21
 
21
22
  const debug = getParam("debugusdz");
@@ -97,6 +98,7 @@ export class USDZExporter extends Behaviour {
97
98
  this.extensions.push(new BehaviorExtension());
98
99
  this.extensions.push(new AudioExtension());
99
100
  this.extensions.push(new TextExtension());
101
+ this.extensions.push(new USDZUIExtension());
100
102
  }
101
103
  }
102
104
 
@@ -146,8 +146,13 @@ export class TextExtension implements IUSDExporterExtension {
146
146
  return "text";
147
147
  }
148
148
 
149
- onExportObject(object: Object3D, model: USDObject, _context: USDZExporterContext) {
149
+ // HACK we should clean this up, text export has moved to USDZUI.ts and is
150
+ // integrated into the hierarchy now
151
+ onExportObject(_object: Object3D, _model: USDObject, _context: USDZExporterContext) {
150
152
 
153
+ return;
154
+
155
+ /*
151
156
  const text = GameObject.getComponent(object, Text);
152
157
  if (text) {
153
158
  const rt = GameObject.getComponent(object, RectTransform);
@@ -186,6 +191,41 @@ export class TextExtension implements IUSDExporterExtension {
186
191
  textObj.writeTo(undefined, writer);
187
192
  });
188
193
  }
194
+ */
195
+ }
196
+
197
+ exportText(object: Object3D, newModel: USDObject, _context: USDZExporterContext) {
198
+
199
+ const text = GameObject.getComponent(object, Text);
200
+ if (!text) return;
201
+
202
+ const rt = GameObject.getComponent(object, RectTransform);
203
+ let width = 100;
204
+ let height = 100;
205
+ if (rt) {
206
+ width = rt.width;
207
+ height = rt.height;
208
+ }
209
+
210
+ newModel.matrix = rotateYAxisMatrix.clone();
211
+ if (rt) // Not ideal but works for now:
212
+ newModel.matrix.premultiply(invertX);
213
+
214
+ const color = new Color().copySRGBToLinear(text.color);
215
+ newModel.material = new MeshStandardMaterial({ color: color, emissive: color });
216
+
217
+ newModel.addEventListener("serialize", (writer: USDWriter, _context: USDZExporterContext) => {
218
+ let txt = text.text;
219
+ txt = txt.replace(/\n/g, "\\n");
220
+ const textObj = TextBuilder.multiLine(txt, width, height, HorizontalAlignment.center, VerticalAlignment.bottom, TextWrapMode.flowing);
221
+ this.setTextAlignment(textObj, text.alignment);
222
+ this.setOverflow(textObj, text);
223
+ if (newModel.material)
224
+ textObj.material = newModel.material;
225
+ textObj.pointSize = this.convertToTextSize(text.fontSize);
226
+ textObj.depth = .001;
227
+ textObj.writeTo(undefined, writer);
228
+ });
189
229
  }
190
230
 
191
231
  private convertToTextSize(pixel: number) {