@needle-tools/engine 4.11.0-next.8bfb2f5 → 4.11.0-next.c7baa24

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 (106) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +3 -1
  3. package/components.needle.json +1 -1
  4. package/dist/{gltf-progressive-CXVECA3a.js → gltf-progressive-BvlZQAkt.js} +3 -3
  5. package/dist/{gltf-progressive-D4Z_Khp3.min.js → gltf-progressive-CftVUJy3.min.js} +1 -1
  6. package/dist/{gltf-progressive-CHeORqEv.umd.cjs → gltf-progressive-GwdQV1Qx.umd.cjs} +1 -1
  7. package/dist/{needle-engine.bundle-8EmDJd2O.umd.cjs → needle-engine.bundle-DBDKvYj5.umd.cjs} +142 -142
  8. package/dist/{needle-engine.bundle-DtF-fcok.min.js → needle-engine.bundle-qNZSuWhD.min.js} +144 -144
  9. package/dist/{needle-engine.bundle-lul9TfB2.js → needle-engine.bundle-tE15q9uM.js} +6264 -6202
  10. package/dist/needle-engine.d.ts +6 -0
  11. package/dist/needle-engine.js +4 -4
  12. package/dist/needle-engine.min.js +1 -1
  13. package/dist/needle-engine.umd.cjs +1 -1
  14. package/dist/{postprocessing-DQ2pynXW.js → postprocessing-CJC0Npcd.js} +2 -2
  15. package/dist/{postprocessing-BsnRNRRS.umd.cjs → postprocessing-DrM4PWU3.umd.cjs} +1 -1
  16. package/dist/{postprocessing-BHMVuZQ1.min.js → postprocessing-l7zsdO_Q.min.js} +1 -1
  17. package/dist/{three-qw28ZtTy.min.js → three-BDW9I486.min.js} +13 -13
  18. package/dist/{three-CJSAehtG.js → three-MHVqtJYj.js} +1 -0
  19. package/dist/{three-examples-Doq0rvFU.js → three-examples-C5Ht-QFN.js} +1 -1
  20. package/dist/{three-examples-Deqc1bNw.umd.cjs → three-examples-CgwGHSgz.umd.cjs} +1 -1
  21. package/dist/{three-examples-BivkhnvN.min.js → three-examples-fvEPSC8L.min.js} +1 -1
  22. package/dist/{three-B-jwTHao.umd.cjs → three-iFaDq9U3.umd.cjs} +13 -13
  23. package/dist/{three-mesh-ui-CktOi6oI.js → three-mesh-ui-BjWTTk1R.js} +1 -1
  24. package/dist/{three-mesh-ui-CsHwj9cJ.umd.cjs → three-mesh-ui-Bm32sS2a.umd.cjs} +1 -1
  25. package/dist/{three-mesh-ui-DhYXcXZe.min.js → three-mesh-ui-CLdkp21K.min.js} +1 -1
  26. package/dist/{vendor-BcsPRUmt.umd.cjs → vendor-CAWj5cBK.umd.cjs} +2 -2
  27. package/dist/{vendor-CyfN5nor.js → vendor-DJBpoQcM.js} +608 -599
  28. package/dist/{vendor-DyavoogU.min.js → vendor-DWGd3dEf.min.js} +20 -20
  29. package/lib/engine/js-extensions/Object3D.d.ts +6 -0
  30. package/lib/engine/js-extensions/Object3D.js +15 -0
  31. package/lib/engine/js-extensions/Object3D.js.map +1 -1
  32. package/lib/engine-components/Collider.d.ts +26 -0
  33. package/lib/engine-components/Collider.js +26 -0
  34. package/lib/engine-components/Collider.js.map +1 -1
  35. package/lib/engine-components/ContactShadows.d.ts +11 -2
  36. package/lib/engine-components/ContactShadows.js +11 -2
  37. package/lib/engine-components/ContactShadows.js.map +1 -1
  38. package/lib/engine-components/DropListener.d.ts +3 -0
  39. package/lib/engine-components/DropListener.js +44 -21
  40. package/lib/engine-components/DropListener.js.map +1 -1
  41. package/lib/engine-components/Duplicatable.d.ts +2 -2
  42. package/lib/engine-components/Duplicatable.js +2 -2
  43. package/lib/engine-components/EventList.d.ts +18 -1
  44. package/lib/engine-components/EventList.js +18 -1
  45. package/lib/engine-components/EventList.js.map +1 -1
  46. package/lib/engine-components/GroundProjection.d.ts +3 -0
  47. package/lib/engine-components/GroundProjection.js +3 -0
  48. package/lib/engine-components/GroundProjection.js.map +1 -1
  49. package/lib/engine-components/Interactable.d.ts +4 -0
  50. package/lib/engine-components/Interactable.js +4 -0
  51. package/lib/engine-components/Interactable.js.map +1 -1
  52. package/lib/engine-components/OrbitControls.d.ts +4 -2
  53. package/lib/engine-components/OrbitControls.js +31 -3
  54. package/lib/engine-components/OrbitControls.js.map +1 -1
  55. package/lib/engine-components/RigidBody.d.ts +5 -0
  56. package/lib/engine-components/RigidBody.js +5 -0
  57. package/lib/engine-components/RigidBody.js.map +1 -1
  58. package/lib/engine-components/SeeThrough.js +20 -0
  59. package/lib/engine-components/SeeThrough.js.map +1 -1
  60. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +4 -2
  61. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +69 -14
  62. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  63. package/lib/engine-components/ui/Text.js +6 -1
  64. package/lib/engine-components/ui/Text.js.map +1 -1
  65. package/lib/engine-components/utils/LookAt.d.ts +3 -0
  66. package/lib/engine-components/utils/LookAt.js +3 -0
  67. package/lib/engine-components/utils/LookAt.js.map +1 -1
  68. package/lib/engine-components/utils/OpenURL.d.ts +2 -1
  69. package/lib/engine-components/utils/OpenURL.js +2 -1
  70. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  71. package/lib/engine-components/web/ScrollFollow.js +1 -2
  72. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  73. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +2 -0
  74. package/lib/engine-components/webxr/WebARCameraBackground.js +2 -0
  75. package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
  76. package/lib/engine-components/webxr/WebARSessionRoot.d.ts +1 -1
  77. package/lib/engine-components/webxr/WebARSessionRoot.js +1 -1
  78. package/lib/engine-components/webxr/WebXR.d.ts +2 -0
  79. package/lib/engine-components/webxr/WebXR.js +2 -0
  80. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  81. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +29 -0
  82. package/lib/engine-components/webxr/WebXRImageTracking.js +29 -0
  83. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  84. package/package.json +2 -2
  85. package/plugins/vite/index.js +4 -1
  86. package/plugins/vite/needle-app.js +103 -57
  87. package/src/engine/js-extensions/Object3D.ts +24 -0
  88. package/src/engine-components/Collider.ts +27 -1
  89. package/src/engine-components/ContactShadows.ts +12 -4
  90. package/src/engine-components/DropListener.ts +45 -24
  91. package/src/engine-components/Duplicatable.ts +2 -2
  92. package/src/engine-components/EventList.ts +18 -1
  93. package/src/engine-components/GroundProjection.ts +4 -1
  94. package/src/engine-components/Interactable.ts +4 -1
  95. package/src/engine-components/OrbitControls.ts +27 -3
  96. package/src/engine-components/RigidBody.ts +6 -1
  97. package/src/engine-components/SeeThrough.ts +42 -2
  98. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +117 -17
  99. package/src/engine-components/ui/Text.ts +11 -2
  100. package/src/engine-components/utils/LookAt.ts +3 -0
  101. package/src/engine-components/utils/OpenURL.ts +3 -2
  102. package/src/engine-components/web/ScrollFollow.ts +1 -1
  103. package/src/engine-components/webxr/WebARCameraBackground.ts +2 -0
  104. package/src/engine-components/webxr/WebARSessionRoot.ts +1 -1
  105. package/src/engine-components/webxr/WebXR.ts +2 -0
  106. package/src/engine-components/webxr/WebXRImageTracking.ts +30 -3
