@needle-tools/engine 3.5.1-alpha → 3.5.3-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 (109) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/needle-engine.js +29554 -29281
  3. package/dist/needle-engine.min.js +357 -356
  4. package/dist/needle-engine.umd.cjs +354 -353
  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/debug/debug_overlay.js +2 -1
  8. package/lib/engine/debug/debug_overlay.js.map +1 -1
  9. package/lib/engine/engine_components.js +2 -1
  10. package/lib/engine/engine_components.js.map +1 -1
  11. package/lib/engine/engine_element_loading.js +4 -4
  12. package/lib/engine/engine_element_loading.js.map +1 -1
  13. package/lib/engine/engine_gameobject.js +2 -0
  14. package/lib/engine/engine_gameobject.js.map +1 -1
  15. package/lib/engine/engine_input.js +4 -1
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_license.d.ts +2 -0
  18. package/lib/engine/engine_license.js +25 -4
  19. package/lib/engine/engine_license.js.map +1 -1
  20. package/lib/engine/engine_physics_rapier.js +2 -1
  21. package/lib/engine/engine_physics_rapier.js.map +1 -1
  22. package/lib/engine/engine_serialization_core.js +16 -1
  23. package/lib/engine/engine_serialization_core.js.map +1 -1
  24. package/lib/engine/extensions/NEEDLE_lighting_settings.js +10 -1
  25. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  26. package/lib/engine-components/Component.js +0 -3
  27. package/lib/engine-components/Component.js.map +1 -1
  28. package/lib/engine-components/SceneSwitcher.d.ts +9 -0
  29. package/lib/engine-components/SceneSwitcher.js +128 -0
  30. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  31. package/lib/engine-components/codegen/components.d.ts +2 -1
  32. package/lib/engine-components/codegen/components.js +2 -1
  33. package/lib/engine-components/codegen/components.js.map +1 -1
  34. package/lib/engine-components/export/usdz/Extension.d.ts +3 -2
  35. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +9 -5
  36. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +64 -20
  37. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  38. package/lib/engine-components/export/usdz/USDZExporter.d.ts +2 -0
  39. package/lib/engine-components/export/usdz/USDZExporter.js +44 -9
  40. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  41. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +9 -0
  42. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +48 -0
  43. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js.map +1 -0
  44. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +4 -5
  45. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +19 -10
  46. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
  47. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +9 -0
  48. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +75 -5
  49. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  50. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +20 -0
  51. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +45 -0
  52. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -1
  53. package/lib/engine-components/ui/BaseUIComponent.d.ts +1 -0
  54. package/lib/engine-components/ui/BaseUIComponent.js +8 -4
  55. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  56. package/lib/engine-components/ui/Canvas.js +1 -1
  57. package/lib/engine-components/ui/Canvas.js.map +1 -1
  58. package/lib/engine-components/ui/EventSystem.js +3 -0
  59. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  60. package/lib/engine-components/ui/Image.d.ts +3 -1
  61. package/lib/engine-components/ui/Image.js +15 -1
  62. package/lib/engine-components/ui/Image.js.map +1 -1
  63. package/lib/engine-components/ui/Interfaces.d.ts +1 -1
  64. package/lib/engine-components/ui/Layout.js +2 -0
  65. package/lib/engine-components/ui/Layout.js.map +1 -1
  66. package/lib/engine-components/ui/PointerEvents.d.ts +8 -1
  67. package/lib/engine-components/ui/PointerEvents.js +9 -1
  68. package/lib/engine-components/ui/PointerEvents.js.map +1 -1
  69. package/lib/engine-components/ui/RaycastUtils.js +5 -0
  70. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  71. package/lib/engine-components/ui/RectTransform.d.ts +2 -2
  72. package/lib/engine-components/ui/RectTransform.js +11 -12
  73. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  74. package/lib/engine-components/ui/Text.d.ts +0 -2
  75. package/lib/engine-components/ui/Text.js +0 -5
  76. package/lib/engine-components/ui/Text.js.map +1 -1
  77. package/lib/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +1 -1
  79. package/plugins/vite/license.js +2 -2
  80. package/src/engine/codegen/register_types.js +4 -2
  81. package/src/engine/debug/debug_overlay.ts +2 -1
  82. package/src/engine/engine_components.ts +2 -1
  83. package/src/engine/engine_element_loading.ts +4 -4
  84. package/src/engine/engine_gameobject.ts +3 -0
  85. package/src/engine/engine_input.ts +4 -1
  86. package/src/engine/engine_license.ts +25 -4
  87. package/src/engine/engine_physics_rapier.ts +2 -1
  88. package/src/engine/engine_serialization_core.ts +17 -1
  89. package/src/engine/extensions/NEEDLE_lighting_settings.ts +11 -1
  90. package/src/engine-components/Component.ts +1 -3
  91. package/src/engine-components/SceneSwitcher.ts +136 -1
  92. package/src/engine-components/codegen/components.ts +2 -1
  93. package/src/engine-components/export/usdz/Extension.ts +3 -2
  94. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +89 -21
  95. package/src/engine-components/export/usdz/USDZExporter.ts +48 -11
  96. package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +63 -0
  97. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +28 -16
  98. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +98 -21
  99. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +48 -2
  100. package/src/engine-components/ui/BaseUIComponent.ts +8 -3
  101. package/src/engine-components/ui/Canvas.ts +1 -1
  102. package/src/engine-components/ui/EventSystem.ts +5 -1
  103. package/src/engine-components/ui/Image.ts +16 -1
  104. package/src/engine-components/ui/Interfaces.ts +1 -1
  105. package/src/engine-components/ui/Layout.ts +2 -0
  106. package/src/engine-components/ui/PointerEvents.ts +16 -2
  107. package/src/engine-components/ui/RaycastUtils.ts +6 -1
  108. package/src/engine-components/ui/RectTransform.ts +11 -11
  109. package/src/engine-components/ui/Text.ts +1 -3
