@needle-tools/engine 4.11.0-next.91b9cf1 → 4.11.0-next.cc37c71

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 (130) 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-B63NpN_i.js → gltf-progressive-BvlZQAkt.js} +4 -4
  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-D4dsuq2U.js → needle-engine.bundle-BPZ6emFK.js} +7858 -7724
  8. package/dist/{needle-engine.bundle-DtfAXDjU.umd.cjs → needle-engine.bundle-CTY0RgBZ.umd.cjs} +150 -150
  9. package/dist/needle-engine.bundle-JV2ghuCa.min.js +1652 -0
  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/engine_physics.js +25 -2
  30. package/lib/engine/engine_physics.js.map +1 -1
  31. package/lib/engine/js-extensions/Object3D.d.ts +6 -0
  32. package/lib/engine/js-extensions/Object3D.js +15 -0
  33. package/lib/engine/js-extensions/Object3D.js.map +1 -1
  34. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +2 -1
  35. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  36. package/lib/engine-components/Collider.d.ts +26 -0
  37. package/lib/engine-components/Collider.js +26 -0
  38. package/lib/engine-components/Collider.js.map +1 -1
  39. package/lib/engine-components/ContactShadows.d.ts +11 -2
  40. package/lib/engine-components/ContactShadows.js +11 -2
  41. package/lib/engine-components/ContactShadows.js.map +1 -1
  42. package/lib/engine-components/DropListener.d.ts +3 -0
  43. package/lib/engine-components/DropListener.js +44 -21
  44. package/lib/engine-components/DropListener.js.map +1 -1
  45. package/lib/engine-components/Duplicatable.d.ts +2 -2
  46. package/lib/engine-components/Duplicatable.js +2 -2
  47. package/lib/engine-components/EventList.d.ts +18 -1
  48. package/lib/engine-components/EventList.js +18 -1
  49. package/lib/engine-components/EventList.js.map +1 -1
  50. package/lib/engine-components/GroundProjection.d.ts +3 -0
  51. package/lib/engine-components/GroundProjection.js +3 -0
  52. package/lib/engine-components/GroundProjection.js.map +1 -1
  53. package/lib/engine-components/Interactable.d.ts +4 -0
  54. package/lib/engine-components/Interactable.js +4 -0
  55. package/lib/engine-components/Interactable.js.map +1 -1
  56. package/lib/engine-components/OrbitControls.d.ts +4 -2
  57. package/lib/engine-components/OrbitControls.js +33 -3
  58. package/lib/engine-components/OrbitControls.js.map +1 -1
  59. package/lib/engine-components/RigidBody.d.ts +5 -0
  60. package/lib/engine-components/RigidBody.js +5 -0
  61. package/lib/engine-components/RigidBody.js.map +1 -1
  62. package/lib/engine-components/SeeThrough.js +20 -0
  63. package/lib/engine-components/SeeThrough.js.map +1 -1
  64. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +4 -2
  65. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +69 -14
  66. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  67. package/lib/engine-components/splines/SplineWalker.d.ts +43 -4
  68. package/lib/engine-components/splines/SplineWalker.js +88 -12
  69. package/lib/engine-components/splines/SplineWalker.js.map +1 -1
  70. package/lib/engine-components/ui/Text.js +6 -1
  71. package/lib/engine-components/ui/Text.js.map +1 -1
  72. package/lib/engine-components/utils/LookAt.d.ts +3 -0
  73. package/lib/engine-components/utils/LookAt.js +3 -0
  74. package/lib/engine-components/utils/LookAt.js.map +1 -1
  75. package/lib/engine-components/utils/OpenURL.d.ts +2 -1
  76. package/lib/engine-components/utils/OpenURL.js +2 -1
  77. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  78. package/lib/engine-components/web/Clickthrough.d.ts +2 -0
  79. package/lib/engine-components/web/Clickthrough.js +23 -1
  80. package/lib/engine-components/web/Clickthrough.js.map +1 -1
  81. package/lib/engine-components/web/ScrollFollow.d.ts +1 -9
  82. package/lib/engine-components/web/ScrollFollow.js +13 -30
  83. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  84. package/lib/engine-components/web/ViewBox.d.ts +16 -0
  85. package/lib/engine-components/web/ViewBox.js +35 -3
  86. package/lib/engine-components/web/ViewBox.js.map +1 -1
  87. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +2 -0
  88. package/lib/engine-components/webxr/WebARCameraBackground.js +2 -0
  89. package/lib/engine-components/webxr/WebARCameraBackground.js.map +1 -1
  90. package/lib/engine-components/webxr/WebARSessionRoot.d.ts +1 -1
  91. package/lib/engine-components/webxr/WebARSessionRoot.js +1 -1
  92. package/lib/engine-components/webxr/WebXR.d.ts +2 -0
  93. package/lib/engine-components/webxr/WebXR.js +2 -0
  94. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  95. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +29 -0
  96. package/lib/engine-components/webxr/WebXRImageTracking.js +29 -0
  97. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  98. package/package.json +2 -2
  99. package/plugins/common/needle-engine.js +41 -0
  100. package/plugins/common/worker.js +129 -0
  101. package/plugins/vite/asap.js +5 -23
  102. package/plugins/vite/dependencies.js +21 -11
  103. package/plugins/vite/index.js +7 -0
  104. package/plugins/vite/needle-app.js +194 -0
  105. package/src/engine/engine_physics.ts +27 -2
  106. package/src/engine/js-extensions/Object3D.ts +24 -0
  107. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +3 -1
  108. package/src/engine-components/Collider.ts +27 -1
  109. package/src/engine-components/ContactShadows.ts +12 -4
  110. package/src/engine-components/DropListener.ts +45 -24
  111. package/src/engine-components/Duplicatable.ts +2 -2
  112. package/src/engine-components/EventList.ts +18 -1
  113. package/src/engine-components/GroundProjection.ts +4 -1
  114. package/src/engine-components/Interactable.ts +4 -1
  115. package/src/engine-components/OrbitControls.ts +32 -5
  116. package/src/engine-components/RigidBody.ts +6 -1
  117. package/src/engine-components/SeeThrough.ts +42 -2
  118. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +117 -17
  119. package/src/engine-components/splines/SplineWalker.ts +99 -14
  120. package/src/engine-components/ui/Text.ts +11 -2
  121. package/src/engine-components/utils/LookAt.ts +3 -0
  122. package/src/engine-components/utils/OpenURL.ts +3 -2
  123. package/src/engine-components/web/Clickthrough.ts +28 -1
  124. package/src/engine-components/web/ScrollFollow.ts +16 -34
  125. package/src/engine-components/web/ViewBox.ts +35 -5
  126. package/src/engine-components/webxr/WebARCameraBackground.ts +2 -0
  127. package/src/engine-components/webxr/WebARSessionRoot.ts +1 -1
  128. package/src/engine-components/webxr/WebXR.ts +2 -0
  129. package/src/engine-components/webxr/WebXRImageTracking.ts +30 -3
  130. package/dist/needle-engine.bundle-B8HfDBoL.min.js +0 -1652
