@needle-tools/engine 4.5.1 → 4.5.4

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{needle-engine.bundle-7a7b6e91.light.min.js → needle-engine.bundle-3de53f53.light.min.js} +130 -130
  3. package/dist/{needle-engine.bundle-2d3f9594.light.umd.cjs → needle-engine.bundle-3fd607cc.light.umd.cjs} +111 -111
  4. package/dist/{needle-engine.bundle-55f7e76b.js → needle-engine.bundle-49a4fe2f.js} +3071 -3022
  5. package/dist/{needle-engine.bundle-f56361b5.light.js → needle-engine.bundle-6da875f4.light.js} +3391 -3342
  6. package/dist/{needle-engine.bundle-4dbc6bd8.umd.cjs → needle-engine.bundle-8959a299.umd.cjs} +111 -111
  7. package/dist/{needle-engine.bundle-c7347339.min.js → needle-engine.bundle-9f1a1512.min.js} +130 -130
  8. package/dist/needle-engine.js +194 -193
  9. package/dist/needle-engine.light.js +194 -193
  10. package/dist/needle-engine.light.min.js +1 -1
  11. package/dist/needle-engine.light.umd.cjs +1 -1
  12. package/dist/needle-engine.min.js +1 -1
  13. package/dist/needle-engine.umd.cjs +1 -1
  14. package/lib/engine/engine_gameobject.js +23 -3
  15. package/lib/engine/engine_gameobject.js.map +1 -1
  16. package/lib/engine/engine_loaders.callbacks.d.ts +1 -0
  17. package/lib/engine/engine_loaders.callbacks.js +1 -0
  18. package/lib/engine/engine_loaders.callbacks.js.map +1 -1
  19. package/lib/engine/engine_loaders.js +2 -0
  20. package/lib/engine/engine_loaders.js.map +1 -1
  21. package/lib/engine/engine_serialization_core.d.ts +10 -1
  22. package/lib/engine/engine_serialization_core.js +5 -4
  23. package/lib/engine/engine_serialization_core.js.map +1 -1
  24. package/lib/engine/engine_utils_format.d.ts +2 -1
  25. package/lib/engine/engine_utils_format.js +30 -3
  26. package/lib/engine/engine_utils_format.js.map +1 -1
  27. package/lib/engine-components/DropListener.js +32 -20
  28. package/lib/engine-components/DropListener.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/engine/engine_gameobject.ts +24 -6
  31. package/src/engine/engine_loaders.callbacks.ts +1 -0
  32. package/src/engine/engine_loaders.ts +3 -0
  33. package/src/engine/engine_serialization_core.ts +22 -12
  34. package/src/engine/engine_utils_format.ts +33 -4
  35. package/src/engine-components/DropListener.ts +38 -23
@@ -6,7 +6,7 @@ const debug = getParam("debugfileformat");
6
6
  /**
7
7
  * The supported file types that can be determined by the engine. Used in {@link tryDetermineMimetypeFromURL} and {@link tryDetermineMimetypeFromBinary}
8
8
  */
9
- export type NeedleMimetype = "unknown" |
9
+ export type NeedleMimetype = "unknown" | "unsupported" |
10
10
  "model/gltf+json" |
11
11
  "model/gltf-binary" |
12
12
  "model/vrm" |
@@ -20,6 +20,31 @@ export type NeedleMimetype = "unknown" |
20
20
  | (string & {})
21
21
 
22
22
 