@@ -1,11 +1,14 @@
1
- import { Material, Object3D, Vector3 } from "three";
1
+ import { Material, Object3D, Object3DEventMap, Vector3 } from "three";
2
2
 
3
3
  import { Gizmos } from "../engine/engine_gizmos.js";
4
4
  import { Mathf } from "../engine/engine_math.js";
5
5
  import { serializable } from "../engine/engine_serialization_decorator.js";
6
6
  import { getTempVector } from "../engine/engine_three_utils.js";
7
7
  import { getParam } from "../engine/engine_utils.js";
8
+ import { USDObject, USDZExporterContext } from "./api.js";
8
9
  import { Behaviour } from "./Component.js";
10
+ import { IUSDExporterExtension } from "./export/usdz/Extension.js";
11
+ import { USDZExporter } from "./export/usdz/USDZExporter.js";
9
12
  import { Renderer } from "./Renderer.js";
10
13
 
11
14
  const debugSeeThrough = getParam("debugseethrough");
@@ -102,6 +105,7 @@ export class SeeThrough extends Behaviour {
102
105
  onEnable() {
103
106
  this._needsUpdate = true;
104
107
  this._renderer = null;
108
+ SeeThroughUsdzExporterPlugin.components.push(this);
105
109
  }
106
110
 
107
111
  /** @internal */
@@ -118,6 +122,9 @@ export class SeeThrough extends Behaviour {
118
122
  this.rendererMaterials.delete(r);
119
123
  this.rendererMaterialsOriginal.delete(r);
120
124
  });
