Needle Engine

Changes between version 3.7.5-alpha and 3.7.5-beta
Files changed (7) hide show
  1. src/engine-components/DropListener.ts +32 -39
  2. src/engine/engine_assetdatabase.ts +2 -2
  3. src/engine/engine_networking_files.ts +3 -4
  4. src/engine/engine_web_api.ts +0 -2
  5. src/engine-components/Networking.ts +1 -1
  6. src/engine-components/ui/Text.ts +1 -1
  7. src/engine-components/export/usdz/USDZExporter.ts +40 -18
src/engine-components/DropListener.ts CHANGED
@@ -4,13 +4,9 @@
4
4
  import { serializable } from "../engine/engine_serialization_decorator";
5
5
  import { Networking } from "../engine-components/Networking";
6
6
  import { GLTF } from "three/examples/jsm/loaders/GLTFLoader";
7
+ import { getParam } from "../engine/engine_utils";
7
8
 
8
- // web.download_file("487f7b22f68312d2c1bbc93b1aea445b.glb", "487f7b22f68312d2c1bbc93b1aea445b", 28152).then(async res => {
9
- // console.log(res);
10
- // const gltf = await parseSync(res);
11
- // scene.add(gltf.scene);
12
- // console.log("GLTF", gltf);
13
- // });
9
+ const debug = getParam("debugdroplistener");
14
10
 
