@needle-tools/engine 3.5.2-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 +16 -0
- package/dist/needle-engine.js +17123 -16970
- package/dist/needle-engine.min.js +349 -349
- package/dist/needle-engine.umd.cjs +347 -347
- package/lib/engine/codegen/register_types.js +4 -0
- 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 +2 -2
- 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_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/codegen/components.d.ts +2 -0
- package/lib/engine-components/codegen/components.js +2 -0
- 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.js +41 -13
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +1 -0
- package/lib/engine-components/export/usdz/USDZExporter.js +30 -4
- 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 +3 -0
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +18 -0
- 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 +71 -1
- 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/src/engine/codegen/register_types.js +6 -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 +2 -2
- package/src/engine/engine_gameobject.ts +3 -0
- package/src/engine/engine_input.ts +4 -1
- 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/codegen/components.ts +2 -0
- package/src/engine-components/export/usdz/Extension.ts +3 -2
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +53 -14
- package/src/engine-components/export/usdz/USDZExporter.ts +34 -4
- package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +63 -0
- package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +23 -1
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +79 -2
- package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +46 -0
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.3-alpha",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in",
|
|
5
5
|
"main": "dist/needle-engine.umd.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TypeStore } from "./../engine_typestore"
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
// Import types
|
|
4
4
|
import { __Ignore } from "../../engine-components/codegen/components";
|
|
5
5
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder";
|
|
@@ -14,6 +14,7 @@ import { Animator } from "../../engine-components/Animator";
|
|
|
14
14
|
import { AnimatorController } from "../../engine-components/AnimatorController";
|
|
15
15
|
import { Antialiasing } from "../../engine-components/postprocessing/Effects/Antialiasing";
|
|
16
16
|
import { AttachedObject } from "../../engine-components/webxr/WebXRController";
|
|
17
|
+
import { AudioExtension } from "../../engine-components/export/usdz/extensions/behavior/AudioExtension";
|
|
17
18
|
import { AudioListener } from "../../engine-components/AudioListener";
|
|
18
19
|
import { AudioSource } from "../../engine-components/AudioSource";
|
|
19
20
|
import { AudioTrackHandler } from "../../engine-components/timeline/TimelineTracks";
|
|
@@ -116,6 +117,7 @@ import { ParticleSystemRenderer } from "../../engine-components/ParticleSystem";
|
|
|
116
117
|
import { PixelationEffect } from "../../engine-components/postprocessing/Effects/Pixelation";
|
|
117
118
|
import { PlayableDirector } from "../../engine-components/timeline/PlayableDirector";
|
|
118
119
|
import { PlayAnimationOnClick } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
|
|
120
|
+
import { PlayAudioOnClick } from "../../engine-components/export/usdz/extensions/behavior/BehaviourComponents";
|
|
119
121
|
import { PlayerColor } from "../../engine-components/PlayerColor";
|
|
120
122
|
import { PlayerState } from "../../engine-components-experimental/networking/PlayerSync";
|
|
121
123
|
import { PlayerSync } from "../../engine-components-experimental/networking/PlayerSync";
|
|
@@ -213,7 +215,7 @@ import { XRGrabModel } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
|
213
215
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
214
216
|
import { XRRig } from "../../engine-components/webxr/WebXRRig";
|
|
215
217
|
import { XRState } from "../../engine-components/XRFlag";
|
|
216
|
-
|
|
218
|
+
|
|
217
219
|
// Register types
|
|
218
220
|
TypeStore.add("__Ignore", __Ignore);
|
|
219
221
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
|
@@ -228,6 +230,7 @@ TypeStore.add("Animator", Animator);
|
|
|
228
230
|
TypeStore.add("AnimatorController", AnimatorController);
|
|
229
231
|
TypeStore.add("Antialiasing", Antialiasing);
|
|
230
232
|
TypeStore.add("AttachedObject", AttachedObject);
|
|
233
|
+
TypeStore.add("AudioExtension", AudioExtension);
|
|
231
234
|
TypeStore.add("AudioListener", AudioListener);
|
|
232
235
|
TypeStore.add("AudioSource", AudioSource);
|
|
233
236
|
TypeStore.add("AudioTrackHandler", AudioTrackHandler);
|
|
@@ -330,6 +333,7 @@ TypeStore.add("ParticleSystemRenderer", ParticleSystemRenderer);
|
|
|
330
333
|
TypeStore.add("PixelationEffect", PixelationEffect);
|
|
331
334
|
TypeStore.add("PlayableDirector", PlayableDirector);
|
|
332
335
|
TypeStore.add("PlayAnimationOnClick", PlayAnimationOnClick);
|
|
336
|
+
TypeStore.add("PlayAudioOnClick", PlayAudioOnClick);
|
|
333
337
|
TypeStore.add("PlayerColor", PlayerColor);
|
|
334
338
|
TypeStore.add("PlayerState", PlayerState);
|
|
335
339
|
TypeStore.add("PlayerSync", PlayerSync);
|
|
@@ -107,7 +107,8 @@ const currentMessages = new Set<string>();
|
|
|
107
107
|
function showMessage(type: LogType, element: HTMLElement, msg: string) {
|
|
108
108
|
const container = getLogsContainer(element);
|
|
109
109
|
if (container.childElementCount >= 20) {
|
|
110
|
-
|
|
110
|
+
const last = container.lastElementChild;
|
|
111
|
+
returnMessageContainer(last as HTMLElement);
|
|
111
112
|
}
|
|
112
113
|
// truncate long messages before they go into the cache/set
|
|
113
114
|
if(msg.length > 300) msg = msg.substring(0, 300) + "...";
|
|
@@ -160,7 +160,8 @@ export function getComponent<T>(obj: Object3D, componentType: Constructor<T>) {
|
|
|
160
160
|
|
|
161
161
|
export function getComponents<T>(obj: Object3D, componentType: Constructor<T>, arr?: T[] | null): T[] {
|
|
162
162
|
if (!arr) arr = [];
|
|
163
|
-
|
|
163
|
+
onGetComponent(obj, componentType, arr);
|
|
164
|
+
return arr;
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
export function getComponentInChildren<T>(obj: Object3D, componentType: Constructor<T>, includeInactive?: boolean) {
|
|
@@ -186,7 +186,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
186
186
|
if (loadingStyle === "light")
|
|
187
187
|
this._loadingElement.style.backgroundColor = "#ddd";
|
|
188
188
|
else
|
|
189
|
-
this._loadingElement.style.backgroundColor = "#
|
|
189
|
+
this._loadingElement.style.backgroundColor = "#222";
|
|
190
190
|
this._loadingElement.style.display = "flex";
|
|
191
191
|
this._loadingElement.style.alignItems = "center";
|
|
192
192
|
this._loadingElement.style.justifyContent = "center";
|
|
@@ -289,7 +289,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
289
289
|
messageContainer.style.fontSize = ".8em";
|
|
290
290
|
messageContainer.style.paddingTop = ".5em";
|
|
291
291
|
messageContainer.style.fontWeight = "200";
|
|
292
|
-
messageContainer.style.fontFamily = "Roboto, sans-serif";
|
|
292
|
+
messageContainer.style.fontFamily = "Roboto, sans-serif, Arial";
|
|
293
293
|
// messageContainer.style.border = "1px solid rgba(255,255,255,.1)";
|
|
294
294
|
messageContainer.style.justifyContent = "center";
|
|
295
295
|
this._loadingElement.appendChild(messageContainer);
|
|
@@ -113,6 +113,9 @@ export function destroy(instance: Object3D | Component, recursive: boolean = tru
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
function internalDestroy(instance: Object3D | Component, recursive: boolean = true, dispose: boolean = false, isRoot: boolean = true) {
|
|
116
|
+
if (instance === null || instance === undefined)
|
|
117
|
+
return;
|
|
118
|
+
|
|
116
119
|
const comp = instance as Component;
|
|
117
120
|
if (comp.isComponent) {
|
|
118
121
|
comp.__internalDisable();
|
|
@@ -611,9 +611,12 @@ export class Input extends EventTarget implements IInput {
|
|
|
611
611
|
|
|
612
612
|
const lf = this._pointerPositionsLastFrame[evt.button];
|
|
613
613
|
lf.copy(this._pointerPositions[evt.button]);
|
|
614
|
+
// accumulate delta (it's reset in end of frame), if we just write it here it's not correct when the browser console is open
|
|
615
|
+
const delta = this._pointerPositionsDelta[evt.button];
|
|
614
616
|
const dx = evt.clientX - lf.x;
|
|
615
617
|
const dy = evt.clientY - lf.y;
|
|
616
|
-
|
|
618
|
+
delta.x += dx;
|
|
619
|
+
delta.y += dy;
|
|
617
620
|
|
|
618
621
|
this._pointerPositions[evt.button].x = evt.clientX;
|
|
619
622
|
this._pointerPositions[evt.button].y = evt.clientY;
|
|
@@ -207,7 +207,8 @@ export class RapierPhysics implements IPhysicsEngine {
|
|
|
207
207
|
private async internalInitialization() {
|
|
208
208
|
// NEEDLE_PHYSICS_INIT_START
|
|
209
209
|
// use .env file with VITE_NEEDLE_USE_RAPIER=false to treeshape rapier
|
|
210
|
-
|
|
210
|
+
// @ts-ignore
|
|
211
|
+
if ("env" in import.meta && import.meta.env.VITE_NEEDLE_USE_RAPIER === "false") {
|
|
211
212
|
return false;
|
|
212
213
|
}
|
|
213
214
|
// Can be transformed during build time to disable rapier
|
|
@@ -434,7 +434,14 @@ function implictlyAssignPrimitiveTypes(obj: any, serializedData: any) {
|
|
|
434
434
|
if (targetMember !== undefined) continue;
|
|
435
435
|
// resolve serialized primitive types
|
|
436
436
|
if (isPrimitiveType(data[key]) && !isPrimitiveType(member)) {
|
|
437
|
-
|
|
437
|
+
|
|
438
|
+
const prop = tryFindPropertyDescriptor(member, key);
|
|
439
|
+
if (!prop?.writable === false || (prop && prop.set === undefined)) {
|
|
440
|
+
if (debug)
|
|
441
|
+
console.warn("Property is not writable \"" + key + "\"", member, prop, data[key], member[key]);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
// console.log("ASSIGN", key, member, member[key], targetMember, data[key], prop);
|
|
438
445
|
member[key] = data[key];
|
|
439
446
|
}
|
|
440
447
|
}
|
|
@@ -442,6 +449,15 @@ function implictlyAssignPrimitiveTypes(obj: any, serializedData: any) {
|
|
|
442
449
|
}
|
|
443
450
|
}
|
|
444
451
|
|
|
452
|
+
function tryFindPropertyDescriptor(obj: object, key: string) : PropertyDescriptor | undefined {
|
|
453
|
+
while(obj){
|
|
454
|
+
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
455
|
+
if(desc) return desc;
|
|
456
|
+
obj = Object.getPrototypeOf(obj);
|
|
457
|
+
}
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
|
|
445
461
|
function isPrimitiveType(val): boolean {
|
|
446
462
|
switch (typeof val) {
|
|
447
463
|
case "number":
|
|
@@ -48,8 +48,9 @@ export class NEEDLE_lighting_settings implements GLTFLoaderPlugin {
|
|
|
48
48
|
let settings: SceneLightSettings | undefined = undefined;
|
|
49
49
|
// If the result scene has only one child we add the LightingSettingsComponent to that child
|
|
50
50
|
if (_result.scene.children.length === 1) {
|
|
51
|
+
const obj = _result.scene.children[0];
|
|
51
52
|
// add a component to the root of the scene
|
|
52
|
-
settings = GameObject.addNewComponent(
|
|
53
|
+
settings = GameObject.addNewComponent(obj, SceneLightSettings, false);
|
|
53
54
|
}
|
|
54
55
|
// if the scene already has multiple children we add it as a new object
|
|
55
56
|
else {
|
|
@@ -117,6 +118,15 @@ export class SceneLightSettings extends Behaviour {
|
|
|
117
118
|
}
|
|
118
119
|
});
|
|
119
120
|
}
|
|
121
|
+
|
|
122
|
+
// make sure the component is in the end of the list
|
|
123
|
+
// (e.g. if we have an animation on the first component from an instance and add the scenelightingcomponent the animation binding will break)
|
|
124
|
+
const comps = this.gameObject.userData?.components;
|
|
125
|
+
if (comps) {
|
|
126
|
+
const index = comps.indexOf(this);
|
|
127
|
+
comps.splice(index, 1);
|
|
128
|
+
comps.push(this);
|
|
129
|
+
}
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
onDestroy(): void {
|
|
@@ -195,9 +195,6 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
195
195
|
* @param instance component to move to the GO
|
|
196
196
|
*/
|
|
197
197
|
public static moveComponent(go: IGameObject, instance: Component): void {
|
|
198
|
-
if (instance.gameObject == null) {
|
|
199
|
-
throw new Error("Did you mean to create a new component? Use addNewComponent");
|
|
200
|
-
}
|
|
201
198
|
moveComponentInstance(go, instance as any);
|
|
202
199
|
}
|
|
203
200
|
|
|
@@ -277,6 +274,7 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
277
274
|
// these are implemented via threejs object extensions
|
|
278
275
|
abstract activeSelf: boolean;
|
|
279
276
|
abstract addNewComponent<T>(type: Constructor<T>): T | null;
|
|
277
|
+
// TODO: add method for addExisting component
|
|
280
278
|
abstract removeComponent(comp: Component): Component;
|
|
281
279
|
abstract getOrAddComponent<T>(typeName: Constructor<T> | null): T;
|
|
282
280
|
abstract getComponent<T>(type: Constructor<T>): T | null;
|
|
@@ -12,6 +12,7 @@ export { Animator } from "../Animator";
|
|
|
12
12
|
export { AnimatorController } from "../AnimatorController";
|
|
13
13
|
export { Antialiasing } from "../postprocessing/Effects/Antialiasing";
|
|
14
14
|
export { AttachedObject } from "../webxr/WebXRController";
|
|
15
|
+
export { AudioExtension } from "../export/usdz/extensions/behavior/AudioExtension";
|
|
15
16
|
export { AudioListener } from "../AudioListener";
|
|
16
17
|
export { AudioSource } from "../AudioSource";
|
|
17
18
|
export { AudioTrackHandler } from "../timeline/TimelineTracks";
|
|
@@ -114,6 +115,7 @@ export { ParticleSystemRenderer } from "../ParticleSystem";
|
|
|
114
115
|
export { PixelationEffect } from "../postprocessing/Effects/Pixelation";
|
|
115
116
|
export { PlayableDirector } from "../timeline/PlayableDirector";
|
|
116
117
|
export { PlayAnimationOnClick } from "../export/usdz/extensions/behavior/BehaviourComponents";
|
|
118
|
+
export { PlayAudioOnClick } from "../export/usdz/extensions/behavior/BehaviourComponents";
|
|
117
119
|
export { PlayerColor } from "../PlayerColor";
|
|
118
120
|
export { PointerEventData } from "../ui/PointerEvents";
|
|
119
121
|
export { PostProcessingHandler } from "../postprocessing/PostProcessingHandler";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { USDObject } from "./ThreeUSDZExporter";
|
|
1
|
+
import { USDObject, USDZExporterContext } from "./ThreeUSDZExporter";
|
|
2
|
+
import { Object3D } from "three";
|
|
2
3
|
|
|
3
4
|
export interface IUSDExporterExtension {
|
|
4
5
|
|
|
5
6
|
get extensionName(): string;
|
|
6
7
|
onBeforeBuildDocument?(context);
|
|
7
8
|
onAfterBuildDocument?(context);
|
|
8
|
-
onExportObject?(object, model : USDObject, context);
|
|
9
|
+
onExportObject?(object: Object3D, model : USDObject, context: USDZExporterContext);
|
|
9
10
|
onAfterSerialize?(context);
|
|
10
11
|
onAfterHierarchy?(context, writer : any);
|
|
11
12
|
}
|
|
@@ -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
|
|
|
@@ -436,7 +441,7 @@ class USDZExporter {
|
|
|
436
441
|
|
|
437
442
|
parseDocument( context );
|
|
438
443
|
|
|
439
|
-
invokeAll( context, 'onAfterSerialize' );
|
|
444
|
+
await invokeAll( context, 'onAfterSerialize' );
|
|
440
445
|
|
|
441
446
|
context.output += buildMaterials( materials, textures );
|
|
442
447
|
|
|
@@ -509,21 +514,33 @@ class USDZExporter {
|
|
|
509
514
|
|
|
510
515
|
}
|
|
511
516
|
|
|
512
|
-
function traverseVisible( object, parentModel, context ) {
|
|
517
|
+
function traverseVisible( object: Object3D, parentModel: USDObject, context: USDZExporterContext ) {
|
|
513
518
|
|
|
514
519
|
if ( ! object.visible ) return;
|
|
515
|
-
|
|
520
|
+
|
|
516
521
|
let model: USDObject | undefined = undefined;
|
|
517
|
-
|
|
518
|
-
|
|
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
|
+
}
|
|
519
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
|
+
}
|
|
520
537
|
|
|
521
|
-
if ( object
|
|
538
|
+
if ( object instanceof Mesh && material && (material instanceof MeshStandardMaterial || material instanceof MeshBasicMaterial) && ! (object instanceof SkinnedMesh )) {
|
|
522
539
|
|
|
523
540
|
const name = getObjectId( object );
|
|
524
541
|
model = new USDObject( object.uuid, name, object.matrix, geometry, material );
|
|
525
542
|
|
|
526
|
-
} else if ( object
|
|
543
|
+
} else if ( object instanceof Camera ) {
|
|
527
544
|
|
|
528
545
|
const name = getObjectId( object );
|
|
529
546
|
model = new USDObject( object.uuid, name, object.matrix, undefined, undefined, object );
|
|
@@ -577,7 +594,7 @@ function traverseVisible( object, parentModel, context ) {
|
|
|
577
594
|
|
|
578
595
|
}
|
|
579
596
|
|
|
580
|
-
function parseDocument( context: USDZExporterContext ) {
|
|
597
|
+
async function parseDocument( context: USDZExporterContext ) {
|
|
581
598
|
|
|
582
599
|
for ( const child of context.document.children ) {
|
|
583
600
|
|
|
@@ -666,14 +683,27 @@ function addResources( object, context: USDZExporterContext ) {
|
|
|
666
683
|
|
|
667
684
|
}
|
|
668
685
|
|
|
669
|
-
function invokeAll( context: USDZExporterContext, name: string, writer: USDWriter | null = null ) {
|
|
686
|
+
async function invokeAll( context: USDZExporterContext, name: string, writer: USDWriter | null = null ) {
|
|
670
687
|
|
|
671
688
|
if ( context.extensions ) {
|
|
672
689
|
|
|
673
690
|
for ( const ext of context.extensions ) {
|
|
674
691
|
|
|
675
|
-
if (
|
|
676
|
-
|
|
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
|
+
}
|
|
677
707
|
|
|
678
708
|
}
|
|
679
709
|
|
|
@@ -841,6 +871,14 @@ export function buildXform( model, writer, context ) {
|
|
|
841
871
|
const material = model.material;
|
|
842
872
|
const camera = model.camera;
|
|
843
873
|
const name = model.name;
|
|
874
|
+
|
|
875
|
+
// postprocess node
|
|
876
|
+
if ( model.onBeforeSerialize ) {
|
|
877
|
+
|
|
878
|
+
model.onBeforeSerialize( writer, context );
|
|
879
|
+
|
|
880
|
+
}
|
|
881
|
+
|
|
844
882
|
const transform = buildMatrix( matrix );
|
|
845
883
|
|
|
846
884
|
if ( matrix.determinant() < 0 ) {
|
|
@@ -849,11 +887,12 @@ export function buildXform( model, writer, context ) {
|
|
|
849
887
|
|
|
850
888
|
}
|
|
851
889
|
|
|
852
|
-
if ( geometry )
|
|
890
|
+
if ( geometry ) {
|
|
853
891
|
writer.beginBlock( `def Xform "${name}" (
|
|
854
|
-
|
|
855
|
-
|
|
892
|
+
prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
|
|
893
|
+
prepend apiSchemas = ["MaterialBindingAPI"]
|
|
856
894
|
)` );
|
|
895
|
+
}
|
|
857
896
|
else if ( camera )
|
|
858
897
|
writer.beginBlock( `def Camera "${name}"` );
|
|
859
898
|
else
|
|
@@ -14,6 +14,7 @@ import { Context } from "../../../engine/engine_setup";
|
|
|
14
14
|
import { WebARSessionRoot } from "../../webxr/WebARSessionRoot";
|
|
15
15
|
import { hasProLicense } from "../../../engine/engine_license";
|
|
16
16
|
import { BehaviorExtension } from "./extensions/behavior/Behaviour";
|
|
17
|
+
import { AudioExtension } from "./extensions/behavior/AudioExtension";
|
|
17
18
|
|
|
18
19
|
const debug = getParam("debugusdz");
|
|
19
20
|
|
|
@@ -41,6 +42,9 @@ export class USDZExporter extends Behaviour {
|
|
|
41
42
|
@serializable()
|
|
42
43
|
exportFileName?: string;
|
|
43
44
|
|
|
45
|
+
@serializable(URL)
|
|
46
|
+
customUsdzFile?: string;
|
|
47
|
+
|
|
44
48
|
@serializable(QuickLookOverlay)
|
|
45
49
|
overlay?: QuickLookOverlay;
|
|
46
50
|
|
|
@@ -91,6 +95,7 @@ export class USDZExporter extends Behaviour {
|
|
|
91
95
|
|
|
92
96
|
if (this.interactive) {
|
|
93
97
|
this.extensions.push(new BehaviorExtension());
|
|
98
|
+
this.extensions.push(new AudioExtension());
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -120,6 +125,35 @@ export class USDZExporter extends Behaviour {
|
|
|
120
125
|
}
|
|
121
126
|
|
|
122
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
|
+
|
|
123
157
|
if (!this.objectToExport) return;
|
|
124
158
|
|
|
125
159
|
// make sure we apply the AR scale
|
|
@@ -145,10 +179,6 @@ export class USDZExporter extends Behaviour {
|
|
|
145
179
|
const eventArgs = { self: this, exporter: exporter, extensions: extensions, object: this.objectToExport };
|
|
146
180
|
this.dispatchEvent(new CustomEvent("before-export", { detail: eventArgs }))
|
|
147
181
|
|
|
148
|
-
let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
|
|
149
|
-
if (!hasProLicense()) name += "-MadeWithNeedle";
|
|
150
|
-
name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
|
|
151
|
-
|
|
152
182
|
//@ts-ignore
|
|
153
183
|
exporter.debug = debug;
|
|
154
184
|
|
|
@@ -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
|
+
}
|
|
@@ -8,6 +8,7 @@ export interface UsdzBehaviour {
|
|
|
8
8
|
createBehaviours?(ext: BehaviorExtension, model: USDObject, context: IContext): void;
|
|
9
9
|
beforeCreateDocument?(ext: BehaviorExtension, context: IContext): void;
|
|
10
10
|
afterCreateDocument?(ext: BehaviorExtension, context: IContext): void;
|
|
11
|
+
afterSerialize?(ext: BehaviorExtension, context: IContext): void;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export class BehaviorExtension implements IUSDExporterExtension {
|
|
@@ -23,6 +24,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
behaviourComponents: Array<UsdzBehaviour> = [];
|
|
27
|
+
private behaviourComponentsCopy: Array<UsdzBehaviour> = [];
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
onBeforeBuildDocument(context) {
|
|
@@ -42,7 +44,6 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
onExportObject(_object, model: USDObject, context) {
|
|
45
|
-
|
|
46
47
|
for (const beh of this.behaviourComponents) {
|
|
47
48
|
beh.createBehaviours?.call(beh, this, model, context);
|
|
48
49
|
}
|
|
@@ -53,6 +54,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
53
54
|
if (typeof beh.afterCreateDocument === "function")
|
|
54
55
|
beh.afterCreateDocument(this, context);
|
|
55
56
|
}
|
|
57
|
+
this.behaviourComponentsCopy = this.behaviourComponents.slice();
|
|
56
58
|
this.behaviourComponents.length = 0;
|
|
57
59
|
}
|
|
58
60
|
|
|
@@ -70,6 +72,26 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
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
|
+
|
|
73
95
|
// combine behaviours that have tap triggers on the same object
|
|
74
96
|
// private combineBehavioursWithSameTapActions() {
|
|
75
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
|