125
+
126
+ const index = SeeThroughUsdzExporterPlugin.components.indexOf(this);
127
+ if (index !== -1) SeeThroughUsdzExporterPlugin.components.splice(index, 1);
121
128
  }
122
129
 
123
130
  /**
@@ -253,4 +260,37 @@ export class SeeThrough extends Behaviour {
253
260
  });
254
261
  }
255
262
 
256
- }
263
+ }
264
+
265
+
266
+ ;
267
+ class SeeThroughUsdzExporterPlugin implements IUSDExporterExtension {
268
+
269
+ static readonly components: SeeThrough[] = [];
270
+
271
+ get extensionName() {
272
+ return "SeeThrough";
273
+ }
274
+
275
+ // onExportObject(object: Object3D<Object3DEventMap>, model: USDObject, context: USDZExporterContext) {
276
+ // const component = SeeThroughUsdzExporterPlugin.components.find(c => c.gameObject === object);
277
+ // if(!component) return;
278
+ // console.log("OH MY GOD SEE THROUGH USDZ EXPORTER", component, model);
279
+
280
+ // model.materialName = "AlphaHashMaterialInstance"; // we could make this unique per object if needed
281
+
282
+ // model.addEventListener("serialize", (writer, context) => {
283
+ // writer.appendLine(`# SeeThrough component on ${object.name}`);
284
+ // });
285
+ // }
286
+
287
+ }
288
+
289
+ const seeThroughUsdzExporterPlugin = new SeeThroughUsdzExporterPlugin();
290
+
291
+ USDZExporter.beforeExport.addEventListener(args => {
292
+ if (SeeThroughUsdzExporterPlugin.components.length === 0) return;
293
+ if (args.exporter.extensions.includes(seeThroughUsdzExporterPlugin) === false) {
294
+ args.exporter.extensions.push(seeThroughUsdzExporterPlugin);
295
+ }
296
+ });
@@ -112,6 +112,10 @@ const PositionIdentity = new Vector3();
112
112
  const QuaternionIdentity = new Quaternion();
113
113
  const ScaleIdentity = new Vector3(1,1,1);
114
114
 
115
+ // #region USDObject
116
+
117
+ type USDObjectEventType = "serialize" & ({} & string);
118
+
115
119
  class USDObject {
116
120
 
117
121
  static USDObject_export_id = 0;
@@ -153,12 +157,13 @@ class USDObject {
153
157
  private set isDynamic( value ) { this._isDynamic = value; }
154
158
  geometry: BufferGeometry | null;
155
159
  material: MeshStandardMaterial | MeshBasicMaterial | Material | MeshPhysicalNodeMaterial | null;
160
+ // usdMaterial?: USDMaterial;
156
161
  camera: PerspectiveCamera | OrthographicCamera | null;
157
162
  parent: USDObject | null;
158
163
  skinnedMesh: SkinnedMesh | null;
159
164
  children: Array<USDObject | null> = [];
160
165
  animations: AnimationClip[] | null;
161
- _eventListeners: {};
166
+ _eventListeners: Record<USDObjectEventType, Function[]>;
162
167
 
163
168
  // these are for tracking which xformops are needed
164
169
  needsTranslate: boolean = false;
@@ -201,7 +206,7 @@ class USDObject {
201
206
  this.camera = camera;
202
207
  this.parent = null;
203
208
  this.children = [];
204
- this._eventListeners = {};
209
+ this._eventListeners = {} as Record<USDObjectEventType, Function[]>;
205
210
  this._isDynamic = false;
206
211
  this.skinnedMesh = skinnedMesh;
207
212
  this.animations = animations;
@@ -287,7 +292,7 @@ class USDObject {
287
292
 
288
293
  }
289
294
 
290
- addEventListener( evt, listener: ( writer: USDWriter, context: USDZExporterContext ) => void ) {
295
+ addEventListener( evt : USDObjectEventType, listener: ( writer: USDWriter, context: USDZExporterContext ) => void ) {
291
296
 
292
297
  if ( ! this._eventListeners[ evt ] ) this._eventListeners[ evt ] = [];
293
298
  this._eventListeners[ evt ].push( listener );
@@ -316,6 +321,67 @@ class USDObject {
316
321
  }
317
322
 
318
323
 
324
+ // #region USDMaterial
325
+
326
+ // class MaterialInput {
327
+ // name: string;
328
+ // }
329
+
330
+ class USDMaterial {
331
+
332
+ static USDMaterial_id = 0;
333
+
334
+ readonly material: Material;
335
+ readonly id: number;
336
+
337
+ name: string;
338
+
339
+ isOverride: boolean = false;
340
+ isInstanceable: boolean = false;
341
+
342
+ constructor( material: Material ) {
343
+ this.material = material;
344
+ this.id = USDMaterial.USDMaterial_id ++;
345
+ this.name = makeNameSafe( material.name || 'Material_' + this.id );
346
+ }
347
+
348
+ readonly inputs: Record<string, string | number | boolean | number[] | undefined> = {};
349
+
350
+ // addInput( name: string, value: "" ) {
351
+
352
+
353
+
354
+ // }
355
+
356
+
357
+ serialize( writer: USDWriter, _context: USDZExporterContext ) {
358
+
359
+ const name = this.name;
360
+
361
+ writer.appendLine( `def Material "${this.name}" ${name ?`( displayName = "${makeDisplayNameSafe(name)}" )` : ''}` );
362
+ writer.beginBlock();
363
+
364
+
365
+ writer.closeBlock();
366
+
367
+ // def Material "${materialName}" ${material.name ?`(
368
+ // displayName = "${material.name}"
369
+ // )` : ''}
370
+ // {
371
+ // token outputs:mtlx:surface.connect = ${materialRoot}/${materialName}/Occlusion.outputs:out>
372
+
373
+ // def Shader "Occlusion"
374
+ // {
375
+ // uniform token info:id = "${mode}"
376
+ // token outputs:out
377
+ // }
378
+ // }`;
379
+
380
+ }
381
+ }
382
+
383
+ // #region USDDocument
384
+
319
385
  class USDDocument extends USDObject {
320
386
 
321
387
  stageLength: number;
@@ -448,6 +514,7 @@ ${comments}
448
514
 
449
515
  }
450
516
 
517
+
451
518
  const newLine = '\n';
452
519
  const materialRoot = '</StageRoot/Materials';
453
520
 
@@ -540,6 +607,8 @@ class USDWriter {
540
607
 
541
608
  declare type TextureMap = {[name: string]: {texture: Texture, scale?: Vector4}};
542
609
 
610
+ // #region USDZExporterContext
611
+
543
612
  class USDZExporterContext {
544
613
  root?: Object3D;
545
614
  exporter: USDZExporter;
@@ -576,6 +645,10 @@ class USDZExporterContext {
576
645
 
577
646
  }
578
647
 
648
+ makeNameSafe( str ) {
649
+ return makeNameSafe( str );
650
+ }
651
+
579
652
  }
580
653
 
581
654
  /**[documentation](https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/preliminary_anchoringapi/preliminary_anchoring_type) */
