@needle-tools/engine 4.5.0-alpha.1 → 4.5.0-alpha.3

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 (71) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/{needle-engine.bundle-3d05185b.js → needle-engine.bundle-1b8f44f4.js} +4945 -4907
  3. package/dist/{needle-engine.bundle-b2e17f0e.light.min.js → needle-engine.bundle-56f095f1.light.min.js} +125 -119
  4. package/dist/{needle-engine.bundle-d7d53476.light.umd.cjs → needle-engine.bundle-9fe9a394.light.umd.cjs} +137 -131
  5. package/dist/{needle-engine.bundle-e4ae93a2.min.js → needle-engine.bundle-baacde19.min.js} +125 -119
  6. package/dist/{needle-engine.bundle-c44e02c7.light.js → needle-engine.bundle-d710d96f.light.js} +4947 -4909
  7. package/dist/{needle-engine.bundle-f496c70e.umd.cjs → needle-engine.bundle-ef2b8438.umd.cjs} +135 -129
  8. package/dist/needle-engine.js +467 -471
  9. package/dist/needle-engine.light.js +467 -471
  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/api.d.ts +2 -1
  15. package/lib/engine/api.js +2 -1
  16. package/lib/engine/api.js.map +1 -1
  17. package/lib/engine/engine_context_registry.d.ts +2 -2
  18. package/lib/engine/engine_context_registry.js +2 -2
  19. package/lib/engine/engine_context_registry.js.map +1 -1
  20. package/lib/engine/engine_gltf.d.ts +2 -2
  21. package/lib/engine/engine_gltf_builtin_components.d.ts +5 -1
  22. package/lib/engine/engine_gltf_builtin_components.js +2 -2
  23. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  24. package/lib/engine/engine_loaders.callbacks.d.ts +62 -0
  25. package/lib/engine/engine_loaders.callbacks.js +56 -0
  26. package/lib/engine/engine_loaders.callbacks.js.map +1 -0
  27. package/lib/engine/engine_loaders.d.ts +44 -9
  28. package/lib/engine/engine_loaders.gltf.d.ts +13 -0
  29. package/lib/engine/engine_loaders.gltf.js +63 -0
  30. package/lib/engine/engine_loaders.gltf.js.map +1 -0
  31. package/lib/engine/engine_loaders.js +305 -48
  32. package/lib/engine/engine_loaders.js.map +1 -1
  33. package/lib/engine/engine_types.d.ts +7 -1
  34. package/lib/engine/engine_types.js +7 -0
  35. package/lib/engine/engine_types.js.map +1 -1
  36. package/lib/engine/engine_utils_format.d.ts +5 -3
  37. package/lib/engine/engine_utils_format.js +26 -10
  38. package/lib/engine/engine_utils_format.js.map +1 -1
  39. package/lib/engine/extensions/extensions.d.ts +3 -2
  40. package/lib/engine/extensions/extensions.js +10 -6
  41. package/lib/engine/extensions/extensions.js.map +1 -1
  42. package/lib/engine/webcomponents/needle-engine.attributes.d.ts +3 -6
  43. package/lib/engine/webcomponents/needle-engine.js +2 -2
  44. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  45. package/lib/engine/webcomponents/needle-engine.loading.js +26 -34
  46. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  47. package/lib/engine-components/AvatarLoader.js +1 -1
  48. package/lib/engine-components/AvatarLoader.js.map +1 -1
  49. package/lib/engine-components/ContactShadows.d.ts +12 -0
  50. package/lib/engine-components/ContactShadows.js +23 -0
  51. package/lib/engine-components/ContactShadows.js.map +1 -1
  52. package/lib/engine-components/webxr/controllers/XRControllerModel.js +4 -3
  53. package/lib/engine-components/webxr/controllers/XRControllerModel.js.map +1 -1
  54. package/package.json +1 -1
  55. package/src/engine/api.ts +2 -1
  56. package/src/engine/engine_context_registry.ts +2 -2
  57. package/src/engine/engine_gltf.ts +2 -2
  58. package/src/engine/engine_gltf_builtin_components.ts +7 -7
  59. package/src/engine/engine_loaders.callbacks.ts +88 -0
  60. package/src/engine/engine_loaders.gltf.ts +82 -0
  61. package/src/engine/engine_loaders.ts +332 -54
  62. package/src/engine/engine_types.ts +34 -18
  63. package/src/engine/engine_utils_format.ts +32 -14
  64. package/src/engine/extensions/extensions.ts +12 -7
  65. package/src/engine/webcomponents/needle-engine.attributes.ts +3 -6
  66. package/src/engine/webcomponents/needle-engine.loading.ts +28 -36
  67. package/src/engine/webcomponents/needle-engine.ts +2 -2
  68. package/src/engine-components/AvatarLoader.ts +1 -1
  69. package/src/engine-components/ContactShadows.ts +24 -0
  70. package/src/engine-components/webxr/controllers/XRControllerModel.ts +4 -5
  71. package/src/engine/engine_scenetools.ts +0 -379
