@needle-tools/engine 2.52.0-pre → 2.53.0-pre
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 +9 -0
- package/dist/needle-engine.d.ts +187 -36
- package/dist/needle-engine.js +385 -385
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +40 -40
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/engine_components.js +2 -2
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_networking_instantiate.d.ts +1 -1
- package/lib/engine/engine_networking_instantiate.js +3 -0
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +6 -0
- package/lib/engine/engine_serialization_builtin_serializer.js +31 -6
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_serialization_core.d.ts +1 -0
- package/lib/engine/engine_serialization_core.js +6 -0
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_setup.js +2 -2
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine/engine_texture.d.ts +3 -0
- package/lib/engine/engine_texture.js +4 -0
- package/lib/engine/engine_texture.js.map +1 -0
- package/lib/engine/extensions/{NEEDLE_deferred_texture.d.ts → NEEDLE_progressive.d.ts} +2 -2
- package/lib/engine/extensions/{NEEDLE_deferred_texture.js → NEEDLE_progressive.js} +6 -6
- package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -0
- package/lib/engine/extensions/extensions.js +2 -2
- package/lib/engine/extensions/extensions.js.map +1 -1
- package/lib/engine-components/Camera.js +17 -2
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/Light.js +1 -0
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/OrbitControls.js +6 -2
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Renderer.js +3 -3
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/Voip.js +13 -4
- package/lib/engine-components/Voip.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +6 -2
- package/lib/engine-components/codegen/components.js +6 -2
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/{GltfExport.d.ts → gltf/GltfExport.d.ts} +2 -2
- package/lib/engine-components/export/{GltfExport.js → gltf/GltfExport.js} +7 -7
- package/lib/engine-components/export/gltf/GltfExport.js.map +1 -0
- package/lib/engine-components/export/usdz/Extension.d.ts +9 -0
- package/lib/engine-components/export/usdz/Extension.js +2 -0
- package/lib/engine-components/export/usdz/Extension.js.map +1 -0
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +25 -0
- package/lib/engine-components/export/usdz/USDZExporter.js +193 -0
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -0
- package/lib/engine-components/export/usdz/extensions/Animation.d.ts +44 -0
- package/lib/engine-components/export/usdz/extensions/Animation.js +264 -0
- package/lib/engine-components/export/usdz/extensions/Animation.js.map +1 -0
- package/lib/engine-components/export/usdz/types.d.ts +34 -0
- package/lib/engine-components/export/usdz/types.js +2 -0
- package/lib/engine-components/export/usdz/types.js.map +1 -0
- package/lib/engine-components/export/usdz/utils/animationutils.d.ts +3 -0
- package/lib/engine-components/export/usdz/utils/animationutils.js +46 -0
- package/lib/engine-components/export/usdz/utils/animationutils.js.map +1 -0
- package/lib/engine-components/export/usdz/utils/quicklook.d.ts +2 -0
- package/lib/engine-components/export/usdz/utils/quicklook.js +36 -0
- package/lib/engine-components/export/usdz/utils/quicklook.js.map +1 -0
- package/lib/engine-components/export/usdz/utils/timeutils.d.ts +1 -0
- package/lib/engine-components/export/usdz/utils/timeutils.js +15 -0
- package/lib/engine-components/export/usdz/utils/timeutils.js.map +1 -0
- package/lib/engine-components/ui/Graphic.d.ts +2 -0
- package/lib/engine-components/ui/Graphic.js +15 -0
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Utils.d.ts +2 -1
- package/lib/engine-components/ui/Utils.js +5 -3
- package/lib/engine-components/ui/Utils.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/codegen/register_types.js +14 -6
- package/src/engine/engine_components.ts +2 -2
- package/src/engine/engine_networking_instantiate.ts +4 -1
- package/src/engine/engine_serialization_builtin_serializer.ts +35 -7
- package/src/engine/engine_serialization_core.ts +7 -1
- package/src/engine/engine_setup.ts +2 -2
- package/src/engine/engine_texture.ts +6 -0
- package/src/engine/extensions/{NEEDLE_deferred_texture.ts → NEEDLE_progressive.ts} +9 -11
- package/src/engine/extensions/extensions.ts +2 -2
- package/src/engine-components/Camera.ts +17 -3
- package/src/engine-components/Light.ts +3 -1
- package/src/engine-components/OrbitControls.ts +7 -3
- package/src/engine-components/Renderer.ts +3 -3
- package/src/engine-components/Voip.ts +14 -4
- package/src/engine-components/codegen/components.ts +6 -2
- package/src/engine-components/export/{GltfExport.ts → gltf/GltfExport.ts} +7 -7
- package/src/engine-components/export/usdz/Extension.ts +12 -0
- package/src/engine-components/export/usdz/USDZExporter.ts +216 -0
- package/src/engine-components/export/usdz/extensions/Animation.ts +306 -0
- package/src/engine-components/export/usdz/types.ts +39 -0
- package/src/engine-components/export/usdz/utils/animationutils.ts +60 -0
- package/src/engine-components/export/usdz/utils/quicklook.ts +43 -0
- package/src/engine-components/export/usdz/utils/timeutils.ts +20 -0
- package/src/engine-components/ui/Graphic.ts +15 -1
- package/src/engine-components/ui/Utils.ts +5 -3
- package/lib/engine/extensions/NEEDLE_deferred_texture.js.map +0 -1
- package/lib/engine-components/export/GltfExport.js.map +0 -1
|
@@ -5,14 +5,13 @@ import { Context } from "../engine_setup";
|
|
|
5
5
|
import { addDracoAndKTX2Loaders } from "../engine_loaders";
|
|
6
6
|
import { getParam, getPath } from "../engine_utils";
|
|
7
7
|
|
|
8
|
-
export const EXTENSION_NAME = "
|
|
8
|
+
export const EXTENSION_NAME = "NEEDLE_progressive";
|
|
9
9
|
|
|
10
10
|
const debug = getParam("debugprogressive");
|
|
11
11
|
|
|
12
|
-
declare type
|
|
12
|
+
declare type ProgressiveTextureSchema = {
|
|
13
13
|
uri: string;
|
|
14
14
|
guid: string;
|
|
15
|
-
usage?: string,
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
const debug_toggle_maps: Map<Material, { [key: string]: { original: Texture, lod0: Texture } }> = new Map();
|
|
@@ -35,8 +34,7 @@ if (debug) {
|
|
|
35
34
|
});
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
export class
|
|
39
|
-
|
|
37
|
+
export class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
40
38
|
|
|
41
39
|
static assignTextureLOD(context: Context, source: SourceIdentifier | undefined, material: Material, level: number = 0) {
|
|
42
40
|
if (!material) return;
|
|
@@ -46,7 +44,7 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
|
|
|
46
44
|
|
|
47
45
|
if (debug) console.log("-----------\n", "FIND", material.name, slot, val?.name, val?.userData, val, material);
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
NEEDLE_progressive.getOrLoadTexture(context, source, material, slot, val, level).then(t => {
|
|
50
48
|
if (t?.isTexture === true) {
|
|
51
49
|
|
|
52
50
|
if (debug) console.log("Assign LOD", material.name, slot, t.name, t["guid"], material, "Prev:", val, "Now:", t, "\n--------------");
|
|
@@ -109,14 +107,14 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
|
|
|
109
107
|
console.log("AFTER", this.sourceId, gltf);
|
|
110
108
|
this.parser.json.textures?.forEach((textureInfo, index) => {
|
|
111
109
|
if (textureInfo?.extensions) {
|
|
112
|
-
const ext:
|
|
110
|
+
const ext: ProgressiveTextureSchema = textureInfo?.extensions[EXTENSION_NAME];
|
|
113
111
|
if (ext) {
|
|
114
112
|
const prom = this.parser.getDependency("texture", index);
|
|
115
113
|
this._loading.splice(this._loading.indexOf(index), 1);
|
|
116
114
|
prom.then(t => {
|
|
117
115
|
if (debug) console.log("register texture", t.name, t.uuid, ext);
|
|
118
116
|
t.userData.deferred = ext;
|
|
119
|
-
|
|
117
|
+
NEEDLE_progressive.cache.set(t.uuid, ext);
|
|
120
118
|
});
|
|
121
119
|
}
|
|
122
120
|
}
|
|
@@ -125,13 +123,13 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
|
|
|
125
123
|
return null;
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
private static cache = new Map<string,
|
|
126
|
+
private static cache = new Map<string, ProgressiveTextureSchema>();
|
|
129
127
|
private static resolved: { [key: string]: Texture } = {};
|
|
130
128
|
|
|
131
129
|
private static async getOrLoadTexture(context: Context, source: SourceIdentifier | undefined, material: Material, slot: string, current: Texture, _level: number): Promise<Texture | null> {
|
|
132
130
|
|
|
133
131
|
const key = current.uuid;
|
|
134
|
-
const ext:
|
|
132
|
+
const ext: ProgressiveTextureSchema | undefined = NEEDLE_progressive.cache.get(key);// || current.userData.deferred;
|
|
135
133
|
if (ext) {
|
|
136
134
|
if (debug)
|
|
137
135
|
console.log(key, ext.uri, ext.guid);
|
|
@@ -158,7 +156,7 @@ export class NEEDLE_deferred_texture implements GLTFLoaderPlugin {
|
|
|
158
156
|
for (const tex of gltf.parser.json.textures) {
|
|
159
157
|
index++;
|
|
160
158
|
if (tex?.extensions) {
|
|
161
|
-
const other:
|
|
159
|
+
const other: ProgressiveTextureSchema = tex?.extensions[EXTENSION_NAME];
|
|
162
160
|
if (other?.guid) {
|
|
163
161
|
if (other.guid === ext.guid) {
|
|
164
162
|
found = true;
|
|
@@ -12,7 +12,7 @@ import { SourceIdentifier } from "../engine_types";
|
|
|
12
12
|
import { Context } from "../engine_setup";
|
|
13
13
|
import { NEEDLE_lighting_settings } from "./NEEDLE_lighting_settings";
|
|
14
14
|
import { NEEDLE_render_objects } from "./NEEDLE_render_objects";
|
|
15
|
-
import {
|
|
15
|
+
import { NEEDLE_progressive } from "./NEEDLE_progressive";
|
|
16
16
|
|
|
17
17
|
export function registerComponentExtension(loader: GLTFLoader): NEEDLE_components {
|
|
18
18
|
const ext = new NEEDLE_components();
|
|
@@ -40,7 +40,7 @@ export function registerExtensions(loader: GLTFLoader, context: Context, sourceI
|
|
|
40
40
|
loader.register(p => new NEEDLE_lighting_settings(p, sourceId, context));
|
|
41
41
|
loader.register(p => new NEEDLE_techniques_webgl(p, sourceId));
|
|
42
42
|
loader.register(p => new NEEDLE_render_objects(p, sourceId));
|
|
43
|
-
loader.register(p => new
|
|
43
|
+
loader.register(p => new NEEDLE_progressive(p, sourceId, context));
|
|
44
44
|
loader.register(p => new EXT_texture_exr(p));
|
|
45
45
|
|
|
46
46
|
const setPointerResolverFunction = loader["setAnimationPointerResolver"];
|
|
@@ -9,6 +9,7 @@ import { XRSessionMode } from "../engine/engine_setup";
|
|
|
9
9
|
import { ICamera } from "../engine/engine_types"
|
|
10
10
|
import { showBalloonMessage } from "../engine/debug/debug";
|
|
11
11
|
import { getWorldPosition } from "../engine/engine_three_utils";
|
|
12
|
+
import { Gizmos } from "../engine/engine_gizmos";
|
|
12
13
|
|
|
13
14
|
export enum ClearFlags {
|
|
14
15
|
Skybox = 1,
|
|
@@ -17,6 +18,7 @@ export enum ClearFlags {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const debug = getParam("debugcam");
|
|
21
|
+
const debugscreenpointtoray = getParam("debugscreenpointtoray");
|
|
20
22
|
|
|
21
23
|
export class Camera extends Behaviour implements ICamera {
|
|
22
24
|
|
|
@@ -157,14 +159,15 @@ export class Camera extends Behaviour implements ICamera {
|
|
|
157
159
|
private static _origin: THREE.Vector3 = new THREE.Vector3();
|
|
158
160
|
private static _direction: THREE.Vector3 = new THREE.Vector3();
|
|
159
161
|
public screenPointToRay(x: number, y: number, ray?: Ray): Ray {
|
|
162
|
+
let cam = this.cam;
|
|
160
163
|
const origin = Camera._origin;
|
|
161
164
|
origin.set(x, y, -1);
|
|
162
165
|
this.context.input.convertScreenspaceToRaycastSpace(origin);
|
|
166
|
+
if(debugscreenpointtoray) console.log("screenPointToRay", x.toFixed(2), y.toFixed(2), "now:", origin.x.toFixed(2), origin.y.toFixed(2), "isInXR:" + this.context.isInXR);
|
|
163
167
|
origin.z = -1;
|
|
164
|
-
origin.unproject(
|
|
165
|
-
|
|
168
|
+
origin.unproject(cam);
|
|
166
169
|
const dir = Camera._direction.set(origin.x, origin.y, origin.z);
|
|
167
|
-
const camPosition = getWorldPosition(
|
|
170
|
+
const camPosition = getWorldPosition(cam);
|
|
168
171
|
dir.sub(camPosition);
|
|
169
172
|
dir.normalize();
|
|
170
173
|
if (ray) {
|
|
@@ -180,6 +183,17 @@ export class Camera extends Behaviour implements ICamera {
|
|
|
180
183
|
if (!this.sourceId) {
|
|
181
184
|
console.warn("Camera has no source - the camera should be exported inside a gltf", this.name);
|
|
182
185
|
}
|
|
186
|
+
|
|
187
|
+
if(debugscreenpointtoray){
|
|
188
|
+
window.addEventListener("pointerdown", evt => {
|
|
189
|
+
const px = evt.clientX;
|
|
190
|
+
const py = evt.clientY;
|
|
191
|
+
console.log("touch", px.toFixed(2), py.toFixed(2))
|
|
192
|
+
const ray = this.screenPointToRay(px, py);
|
|
193
|
+
const randomHex = "#" + Math.floor(Math.random()*16777215).toString(16);
|
|
194
|
+
Gizmos.DrawRay(ray.origin, ray.direction, randomHex, 10);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
183
197
|
}
|
|
184
198
|
|
|
185
199
|
onEnable(): void {
|
|
@@ -292,6 +292,7 @@ export class Light extends Behaviour implements ILight {
|
|
|
292
292
|
|
|
293
293
|
if (lightAlreadyCreated && !this.light) {
|
|
294
294
|
this.light = this.gameObject as unknown as THREE.Light;
|
|
295
|
+
this._intensity = this.light.intensity;
|
|
295
296
|
|
|
296
297
|
switch (this.type) {
|
|
297
298
|
case LightType.Directional:
|
|
@@ -346,7 +347,8 @@ export class Light extends Behaviour implements ILight {
|
|
|
346
347
|
if (this.light) {
|
|
347
348
|
if (this._intensity >= 0)
|
|
348
349
|
this.light.intensity = this._intensity;
|
|
349
|
-
else
|
|
350
|
+
else
|
|
351
|
+
this._intensity = this.light.intensity;
|
|
350
352
|
|
|
351
353
|
if (this.shadows !== LightShadows.None) {
|
|
352
354
|
this.light.castShadow = true;
|
|
@@ -11,6 +11,9 @@ import { getParam, isMobileDevice } from "../engine/engine_utils";
|
|
|
11
11
|
|
|
12
12
|
const freeCam = getParam("freecam");
|
|
13
13
|
|
|
14
|
+
const disabledKeys = { LEFT: "", UP: "", RIGHT: "", BOTTOM: "" };
|
|
15
|
+
let defaultKeys: any = undefined;
|
|
16
|
+
|
|
14
17
|
export class OrbitControls extends Behaviour {
|
|
15
18
|
public get controls() {
|
|
16
19
|
return this._controls;
|
|
@@ -73,6 +76,7 @@ export class OrbitControls extends Behaviour {
|
|
|
73
76
|
if (cam)
|
|
74
77
|
this._cameraObject = cam;
|
|
75
78
|
this._controls = new ThreeOrbitControls(cam!, this.context.renderer.domElement);
|
|
79
|
+
if (defaultKeys === undefined) defaultKeys = { ...this._controls.keys };
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
if (this._controls) {
|
|
@@ -80,12 +84,11 @@ export class OrbitControls extends Behaviour {
|
|
|
80
84
|
this.enablePan = true;
|
|
81
85
|
this.enableZoom = true;
|
|
82
86
|
this.middleClickToFocus = true;
|
|
83
|
-
if(isMobileDevice()) this.doubleClickToFocus = true;
|
|
87
|
+
if (isMobileDevice()) this.doubleClickToFocus = true;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
this._controls.enableDamping = this.enableDamping;
|
|
87
|
-
|
|
88
|
-
this._controls.enableKeys = this.enableKeys;
|
|
91
|
+
this._controls.keys = this.enableKeys ? defaultKeys : disabledKeys;
|
|
89
92
|
this._controls.autoRotate = this.autoRotate;
|
|
90
93
|
this._controls.autoRotateSpeed = this.autoRotateSpeed;
|
|
91
94
|
this._controls.enableZoom = this.enableZoom;
|
|
@@ -164,6 +167,7 @@ export class OrbitControls extends Behaviour {
|
|
|
164
167
|
this._inputs += 1;
|
|
165
168
|
}
|
|
166
169
|
if (this._inputs > 0) {
|
|
170
|
+
this.autoRotate = false;
|
|
167
171
|
this._controls.autoRotate = false;
|
|
168
172
|
this._lerpCameraToTarget = false;
|
|
169
173
|
this._lerpToTargetPosition = false;
|
|
@@ -7,7 +7,7 @@ import { getParam } from "../engine/engine_utils";
|
|
|
7
7
|
import { serializable } from "../engine/engine_serialization_decorator";
|
|
8
8
|
import { AxesHelper, Material, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
|
|
9
9
|
import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects";
|
|
10
|
-
import {
|
|
10
|
+
import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive";
|
|
11
11
|
import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing";
|
|
12
12
|
import { IRenderer, ISharedMaterials } from "../engine/engine_types";
|
|
13
13
|
import { debug, ReflectionProbe } from "./ReflectionProbe";
|
|
@@ -465,11 +465,11 @@ export class Renderer extends Behaviour implements IRenderer {
|
|
|
465
465
|
if (debugProgressiveLoading) {
|
|
466
466
|
console.log("Load material LOD (with delay)", material.name);
|
|
467
467
|
setTimeout(() => {
|
|
468
|
-
|
|
468
|
+
NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
|
|
469
469
|
}, 2000);
|
|
470
470
|
}
|
|
471
471
|
else {
|
|
472
|
-
|
|
472
|
+
NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
475
|
|
|
@@ -307,6 +307,11 @@ export class Voip extends Behaviour {
|
|
|
307
307
|
return;
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
if (utils.isiOS() && utils.isSafari()) {
|
|
311
|
+
console.log("VOIP is currently not supported on Safari iOS")
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
310
315
|
this.peer = new Peer();
|
|
311
316
|
navigator["getUserMedia"] = (navigator["getUserMedia"] || navigator["webkitGetUserMedia"] || navigator["mozGetUserMedia"] || navigator["msGetUserMedia"]);
|
|
312
317
|
|
|
@@ -360,10 +365,15 @@ export class Voip extends Behaviour {
|
|
|
360
365
|
onDisable(): void {
|
|
361
366
|
console.log("TODO: close all");
|
|
362
367
|
for (const key in this.currentIncomingCalls) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
368
|
+
try {
|
|
369
|
+
const call = this.currentIncomingCalls[key];
|
|
370
|
+
call?.close();
|
|
371
|
+
const con = this.connections[key];
|
|
372
|
+
con?.close();
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
console.error(err);
|
|
376
|
+
}
|
|
367
377
|
}
|
|
368
378
|
}
|
|
369
379
|
|
|
@@ -123,8 +123,6 @@ export { Avatar_Brain_LookAt } from "../avatar/Avatar_Brain_LookAt";
|
|
|
123
123
|
export { Avatar_MouthShapes } from "../avatar/Avatar_MouthShapes";
|
|
124
124
|
export { Avatar_MustacheShake } from "../avatar/Avatar_MustacheShake";
|
|
125
125
|
export { LogStats } from "../debug/LogStats";
|
|
126
|
-
export { GltfExportBox } from "../export/GltfExport";
|
|
127
|
-
export { GltfExport } from "../export/GltfExport";
|
|
128
126
|
export { RGBAColor } from "../js-extensions/RGBAColor";
|
|
129
127
|
export { PlayableDirector } from "../timeline/PlayableDirector";
|
|
130
128
|
export { SignalAsset } from "../timeline/SignalAsset";
|
|
@@ -160,3 +158,9 @@ export { Rect } from "../ui/RectTransform";
|
|
|
160
158
|
export { RectTransform } from "../ui/RectTransform";
|
|
161
159
|
export { SpatialHtml } from "../ui/SpatialHtml";
|
|
162
160
|
export { Text } from "../ui/Text";
|
|
161
|
+
export { GltfExportBox } from "../export/gltf/GltfExport";
|
|
162
|
+
export { GltfExport } from "../export/gltf/GltfExport";
|
|
163
|
+
export { USDZExporter } from "../export/usdz/USDZExporter";
|
|
164
|
+
export { RegisteredAnimationInfo } from "../export/usdz/extensions/Animation";
|
|
165
|
+
export { TransformData } from "../export/usdz/extensions/Animation";
|
|
166
|
+
export { AnimationExtension } from "../export/usdz/extensions/Animation";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Behaviour, GameObject } from "
|
|
1
|
+
import { Behaviour, GameObject } from "../../Component";
|
|
2
2
|
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
|
|
3
|
-
import GLTFMeshGPUInstancingExtension from '
|
|
4
|
-
import { Renderer } from "
|
|
3
|
+
import GLTFMeshGPUInstancingExtension from '../../../include/three/EXT_mesh_gpu_instancing_exporter.js';
|
|
4
|
+
import { Renderer } from "../../Renderer";
|
|
5
5
|
import { Object3D, Vector3 } from "three";
|
|
6
|
-
import { SerializationContext } from "
|
|
7
|
-
import { NEEDLE_components } from "
|
|
8
|
-
import { getWorldPosition } from "
|
|
9
|
-
import { BoxHelperComponent } from "
|
|
6
|
+
import { SerializationContext } from "../../../engine/engine_serialization_core";
|
|
7
|
+
import { NEEDLE_components } from "../../../engine/extensions/NEEDLE_components";
|
|
8
|
+
import { getWorldPosition } from "../../../engine/engine_three_utils";
|
|
9
|
+
import { BoxHelperComponent } from "../../BoxHelperComponent";
|
|
10
10
|
import { AnimationClip } from "three";
|
|
11
11
|
|
|
12
12
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { USDZObject } from "./types";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export interface IUSDZExporterExtension {
|
|
5
|
+
|
|
6
|
+
get extensionName(): string;
|
|
7
|
+
onBeforeBuildDocument?(context);
|
|
8
|
+
onAfterBuildDocument?(context);
|
|
9
|
+
onExportObject?(object, model : USDZObject, context);
|
|
10
|
+
onAfterSerialize?(context);
|
|
11
|
+
onAfterHierarchy?(context);
|
|
12
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { delay, getParam, isiOS, isMobileDevice, isSafari } from "../../../engine/engine_utils";
|
|
2
|
+
import { Object3D, Color } from "three";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
import { USDZExporter as ThreeUSDZExporter } from "three/examples/jsm/exporters/USDZExporter";
|
|
5
|
+
import { AnimationExtension } from "./extensions/Animation"
|
|
6
|
+
import { ensureQuicklookLinkIsCreated } from "./utils/quicklook";
|
|
7
|
+
import { getFormattedDate } from "./utils/timeutils";
|
|
8
|
+
import { registerAnimatorsImplictly } from "./utils/animationutils";
|
|
9
|
+
import { IUSDZExporterExtension } from "./Extension";
|
|
10
|
+
import { Behaviour, GameObject } from "../../Component";
|
|
11
|
+
import { WebXR } from "../../WebXR"
|
|
12
|
+
import { serializable } from "../../../engine/engine_serialization";
|
|
13
|
+
import { showBalloonWarning } from "../../../engine/debug/debug";
|
|
14
|
+
import { Context } from "../../../engine/engine_setup";
|
|
15
|
+
|
|
16
|
+
const debug = getParam("debugusdz");
|
|
17
|
+
|
|
18
|
+
export type QuickLookOverlay = {
|
|
19
|
+
callToAction?: string;
|
|
20
|
+
checkoutTitle?: string;
|
|
21
|
+
checkoutSubtitle?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class USDZExporter extends Behaviour {
|
|
25
|
+
|
|
26
|
+
@serializable(Object3D)
|
|
27
|
+
objectToExport?: THREE.Object3D;
|
|
28
|
+
|
|
29
|
+
@serializable()
|
|
30
|
+
autoExportAnimations: boolean = false;
|
|
31
|
+
|
|
32
|
+
extensions: IUSDZExporterExtension[] = [];
|
|
33
|
+
|
|
34
|
+
private link!: HTMLAnchorElement;
|
|
35
|
+
private webxr?: WebXR;
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
start() {
|
|
39
|
+
if (debug) {
|
|
40
|
+
window.addEventListener("keydown", (evt) => {
|
|
41
|
+
switch (evt.key) {
|
|
42
|
+
case "t":
|
|
43
|
+
this.exportAsync();
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (isMobileDevice()) {
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
this.exportAsync();
|
|
50
|
+
}, 2000)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
document.getElementById("open-in-ar")?.addEventListener("click", (evt) => {
|
|
54
|
+
evt.preventDefault();
|
|
55
|
+
this.exportAsync();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!this.objectToExport) this.objectToExport = this.gameObject;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
onEnable() {
|
|
64
|
+
const ios = isiOS()
|
|
65
|
+
const safari = isSafari();
|
|
66
|
+
if (debug || (ios && safari)) {
|
|
67
|
+
this.createQuicklookButton();
|
|
68
|
+
this.lastCallback = this.quicklookCallback.bind(this);
|
|
69
|
+
this.link = ensureQuicklookLinkIsCreated(this.context);
|
|
70
|
+
this.link.addEventListener('message', this.lastCallback);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
onDisable() {
|
|
75
|
+
this.link?.removeEventListener('message', this.lastCallback);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async exportAsync() {
|
|
79
|
+
if (!this.objectToExport) return;
|
|
80
|
+
|
|
81
|
+
const exporter = new ThreeUSDZExporter();
|
|
82
|
+
const extensions: any = [...this.extensions]
|
|
83
|
+
|
|
84
|
+
// collect animators and their clips
|
|
85
|
+
const animExt = new AnimationExtension();
|
|
86
|
+
extensions.push(animExt);
|
|
87
|
+
|
|
88
|
+
if (this.autoExportAnimations)
|
|
89
|
+
registerAnimatorsImplictly(this.objectToExport, animExt);
|
|
90
|
+
|
|
91
|
+
const eventArgs = { self: this, exporter: exporter, extensions: extensions };
|
|
92
|
+
this.dispatchEvent(new CustomEvent("before-export", { detail: eventArgs }))
|
|
93
|
+
|
|
94
|
+
let name = "needle";
|
|
95
|
+
if (debug) name += "-" + getFormattedDate();
|
|
96
|
+
|
|
97
|
+
//@ts-ignore
|
|
98
|
+
exporter.debug = debug;
|
|
99
|
+
//@ts-ignore
|
|
100
|
+
const arraybuffer = await exporter.parse(this.objectToExport, extensions);
|
|
101
|
+
const blob = new Blob([arraybuffer], { type: 'application/octet-stream' });
|
|
102
|
+
|
|
103
|
+
// second file: USDA (without assets)
|
|
104
|
+
//@ts-ignore
|
|
105
|
+
// const usda = exporter.lastUsda;
|
|
106
|
+
// const blob2 = new Blob([usda], { type: 'text/plain' });
|
|
107
|
+
// this.link.download = name + ".usda";
|
|
108
|
+
// this.link.href = URL.createObjectURL(blob2);
|
|
109
|
+
// this.link.click();
|
|
110
|
+
|
|
111
|
+
// see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
|
|
112
|
+
const overlay = this.buildQuicklookOverlay();
|
|
113
|
+
const callToAction = overlay.callToAction ? encodeURIComponent(overlay.callToAction) : "";
|
|
114
|
+
const checkoutTitle = overlay.checkoutTitle ? encodeURIComponent(overlay.checkoutTitle) : "";
|
|
115
|
+
const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
|
|
116
|
+
this.link.href = URL.createObjectURL(blob) + `#callToAction=${callToAction}&checkoutTitle=${checkoutTitle}&checkoutSubtitle=${checkoutSubtitle}&`;
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if (!this.lastCallback) {
|
|
120
|
+
this.lastCallback = this.quicklookCallback.bind(this);
|
|
121
|
+
this.link.addEventListener('message', this.lastCallback);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// open quicklook
|
|
125
|
+
this.link.download = name + ".usdz";
|
|
126
|
+
this.link.click();
|
|
127
|
+
|
|
128
|
+
// TODO detect QuickLook availability:
|
|
129
|
+
// https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/#:~:text=inside%20the%20anchor.-,Feature%20Detection,-To%20detect%20support
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private lastCallback?: any;
|
|
133
|
+
|
|
134
|
+
private quicklookCallback(event) {
|
|
135
|
+
if ((event as any)?.data == '_apple_ar_quicklook_button_tapped') {
|
|
136
|
+
if (debug) showBalloonWarning("Quicklook closed via call to action button");
|
|
137
|
+
this.dispatchEvent(new CustomEvent("quicklook-button-tapped", { detail: this }));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private buildQuicklookOverlay(): QuickLookOverlay {
|
|
142
|
+
const obj: QuickLookOverlay = {};
|
|
143
|
+
obj.callToAction = "Close";
|
|
144
|
+
obj.checkoutTitle = "🌵 Made with Needle";
|
|
145
|
+
obj.checkoutSubtitle = "_";
|
|
146
|
+
// Use the quicklook-overlay event to customize the overlay
|
|
147
|
+
this.dispatchEvent(new CustomEvent("quicklook-overlay", { detail: obj }));
|
|
148
|
+
return obj;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private _arButton?: HTMLElement;
|
|
152
|
+
private async createQuicklookButton() {
|
|
153
|
+
if (!this.webxr) {
|
|
154
|
+
await delay(1);
|
|
155
|
+
this.webxr = GameObject.findObjectOfType(WebXR) ?? undefined;
|
|
156
|
+
if (this.webxr) {
|
|
157
|
+
if(this.webxr.VRButton) this.webxr.VRButton.parentElement?.removeChild(this.webxr.VRButton);
|
|
158
|
+
// check if we have an AR button already and re-use that
|
|
159
|
+
if (this.webxr.ARButton && this._arButton !== this.webxr.ARButton) {
|
|
160
|
+
this._arButton = this.webxr.ARButton;
|
|
161
|
+
// Hack to remove the immersiveweb link
|
|
162
|
+
const linkInButton = this.webxr.ARButton.parentElement?.querySelector("a");
|
|
163
|
+
if (linkInButton) {
|
|
164
|
+
linkInButton.href = "";
|
|
165
|
+
}
|
|
166
|
+
this.webxr.ARButton.innerText = "Open in Quicklook";
|
|
167
|
+
this.webxr.ARButton.disabled = false;
|
|
168
|
+
this.webxr.ARButton.addEventListener("click", evt => {
|
|
169
|
+
evt.preventDefault();
|
|
170
|
+
this.exportAsync();
|
|
171
|
+
});
|
|
172
|
+
this.webxr.ARButton.classList.add("quicklook-ar-button");
|
|
173
|
+
}
|
|
174
|
+
// create a button if WebXR didnt create one yet
|
|
175
|
+
else {
|
|
176
|
+
this.webxr.createARButton = false;
|
|
177
|
+
this.webxr.createVRButton = false;
|
|
178
|
+
let container = window.document.querySelector(".webxr-buttons");
|
|
179
|
+
if (!container) {
|
|
180
|
+
container = document.createElement("div");
|
|
181
|
+
container.classList.add("webxr-buttons");
|
|
182
|
+
}
|
|
183
|
+
const button = document.createElement("button");
|
|
184
|
+
button.innerText = "Open in Quicklook";
|
|
185
|
+
button.addEventListener("click", () => {
|
|
186
|
+
this.exportAsync();
|
|
187
|
+
});
|
|
188
|
+
button.classList.add('webxr-ar-button');
|
|
189
|
+
button.classList.add('webxr-button');
|
|
190
|
+
button.classList.add("quicklook-ar-button");
|
|
191
|
+
container.appendChild(button);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.warn("Could not find WebXR component: will not create Quicklook button", Context.Current);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private resetStyles(el: HTMLElement) {
|
|
201
|
+
el.style.position = "";
|
|
202
|
+
el.style.top = "";
|
|
203
|
+
el.style.left = "";
|
|
204
|
+
el.style.width = "";
|
|
205
|
+
el.style.height = "";
|
|
206
|
+
el.style.margin = "";
|
|
207
|
+
el.style.padding = "";
|
|
208
|
+
el.style.border = "";
|
|
209
|
+
el.style.background = "";
|
|
210
|
+
el.style.color = "";
|
|
211
|
+
el.style.font = "";
|
|
212
|
+
el.style.textAlign = "";
|
|
213
|
+
el.style.opacity = "";
|
|
214
|
+
el.style.zIndex = "";
|
|
215
|
+
}
|
|
216
|
+
}
|