15
11
  export enum DropListenerEvents {
16
12
  ObjectAdded = "object-added",
@@ -23,43 +19,29 @@
23
19
  filesBackendUrl?: string;
24
20
 
25
21
  @serializable()
26
- localhost?:string;
27
-
28
- private _dragOver!: (evt: DragEvent) => void;
29
- private _drop!: (evt: DragEvent) => void;
22
+ localhost?: string;
30
23
 
24
+
31
25
  onEnable(): void {
32
- this.filesBackendUrl = this.filesBackendUrl ? Networking.GetUrl(this.filesBackendUrl, this.localhost) : undefined;
26
+ this.filesBackendUrl = Networking.GetUrl(this.filesBackendUrl, this.localhost) ?? this.filesBackendUrl;
27
+ if (debug) console.log(this, this.filesBackendUrl);
33
28
 
34
- this._dragOver = this.onDrag.bind(this);
35
- this._drop = this.onDrop.bind(this);
36
- this.context.domElement.addEventListener("dragover", this._dragOver);
37
- this.context.domElement.addEventListener("drop", this._drop);
29
+ this.context.domElement.addEventListener("dragover", this.onDrag);
30
+ this.context.domElement.addEventListener("drop", this.onDrop);
38
31
  }
39
32
 
40
33
  onDisable(): void {
41
- this.context.domElement.removeEventListener("dragover", this._dragOver);
42
- this.context.domElement.removeEventListener("drop", this._drop);
34
+ this.context.domElement.removeEventListener("dragover", this.onDrag);
35
+ this.context.domElement.removeEventListener("drop", this.onDrop);
43
36
  }
44
37
 
45
- private onDrag(evt: DragEvent) {
38
+ private onDrag = (evt: DragEvent) => {
46
39
  // necessary to get drop event
47
40
  evt.preventDefault();
48
41
  }
49
42
 
50
- async addFiles(fileList: Array<File>) {
51
- for (const file of fileList) {
52
- if (!file) continue;
53
- console.log("Register file " + file.name + " to", this.filesBackendUrl, file);
54
- const res = await files.addFile(file, this.context, this.filesBackendUrl);
55
- this.dispatchEvent(new CustomEvent(DropListenerEvents.FileDropped, { detail: file }));
56
- if (res)
57
- this.addObject(undefined, res);
58
- }
59
- }
60
-
61
- private async onDrop(evt: DragEvent) {
62
- console.log(evt);
43
+ private onDrop = async (evt: DragEvent) => {
44
+ if (debug) console.log(evt);
63
45
  if (!evt.dataTransfer) return;
64
46
  evt.preventDefault();
65
47
  const items = evt.dataTransfer.items;
@@ -70,15 +52,11 @@
70
52
  if (it.kind === "file") {
71
53
  const file = it.getAsFile();
72
54
  if (!file) continue;
73
- console.log("Register file " + file.name + " to", this.filesBackendUrl, file);
74
- const res = await files.addFile(file, this.context, this.filesBackendUrl);
75
- this.dispatchEvent(new CustomEvent(DropListenerEvents.FileDropped, { detail: file }));
76
- if (res)
77
- this.addObject(evt, res);
55
+ await this.addFiles(file);
78
56
  }
79
57
  else if (it.kind === "string" && it.type == "text/plain") {
80
58
  it.getAsString(async str => {
81
- console.log("dropped url", str);
59
+ if (debug) console.log("dropped url", str);
82
60
  try {
83
61
  const url = new URL(str);
84
62
  if (!url) return;
@@ -94,9 +72,24 @@
94
72
  }
95
73
  }
96
74
 
75
+ async addFiles(...fileList: Array<File>) {
76
+ if(debug) console.log("Add files", fileList)
77
+ if (!Array.isArray(fileList)) return;
78
+ if (!fileList.length) return;
79
+
80
+ for (const file of fileList) {
81
+ if (!file) continue;
82
+ if (debug) console.log("Register file " + file.name + " to", this.filesBackendUrl, file);
83
+ const res = await files.addFile(file, this.context, this.filesBackendUrl);
84
+ this.dispatchEvent(new CustomEvent(DropListenerEvents.FileDropped, { detail: file }));
85
+ if (res)
86
+ this.addObject(undefined, res);
87
+ }
88
+ }
89
+
97
90
  private async addObject(evt: DragEvent | undefined, gltf: GLTF) {
98
- console.log("Dropped", gltf);
99
-
91
+ if (debug) console.log("Dropped", gltf);
92
+
100
93
  const obj = gltf.scene;
101
94
  if (evt !== undefined) {
102
95
  const opts = new RaycastOptions();
src/engine/engine_assetdatabase.ts CHANGED
@@ -99,9 +99,9 @@
99
99
  if (debug)
100
100
  console.warn("BufferAttribute dispose not supported", obj.count);
101
101
  }
102
- else if (obj instanceof Array<Material>) {
102
+ else if (obj instanceof Array) {
103
103
  for (const entry of obj) {
104
- if (entry)
104
+ if (entry instanceof Material)
105
105
  disposeObjectResources(entry);
106
106
  }
107
107
  }
src/engine/engine_networking_files.ts CHANGED
@@ -72,8 +72,7 @@
72
72
  });
73
73
  }
74
74
  else {
75
- console.warn("Unsupported file type: " + name);
76
- console.log(file);
75
+ console.warn("Unsupported file type: " + name, file)
77
76
  }
78
77
 
79
78
  return null;
@@ -118,7 +117,7 @@
118
117
  }
119
118
  if (bin) {
120
119
  const prov = new InstantiateIdProvider(evt.seed);
121
- const gltf = await getLoader().parseSync(context, bin, null!, prov);
120
+ const gltf = await getLoader().parseSync(context, bin, evt.file_name, prov);
122
121
  if (gltf && gltf.scene) {
123
122
  const obj = gltf.scene;
124
123
  def.onDynamicObjectAdded(obj, prov, gltf);
@@ -129,7 +128,7 @@
129
128
  // add object to proper parent
130
129
  if (evt.parentGuid) {
131
130
  const parent = findByGuid(evt.parentGuid, context.scene) as Object3D;
132
- if ("add" in parent) parent.add(obj);
131
+ if (parent && "add" in parent) parent.add(obj);
133
132
  }
134
133
  if (!obj.parent)
135
134
  context.scene.add(obj);
src/engine/engine_web_api.ts CHANGED
@@ -2,8 +2,6 @@
2
2
  // see https://github.com/pvorb/node-md5/issues/52
3
3
  import md5 from "md5";
4
4
 
5
- // const backend_url = null;// "https://needle-storage-castle-demo.glitch.me";
6
-
7
5
  export class Upload_Result {
8
6
  success: boolean;
9
7
  filename: string | null;
src/engine-components/Networking.ts CHANGED
@@ -48,7 +48,7 @@
48
48
  }
49
49
 
50
50
 
51
- public static GetUrl(url: string, localhostFallback?: string | null): string {
51
+ public static GetUrl(url: string | null | undefined, localhostFallback?: string | null): string | null | undefined {
52
52
 
53
53
  let result = url;
54
54
 
src/engine-components/ui/Text.ts CHANGED
@@ -568,5 +568,5 @@
568
568
 
569
569
 
570
570
  const unsupportedStyleNames = [
571
- "medium", "mediumitalic", "black", "blackitalic", "thin", "thinitalic", "extrabold", "light", "lightitalic"
571
+ "medium", "mediumitalic", "black", "blackitalic", "thin", "thinitalic", "extrabold", "light", "lightitalic", "semibold"
572
572
  ]
src/engine-components/export/usdz/USDZExporter.ts CHANGED
@@ -68,7 +68,6 @@
68
68
 
69
69
  private link!: HTMLAnchorElement;
70
70
  private webxr?: WebXR;
71
- private webARSessionRoot: WebARSessionRoot | undefined;
72
71
 
73
72
  start() {
74
73
  if (debug) {
@@ -107,7 +106,6 @@
107
106
  if (debug || (ios && safari)) {
108
107
  if (debug || this.allowCreateQuicklookButton)
109
108
  this.addQuicklookButton();
110
- this.webARSessionRoot = GameObject.findObjectOfType(WebARSessionRoot) ?? undefined;
111
109
  this.lastCallback = this.quicklookCallback.bind(this);
112
110
  this.link = ensureQuicklookLinkIsCreated(this.context);
113
111
  this.link.addEventListener('message', this.lastCallback);
@@ -144,6 +142,7 @@
144
142
 
145
143
  // ability to specify a custom USDZ file to be used instead of a dynamic one
146
144
  if (this.customUsdzFile) {
145
+ if(debug) console.log("Exporting custom usdz", this.customUsdzFile)
147
146
 
148
147
  // see https://developer.apple.com/documentation/arkit/adding_an_apple_pay_button_or_a_custom_action_in_ar_quick_look
149
148
  const overlay = this.buildQuicklookOverlay();
@@ -153,7 +152,7 @@
153
152
  const checkoutSubtitle = overlay.checkoutSubtitle ? encodeURIComponent(overlay.checkoutSubtitle) : "";
154
153
  this.link.href = this.customUsdzFile + `#callToAction=${callToAction}&checkoutTitle=${checkoutTitle}&checkoutSubtitle=${checkoutSubtitle}&callToActionURL=${overlay.callToActionURL}`;
155
154
 
156
- console.log(this.link.href)
155
+ if(debug) console.log(this.link.href)
157
156
 
158
157
  if (!this.lastCallback) {
159
158
  this.lastCallback = this.quicklookCallback.bind(this)
@@ -182,17 +181,10 @@
182
181
  }
183
182
  if (debug) showBalloonMessage("Load textures: " + progressiveLoading.length);
184
183
  await Promise.all(progressiveLoading);
185
- if(debug) showBalloonMessage("Load textures: done");
184
+ if (debug) showBalloonMessage("Load textures: done");
186
185
 
187
186
  // make sure we apply the AR scale
188
- if (this.webARSessionRoot) {
189
- const scene = this.webARSessionRoot.gameObject;
190
- const scale = 1 / this.webARSessionRoot!.arScale;
191
- scene.matrix.makeScale(scale, scale, scale);
192
- if (this.webARSessionRoot.invertForward) {
193
- scene.matrix.multiply(new Matrix4().makeRotationY(Math.PI));
194
- }
195
- }
187
+ this.applyWebARSessionRoot();
196
188
 
197
189
  const exporter = new ThreeUSDZExporter();
198
190
  const extensions: any = [...this.extensions]
@@ -298,12 +290,15 @@
298
290
  obj.checkoutTitle = "🌵 Made with Needle";
299
291
  obj.checkoutSubtitle = "_";
300
292
  }
301
- if (!obj.callToAction?.length)
302
- obj.callToAction = "Close";
303
- if (!obj.checkoutTitle?.length)
304
- obj.checkoutTitle = "🌵 Made with Needle";
305
- if (!obj.checkoutSubtitle?.length)
306
- obj.checkoutSubtitle = "_";
293
+ const needsDefaultValues = obj.callToAction?.length || obj.checkoutTitle?.length || obj.checkoutSubtitle?.length;
294
+ if (needsDefaultValues) {
295
+ if (!obj.callToAction?.length)
296
+ obj.callToAction = "\0";
297
+ if (!obj.checkoutTitle?.length)
298
+ obj.checkoutTitle = "\0";
299
+ if (!obj.checkoutSubtitle?.length)
300
+ obj.checkoutSubtitle = "\0";
301
+ }
307
302
  // Use the quicklook-overlay event to customize the overlay
308
303
  this.dispatchEvent(new CustomEvent("quicklook-overlay", { detail: obj }));
309
304
  return obj;
@@ -378,4 +373,31 @@
378
373
  private removeQuicklookButton() {
379
374
  this._quicklookButton?.remove();
380
375
  }
376
+
377
+ private applyWebARSessionRoot() {
378
+ if(debug) console.log("applyWebARSessionRoot")
379
+ if (!this.objectToExport) return;
380
+
381
+ // first check if the sessionroot is in the parent hierarchy
382
+ // if that's the case we apply the scale to the object being exported
383
+ let sessionRoot = GameObject.getComponentInParent(this.objectToExport, WebARSessionRoot);
384
+ const hasSessionRootInParentHierarchy = sessionRoot !== null && sessionRoot !== undefined;
385
+ // if it's not in the parent hierarchy BUT in the child hierarchy we apply it to the sessionRoot object itself
386
+ // that#s the case when no objectToExport is explictly assigned and the whole scene is being exported
387
+ if(!sessionRoot) sessionRoot = GameObject.getComponentInChildren(this.objectToExport, WebARSessionRoot);
388
+
389
+ if (!sessionRoot) {
390
+ if(debug) console.warn("No WebARSessionRoot found in parent hierarchy", this.objectToExport);
391
+ return;
392
+ }
393
+
394
+ // either apply the scale to the object being exported or to the sessionRoot object itself
395
+ const target = hasSessionRootInParentHierarchy ? this.objectToExport : sessionRoot.gameObject;
396
+ const scale = 1 / sessionRoot!.arScale;
397
+ if(debug) console.log("Scale", scale, target);
398
+ target.matrix.makeScale(scale, scale, scale);
399
+ if (sessionRoot.invertForward) {
400
+ target.matrix.multiply(new Matrix4().makeRotationY(Math.PI));
401
+ }
402
+ }
381
403
  }