@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.
- package/CHANGELOG.md +22 -0
- package/dist/needle-engine.js +29554 -29281
- package/dist/needle-engine.min.js +357 -356
- package/dist/needle-engine.umd.cjs +354 -353
- package/lib/engine/codegen/register_types.js +4 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/debug/debug_overlay.js +2 -1
- package/lib/engine/debug/debug_overlay.js.map +1 -1
- package/lib/engine/engine_components.js +2 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_element_loading.js +4 -4
- package/lib/engine/engine_element_loading.js.map +1 -1
- package/lib/engine/engine_gameobject.js +2 -0
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_input.js +4 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +25 -4
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +2 -1
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_serialization_core.js +16 -1
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lighting_settings.js +10 -1
- package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
- package/lib/engine-components/Component.js +0 -3
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +9 -0
- package/lib/engine-components/SceneSwitcher.js +128 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +2 -1
- package/lib/engine-components/codegen/components.js +2 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/Extension.d.ts +3 -2
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +9 -5
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +64 -20
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +2 -0
- package/lib/engine-components/export/usdz/USDZExporter.js +44 -9
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +9 -0
- package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +48 -0
- package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +4 -5
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +19 -10
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +9 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +75 -5
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +20 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +45 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -1
- package/lib/engine-components/ui/BaseUIComponent.d.ts +1 -0
- package/lib/engine-components/ui/BaseUIComponent.js +8 -4
- package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
- package/lib/engine-components/ui/Canvas.js +1 -1
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.js +3 -0
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/engine-components/ui/Image.d.ts +3 -1
- package/lib/engine-components/ui/Image.js +15 -1
- package/lib/engine-components/ui/Image.js.map +1 -1
- package/lib/engine-components/ui/Interfaces.d.ts +1 -1
- package/lib/engine-components/ui/Layout.js +2 -0
- package/lib/engine-components/ui/Layout.js.map +1 -1
- package/lib/engine-components/ui/PointerEvents.d.ts +8 -1
- package/lib/engine-components/ui/PointerEvents.js +9 -1
- package/lib/engine-components/ui/PointerEvents.js.map +1 -1
- package/lib/engine-components/ui/RaycastUtils.js +5 -0
- package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
- package/lib/engine-components/ui/RectTransform.d.ts +2 -2
- package/lib/engine-components/ui/RectTransform.js +11 -12
- package/lib/engine-components/ui/RectTransform.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +0 -2
- package/lib/engine-components/ui/Text.js +0 -5
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugins/vite/license.js +2 -2
- package/src/engine/codegen/register_types.js +4 -2
- package/src/engine/debug/debug_overlay.ts +2 -1
- package/src/engine/engine_components.ts +2 -1
- package/src/engine/engine_element_loading.ts +4 -4
- package/src/engine/engine_gameobject.ts +3 -0
- package/src/engine/engine_input.ts +4 -1
- package/src/engine/engine_license.ts +25 -4
- package/src/engine/engine_physics_rapier.ts +2 -1
- package/src/engine/engine_serialization_core.ts +17 -1
- package/src/engine/extensions/NEEDLE_lighting_settings.ts +11 -1
- package/src/engine-components/Component.ts +1 -3
- package/src/engine-components/SceneSwitcher.ts +136 -1
- package/src/engine-components/codegen/components.ts +2 -1
- package/src/engine-components/export/usdz/Extension.ts +3 -2
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +89 -21
- package/src/engine-components/export/usdz/USDZExporter.ts +48 -11
- package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +63 -0
- package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +28 -16
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +98 -21
- package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +48 -2
- package/src/engine-components/ui/BaseUIComponent.ts +8 -3
- package/src/engine-components/ui/Canvas.ts +1 -1
- package/src/engine-components/ui/EventSystem.ts +5 -1
- package/src/engine-components/ui/Image.ts +16 -1
- package/src/engine-components/ui/Interfaces.ts +1 -1
- package/src/engine-components/ui/Layout.ts +2 -0
- package/src/engine-components/ui/PointerEvents.ts +16 -2
- package/src/engine-components/ui/RaycastUtils.ts +6 -1
- package/src/engine-components/ui/RectTransform.ts +11 -11
- 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.
|
|
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: {
|
|
388
|
-
|
|
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
|
-
|
|
508
|
-
|
|
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
|
|
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
|
|
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 (
|
|
666
|
-
|
|
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
|
-
|
|
845
|
-
|
|
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 =
|
|
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
|
-
|
|
163
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
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
|