@@ -1,379 +0,0 @@
1
- import { Cache, Camera, Loader, Material, Mesh, Object3D } from "three";
2
- import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
3
- import { type GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
4
- import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
5
- import { USDZLoader } from 'three/examples/jsm/loaders/USDZLoader.js';
6
-
7
- import { showBalloonMessage } from "./debug/index.js";
8
- import { getLoader, type INeedleGltfLoader, registerLoader } from "./engine_gltf.js";
9
- import { createBuiltinComponents, writeBuiltinComponentData } from "./engine_gltf_builtin_components.js";
10
- // import * as object from "./engine_gltf_builtin_components.js";
11
- import * as loaders from "./engine_loaders.js"
12
- import { registerPrewarmObject } from "./engine_mainloop_utils.js";
13
- import { SerializationContext } from "./engine_serialization_core.js";
14
- import { Context } from "./engine_setup.js"
15
- import { postprocessFBXMaterials } from "./engine_three_utils.js";
16
- import { Model, type UIDProvider } from "./engine_types.js";
17
- import * as utils from "./engine_utils.js";
18
- import { tryDetermineFileTypeFromURL } from "./engine_utils_format.js"
19
- import { invokeAfterImportPluginHooks, registerComponentExtension, registerExtensions } from "./extensions/extensions.js";
20
- import { NEEDLE_components } from "./extensions/NEEDLE_components.js";
21
-
22
- /** @internal */
23
- export class NeedleLoader implements INeedleGltfLoader {
24
- createBuiltinComponents(context: Context, gltfId: string, gltf: any, seed: number | UIDProvider | null, extension?: NEEDLE_components | undefined) {
25
- return createBuiltinComponents(context, gltfId, gltf, seed, extension);
26
- }
27
- writeBuiltinComponentData(comp: any, context: SerializationContext) {
28
- return writeBuiltinComponentData(comp, context);
29
- }
30
- parseSync(context: Context, data: string | ArrayBuffer, path: string, seed: number | UIDProvider | null): Promise<Model | undefined> {
31
- return parseSync(context, data, path, seed);
32
- }
33
- loadSync(context: Context, url: string, sourceId: string, seed: number | UIDProvider | null, prog?: ((ProgressEvent: any) => void) | undefined): Promise<Model | undefined> {
34
- return loadSync(context, url, sourceId, seed, prog);
35
- }
36
- }
37
- registerLoader(NeedleLoader); // Register the loader
38
-
39
-
40
- const printGltf = utils.getParam("printGltf") || utils.getParam("printgltf");
41
- const downloadGltf = utils.getParam("downloadgltf");
42
- const debugFileTypes = utils.getParam("debugfileformat");
43
-
44
- // const loader = new GLTFLoader();
45
- // registerExtensions(loader);
46
-
47
- export enum GltfLoadEventType {
48
- BeforeLoad = 0,
49
- AfterLoaded = 1,
50
- FinishedSetup = 10,
51
- }
52
-
53
- export class GltfLoadEvent {
54
- context: Context
55
- loader: GLTFLoader;
56
- path: string;
57
- gltf?: GLTF;
58
- constructor(context: Context, path: string, loader: GLTFLoader, gltf?: GLTF) {
59
- this.context = context;
60
- this.path = path;
61
- this.loader = loader;
62
- this.gltf = gltf;
63
- }
64
- }
65
-
66
- export type GltfLoadEventCallback = (event: GltfLoadEvent) => void;
67
-
68
- const eventListeners: { [key: string]: GltfLoadEventCallback[] } = {};
69
-
70
- export function addGltfLoadEventListener(type: GltfLoadEventType, listener: GltfLoadEventCallback) {
71
- eventListeners[type] = eventListeners[type] || [];
72
- eventListeners[type].push(listener);
73
- }
74
- export function removeGltfLoadEventListener(type: GltfLoadEventType, listener: GltfLoadEventCallback) {
75
- if (eventListeners[type]) {
76
- const index = eventListeners[type].indexOf(listener);
77
- if (index >= 0) {
78
- eventListeners[type].splice(index, 1);
79
- }
80
- }
81
- }
82
-
83
- function invokeEvents(type: GltfLoadEventType, event: GltfLoadEvent) {
84
- if (eventListeners[type]) {
85
- for (const listener of eventListeners[type]) {
86
- listener(event);
87
- }
88
- }
89
- }
90
-
91
- async function handleLoadedGltf(context: Context, gltfId: string, gltf: GLTF, seed: number | null | UIDProvider, componentsExtension) {
92
- if (printGltf)
93
- console.warn("glTF", gltfId, gltf);
94
- // Remove query parameters from gltfId
95
- if (gltfId.includes("?")) {
96
- gltfId = gltfId.split("?")[0];
97
- }
98
- // assign animations of loaded glTF to all scenes
99
- gltf.scenes.forEach(scene => {
100
- if (!scene.animations?.length) scene.animations = [...gltf.animations]
101
- });
102
- await getLoader().createBuiltinComponents(context, gltfId, gltf, seed, componentsExtension);
103
- }
104
-
105
-
106
- export async function createLoader(url: string, context: Context): Promise<GLTFLoader | FBXLoader | USDZLoader | OBJLoader | null> {
107
-
108
- const type = await tryDetermineFileTypeFromURL(url) || "unknown";
109
- if (debugFileTypes) console.debug("Determined file type: " + type + " for url", url);
110
-
111
- switch (type) {
112
- case "unknown":
113
- {
114
- console.warn("Unknown file type. Assuming glTF:", url);
115
- const loader = new GLTFLoader();
116
- await registerExtensions(loader, context, url);
117
- return loader;
118
- }
119
- case "fbx":
120
- return new FBXLoader();
121
- case "obj":
122
- return new OBJLoader();
123
- case "usd":
124
- case "usda":
125
- case "usdz":
126
- console.warn(type.toUpperCase() + " files are not supported.")
127
- return null;
128
- // return new USDZLoader();
129
- default:
130
- console.warn("Unknown file type:", type);
131
- case "gltf":
132
- case "glb":
133
- case "vrm":
134
- {
135
- const loader = new GLTFLoader();
136
- await registerExtensions(loader, context, url);
137
- return loader;
138
- }
139
- }
140
- }
141
-
142
- /**
143
- * Load a 3D model file from a remote URL
144
- * @param url URL to glTF, FBX or OBJ file
145
- * @param options
146
- * @returns
147
- */
148
- export function loadAsset(url: string, options?: { context?: Context, path?: string, seed?: number, onprogress?: (evt: ProgressEvent) => void }): Promise<Model | undefined> {
149
- return loadSync(options?.context || Context.Current, url, url, options?.seed || null, options?.onprogress);
150
- }
151
-
152
- /** Load a gltf file from a url. This is the core method used by Needle Engine to load gltf files. All known extensions are registered here.
153
- * @param context The current context
154
- * @param data The gltf data as string or ArrayBuffer
155
- * @param path The path to the gltf file
156
- * @param seed The seed for generating unique ids
157
- * @returns The loaded gltf object
158
- */
159
- export async function parseSync(context: Context, data: string | ArrayBuffer, path: string, seed: number | UIDProvider | null): Promise<Model | undefined> {
160
- if (typeof path !== "string") {
161
- console.warn("Parse gltf binary without path, this might lead to errors in resolving extensions. Please provide the source path of the gltf/glb file", path, typeof path);
162
- path = "";
163
- }
164
- if (printGltf) console.log("Parse glTF", path)
165
- const loader = await createLoader(path, context);
166
- if (!loader) {
167
- return undefined;
168
- }
169
-
170
- // Handle OBJ Loader
171
- if (loader instanceof OBJLoader) {
172
- if (typeof data !== "string") { data = new TextDecoder().decode(data); }
173
- const res = loader.parse(data);
174
- return {
175
- animations: res.animations,
176
- scene: res,
177
- scenes: [res]
178
- } as GLTF;
179
- }
180
- // Handle any other loader that is not a GLTFLoader
181
- const isNotGLTF = !(loader instanceof GLTFLoader);
182
- if (isNotGLTF) {
183
- const res = loader.parse(data, path);
184
- postprocessLoadedFile(loader, res);
185
- return {
186
- animations: res.animations,
187
- scene: res,
188
- scenes: [res]
189
- } as GLTF;
190
- }
191
-
192
- const componentsExtension = registerComponentExtension(loader);
193
- return new Promise((resolve, reject) => {
194
- try {
195
-
196
- // GltfLoader expects a base path for resolving referenced assets
197
- // https://threejs.org/docs/#examples/en/loaders/GLTFLoader.parse
198
- // so we make sure that "path" is never a file path
199
- let gltfLoaderPath = path.split("?")[0].trimEnd();
200
- // This assumes that the path is a FILE path and not already a directory
201
- // (it does not end with "/") – see https://linear.app/needle/issue/NE-6075
202
- {
203
- // strip file from path
204
- const parts = gltfLoaderPath.split("/");
205
- // check if the last part is a /, otherwise remove it
206
- if (parts.length > 0 && parts[parts.length - 1] !== "")
207
- parts.pop();
208
- gltfLoaderPath = parts.join("/");
209
- if (!gltfLoaderPath.endsWith("/")) gltfLoaderPath += "/";
210
- }
211
- loader.resourcePath = gltfLoaderPath;
212
- loaders.addDracoAndKTX2Loaders(loader, context);
213
-
214
- invokeEvents(GltfLoadEventType.BeforeLoad, new GltfLoadEvent(context, path, loader));
215
- const camera = context.mainCamera;
216
- loader.parse(data, "", async res => {
217
- invokeAfterImportPluginHooks(path, res, context);
218
- invokeEvents(GltfLoadEventType.AfterLoaded, new GltfLoadEvent(context, path, loader, res));
219
- await handleLoadedGltf(context, path, res, seed, componentsExtension);
220
- await compileAsync(res.scene, context, camera);
221
- invokeEvents(GltfLoadEventType.FinishedSetup, new GltfLoadEvent(context, path, loader, res));
222
-
223
- resolve(res);
224
- if (downloadGltf) {
225
- _downloadGltf(data)
226
- }
227
-
228
- }, err => {
229
- console.error("Loading asset at \"" + path + "\" failed\n", err);
230
- resolve(undefined);
231
- });
232
- }
233
- catch (err) {
234
- console.error(err);
235
- reject(err);
236
- }
237
- });
238
- }
239
-
240
- /**
241
- * Load a gltf file from a url. This is the core method used by Needle Engine to load gltf files. All known extensions are registered here.
242
- * @param context The current context
243
- * @param url The url to the gltf file
244
- * @param sourceId The source id of the gltf file - this is usually the url
245
- * @param seed The seed for generating unique ids
246
- * @param prog A progress callback
247
- * @returns The loaded gltf object
248
- */
249
- export async function loadSync(context: Context, url: string, sourceId: string, seed: number | UIDProvider | null, prog?: (ProgressEvent) => void): Promise<Model | undefined> {
250
- // better to create new loaders every time
251
- // (maybe we can cache them...)
252
- // but due to the async nature and potentially triggering multiple loads at the same time
253
- // we need to make sure the extensions dont override each other
254
- // creating new loaders should not be expensive as well
255
- checkIfUserAttemptedToLoadALocalFile(url)
256
- const loader = await createLoader(url, context);
257
- if (!loader) {
258
- return undefined;
259
- }
260
-
261
- // Handle any loader that is not a GLTFLoader
262
- if (!(loader instanceof GLTFLoader)) {
263
- const res = await loader.loadAsync(url, prog);
264
- postprocessLoadedFile(loader, res);
265
- return {
266
- animations: res.animations,
267
- scene: res,
268
- scenes: [res]
269
- } as GLTF;
270
- }
271
-
272
- const componentsExtension = registerComponentExtension(loader);
273
- return new Promise((resolve, reject) => {
274
- try {
275
- loaders.addDracoAndKTX2Loaders(loader, context);
276
- invokeEvents(GltfLoadEventType.BeforeLoad, new GltfLoadEvent(context, url, loader));
277
- const camera = context.mainCamera;
278
- loader.load(url, async res => {
279
- invokeAfterImportPluginHooks(url, res, context);
280
- invokeEvents(GltfLoadEventType.AfterLoaded, new GltfLoadEvent(context, url, loader, res));
281
- await handleLoadedGltf(context, sourceId, res, seed, componentsExtension);
282
- await compileAsync(res.scene, context, camera);
283
- invokeEvents(GltfLoadEventType.FinishedSetup, new GltfLoadEvent(context, url, loader, res));
284
- resolve(res);
285
- if (downloadGltf) {
286
- _downloadGltf(url)
287
- }
288
- }, evt => {
289
- prog?.call(loader, evt);
290
- }, err => {
291
- console.error("Loading asset at \"" + url + "\" failed\n", err);
292
- resolve(undefined);
293
- });
294
- }
295
- catch (err) {
296
- console.error(err);
297
- reject(err);
298
- }
299
- });
300
- }
301
-
302
- async function compileAsync(scene: Object3D, context: Context, camera?: Camera | null) {
303
- if (!camera) camera = context.mainCamera;
304
- try {
305
- if (camera) {
306
- await context.renderer.compileAsync(scene, camera, context.scene)
307
- .catch(err => {
308
- console.warn(err.message);
309
- });
310
- }
311
- else
312
- registerPrewarmObject(scene, context);
313
- }
314
- catch (err: Error | any) {
315
- console.warn(err?.message || err);
316
- }
317
- }
318
-
319
- function checkIfUserAttemptedToLoadALocalFile(url: string) {
320
- const fullurl = new URL(url, window.location.href).href;
321
- if (fullurl.startsWith("file://")) {
322
- const msg = "Hi - it looks like you are trying to load a local file which will not work. You need to use a webserver to serve your files.\nPlease refer to the documentation on <a href=\"https://fwd.needle.tools/needle-engine/docs/local-server\">https://docs.needle.tools</a> or ask for help in our <a href=\"https://discord.needle.tools\">discord community</a>";
323
- showBalloonMessage(msg);
324
- console.warn(msg)
325
- }
326
- }
327
-
328
- function _downloadGltf(data: string | ArrayBuffer) {
329
- if (typeof data === "string") {
330
- const a = document.createElement("a") as HTMLAnchorElement;
331
- a.href = data;
332
- a.download = data.split("/").pop()!;
333
- a.click();
334
- }
335
- else {
336
- const blob = new Blob([data], { type: "application/octet-stream" });
337
- const url = window.URL.createObjectURL(blob);
338
- const a = document.createElement("a") as HTMLAnchorElement;
339
- a.href = url;
340
- a.download = "download.glb";
341
- a.click();
342
- }
343
- }
344
-
345
-
346
-
347
-
348
- function postprocessLoadedFile(loader: Loader, result: Object3D | GLTF) {
349
-
350
- if ((result as Object3D)?.isObject3D) {
351
- const obj = result as Object3D;
352
-
353
- if (loader instanceof FBXLoader || loader instanceof OBJLoader) {
354
- obj.traverse((child) => {
355
- const mesh = child as Mesh;
356
-
357
- // See https://github.com/needle-tools/three.js/blob/b8df3843ff123ac9dc0ed0d3ccc5b568f840c804/examples/webgl_loader_multiple.html#L377
358
- if (mesh?.isMesh) {
359
- postprocessFBXMaterials(mesh, mesh.material as Material);
360
- }
361
- });
362
- }
363
- // else if (loader instanceof OBJLoader) {
364
-
365
- // obj.traverse(_child => {
366
-
367
- // // TODO: Needs testing
368
-
369
- // // if (!(child instanceof Mesh)) return;
370
-
371
- // // child.material = new MeshStandardMaterial();
372
-
373
- // });
374
- // }
375
-
376
-
377
- }
378
- }
379
-