@@ -597,6 +670,8 @@ class USDZExporterOptions {
597
670
  exportInvisible: boolean = false;
598
671
  }
599
672
 
673
+ // #region USDZExporter
674
+
600
675
  class USDZExporter {
601
676
  debug: boolean;
602
677
  pruneUnusedNodes: boolean;
@@ -698,13 +773,7 @@ class USDZExporter {
698
773
  }
699
774
 
700
775
  Progress.report('export-usdz', { message: "Parsing document", autoStep: 10 });
701
- await parseDocument( context, () => {
702
- // injected after stageRoot.
703
- // TODO property use context/writer instead of string concat
704
- Progress.report('export-usdz', "Building materials");
705
- const result = buildMaterials( materials, textures, options.quickLookCompatible );
706
- return result;
707
- } );
776
+ await parseDocument( context, options );
708
777
 
709
778
  Progress.report("export-usdz", "Invoking onAfterSerialize");
710
779
  await invokeAll( context, 'onAfterSerialize' );
@@ -726,7 +795,7 @@ class USDZExporter {
726
795
  const final = header + '\n' + context.output;
727
796
 
728
797
  // full output file
729
- if ( this.debug ) console.log( final );
798
+ if ( this.debug ) console.debug( final );
730
799
 
731
800
  files[ modelFileName ] = fflate.strToU8( final );
732
801
  context.output = '';
@@ -822,6 +891,9 @@ class USDZExporter {
822
891
 
823
892
  }
824
893
 
894
+ // #endregion
895
+
896
+ // #region traverse
825
897
  function traverse( object: Object3D, parentModel: USDObject, context: USDZExporterContext, keepObject?: (object: Object3D) => boolean ) {
826
898
 
827
899
  if (!context.exportInvisible && !object.visible) return;
@@ -919,6 +991,8 @@ function traverse( object: Object3D, parentModel: USDObject, context: USDZExport
919
991
 
920
992
  }
921
993
 
994
+ // #endregion
995
+
922
996
  function logUsdHierarchy( object: USDObject, prefix: string, ...extraLogObjects: any[] ) {
923
997
 
924
998
  const item = {};
@@ -1020,7 +1094,8 @@ function prune ( object: USDObject, options : {
1020
1094
  return canBePruned;
1021
1095
  }
1022
1096
 
1023
- async function parseDocument( context: USDZExporterContext, afterStageRoot: () => string ) {
1097
+ // #region parseDocument
1098
+ async function parseDocument( context: USDZExporterContext, options: USDZExporterOptions ) {
1024
1099
 
1025
1100
  Progress.start("export-usdz-resources", "export-usdz");
1026
1101
  const resources: Array<() => void> = [];
@@ -1086,14 +1161,21 @@ async function parseDocument( context: USDZExporterContext, afterStageRoot: () =
1086
1161
 
1087
1162
  writer.closeBlock();
1088
1163
  writer.closeBlock();
1089
- writer.appendLine(afterStageRoot());
1164
+
1165
+ // TODO property use context/writer instead of string concat
1166
+ Progress.report('export-usdz', "Building materials");
1167
+ const result = buildMaterials( context.materials, context.textures, options.quickLookCompatible );
1168
+ writer.appendLine(result);
1169
+
1090
1170
  writer.closeBlock();
1091
1171
 
1092
1172
  Progress.report("export-usdz", "write to string")
1093
1173
  context.output += writer.toString();
1094
1174
 
1095
1175
  }
1176
+ // #endregion
1096
1177
 
1178
+ // #region addResources
1097
1179
  function addResources( object: USDObject | null, context: USDZExporterContext, resources: Array<() => void>) {
1098
1180
 
1099
1181
  if ( !object ) return;
@@ -1132,7 +1214,7 @@ function addResources( object: USDObject | null, context: USDZExporterContext, r
1132
1214
 
1133
1215
  if ( material ) {
1134
1216
 
1135
- if ( ! ( material.uuid in context.materials ) ) {
1217
+ if ( context.materials.get( material.uuid ) === undefined ) {
1136
1218
 
1137
1219
  context.materials[ material.uuid ] = material;
1138
1220
 
@@ -1169,6 +1251,10 @@ async function invokeAll( context: USDZExporterContext, name: string, writer: US
1169
1251
  }
1170
1252
 
1171
1253
  }
1254
+
1255
+ // #endregion
1256
+
1257
+ // #region GPU utils
1172
1258
  let _renderer: WebGLRenderer | null = null;
1173
1259
  let renderTarget: WebGLRenderTarget | null = null;
1174
1260
  let fullscreenQuadGeometry: PlaneGeometry | null;
@@ -1431,7 +1517,9 @@ function getPathToSkeleton(bone: Object3D, assumedRoot: Object3D) {
1431
1517
  return path;
1432
1518
  }
1433
1519
 
1434
- // Xform
1520
+ // #endregion
1521
+
1522
+ // #region XForm
1435
1523
 
1436
1524
  export function buildXform( model: USDObject | null, writer: USDWriter, context: USDZExporterContext ) {
1437
1525
 
@@ -1635,7 +1723,7 @@ function buildMatrixRow( array, offset ) {
1635
1723
 
1636
1724
  }
1637
1725
 
1638
- // Mesh
1726
+ // #region Mesh
1639
1727
 
1640
1728
  function buildMeshObject( geometry: BufferGeometry, bonesArray: Bone[] = [], quickLookCompatible: boolean = true) {
1641
1729
 
@@ -1948,7 +2036,9 @@ function buildVector2Array( attribute: BufferAttribute | InterleavedBufferAttrib
1948
2036
 
1949
2037
  }
1950
2038
 
1951
- // Materials
2039
+ // #endregion
2040
+
2041
+ // #region Materials
1952
2042
 
1953
2043
  function buildMaterials( materials: Map<string, Material>, textures: TextureMap, quickLookCompatible = false ) {
1954
2044
 
@@ -2103,10 +2193,14 @@ function buildTexture( texture: Texture, mapType: MapType, quickLookCompatible:
2103
2193
  }`;
2104
2194
  }
2105
2195
 
2196
+
2106
2197
  function buildMaterial( material: MeshBasicMaterial, textures: TextureMap, quickLookCompatible = false ) {
2107
2198
 
2108
2199
  const materialName = getMaterialName(material);
2109
2200
 
2201
+ // TODO: refactor to USDMaterial class
2202
+ // const usdMaterial = new USDMaterial(material);
2203
+
2110
2204
  // Special case: occluder material
2111
2205
  // Supported on iOS 18+ and visionOS 1+
2112
2206
  const isShadowCatcherMaterial =
@@ -2330,6 +2424,7 @@ function buildMaterial( material: MeshBasicMaterial, textures: TextureMap, quick
2330
2424
  }
2331
2425
  }
2332
2426
 
2427
+
2333
2428
  return `
2334
2429
 
2335
2430
  def Material "${materialName}" ${material.name ?`(
@@ -2368,6 +2463,8 @@ ${samplers.join( '\n' )}` : ''}
2368
2463
  }`;
2369
2464
  }
2370
2465
 
2466
+ // #endregion
2467
+
2371
2468
  function buildColor( color ) {
2372
2469
 
2373
2470
  return `(${color.r}, ${color.g}, ${color.b})`;
@@ -2407,6 +2504,9 @@ const formatsWithAlphaChannel = [
2407
2504
  36492, // RGBA_BPTC_Format
2408
2505
  ];
2409
2506
 
2507
+
2508
+ // #region exports
2509
+
2410
2510
  export {
2411
2511
  buildMatrix,
2412
2512
  decompressGpuTexture,
@@ -284,7 +284,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
284
284
  return opts;
285
285
  }
286
286
 
287
- private feedText(text: string, richText: boolean) : void {
287
+ private feedText(text: string, richText: boolean): void {
288
288
  // if (!text || text.length <= 0) return;
289
289
  // if (!text ) return;
290
290
  if (debug) console.log("feedText", this.uiObject, text, richText);
@@ -511,6 +511,15 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
511
511
  }
512
512
 
513
513
  private getFamilyNameWithCorrectSuffix(familyName: string, style: FontStyle): string {
514
+
515
+
516
+ // the URL decorator resolves the URL to absolute URLs - we need to remove the domain part since we're only interested in the path
517
+ if (familyName.startsWith("https:") || familyName.startsWith("http:")) {
518
+ const url = new URL(familyName);
519
+ familyName = url.pathname;
520
+ }
521
+
522
+
514
523
  // we can only change the style for the family if the name has a suffix (e.g. Arial-Bold)
515
524
  const styleSeparator = familyName.lastIndexOf('-');
516
525
  if (styleSeparator < 0) return familyName;
@@ -532,7 +541,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
532
541
  fontBaseName = fontBaseName.substring(pathSeparatorIndex + 1);
533
542
  }
534
543
  const isUpperCase = fontBaseName[0] === fontBaseName[0].toUpperCase();
535
- const fontNameWithoutSuffix = familyName.substring(0, styleSeparator);
544
+ const fontNameWithoutSuffix = familyName.substring(0, styleSeparator > pathSeparatorIndex ? styleSeparator : familyName.length);
536
545
  if (debug) console.log("Select font: ", familyName, FontStyle[style], fontBaseName, isUpperCase, fontNameWithoutSuffix);
537
546
 
538
547
  switch (style) {
@@ -11,6 +11,9 @@ import { Behaviour } from "../Component.js";
11
11
  /**
12
12
  * LookAt behaviour makes the object look at a target object or the camera.
13
13
  * It can also invert the forward direction and keep the up direction.
14
+ *
15
+ * @category Interactivity, Everywhere Actions
16
+ * @group Components
14
17
  */
15
18
  export class LookAt extends Behaviour implements UsdzBehaviour {
16
19
 
@@ -15,8 +15,9 @@ export enum OpenURLMode {
15
15
  NewWindow = 2
16
16
  }
17
17
 
18
- /**
19
- * OpenURL behaviour opens a URL in a new tab or window.
18
+ /**
19
+ * OpenURL behaviour opens a URL in a new tab or window when the object (or any if it's children) is clicked.
20
+ *
20
21
  * @category Interactivity
21
22
  * @group Components
22
23
  */
@@ -473,7 +473,7 @@ function tryGetElementsForSelector(index: number): Element | null {
473
473
 
474
474
  if (!needsScrollMarkerRefresh) {
475
475
  const element = needleScrollMarkerCache[index] || null;
476
- if (element) return element;
476
+ return element;
477
477
  }
478
478
  needsScrollMarkerRefresh = false;
479
479
  needleScrollMarkerCache.length = 0;
@@ -24,6 +24,8 @@ const debug = getParam("debugarcamera");
24
24
  /**
25
25
  * WebARCameraBackground is a component that allows to display the camera feed as a background in an AR session to more easily blend the real world with the virtual world or applying effects to the camera feed.
26
26
  *
27
+ * - Example: https://samples.needle.tools/ar-camera-background
28
+ *
27
29
  * @category XR
28
30
  * @group Components
29
31
  */
@@ -20,7 +20,7 @@ const invertForwardMatrix = new Matrix4().makeRotationY(Math.PI);
20
20
 
21
21
  /**
22
22
  * The WebARSessionRoot is the root object for a WebAR session and used to place the scene in AR.
23
- * It is also responsible for scaling the user in AR.
23
+ * It is also responsible for scaling the user in AR and to define the center of the AR scene. If not present in the scene it will be created automatically by the WebXR component when entering an AR session.
24
24
  *
25
25
  * @example
26
26
  * ```ts
@@ -27,6 +27,8 @@ const debugQuicklook = getParam("debugusdz");
27
27
  *
28
28
  * The WebXR component is a simple to use wrapper around the {@link NeedleXRSession} API and adds some additional features like creating buttons for AR, VR, enabling default movement behaviour ({@link XRControllerMovement}) and controller rendering ({@link XRControllerModel}), as well as handling AR placement and Quicklook USDZ export.
29
29
  *
30
+ * - Example: https://samples.needle.tools/collaborative-sandbox
31
+ *
30
32
  * @example Enable VR and AR support using code
31
33
  * ```ts
32
34
  * import { onStart, WebXR } from "@needle-tools/engine";
@@ -290,6 +290,16 @@ class ImageTrackingExtension implements IUSDExporterExtension {
290
290
 
291
291
 
292
292
  /**
293
+ * Add this component to a object to enable image tracking in WebXR sessions.
294
+ *
295
+ * You need to add at least one {@link WebXRImageTrackingModel} to the `trackedImages` array to define which images to track and which objects to place on top of them.
296
+ *
297
+ * **NOTE:** For Android devices, image tracking currently requires the user to enable the `chrome://flags/#webxr-incubations` flag in Chrome.
298
+ *
299
+ * **NOTE:** For iOS only one image can be tracked at a time. If you have multiple images in the `trackedImages` array, only the first one will be tracked. You can use the {@link setPrimaryImage} method to change which image is tracked before entering the XR session.
300
+ *
301
+ * - Example: https://samples.needle.tools/image-tracking
302
+ *
293
303
  * @category XR
294
304
  * @group Components
295
305
  */
@@ -313,7 +323,14 @@ export class WebXRImageTracking extends Behaviour {
313
323
 
314
324
  /**
315
325
  * Add an image to track. If the image is already in the trackedImages array it won't be added again.
316
- * Note: that adding images at runtime *while* in AR is not supported.
326
+ * Note: that adding images at runtime *while* in AR is not supported.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * const imageTracking = GameObject.getComponent(myGameObject, WebXRImageTracking);
331
+ * const image = new WebXRImageTrackingModel({ url: "https://example.com/my-marker.png", widthInMeters: 0.2, object: myObject });
332
+ * imageTracking.addImage(image, true); // add and set as primary image
333
+ * ```
317
334
  */
318
335
  addImage(image: WebXRImageTrackingModel, asPrimary: boolean = false) {
319
336
  if (!this.trackedImages.includes(image)) {
@@ -324,7 +341,11 @@ export class WebXRImageTracking extends Behaviour {
324
341
  }
325
342
 
326
343
 
327
-
344
+ /**
345
+ * List of images to track in the WebXR session. Use {@link WebXRImageTrackingModel} to define each image and the object to place on top of it.
346
+ *
347
+ * Use the `addImage()` and `setPrimaryImage()` methods to modify this array at runtime.
348
+ */
328
349
  @serializable(WebXRImageTrackingModel)
329
350
  readonly trackedImages: WebXRImageTrackingModel[] = [];
330
351
 
@@ -339,6 +360,7 @@ export class WebXRImageTracking extends Behaviour {
339
360
 
340
361
  private _supported: boolean = true;
341
362
 
363
+ /** @internal */
342
364
  awake(): void {
343
365
  if (debug) console.log(this)
344
366
  if (!this.trackedImages) return;
@@ -348,9 +370,11 @@ export class WebXRImageTracking extends Behaviour {
348
370
  }
349
371
  }
350
372
  }
373
+ /** @internal */
351
374
  onEnable() {
352
375
  USDZExporter.beforeExport.addEventListener(this.onBeforeUSDZExport);
353
376
  }
377
+ /** @internal */
354
378
  onDisable(): void {
355
379
  USDZExporter.beforeExport.removeEventListener(this.onBeforeUSDZExport);
356
380
  }
@@ -362,7 +386,7 @@ export class WebXRImageTracking extends Behaviour {
362
386
  }
363
387
 
364
388
 
365
-
389
+ /** @internal */
366
390
  onBeforeXR(_mode: XRSessionMode, args: XRSessionInit & { trackedImages: Array<any> }): void {
367
391
  // console.log("onXRRequested", args, this.trackedImages)
368
392
  if (this.trackedImages) {
@@ -386,6 +410,7 @@ export class WebXRImageTracking extends Behaviour {
386
410
  }
387
411
  }
388
412
 
413
+ /** @internal */
389
414
  onEnterXR(_args: NeedleXREventArgs): void {
390
415
  if (this.trackedImages) {
391
416
  for (const trackedImage of this.trackedImages) {
@@ -408,6 +433,7 @@ export class WebXRImageTracking extends Behaviour {
408
433
  }
409
434
  };
410
435
 
436
+ /** @internal */
411
437
  onLeaveXR(_args: NeedleXREventArgs): void {
412
438
 
413
439
  if (!this.supported && DeviceUtilities.isAndroidDevice()) {
@@ -440,6 +466,7 @@ export class WebXRImageTracking extends Behaviour {
440
466
 
441
467
  private readonly webXRIncubationsWarning = "Image tracking is currently not supported on this device. On Chrome for Android, you can enable the <a target=\"_blank\" href=\"#\" onclick=\"() => console.log('I')\">chrome://flags/#webxr-incubations</a> flag.";
442
468
 
469
+ /** @internal */
443
470
  onUpdateXR(args: NeedleXREventArgs): void {
444
471
  this.currentImages.length = 0;
445
472