23
+
24
+ export function determineMimeTypeFromExtension(name: string): NeedleMimetype | null {
25
+ const ext = name.split(".").pop() || name;
26
+ switch (ext.toUpperCase()) {
27
+ case "GLTF":
28
+ return "model/gltf+json";
29
+ case "VRM":
30
+ return "model/vrm";
31
+ case "GLB":
32
+ return "model/gltf-binary";
33
+ case "FBX":
34
+ return "model/fbx";
35
+ case "USD":
36
+ return "model/vnd.usd+zip";
37
+ case "USDA":
38
+ return "model/vnd.usda+zip";
39
+ case "USDZ":
40
+ return "model/vnd.usdz+zip";
41
+ case "OBJ":
42
+ return "model/obj";
43
+ default:
44
+ return null;
45
+ }
46
+ }
47
+
23
48
  /**
24
49
  * Tries to determine the file type of a file from its URL
25
50
  * This method does perform a range request to the server to get the first few bytes of the file
@@ -45,7 +70,7 @@ export async function tryDetermineMimetypeFromURL(url: string, opts: { useExtens
45
70
  // // _url = "file:" + url;
46
71
  // }
47
72
 
48
- const urlobj = new URL(_url, globalThis.location.origin);
73
+ const urlobj = new URL(_url, globalThis.location.href);
49
74
  let ext: string | null | undefined = null;
50
75
  const query = urlobj.searchParams.get("filetype");
51
76
  if (query) ext = query.toUpperCase();
@@ -82,7 +107,7 @@ export async function tryDetermineMimetypeFromURL(url: string, opts: { useExtens
82
107
  // We can't modify the blob URL
83
108
  }
84
109
  else {
85
- const newUrl = new URL(url);
110
+ const newUrl = new URL(url, globalThis.location.href);
86
111
  // Adding a URL parameter to avoid the brower to bust the full cache
87
112
  // If we don't do this the file that might already be disc cached will be deleted from the cache
88
113
  newUrl.searchParams.append("range", "true");
@@ -124,7 +149,7 @@ export function tryDetermineMimetypeFromBinary(url: string, data: ArrayBuffer, r
124
149
  }
125
150
 
126
151
  // GLTF
127
- if (bytes[0] == 103 && bytes[1] == 108 && bytes[2] == 84 && bytes[3] == 70 && bytes[4] == 10) {
152
+ if (bytes[0] == 103 && bytes[1] == 108 && bytes[2] == 84 && bytes[3] == 70 && (bytes[4] == 10 || bytes[4] === 2)) {
128
153
  // GLTF
129
154
  console.debug("GLTF detected");
130
155
  return "model/gltf+json";
@@ -172,6 +197,10 @@ export function tryDetermineMimetypeFromBinary(url: string, data: ArrayBuffer, r
172
197
  }
173
198
  else if (response.headers.has("content-type")) {
174
199
  const content_type = response.headers.get("content-type");
200
+ if (content_type?.startsWith("image/")) {
201
+ console.debug("Image detected, not a model file");
202
+ return "unsupported";
203
+ }
175
204
  console.debug("Content-Type: " + content_type);
176
205
  switch (content_type) {
177
206
  case "model/gltf+json":
@@ -1,5 +1,4 @@
1
1
  import { AxesHelper, Box3, Cache, Object3D, Vector2, Vector3 } from "three";
2
- import type { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
3
2
 
4
3
  import { isDevEnvironment } from "../engine/debug/index.js";
5
4
  import { AnimationUtils } from "../engine/engine_animation.js";
@@ -15,6 +14,7 @@ import { serializable } from "../engine/engine_serialization_decorator.js";
15
14
  import { fitObjectIntoVolume, getBoundingBox, placeOnSurface } from "../engine/engine_three_utils.js";
16
15
  import { IGameObject, Model, Vec3 } from "../engine/engine_types.js";
17
16
  import { getParam, setParamWithoutReload } from "../engine/engine_utils.js";
17
+ import { determineMimeTypeFromExtension } from "../engine/engine_utils_format.js";
18
18
  import { Animation } from "./Animation.js";
19
19
  import { Behaviour } from "./Component.js";
20
20
  import { EventList } from "./EventList.js";
@@ -398,6 +398,7 @@ export class DropListener extends Behaviour {
398
398
  if (!Array.isArray(fileList)) return;
399
399
  if (!fileList.length) return;
400
400
 
401
+
401
402
  this.deleteDropEvent();
402
403
  this.removePreviouslyAddedObjects();
403
404
  setParamWithoutReload(blobKeyName, null);
@@ -408,7 +409,20 @@ export class DropListener extends Behaviour {
408
409
 
409
410
  for (const file of fileList) {
410
411
  if (!file) continue;
411
- console.debug("Load file " + file.name);
412
+
413
+ if (file.type.startsWith("image/")) {
414
+ // Ignore dropped images
415
+ if (debug) console.warn("Ignoring dropped image file", file.name, file.type);
416
+ continue;
417
+ }
418
+ else if (file.name.endsWith(".bin")) {
419
+ // Ignore dropped binary files
420
+ if (debug) console.warn("Ignoring dropped binary file", file.name, file.type);
421
+ continue;
422
+ }
423
+
424
+
425
+ console.debug("Load file " + file.name + " + " + file.type);
412
426
  const res = await FileHelper.loadFile(file, this.context, { guid: this.guid });
413
427
  if (res) {
414
428
  this.dispatchEvent(new CustomEvent(DropListenerEvents.FileDropped, { detail: file }));
@@ -625,38 +639,39 @@ namespace FileHelper {
625
639
  * @returns Promise containing the loaded model and its content hash, or null if loading failed
626
640
  */
627
641
  export async function loadFile(file: File, context: Context, args: { guid: string }): Promise<{ model: Model, contentMD5: string } | null> {
628
- const name = file.name.toLowerCase();
629
- if (name.endsWith(".gltf") ||
630
- name.endsWith(".glb") ||
631
- name.endsWith(".fbx") ||
632
- name.endsWith(".obj") ||
633
- name.endsWith(".usdz") ||
634
- name.endsWith(".vrm") ||
635
- file.type === "model/gltf+json" ||
636
- file.type === "model/gltf-binary"
637
- ) {
642
+ // first load it locally
643
+ const seed = args.guid;
644
+ const prov = new InstantiateIdProvider(seed);
645
+
646
+ const blob = new Blob([file], { type: file.type || determineMimeTypeFromExtension(file.name) || undefined });
647
+ const objectUrl = URL.createObjectURL(blob);
648
+
649
+ const model = await getLoader().loadSync(context, objectUrl, file.name, prov).catch(err => {
650
+ console.error(`Failed to load file "${file.name}" (${file.type}):`, err);
651
+ return null;
652
+ })
653
+
654
+ URL.revokeObjectURL(objectUrl); // clean up the object URL
655
+
656
+ if (model) {
638
657
  return new Promise((resolve, _reject) => {
639
658
  const reader = new FileReader()
640
659
  reader.readAsArrayBuffer(file);
641
660
  reader.onloadend = async (_ev: ProgressEvent<FileReader>) => {
642
661
  const content = reader.result as ArrayBuffer;
643
- // first load it locally
644
- const seed = args.guid;
645
- const prov = new InstantiateIdProvider(seed);
646
- const model = await getLoader().parseSync(context, content, file.name, prov);
647
- if (model) {
648
- const hash = BlobStorage.hashMD5(content);
649
- resolve({ model, contentMD5: hash });
650
- }
662
+ const hash = BlobStorage.hashMD5(content);
663
+ return resolve({ model, contentMD5: hash });
651
664
  };
652
665
  });
653
666
  }
654
667
  else {
655
- console.warn("Unsupported file type: " + name, file.type)
668
+ console.warn(`Failed to load "${file.name}" (${file.type})`);
669
+ return null;
656
670
  }
657
-
658
- return null;
659
671
  }
672
+ // return new Promise((resolve, _reject) => {
673
+ // });
674
+ // }
660
675
 
661
676
  /**
662
677
  * Loads a 3D model from a URL with progress visualization