@needle-tools/engine 3.5.1-alpha → 3.5.2-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 +6 -0
- package/dist/needle-engine.js +10373 -10253
- package/dist/needle-engine.min.js +320 -319
- package/dist/needle-engine.umd.cjs +316 -315
- package/lib/engine/codegen/register_types.js +0 -2
- package/lib/engine/codegen/register_types.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_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-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 +0 -1
- package/lib/engine-components/codegen/components.js +0 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +9 -5
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +23 -7
- 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 +14 -5
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +1 -5
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +1 -10
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +4 -4
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.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 +2 -4
- package/src/engine/engine_element_loading.ts +2 -2
- package/src/engine/engine_license.ts +25 -4
- package/src/engine-components/SceneSwitcher.ts +136 -1
- package/src/engine-components/codegen/components.ts +0 -1
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +36 -7
- package/src/engine-components/export/usdz/USDZExporter.ts +14 -7
- package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +5 -15
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +19 -19
- package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.2-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",
|
package/plugins/vite/license.js
CHANGED
|
@@ -13,8 +13,8 @@ export const needleLicense = (command, config, userSettings) => {
|
|
|
13
13
|
if (isNeedleEngineFile || isViteChunkFile) {
|
|
14
14
|
const needleConfig = await loadConfig();
|
|
15
15
|
if (needleConfig) {
|
|
16
|
-
if (needleConfig.
|
|
17
|
-
src = src.replace("
|
|
16
|
+
if (typeof needleConfig.license === "string") {
|
|
17
|
+
src = src.replace("const NEEDLE_ENGINE_LICENSE_TYPE: string = \"\";", "const NEEDLE_ENGINE_LICENSE_TYPE: string = \"" + needleConfig.license + "\";");
|
|
18
18
|
return { code: src, map: null }
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -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";
|
|
@@ -184,7 +184,6 @@ import { TriggerModel } from "../../engine-components/export/usdz/extensions/beh
|
|
|
184
184
|
import { UIRaycastUtils } from "../../engine-components/ui/RaycastUtils";
|
|
185
185
|
import { UIRootComponent } from "../../engine-components/ui/BaseUIComponent";
|
|
186
186
|
import { UsageMarker } from "../../engine-components/Interactable";
|
|
187
|
-
import { USDZBehaviours } from "../../engine-components/export/usdz/extensions/behavior/Behaviour";
|
|
188
187
|
import { USDZExporter } from "../../engine-components/export/usdz/USDZExporter";
|
|
189
188
|
import { USDZText } from "../../engine-components/export/usdz/extensions/USDZText";
|
|
190
189
|
import { VariantAction } from "../../engine-components/export/usdz/extensions/behavior/Actions";
|
|
@@ -214,7 +213,7 @@ import { XRGrabModel } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
|
214
213
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering";
|
|
215
214
|
import { XRRig } from "../../engine-components/webxr/WebXRRig";
|
|
216
215
|
import { XRState } from "../../engine-components/XRFlag";
|
|
217
|
-
|
|
216
|
+
|
|
218
217
|
// Register types
|
|
219
218
|
TypeStore.add("__Ignore", __Ignore);
|
|
220
219
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
|
@@ -399,7 +398,6 @@ TypeStore.add("TriggerModel", TriggerModel);
|
|
|
399
398
|
TypeStore.add("UIRaycastUtils", UIRaycastUtils);
|
|
400
399
|
TypeStore.add("UIRootComponent", UIRootComponent);
|
|
401
400
|
TypeStore.add("UsageMarker", UsageMarker);
|
|
402
|
-
TypeStore.add("USDZBehaviours", USDZBehaviours);
|
|
403
401
|
TypeStore.add("USDZExporter", USDZExporter);
|
|
404
402
|
TypeStore.add("USDZText", USDZText);
|
|
405
403
|
TypeStore.add("VariantAction", VariantAction);
|
|
@@ -3,7 +3,7 @@ import { Mathf } from "./engine_math";
|
|
|
3
3
|
import { LoadingProgressArgs } from "./engine_setup";
|
|
4
4
|
import { getParam } from "./engine_utils";
|
|
5
5
|
import { logoSVG } from "./assets"
|
|
6
|
-
import { hasProLicense } from "./engine_license";
|
|
6
|
+
import { hasCommercialLicense, hasProLicense } from "./engine_license";
|
|
7
7
|
|
|
8
8
|
const debug = getParam("debugloadingbar");
|
|
9
9
|
const debugRendering = getParam("debugloadingbarrendering");
|
|
@@ -301,7 +301,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
if (!
|
|
304
|
+
if (!hasCommercialLicense()) {
|
|
305
305
|
const nonCommercialContainer = document.createElement("div");
|
|
306
306
|
nonCommercialContainer.style.paddingTop = ".6em";
|
|
307
307
|
nonCommercialContainer.style.fontSize = ".8em";
|
|
@@ -7,10 +7,28 @@ const debug = getParam("debuglicense");
|
|
|
7
7
|
|
|
8
8
|
// This is modified by a bundler (e.g. vite)
|
|
9
9
|
// Do not edit manually
|
|
10
|
-
const
|
|
10
|
+
const NEEDLE_ENGINE_LICENSE_TYPE: string = "";
|
|
11
|
+
if (debug) console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)
|
|
11
12
|
|
|
12
13
|
export function hasProLicense() {
|
|
13
|
-
|
|
14
|
+
switch (NEEDLE_ENGINE_LICENSE_TYPE) {
|
|
15
|
+
case "pro":
|
|
16
|
+
case "enterprise":
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasIndieLicense() {
|
|
23
|
+
switch (NEEDLE_ENGINE_LICENSE_TYPE) {
|
|
24
|
+
case "indie":
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function hasCommercialLicense() {
|
|
31
|
+
return hasProLicense() || hasIndieLicense();
|
|
14
32
|
}
|
|
15
33
|
|
|
16
34
|
|
|
@@ -52,7 +70,8 @@ function insertNonCommercialUseHint(ctx: IContext) {
|
|
|
52
70
|
}
|
|
53
71
|
}, 100);
|
|
54
72
|
|
|
55
|
-
|
|
73
|
+
if (!hasCommercialLicense())
|
|
74
|
+
logNonCommercialUse();
|
|
56
75
|
|
|
57
76
|
let svg = `<img class="logo" src="${logoSVG}" style="width: 40px; height: 40px; margin-right: 2px; vertical-align: middle; margin-bottom: 2px;"/>`;
|
|
58
77
|
const logoElement = document.createElement("div");
|
|
@@ -66,7 +85,9 @@ function insertNonCommercialUseHint(ctx: IContext) {
|
|
|
66
85
|
// textElement.innerHTML = "Needle Engine<br/><span class=\"non-commercial\">Non Commercial</span>";
|
|
67
86
|
licenseElement.appendChild(textElement);
|
|
68
87
|
|
|
69
|
-
licenseElement.title = "Needle Engine
|
|
88
|
+
licenseElement.title = "Needle Engine";
|
|
89
|
+
if (!hasCommercialLicense())
|
|
90
|
+
licenseElement.title += " non commercial version";
|
|
70
91
|
licenseElement.addEventListener("click", () => {
|
|
71
92
|
globalThis.open("https://needle.tools", "_blank");
|
|
72
93
|
});
|
|
@@ -56,11 +56,27 @@ export class SceneSwitcher extends Behaviour {
|
|
|
56
56
|
@serializable()
|
|
57
57
|
useSceneLighting: boolean = true;
|
|
58
58
|
|
|
59
|
+
/** how many scenes after the currently active scene should be preloaded */
|
|
60
|
+
@serializable()
|
|
61
|
+
preloadNext: number = 1;
|
|
62
|
+
|
|
63
|
+
/** how many scenes before the currently active scene should be preloaded */
|
|
64
|
+
@serializable()
|
|
65
|
+
preloadPrevious: number = 1;
|
|
66
|
+
|
|
67
|
+
/** how many scenes can be loaded in parallel */
|
|
68
|
+
@serializable()
|
|
69
|
+
preloadConcurrent: number = 2;
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
get currentIndex(): number { return this._currentIndex; }
|
|
59
73
|
|
|
60
74
|
private _currentIndex: number = -1;
|
|
61
75
|
private _currentScene: AssetReference | undefined = undefined;
|
|
62
76
|
private _engineElementOverserver: MutationObserver | undefined = undefined;
|
|
63
77
|
|
|
78
|
+
private _preloadScheduler?: PreLoadScheduler;
|
|
79
|
+
|
|
64
80
|
async start() {
|
|
65
81
|
if (this._currentIndex === -1 && !await this.tryLoadFromQueryParam()) {
|
|
66
82
|
const value = this.context.domElement.getAttribute(ENGINE_ELEMENT_SCENE_ATTRIBUTE_NAME);
|
|
@@ -100,6 +116,13 @@ export class SceneSwitcher extends Behaviour {
|
|
|
100
116
|
this._engineElementOverserver.observe(this.context.domElement, {
|
|
101
117
|
attributes: true
|
|
102
118
|
});
|
|
119
|
+
|
|
120
|
+
if (!this._preloadScheduler)
|
|
121
|
+
this._preloadScheduler = new PreLoadScheduler(this);
|
|
122
|
+
this._preloadScheduler.maxLoadAhead = this.preloadNext;
|
|
123
|
+
this._preloadScheduler.maxLoadBehind = this.preloadPrevious;
|
|
124
|
+
this._preloadScheduler.maxConcurrent = this.preloadConcurrent;
|
|
125
|
+
this._preloadScheduler.begin();
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
onDisable(): void {
|
|
@@ -107,6 +130,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
107
130
|
this.context.input.removeEventListener(InputEvents.KeyDown, this.onKeyDown);
|
|
108
131
|
this.context.input.removeEventListener(InputEvents.PointerMove, this.onPointerMove);
|
|
109
132
|
this.context.input.removeEventListener(InputEvents.PointerUp, this.onPointerUp);
|
|
133
|
+
this._preloadScheduler?.stop();
|
|
110
134
|
}
|
|
111
135
|
|
|
112
136
|
private onPopState = async (_state: PopStateEvent) => {
|
|
@@ -189,7 +213,7 @@ export class SceneSwitcher extends Behaviour {
|
|
|
189
213
|
select(index: number | string): Promise<boolean> {
|
|
190
214
|
if (debug) console.log("select", index);
|
|
191
215
|
|
|
192
|
-
if(typeof index === "object"){
|
|
216
|
+
if (typeof index === "object") {
|
|
193
217
|
// If a user tries to reference a scene object in a UnityEvent and invoke select(obj)
|
|
194
218
|
// Then the object will be serialized as a object { guid : ... } or with the index json pointer
|
|
195
219
|
// This case is not supported right now. Object references in the editor must not be scene references
|
|
@@ -271,6 +295,15 @@ export class SceneSwitcher extends Behaviour {
|
|
|
271
295
|
return false;
|
|
272
296
|
}
|
|
273
297
|
|
|
298
|
+
preload(index: number) {
|
|
299
|
+
if (index >= 0 && index < this.scenes.length) {
|
|
300
|
+
const scene = this.scenes[index];
|
|
301
|
+
if(scene instanceof AssetReference)
|
|
302
|
+
return scene.preload();
|
|
303
|
+
}
|
|
304
|
+
return couldNotLoadScenePromise;
|
|
305
|
+
}
|
|
306
|
+
|
|
274
307
|
private tryLoadFromQueryParam() {
|
|
275
308
|
if (!this.queryParameterName?.length) return couldNotLoadScenePromise;
|
|
276
309
|
// try restore the scene from the url
|
|
@@ -308,3 +341,105 @@ export class SceneSwitcher extends Behaviour {
|
|
|
308
341
|
return couldNotLoadScenePromise;
|
|
309
342
|
}
|
|
310
343
|
}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class PreLoadScheduler {
|
|
349
|
+
maxLoadAhead: number;
|
|
350
|
+
maxLoadBehind: number;
|
|
351
|
+
maxConcurrent: number;
|
|
352
|
+
|
|
353
|
+
private _isRunning: boolean = false;
|
|
354
|
+
private _rooms: SceneSwitcher;
|
|
355
|
+
private _roomTasks: LoadTask[] = [];
|
|
356
|
+
private _maxConcurrentLoads: number = 1;
|
|
357
|
+
|
|
358
|
+
constructor(rooms: SceneSwitcher, ahead: number = 1, behind: number = 1, maxConcurrent: number = 2) {
|
|
359
|
+
this._rooms = rooms;
|
|
360
|
+
this.maxLoadAhead = ahead;
|
|
361
|
+
this.maxLoadBehind = behind;
|
|
362
|
+
this.maxConcurrent = maxConcurrent;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
begin() {
|
|
366
|
+
if (this._isRunning) return;
|
|
367
|
+
if (debug) console.log("Preload begin")
|
|
368
|
+
this._isRunning = true;
|
|
369
|
+
let lastRoom: number = -1;
|
|
370
|
+
let searchDistance: number;
|
|
371
|
+
let searchCall: number;
|
|
372
|
+
const array = this._rooms.scenes;
|
|
373
|
+
let interval = setInterval(() => {
|
|
374
|
+
if (this.allLoaded()) {
|
|
375
|
+
if (debug)
|
|
376
|
+
console.log("All scenes loaded");
|
|
377
|
+
this.stop();
|
|
378
|
+
}
|
|
379
|
+
if (!this._isRunning) {
|
|
380
|
+
clearInterval(interval);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (this.canLoadNewScene() === false) return;
|
|
384
|
+
if (lastRoom !== this._rooms.currentIndex) {
|
|
385
|
+
lastRoom = this._rooms.currentIndex;
|
|
386
|
+
searchCall = 0;
|
|
387
|
+
searchDistance = 0;
|
|
388
|
+
}
|
|
389
|
+
const searchForward = searchCall % 2 === 0;
|
|
390
|
+
if (searchForward) searchDistance += 1;
|
|
391
|
+
searchCall += 1;
|
|
392
|
+
const maxSearchDistance = searchForward ? this.maxLoadAhead : this.maxLoadBehind;
|
|
393
|
+
if (searchDistance > maxSearchDistance) return;
|
|
394
|
+
let roomIndex = searchForward ? lastRoom + searchDistance : lastRoom - searchDistance;
|
|
395
|
+
if (roomIndex < 0) return;
|
|
396
|
+
// if (roomIndex < 0) roomIndex = array.length + roomIndex;
|
|
397
|
+
if (roomIndex < 0 || roomIndex >= array.length) return;
|
|
398
|
+
const scene = array[roomIndex];
|
|
399
|
+
new LoadTask(roomIndex, scene, this._roomTasks);
|
|
400
|
+
}, 200);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
stop() {
|
|
404
|
+
this._isRunning = false;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
canLoadNewScene(): boolean {
|
|
408
|
+
return this._roomTasks.length < this._maxConcurrentLoads;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
allLoaded(): boolean {
|
|
412
|
+
for (const room of this._rooms.scenes) {
|
|
413
|
+
if (room.isLoaded() === false) return false;
|
|
414
|
+
}
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
class LoadTask {
|
|
420
|
+
|
|
421
|
+
index: number;
|
|
422
|
+
asset: AssetReference;
|
|
423
|
+
tasks: LoadTask[];
|
|
424
|
+
|
|
425
|
+
constructor(index: number, asset: AssetReference, tasks: LoadTask[]) {
|
|
426
|
+
this.index = index;
|
|
427
|
+
this.asset = asset;
|
|
428
|
+
this.tasks = tasks;
|
|
429
|
+
tasks.push(this);
|
|
430
|
+
this.awaitLoading();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private async awaitLoading() {
|
|
434
|
+
if (!this.asset.isLoaded()) {
|
|
435
|
+
if (debug)
|
|
436
|
+
console.log("Preload start: " + this.asset.uri, this.index);
|
|
437
|
+
await this.asset.preload();
|
|
438
|
+
if (debug)
|
|
439
|
+
console.log("Preload finished: " + this.asset.uri, this.index);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const i = this.tasks.indexOf(this);
|
|
443
|
+
if (i >= 0) this.tasks.splice(i, 1);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
@@ -179,7 +179,6 @@ export { TriggerModel } from "../export/usdz/extensions/behavior/BehavioursBuild
|
|
|
179
179
|
export { UIRaycastUtils } from "../ui/RaycastUtils";
|
|
180
180
|
export { UIRootComponent } from "../ui/BaseUIComponent";
|
|
181
181
|
export { UsageMarker } from "../Interactable";
|
|
182
|
-
export { USDZBehaviours } from "../export/usdz/extensions/behavior/Behaviour";
|
|
183
182
|
export { USDZExporter } from "../export/usdz/USDZExporter";
|
|
184
183
|
export { USDZText } from "../export/usdz/extensions/USDZText";
|
|
185
184
|
export { VariantAction } from "../export/usdz/extensions/behavior/Actions";
|
|
@@ -48,7 +48,6 @@ class USDObject {
|
|
|
48
48
|
parent: USDObject | null;
|
|
49
49
|
children: Array<USDObject | null> = [];
|
|
50
50
|
_eventListeners: {};
|
|
51
|
-
mesh: any;
|
|
52
51
|
|
|
53
52
|
static createEmptyParent( object ) {
|
|
54
53
|
|
|
@@ -92,7 +91,7 @@ class USDObject {
|
|
|
92
91
|
|
|
93
92
|
clone() {
|
|
94
93
|
|
|
95
|
-
const clone = new USDObject( MathUtils.generateUUID(), this.name, this.matrix, this.
|
|
94
|
+
const clone = new USDObject( MathUtils.generateUUID(), this.name, this.matrix, this.geometry, this.material );
|
|
96
95
|
clone.isDynamic = this.isDynamic;
|
|
97
96
|
return clone;
|
|
98
97
|
|
|
@@ -383,9 +382,20 @@ class USDZExporterContext {
|
|
|
383
382
|
|
|
384
383
|
}
|
|
385
384
|
|
|
385
|
+
/**[documentation](https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/preliminary_anchoringapi/preliminary_anchoring_type) */
|
|
386
|
+
export type Anchoring = "plane" | "image" | "face" | "none"
|
|
387
|
+
/**[documentation](https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/preliminary_anchoringapi/preliminary_planeanchoring_alignment) */
|
|
388
|
+
export type Alignment = "horizontal" | "vertical" | "any";
|
|
389
|
+
|
|
386
390
|
class USDZExporterOptions {
|
|
387
|
-
ar: {
|
|
388
|
-
|
|
391
|
+
ar: {
|
|
392
|
+
anchoring: { type: Anchoring },
|
|
393
|
+
planeAnchoring: { alignment: Alignment },
|
|
394
|
+
} = {
|
|
395
|
+
anchoring: { type: 'plane' },
|
|
396
|
+
planeAnchoring: { alignment: 'horizontal' }
|
|
397
|
+
};
|
|
398
|
+
quickLookCompatible: boolean = false;
|
|
389
399
|
extensions: any[] = [];
|
|
390
400
|
}
|
|
391
401
|
|
|
@@ -594,7 +604,7 @@ function parseDocument( context: USDZExporterContext ) {
|
|
|
594
604
|
|
|
595
605
|
writer.appendLine( `token preliminary:anchoring:type = "${context.exporter.sceneAnchoringOptions.ar.anchoring.type}"` );
|
|
596
606
|
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'plane')
|
|
597
|
-
writer.appendLine( `token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.planeAnchoring.alignment}"` );
|
|
607
|
+
writer.appendLine( `token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.ar.planeAnchoring.alignment}"` );
|
|
598
608
|
// 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
609
|
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'image')
|
|
600
610
|
writer.appendLine( `rel preliminary:imageAnchoring:referenceImage = </${context.document.name}/Scenes/Scene/AnchoringReferenceImage>` );
|
|
@@ -1076,7 +1086,7 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
|
|
|
1076
1086
|
const pad = ' ';
|
|
1077
1087
|
const inputs: Array<string> = [];
|
|
1078
1088
|
const samplers: Array<string> = [];
|
|
1079
|
-
const exportForQuickLook =
|
|
1089
|
+
const exportForQuickLook = false;
|
|
1080
1090
|
|
|
1081
1091
|
function buildTexture( texture, mapType, color: Color | undefined = undefined, opacity: number | undefined = undefined ) {
|
|
1082
1092
|
|
|
@@ -1088,6 +1098,11 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
|
|
|
1088
1098
|
|
|
1089
1099
|
const repeat = texture.repeat.clone();
|
|
1090
1100
|
const offset = texture.offset.clone();
|
|
1101
|
+
const rotation = texture.rotation;
|
|
1102
|
+
|
|
1103
|
+
// rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
|
|
1104
|
+
let xRotationOffset = Math.sin(rotation);
|
|
1105
|
+
let yRotationOffset = Math.cos(rotation);
|
|
1091
1106
|
|
|
1092
1107
|
// texture coordinates start in the opposite corner, need to correct
|
|
1093
1108
|
offset.y = 1 - offset.y - repeat.y;
|
|
@@ -1096,15 +1111,28 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
|
|
|
1096
1111
|
// Apple Feedback: FB10036297 and FB11442287
|
|
1097
1112
|
if ( exportForQuickLook ) {
|
|
1098
1113
|
|
|
1114
|
+
// This is NOT correct yet in QuickLook, but comes close for a range of models.
|
|
1115
|
+
// It becomes more incorrect the bigger the offset is
|
|
1116
|
+
|
|
1099
1117
|
offset.x = offset.x / repeat.x;
|
|
1100
1118
|
offset.y = offset.y / repeat.y;
|
|
1101
1119
|
|
|
1120
|
+
offset.x += xRotationOffset / repeat.x;
|
|
1121
|
+
offset.y += yRotationOffset - 1;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
else {
|
|
1125
|
+
|
|
1126
|
+
// results match glTF results exactly. verified correct in usdview.
|
|
1127
|
+
offset.x += xRotationOffset * repeat.x;
|
|
1128
|
+
offset.y += (1 - yRotationOffset) * repeat.y;
|
|
1129
|
+
|
|
1102
1130
|
}
|
|
1103
1131
|
|
|
1104
1132
|
textures[ id ] = texture;
|
|
1105
1133
|
const uvReader = mapType == 'occlusion' ? 'uvReader_st2' : 'uvReader_st';
|
|
1106
1134
|
|
|
1107
|
-
const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 );
|
|
1135
|
+
const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0 );
|
|
1108
1136
|
const textureTransformInput = `</Materials/Material_${material.id}/${uvReader}.outputs:result>`;
|
|
1109
1137
|
const textureTransformOutput = `</Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
|
1110
1138
|
|
|
@@ -1123,6 +1151,7 @@ function buildMaterial( material: MeshStandardMaterial, textures ) {
|
|
|
1123
1151
|
float2 inputs:in.connect = ${textureTransformInput}
|
|
1124
1152
|
float2 inputs:scale = ${buildVector2( repeat )}
|
|
1125
1153
|
float2 inputs:translation = ${buildVector2( offset )}
|
|
1154
|
+
float inputs:rotation = ${(rotation / Math.PI * 180).toFixed( PRECISION )}
|
|
1126
1155
|
float2 outputs:result
|
|
1127
1156
|
}
|
|
1128
1157
|
` : '' }
|
|
@@ -13,6 +13,7 @@ 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";
|
|
16
17
|
|
|
17
18
|
const debug = getParam("debugusdz");
|
|
18
19
|
|
|
@@ -51,6 +52,9 @@ export class USDZExporter extends Behaviour {
|
|
|
51
52
|
@serializable()
|
|
52
53
|
planeAnchoringAlignment: "horizontal" | "vertical" | "any" = "horizontal";
|
|
53
54
|
|
|
55
|
+
@serializable()
|
|
56
|
+
interactive: boolean = true;
|
|
57
|
+
|
|
54
58
|
extensions: IUSDExporterExtension[] = [];
|
|
55
59
|
|
|
56
60
|
private link!: HTMLAnchorElement;
|
|
@@ -84,9 +88,11 @@ export class USDZExporter extends Behaviour {
|
|
|
84
88
|
this.objectToExport = this.gameObject;
|
|
85
89
|
if (!this.objectToExport?.children?.length && !(this.objectToExport as Mesh)?.isMesh)
|
|
86
90
|
this.objectToExport = this.context.scene;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
91
|
|
|
92
|
+
if (this.interactive) {
|
|
93
|
+
this.extensions.push(new BehaviorExtension());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
90
96
|
|
|
91
97
|
onEnable() {
|
|
92
98
|
const ios = isiOS()
|
|
@@ -157,12 +163,13 @@ export class USDZExporter extends Behaviour {
|
|
|
157
163
|
ar: {
|
|
158
164
|
anchoring: {
|
|
159
165
|
type: this.anchoringType,
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
},
|
|
167
|
+
planeAnchoring: {
|
|
168
|
+
alignment: this.planeAnchoringAlignment,
|
|
169
|
+
},
|
|
164
170
|
},
|
|
165
|
-
extensions: extensions
|
|
171
|
+
extensions: extensions,
|
|
172
|
+
quickLookCompatible: true,
|
|
166
173
|
});
|
|
167
174
|
const blob = new Blob([arraybuffer], { type: 'application/octet-stream' });
|
|
168
175
|
|
|
@@ -1,9 +1,8 @@
|
|
|
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;
|
|
@@ -11,15 +10,6 @@ export interface UsdzBehaviour {
|
|
|
11
10
|
afterCreateDocument?(ext: BehaviorExtension, context: IContext): void;
|
|
12
11
|
}
|
|
13
12
|
|
|
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
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
13
|
export class BehaviorExtension implements IUSDExporterExtension {
|
|
24
14
|
|
|
25
15
|
get extensionName(): string {
|
|
@@ -40,8 +30,8 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
40
30
|
GameObject.foreachComponent(e, (comp) => {
|
|
41
31
|
const c = comp as unknown as UsdzBehaviour;
|
|
42
32
|
if (
|
|
43
|
-
typeof c.createBehaviours === "function" ||
|
|
44
|
-
typeof c.beforeCreateDocument === "function" ||
|
|
33
|
+
typeof c.createBehaviours === "function" ||
|
|
34
|
+
typeof c.beforeCreateDocument === "function" ||
|
|
45
35
|
typeof c.afterCreateDocument === "function"
|
|
46
36
|
) {
|
|
47
37
|
this.behaviourComponents.push(c);
|
|
@@ -66,7 +56,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
|
|
|
66
56
|
this.behaviourComponents.length = 0;
|
|
67
57
|
}
|
|
68
58
|
|
|
69
|
-
onAfterHierarchy(context, writer
|
|
59
|
+
onAfterHierarchy(context, writer: USDWriter) {
|
|
70
60
|
if (this.behaviours?.length) {
|
|
71
61
|
|
|
72
62
|
// this.combineBehavioursWithSameTapActions();
|
|
@@ -7,7 +7,7 @@ import { RegisteredAnimationInfo, UsdzAnimation } from "../Animation";
|
|
|
7
7
|
import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPosition, setWorldQuaternion, setWorldScale } from "../../../../../engine/engine_three_utils";
|
|
8
8
|
|
|
9
9
|
import { Object3D, Material, Vector3, Quaternion, AnimationAction } from "three";
|
|
10
|
-
import { USDObject } from "../../ThreeUSDZExporter";
|
|
10
|
+
import { USDDocument, USDObject } from "../../ThreeUSDZExporter";
|
|
11
11
|
|
|
12
12
|
import { BehaviorExtension, UsdzBehaviour } from "./Behaviour";
|
|
13
13
|
import { ActionBuilder, ActionModel, BehaviorModel, IBehaviorElement, MotionType, Space, TriggerBuilder } from "./BehavioursBuilder";
|
|
@@ -44,7 +44,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
44
44
|
|
|
45
45
|
const thisScale = getWorldScale(this.object).clone();
|
|
46
46
|
const targetScale = getWorldScale(this.target).clone();
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
const dist = thisPos.distanceTo(targetPos);
|
|
49
49
|
const rotDist = thisRot.angleTo(targetRot);
|
|
50
50
|
const scaleDist = thisScale.distanceTo(targetScale);
|
|
@@ -63,10 +63,10 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
63
63
|
|
|
64
64
|
t01 += this.context.time.deltaTime / this.duration;
|
|
65
65
|
if (t01 > 1) t01 = 1;
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
// apply ease-in-out
|
|
68
68
|
// https://easings.net/
|
|
69
|
-
eased =
|
|
69
|
+
eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
|
|
70
70
|
|
|
71
71
|
this.targetPos.lerpVectors(thisPos, targetPos, eased);
|
|
72
72
|
this.targetRot.slerpQuaternions(thisRot, targetRot, eased);
|
|
@@ -83,7 +83,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
private *moveRelative() {
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
if (!this.target || !this.object) return;
|
|
88
88
|
|
|
89
89
|
const thisPos = this.object.position.clone();
|
|
@@ -107,10 +107,10 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
107
107
|
|
|
108
108
|
t01 += this.context.time.deltaTime / this.duration;
|
|
109
109
|
if (t01 > 1) t01 = 1;
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
// apply ease-in-out
|
|
112
112
|
// https://easings.net/
|
|
113
|
-
eased =
|
|
113
|
+
eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
|
|
114
114
|
|
|
115
115
|
this.object.position.lerpVectors(thisPos, this.targetPos, eased);
|
|
116
116
|
this.object.quaternion.slerpQuaternions(thisRot, this.targetRot, eased);
|
|
@@ -120,7 +120,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
this.coroutine = null;
|
|
123
|
-
}
|
|
123
|
+
}
|
|
124
124
|
|
|
125
125
|
onPointerClick() {
|
|
126
126
|
if (this.coroutine) this.stopCoroutine(this.coroutine);
|
|
@@ -313,11 +313,12 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
313
313
|
hideClickedObject = true;
|
|
314
314
|
targetState = !this.target.visible;
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
if (!this.selfModel.parent || this.selfModel.parent.isEmpty())
|
|
317
|
+
USDDocument.createEmptyParent(this.selfModel);
|
|
318
|
+
|
|
317
319
|
this.toggleModel = this.selfModel.clone();
|
|
318
320
|
this.toggleModel.name += "_toggle";
|
|
319
|
-
|
|
320
|
-
this.selfModel.parent.add(this.toggleModel);
|
|
321
|
+
this.selfModel.parent!.add(this.toggleModel);
|
|
321
322
|
}
|
|
322
323
|
|
|
323
324
|
const sequence: ActionModel[] = [];
|
|
@@ -344,8 +345,8 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
|
|
|
344
345
|
ActionBuilder.sequence(...toggleSequence)
|
|
345
346
|
));
|
|
346
347
|
|
|
347
|
-
ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
|
|
348
|
-
TriggerBuilder.sceneStartTrigger(),
|
|
348
|
+
ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
|
|
349
|
+
TriggerBuilder.sceneStartTrigger(),
|
|
349
350
|
ActionBuilder.fadeAction(this.toggleModel, 0, false)
|
|
350
351
|
));
|
|
351
352
|
}
|
|
@@ -363,8 +364,8 @@ export class HideOnStart extends Behaviour implements UsdzBehaviour {
|
|
|
363
364
|
|
|
364
365
|
createBehaviours(ext, model, _context) {
|
|
365
366
|
if (model.uuid === this.gameObject.uuid)
|
|
366
|
-
ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
|
|
367
|
-
TriggerBuilder.sceneStartTrigger(),
|
|
367
|
+
ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
|
|
368
|
+
TriggerBuilder.sceneStartTrigger(),
|
|
368
369
|
ActionBuilder.fadeAction(model, 0, false)
|
|
369
370
|
));
|
|
370
371
|
}
|
|
@@ -394,8 +395,7 @@ export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
|
|
|
394
395
|
createBehaviours(ext, model, _context) {
|
|
395
396
|
if (!this.target) return;
|
|
396
397
|
|
|
397
|
-
if (model.uuid === this.gameObject.uuid)
|
|
398
|
-
{
|
|
398
|
+
if (model.uuid === this.gameObject.uuid) {
|
|
399
399
|
const emphasize = new BehaviorModel("emphasize " + this.name,
|
|
400
400
|
TriggerBuilder.tapTrigger(this.gameObject),
|
|
401
401
|
ActionBuilder.emphasize(this.target, this.duration, this.motionType, undefined, "basic"),
|
|
@@ -417,7 +417,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
417
417
|
|
|
418
418
|
@serializable()
|
|
419
419
|
stateName?: string;
|
|
420
|
-
|
|
420
|
+
|
|
421
421
|
@serializable()
|
|
422
422
|
stateNameAfterPlaying?: string;
|
|
423
423
|
|
|
@@ -486,7 +486,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
|
|
|
486
486
|
|
|
487
487
|
createAnimation(ext, model, _context) {
|
|
488
488
|
if (this.target && this.animator) {
|
|
489
|
-
|
|
489
|
+
|
|
490
490
|
const state = this.animator?.runtimeAnimatorController?.findState(this.stateName);
|
|
491
491
|
this.stateAnimationModel = model;
|
|
492
492
|
this.stateAnimation = ext.registerAnimation(this.target, state?.motion.clip);
|
|
@@ -74,7 +74,7 @@ function resolve(targetObject: Target, document: USDDocument): string {
|
|
|
74
74
|
let obj = targetObject[i];
|
|
75
75
|
if (typeof obj === "string")
|
|
76
76
|
str += obj;
|
|
77
|
-
else if (
|
|
77
|
+
else if (typeof obj === "object") {
|
|
78
78
|
//@ts-ignore
|
|
79
79
|
if (obj.isObject3D) {
|
|
80
80
|
//@ts-ignore
|
|
@@ -436,7 +436,7 @@ export class ActionBuilder {
|
|
|
436
436
|
act.tokenId = "Emphasize";
|
|
437
437
|
act.duration = duration;
|
|
438
438
|
act.style = style ?? "basic";
|
|
439
|
-
act.motionType = MotionType[motionType];
|
|
439
|
+
act.motionType = MotionType[motionType];
|
|
440
440
|
act.moveDistance = moveDistance;
|
|
441
441
|
return act;
|
|
442
442
|
}
|