@@ -10,6 +10,9 @@ export class UsageMarker extends Behaviour
10
10
  public usedBy: any = null;
11
11
  }
12
12
 
13
-
13
+ /**
14
+ * An empty component that can be used to mark an object as interactable.
15
+ * @group Components
16
+ */
14
17
  /** @deprecated */
15
18
  export class Interactable extends Behaviour {}
@@ -255,7 +255,12 @@ export class OrbitControls extends Behaviour implements ICameraController {
255
255
  * @default 1
256
256
  */
257
257
  @serializable()
258
- targetLerpDuration = 1;
258
+ get targetLerpDuration() { return this._lookTargetLerpDuration; }
259
+ set targetLerpDuration(v) { this._lookTargetLerpDuration = v; }
260
+ private _lookTargetLerpDuration: number = 1;
261
+
262
+ @serializable(Object3D)
263
+ targetBounds: Object3D | null = null;
259
264
 
260
265
  /**
261
266
  * Rotate the camera left (or right) by the specified angle in radians.
@@ -326,7 +331,6 @@ export class OrbitControls extends Behaviour implements ICameraController {
326
331
  private _lookTargetStartPosition: Vector3 = new Vector3();
327
332
  private _lookTargetEndPosition: Vector3 = new Vector3();
328
333
  private _lookTargetLerp01: number = 0;
329
- private _lookTargetLerpDuration: number = 0;
330
334
 
331
335
  private _cameraLerpActive: boolean = false;
332
336
  private _cameraStartPosition: Vector3 = new Vector3();
@@ -685,10 +689,32 @@ export class OrbitControls extends Behaviour implements ICameraController {
685
689
  }
686
690
  }
687
691
 
692
+ if (this.targetBounds) {
693
+ // #region target bounds
694
+ const targetVector = this._controls.target;
695
+ const boundsCenter = this.targetBounds.worldPosition;
696
+ const boundsHalfSize = getTempVector(this.targetBounds.worldScale).multiplyScalar(0.5);
697
+ const min = getTempVector(boundsCenter).sub(boundsHalfSize);
698
+ const max = getTempVector(boundsCenter).add(boundsHalfSize);
699
+ const newTarget = getTempVector(this._controls.target).clamp(min, max);
700
+ const duration = .1;
701
+ if (duration <= 0) targetVector.copy(newTarget);
702
+ else targetVector.lerp(newTarget, this.context.time.deltaTime / duration);
703
+ if (this._lookTargetLerpActive) {
704
+ if (duration <= 0) this._lookTargetEndPosition.copy(newTarget);
705
+ else this._lookTargetEndPosition.lerp(newTarget, this.context.time.deltaTime / (duration * 5));
706
+ }
707
+ if (debug) {
708
+ Gizmos.DrawWireBox(boundsCenter, boundsHalfSize.multiplyScalar(2), 0xffaa00);
709
+ }
710
+ }
711
+
688
712
 
689
713
  if (this._controls) {
690
- if (this.debugLog)
691
- this._controls.domElement = this.context.renderer.domElement;
714
+ if (this.debugLog) this._controls.domElement = this.context.renderer.domElement;
715
+
716
+ const viewZoomFactor = 1 / (this.context.focusRectSettings?.zoom || 1);
717
+
692
718
  this._controls.enabled = !this._shouldDisable && this._camera === this.context.mainCameraComponent && !this.context.isInXR && !this._activePointerEvents.some(e => e.used);
693
719
  this._controls.keys = this.enableKeys ? defaultKeys : disabledKeys;
694
720
  this._controls.autoRotate = this.autoRotate;
@@ -699,6 +725,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
699
725
  this._controls.enableDamping = this.enableDamping;
700
726
  this._controls.dampingFactor = this.dampingFactor;
701
727
  this._controls.enablePan = this.enablePan;
728
+ this._controls.panSpeed = viewZoomFactor;
702
729
  this._controls.enableRotate = this.enableRotate;
703
730
  this._controls.minAzimuthAngle = this.minAzimuthAngle;
704
731
  this._controls.maxAzimuthAngle = this.maxAzimuthAngle;
@@ -1000,7 +1027,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
1000
1027
  */
1001
1028
  fitCamera(options?: OrbitFitCameraOptions);
1002
1029
  /** @deprecated Use fitCamera(options) */
1003
- fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<OrbitFitCameraOptions, "objects">);
1030
+ fitCamera(objects?: Object3D | Array<Object3D>, options?: Omit<OrbitFitCameraOptions, "objects">);
1004
1031
  fitCamera(objectsOrOptions?: Object3D | Array<Object3D> | OrbitFitCameraOptions, options?: OrbitFitCameraOptions): void {
1005
1032
 
1006
1033
 
@@ -134,7 +134,12 @@ class TransformWatch {
134
134
  }
135
135
 
136
136
  /**
137
- * A Rigidbody is used together with a Collider to create physical interactions between objects in the scene.
137
+ * A Rigidbody is used together with a Collider to create physical interactions between objects in the scene.
138
+ *
139
+ * - Example: https://samples.needle.tools/physics-basic
140
+ * - Example: https://samples.needle.tools/physics-playground
141
+ * - Example: https://samples.needle.tools/physics-&-animation
142
+ *
138
143
  * @category Physics
139
144
  * @group Components
140
145
  */
@@ -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,
@@ -1,5 +1,5 @@
1
1
 
2
- import { Object3D } from "three"
2
+ import { Object3D, Vector3 } from "three"
3
3
 
4
4
  import { Mathf } from "../../engine/engine_math.js";
5
5
  import { serializeable } from "../../engine/engine_serialization_decorator.js";
@@ -7,7 +7,10 @@ import { Behaviour } from "../Component.js";
7
7
  import { SplineContainer } from "./Spline.js";
8
8
 
9
9
  /**
10
- * Moves an object along a spline. Use this with a SplineContainer component.
10
+ * Moves an object along a spline.
11
+ * Use this with a SplineContainer component.
12
+ *
13
+ * - Example http://samples.needle.tools/splines
11
14
  */
12
15
  export class SplineWalker extends Behaviour {
13
16
 
@@ -17,18 +20,35 @@ export class SplineWalker extends Behaviour {
17
20
  @serializeable(SplineContainer)
18
21
  spline: SplineContainer | null = null;
19
22
 
20
- /** The object to move along the spline. If object is undefined then the spline walker will use the gameObject the component has been added to
23
+ /**
24
+ * The object to move along the spline.
25
+ * If object is undefined then the spline walker will use it's own object (gameObject).
26
+ * If object is null the spline walker will not move any object.
21
27
  * @default undefined
22
28
  */
23
29
  @serializeable(Object3D)
24
30
  object?: Object3D | null = undefined;
25
31
 
26
- /** The object to look at while moving along the spline
32
+
33
+
34
+ /**
35
+ * If true the object will rotate to look in the direction of the spline while moving along it.
36
+ * @default true
37
+ */
38
+ @serializeable()
39
+ useLookAt = true;
40
+
41
+ /**
42
+ * The object to look at while moving along the spline.
43
+ * If null the object will look in the direction of the spline.
44
+ * This can be disabled by setting useLookAt to false.
27
45
  * @default null
28
46
  */
29
47
  @serializeable(Object3D)
30
48
  lookAt: Object3D | null = null;
31
-
49
+
50
+
51
+
32
52
  /**
33
53
  * When clamp is set to true, the position01 value will be clamped between 0 and 1 and the object will not loop the spline.
34
54
  * @default false
@@ -36,7 +56,10 @@ export class SplineWalker extends Behaviour {
36
56
  @serializeable()
37
57
  clamp: boolean = false;
38
58
 
39
- /** The current position on the spline. The value ranges from 0 (start of the spline curve) to 1 (end of the spline curve)
59
+ /**
60
+ * The current position on the spline. The value ranges from 0 (start of the spline curve) to 1 (end of the spline curve)
61
+ *
62
+ * When setting this value, the position will be updated in the next frame.
40
63
  * @default 0
41
64
  */
42
65
  // @type float
@@ -46,7 +69,7 @@ export class SplineWalker extends Behaviour {
46
69
  }
47
70
  set position01(v: number) {
48
71
  this._position01 = v;
49
- this.updateFromPosition();
72
+ this._needsUpdate = true;
50
73
  }
51
74
 
52
75
  /** Resets the position to 0 */
@@ -68,25 +91,60 @@ export class SplineWalker extends Behaviour {
68
91
  @serializeable()
69
92
  duration: number = 10;
70
93
 
94
+ /**
95
+ * The strength with which the object is pulled to the spline.
96
+ * This can be used to create a "rubber band" effect when the object is moved away from the spline by other forces.
97
+ * A value of 0 means no pull, a value of 1 means the object is always on the spline.
98
+ * @default 1
99
+ */
100
+ pullStrength: number = 1;
101
+
71
102
 
72
103
  // #region internal
73
104
 
74
105
  private _position01: number = 0;
106
+ private _needsUpdate = false;
75
107
 
76
108
  /** @internal */
77
109
  start() {
78
- if(this.object === undefined) this.object = this.gameObject;
110
+ if (this.object === undefined) this.object = this.gameObject;
79
111
  this.updateFromPosition();
80
112
  }
81
113
 
114
+ /** @internal */
115
+ onEnable(): void {
116
+ window.addEventListener("pointerdown", this.onUserInput, { passive: true });
117
+ window.addEventListener("wheel", this.onUserInput, { passive: true });
118
+ }
119
+ /** @internal */
120
+ onDisable(): void {
121
+ window.removeEventListener("pointerdown", this.onUserInput);
122
+ window.removeEventListener("wheel", this.onUserInput);
123
+ }
124
+ private onUserInput = () => {
125
+ if (this.object?.contains(this.context.mainCamera)) {
126
+ this._needsUpdate = false;
127
+ this._performedUpdates += 999;
128
+ }
129
+ }
130
+
131
+ /** @internal */
82
132
  update() {
83
133
  if (this.autoRun) {
134
+ this._needsUpdate = true;
84
135
  this._position01 += this.context.time.deltaTime / this.duration;
136
+ }
137
+
138
+ if (this._needsUpdate) {
139
+ this._needsUpdate = false;
85
140
  this.updateFromPosition();
86
141
  }
87
142
  }
88
143
 
89
-
144
+ /**
145
+ * Updates the position of the object based on the current position01 value.
146
+ * @internal
147
+ */
90
148
  private updateFromPosition() {
91
149
  if (!this.spline || !this.spline.curve) return;
92
150
  if (!this.object) return;
@@ -96,11 +154,38 @@ export class SplineWalker extends Behaviour {
96
154
 
97
155
  const t = this._position01 >= 1 ? 1 : this._position01 % 1;
98
156
  const pt = this.spline.getPointAt(t);
99
- this.object.worldPosition = pt;
100
- if (!this.lookAt) {
101
- const tan = this.spline.getTangentAt(t);
102
- this.object.lookAt(pt.add(tan));
157
+
158
+ if (this.pullStrength >= 1) {
159
+ this.object.worldPosition = pt;
160
+ }
161
+ else {
162
+ if (this._position01 !== this._lastPosition01) {
163
+ this._performedUpdates = 0;
164
+ }
165
+ this._requiredUpdates = Math.round(100 / this.pullStrength);
166
+ if (this._performedUpdates < this._requiredUpdates) {
167
+ const wp = this.object.worldPosition;
168
+ this._performedUpdates++;
169
+ const pull = Mathf.clamp01(this.pullStrength);
170
+ const newPosition = this.object.worldPosition = wp.lerp(pt, pull * (this.context.time.deltaTime / .3));
171
+ this._lastPositionVector.copy(newPosition);
172
+ this._needsUpdate = true;
173
+ }
103
174
  }
104
- else this.object.lookAt(this.lookAt.worldPosition);
175
+
176
+ if (this.useLookAt) {
177
+ if (!this.lookAt) {
178
+ const tan = this.spline.getTangentAt(t);
179
+ this.object.lookAt(pt.add(tan));
180
+ }
181
+ else this.object.lookAt(this.lookAt.worldPosition);
182
+ }
183
+
184
+ this._lastPosition01 = this._position01;
105
185
  }
186
+
187
+ private _lastPosition01 = 0;
188
+ private _requiredUpdates: number = 0;
189
+ private _performedUpdates: number = 0;
190
+ private _lastPositionVector = new Vector3();
106
191
  }