@@ -1,3 +1,5 @@
1
+ import { Renderer } from '../../Renderer';
2
+ import { GameObject } from '../../Component';
1
3
  import {
2
4
  PlaneGeometry,
3
5
  Texture,
@@ -19,6 +21,9 @@ import {
19
21
  MeshStandardMaterial,
20
22
  sRGBEncoding,
21
23
  MeshPhysicalMaterial,
24
+ Object3D,
25
+ MeshBasicMaterial,
26
+ SkinnedMesh,
22
27
  } from 'three';
23
28
  import * as fflate from 'three/examples/jsm/libs/fflate.module.js';
24
29
 
@@ -48,7 +53,6 @@ class USDObject {
48
53
  parent: USDObject | null;
49
54
  children: Array<USDObject | null> = [];
50
55
  _eventListeners: {};
51
- mesh: any;
52
56
 
53
57
  static createEmptyParent( object ) {
54
58
 
@@ -92,7 +96,7 @@ class USDObject {
92
96
 
93
97
  clone() {
94
98
 
95
- const clone = new USDObject( MathUtils.generateUUID(), this.name, this.matrix, this.mesh, this.material );
99
+ const clone = new USDObject( MathUtils.generateUUID(), this.name, this.matrix, this.geometry, this.material );
96
100
  clone.isDynamic = this.isDynamic;
97
101
  return clone;
98
102
 
@@ -383,9 +387,20 @@ class USDZExporterContext {
383
387
 
384
388
  }
385
389
 
390
+ /**[documentation](https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/preliminary_anchoringapi/preliminary_anchoring_type) */
391
+ export type Anchoring = "plane" | "image" | "face" | "none"
392
+ /**[documentation](https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/preliminary_anchoringapi/preliminary_planeanchoring_alignment) */
393
+ export type Alignment = "horizontal" | "vertical" | "any";
394
+
386
395
  class USDZExporterOptions {
387
- ar: { anchoring: { type: string } } = { anchoring: { type: 'plane' } };
388
- planeAnchoring: { alignment: string } = { alignment: 'horizontal' };
396
+ ar: {
397
+ anchoring: { type: Anchoring },
398
+ planeAnchoring: { alignment: Alignment },
399
+ } = {
400
+ anchoring: { type: 'plane' },
401
+ planeAnchoring: { alignment: 'horizontal' }
402
+ };
403
+ quickLookCompatible: boolean = false;
389
404
  extensions: any[] = [];
390
405
  }
391
406
 
@@ -426,7 +441,7 @@ class USDZExporter {
426
441
 
427
442
  parseDocument( context );
428
443
 
429
- invokeAll( context, 'onAfterSerialize' );
444
+ await invokeAll( context, 'onAfterSerialize' );
430
445
 
431
446
  context.output += buildMaterials( materials, textures );
432
447
 
@@ -499,21 +514,33 @@ class USDZExporter {
499
514
 
500
515
  }
501
516
 
502
- function traverseVisible( object, parentModel, context ) {
517
+ function traverseVisible( object: Object3D, parentModel: USDObject, context: USDZExporterContext ) {
503
518
 
504
519
  if ( ! object.visible ) return;
505
-
520
+
506
521
  let model: USDObject | undefined = undefined;
507
- const geometry = object.geometry;
508
- const material = object.material;
522
+ let geometry: BufferGeometry | undefined = undefined;
523
+ let material: Material | Material[] | undefined = undefined;
524
+
525
+ if (object instanceof Mesh) {
526
+ geometry = object.geometry;
527
+ material = object.material;
528
+ }
509
529
 
530
+ // TODO what should be do with disabled renderers?
531
+ // Here we just assume they're off, and don't export them
532
+ const renderer = GameObject.getComponent( object, Renderer )
533
+ if (renderer && !renderer.enabled) {
534
+ geometry = undefined;
535
+ material = undefined;
536
+ }
510
537
 
511
- if ( object.isMesh && material && (material.isMeshStandardMaterial || material.isMeshBasicMaterial) && ! object.isSkinnedMesh ) {
538
+ if ( object instanceof Mesh && material && (material instanceof MeshStandardMaterial || material instanceof MeshBasicMaterial) && ! (object instanceof SkinnedMesh )) {
512
539
 
513
540
  const name = getObjectId( object );
514
541
  model = new USDObject( object.uuid, name, object.matrix, geometry, material );
515
542
 
516
- } else if ( object.isCamera ) {
543
+ } else if ( object instanceof Camera ) {
517
544
 
518
545
  const name = getObjectId( object );
519
546
  model = new USDObject( object.uuid, name, object.matrix, undefined, undefined, object );
@@ -567,7 +594,7 @@ function traverseVisible( object, parentModel, context ) {
567
594
 
568
595
  }
569
596
 
570
- function parseDocument( context: USDZExporterContext ) {
597
+ async function parseDocument( context: USDZExporterContext ) {
571
598
 
572
599
  for ( const child of context.document.children ) {
573
600
 
@@ -594,7 +621,7 @@ function parseDocument( context: USDZExporterContext ) {
594
621
 
595
622
  writer.appendLine( `token preliminary:anchoring:type = "${context.exporter.sceneAnchoringOptions.ar.anchoring.type}"` );
596
623
  if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'plane')
597
- writer.appendLine( `token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.planeAnchoring.alignment}"` );
624
+ writer.appendLine( `token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.ar.planeAnchoring.alignment}"` );
598
625
  // bit hacky as we don't have a callback here yet. Relies on the fact that the image is named identical in the ImageTracking extension.
599
626
  if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'image')
600
627
  writer.appendLine( `rel preliminary:imageAnchoring:referenceImage = </${context.document.name}/Scenes/Scene/AnchoringReferenceImage>` );
@@ -656,14 +683,27 @@ function addResources( object, context: USDZExporterContext ) {
656
683
 
657
684
  }
658
685
 
659
- function invokeAll( context: USDZExporterContext, name: string, writer: USDWriter | null = null ) {
686
+ async function invokeAll( context: USDZExporterContext, name: string, writer: USDWriter | null = null ) {
660
687
 
661
688
  if ( context.extensions ) {
662
689
 
663
690
  for ( const ext of context.extensions ) {
664
691
 
665
- if ( typeof ext[ name ] === 'function' )
666
- ext[ name ]( context, writer );
692
+ if ( !ext ) continue;
693
+
694
+ if ( typeof ext[ name ] === 'function' ) {
695
+
696
+ const method = ext[ name ];
697
+
698
+ const isAsync = method.constructor.name === "AsyncFunction";
699
+
700
+ if ( isAsync ) {
701
+ await method.call( ext, context, writer );
702
+ } else {
703
+ method.call( ext, context, writer );
704
+ }
705
+
706
+ }
667
707
 
668
708
  }
669
709
 
@@ -831,6 +871,14 @@ export function buildXform( model, writer, context ) {
831
871
  const material = model.material;
832
872
  const camera = model.camera;
833
873
  const name = model.name;
874
+
875
+ // postprocess node
876
+ if ( model.onBeforeSerialize ) {
877
+
878
+ model.onBeforeSerialize( writer, context );
879
+
880
+ }
881
+
834
882
  const transform = buildMatrix( matrix );
835
883
 
836
884
  if ( matrix.determinant() < 0 ) {
@@ -839,11 +887,12 @@ export function buildXform( model, writer, context ) {
839
887
 
840
888
  }
841
889
 
842
- if ( geometry )
890
+ if ( geometry ) {
843
891
  writer.beginBlock( `def Xform "${name}" (
844
- prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
845
- prepend apiSchemas = ["MaterialBindingAPI"]
892
+ prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
893
+ prepend apiSchemas = ["MaterialBindingAPI"]
846
894
  )` );
895
+ }
847
896
  else if ( camera )
848
897
  writer.beginBlock( `def Camera "${name}"` );
849
898
  else
@@ -1076,7 +1125,7 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
1076
1125
  const pad = ' ';
1077
1126
  const inputs: Array<string> = [];
1078
1127
  const samplers: Array<string> = [];
1079
- const exportForQuickLook = true;
1128
+ const exportForQuickLook = false;
1080
1129
 
1081
1130
  function buildTexture( texture, mapType, color: Color | undefined = undefined, opacity: number | undefined = undefined ) {
1082
1131
 
@@ -1088,6 +1137,11 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
1088
1137
 
1089
1138
  const repeat = texture.repeat.clone();
1090
1139
  const offset = texture.offset.clone();
1140
+ const rotation = texture.rotation;
1141
+
1142
+ // rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
1143
+ let xRotationOffset = Math.sin(rotation);
1144
+ let yRotationOffset = Math.cos(rotation);
1091
1145
 
1092
1146
  // texture coordinates start in the opposite corner, need to correct
1093
1147
  offset.y = 1 - offset.y - repeat.y;
@@ -1096,15 +1150,28 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
1096
1150
  // Apple Feedback: FB10036297 and FB11442287
1097
1151
  if ( exportForQuickLook ) {
1098
1152
 
1153
+ // This is NOT correct yet in QuickLook, but comes close for a range of models.
1154
+ // It becomes more incorrect the bigger the offset is
1155
+
1099
1156
  offset.x = offset.x / repeat.x;
1100
1157
  offset.y = offset.y / repeat.y;
1101
1158
 
1159
+ offset.x += xRotationOffset / repeat.x;
1160
+ offset.y += yRotationOffset - 1;
1161
+ }
1162
+
1163
+ else {
1164
+
1165
+ // results match glTF results exactly. verified correct in usdview.
1166
+ offset.x += xRotationOffset * repeat.x;
1167
+ offset.y += (1 - yRotationOffset) * repeat.y;
1168
+
1102
1169
  }
1103
1170
 
1104
1171
  textures[ id ] = texture;
1105
1172
  const uvReader = mapType == 'occlusion' ? 'uvReader_st2' : 'uvReader_st';
1106
1173
 
1107
- const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 );
1174
+ const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0 );
1108
1175
  const textureTransformInput = `</Materials/Material_${material.id}/${uvReader}.outputs:result>`;
1109
1176
  const textureTransformOutput = `</Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
1110
1177
 
@@ -1123,6 +1190,7 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
1123
1190
  float2 inputs:in.connect = ${textureTransformInput}
1124
1191
  float2 inputs:scale = ${buildVector2( repeat )}
1125
1192
  float2 inputs:translation = ${buildVector2( offset )}
1193
+ float inputs:rotation = ${(rotation / Math.PI * 180).toFixed( PRECISION )}
1126
1194
  float2 outputs:result
1127
1195
  }
1128
1196
  ` : '' }
@@ -13,6 +13,8 @@ import { showBalloonMessage, showBalloonWarning } from "../../../engine/debug/de
13
13
  import { Context } from "../../../engine/engine_setup";
14
14
  import { WebARSessionRoot } from "../../webxr/WebARSessionRoot";
15
15
  import { hasProLicense } from "../../../engine/engine_license";
16
+ import { BehaviorExtension } from "./extensions/behavior/Behaviour";
17
+ import { AudioExtension } from "./extensions/behavior/AudioExtension";
16
18
 
17
19
  const debug = getParam("debugusdz");
18
20
 
@@ -40,6 +42,9 @@ export class USDZExporter extends Behaviour {
40
42
  @serializable()
41
43
  exportFileName?: string;
42
44
 
45
+ @serializable(URL)
46
+ customUsdzFile?: string;
47
+
43
48
  @serializable(QuickLookOverlay)
44
49
  overlay?: QuickLookOverlay;
45
50
 
@@ -51,6 +56,9 @@ export class USDZExporter extends Behaviour {
51
56
  @serializable()
52
57
  planeAnchoringAlignment: "horizontal" | "vertical" | "any" = "horizontal";
53
58
 
59
+ @serializable()
60
+ interactive: boolean = true;
61
+
54
62
  extensions: IUSDExporterExtension[] = [];
55
63
 
56
64
  private link!: HTMLAnchorElement;
@@ -84,9 +92,12 @@ export class USDZExporter extends Behaviour {
84
92
  this.objectToExport = this.gameObject;
85
93
  if (!this.objectToExport?.children?.length && !(this.objectToExport as Mesh)?.isMesh)
86
94
  this.objectToExport = this.context.scene;
87
- }
88
-
89
95
 
96
+ if (this.interactive) {
97
+ this.extensions.push(new BehaviorExtension());
98
+ this.extensions.push(new AudioExtension());
99
+ }
100
+ }
90
101
 
91
102
  onEnable() {
92
103
  const ios = isiOS()
@@ -114,6 +125,35 @@ export class USDZExporter extends Behaviour {
114
125
  }
115
126
 
116
127
  async exportAsync() {
128
+
129
+ let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
130
+ if (!hasProLicense()) name += "-MadeWithNeedle";
131
+ name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
132
+
133
+ // ability to specify a custom USDZ file to be used instead of a dynamic one
134
+ if (this.customUsdzFile) {
135
+
136
+ // see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
137
+ const overlay = this.buildQuicklookOverlay();
138
+ if(debug) console.log(overlay);
139
+ const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
140
+ const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
141
+ const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
142
+ this.link.href = this.customUsdzFile + `#callToAction=${callToAction}&checkoutTitle=${checkoutTitle}&checkoutSubtitle=${checkoutSubtitle}&callToActionURL=${overlay.callToActionURL}`;
143
+
144
+ console.log(this.link.href)
145
+
146
+ if (!this.lastCallback) {
147
+ this.lastCallback = this.quicklookCallback.bind(this)
148
+ this.link.addEventListener('message', this.lastCallback);
149
+ }
150
+
151
+ // open quicklook
152
+ this.link.download = name + ".usdz";
153
+ this.link.click();
154
+ return;
155
+ }
156
+
117
157
  if (!this.objectToExport) return;
118
158
 
119
159
  // make sure we apply the AR scale
@@ -139,10 +179,6 @@ export class USDZExporter extends Behaviour {
139
179
  const eventArgs = { self: this, exporter: exporter, extensions: extensions, object: this.objectToExport };
140
180
  this.dispatchEvent(new CustomEvent("before-export", { detail: eventArgs }))
141
181
 
142
- let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
143
- if (!hasProLicense()) name += "-MadeWithNeedle";
144
- name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
145
-
146
182
  //@ts-ignore
147
183
  exporter.debug = debug;
148
184
 
@@ -157,12 +193,13 @@ export class USDZExporter extends Behaviour {
157
193
  ar: {
158
194
  anchoring: {
159
195
  type: this.anchoringType,
160
- }
161
- },
162
- planeAnchoring: {
163
- alignment: this.planeAnchoringAlignment,
196
+ },
197
+ planeAnchoring: {
198
+ alignment: this.planeAnchoringAlignment,
199
+ },
164
200
  },
165
- extensions: extensions
201
+ extensions: extensions,
202
+ quickLookCompatible: true,
166
203
  });
167
204
  const blob = new Blob([arraybuffer], { type: 'application/octet-stream' });
168
205
 
@@ -0,0 +1,63 @@
1
+ import { GameObject } from "../../../../Component";
2
+ import { IUSDExporterExtension } from "../../Extension";
3
+ import { USDObject, USDWriter, USDZExporterContext } from "../../ThreeUSDZExporter";
4
+ import { Object3D } from "three";
5
+ import { AudioSource } from "../../../../AudioSource";
6
+
7
+ export class AudioExtension implements IUSDExporterExtension {
8
+
9
+ get extensionName(): string {
10
+ return "Audio";
11
+ }
12
+
13
+ private files: string[] = [];
14
+
15
+ onExportObject?(object: Object3D, model : USDObject, _context: USDZExporterContext) {
16
+ // check if this object has an audio source, add the relevant schema in that case.
17
+ const audioSources = GameObject.getComponents(object, AudioSource);
18
+ if (audioSources.length) {
19
+ for (const audioSource of audioSources) {
20
+
21
+ // do nothing if this audio source is not set to play on awake -
22
+ // should be controlled via PlayAudioOnClick instead then.
23
+ if (!audioSource.playOnAwake)
24
+ continue;
25
+
26
+ const clipName = audioSource.clip.split("/").pop();
27
+
28
+ if (!this.files.includes(audioSource.clip)) {
29
+ this.files.push(audioSource.clip);
30
+ }
31
+
32
+ model.addEventListener('serialize', (writer: USDWriter, _context: USDZExporterContext) => {
33
+ writer.appendLine();
34
+ writer.beginBlock(`def SpatialAudio "${model.name}"`);
35
+ writer.appendLine(`uniform asset filePath = @audio/${clipName}@`);
36
+ writer.appendLine(`uniform token auralMode = "${ audioSource.spatialBlend > 0 ? "spatial" : "nonSpatial" }"`);
37
+ // theoretically we could do timeline-like audio sequencing with this.
38
+ writer.appendLine(`uniform token playbackMode = "${audioSource.loop ? "loopFromStage" : "onceFromStart" }"`);
39
+ writer.appendLine(`uniform float gain = ${audioSource.volume}`);
40
+ writer.closeBlock();
41
+ });
42
+ }
43
+ }
44
+ }
45
+
46
+ async onAfterSerialize(context: USDZExporterContext) {
47
+ console.warn("onAfterSerialize", this);
48
+ // write the files to the context.
49
+ for (const file of this.files) {
50
+
51
+ const clipName = file.split("/").pop();
52
+
53
+ // convert file (which is a path) to a blob.
54
+ const audio = await fetch(file);
55
+ const audioBlob = await audio.blob();
56
+ const arrayBuffer = await audioBlob.arrayBuffer();
57
+
58
+ const audioData: Uint8Array = new Uint8Array(arrayBuffer)
59
+
60
+ context.files["audio/" + clipName] = audioData;
61
+ }
62
+ }
63
+ }
@@ -1,23 +1,14 @@
1
- import { Behaviour, GameObject } from "../../../../Component";
2
- import { USDZExporter } from "../../USDZExporter";
1
+ import { GameObject } from "../../../../Component";
2
+ import { IContext } from "../../../../../engine/engine_types";
3
3
  import { IUSDExporterExtension } from "../../Extension";
4
4
  import { USDObject, USDWriter } from "../../ThreeUSDZExporter";
5
5
  import { BehaviorModel } from "./BehavioursBuilder";
6
- import { IContext } from "../../../../../engine/engine_types";
7
6
 
8
7
  export interface UsdzBehaviour {
9
8
  createBehaviours?(ext: BehaviorExtension, model: USDObject, context: IContext): void;
10
9
  beforeCreateDocument?(ext: BehaviorExtension, context: IContext): void;
11
10
  afterCreateDocument?(ext: BehaviorExtension, context: IContext): void;
12
- }
13
-
14
- export class USDZBehaviours extends Behaviour {
15
- start() {
16
- const exporter = GameObject.findObjectOfType(USDZExporter);
17
- if (exporter) {
18
- exporter.extensions.push(new BehaviorExtension());
19
- }
20
- }
11
+ afterSerialize?(ext: BehaviorExtension, context: IContext): void;
21
12
  }
22
13
 
23
14
  export class BehaviorExtension implements IUSDExporterExtension {
@@ -33,6 +24,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
33
24
  }
34
25
 
35
26
  behaviourComponents: Array<UsdzBehaviour> = [];
27
+ private behaviourComponentsCopy: Array<UsdzBehaviour> = [];
36
28
 
37
29
 
38
30
  onBeforeBuildDocument(context) {
@@ -40,8 +32,8 @@ export class BehaviorExtension implements IUSDExporterExtension {
40
32
  GameObject.foreachComponent(e, (comp) => {
41
33
  const c = comp as unknown as UsdzBehaviour;
42
34
  if (
43
- typeof c.createBehaviours === "function" ||
44
- typeof c.beforeCreateDocument === "function" ||
35
+ typeof c.createBehaviours === "function" ||
36
+ typeof c.beforeCreateDocument === "function" ||
45
37
  typeof c.afterCreateDocument === "function"
46
38
  ) {
47
39
  this.behaviourComponents.push(c);
@@ -52,7 +44,6 @@ export class BehaviorExtension implements IUSDExporterExtension {
52
44
  }
53
45
 
54
46
  onExportObject(_object, model: USDObject, context) {
55
-
56
47
  for (const beh of this.behaviourComponents) {
57
48
  beh.createBehaviours?.call(beh, this, model, context);
58
49
  }
@@ -63,10 +54,11 @@ export class BehaviorExtension implements IUSDExporterExtension {
63
54
  if (typeof beh.afterCreateDocument === "function")
64
55
  beh.afterCreateDocument(this, context);
65
56
  }
57
+ this.behaviourComponentsCopy = this.behaviourComponents.slice();
66
58
  this.behaviourComponents.length = 0;
67
59
  }
68
60
 
69
- onAfterHierarchy(context, writer : USDWriter) {
61
+ onAfterHierarchy(context, writer: USDWriter) {
70
62
  if (this.behaviours?.length) {
71
63
 
72
64
  // this.combineBehavioursWithSameTapActions();
@@ -80,6 +72,26 @@ export class BehaviorExtension implements IUSDExporterExtension {
80
72
  }
81
73
  }
82
74
 
75
+ async onAfterSerialize(context) {
76
+ console.log("onAfterSerialize", this.behaviourComponentsCopy)
77
+ for (const beh of this.behaviourComponentsCopy) {
78
+
79
+ console.log("behaviour", beh)
80
+ if (typeof beh.afterSerialize === "function") {
81
+
82
+ console.log("beh has afterSerialize", beh)
83
+
84
+ const isAsync = beh.afterSerialize.constructor.name === "AsyncFunction";
85
+
86
+ if ( isAsync ) {
87
+ await beh.afterSerialize(this, context);
88
+ } else {
89
+ beh.afterSerialize(this, context);
90
+ }
91
+ }
92
+ }
93
+ }
94
+
83
95
  // combine behaviours that have tap triggers on the same object
84
96
  // private combineBehavioursWithSameTapActions() {
85
97
  // // TODO: if behaviours have different settings (e.g. one is exclusive and another one is not) this wont work - we need more logic for that