@needle-tools/engine 4.8.6-next.f3ce848 → 4.8.7-next.1715881
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 +4 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DXRy9EQz.js → gltf-progressive-BcHT3Nyo.js} +1 -1
- package/dist/{gltf-progressive-C-U_onhf.umd.cjs → gltf-progressive-CH3Q4H06.umd.cjs} +1 -1
- package/dist/{gltf-progressive-DViD_J_l.min.js → gltf-progressive-DR6HqF_h.min.js} +1 -1
- package/dist/{needle-engine.bundle-fXDFH_oR.js → needle-engine.bundle-BAha1j_T.js} +4866 -4813
- package/dist/{needle-engine.bundle-CJSl-mXb.min.js → needle-engine.bundle-Bw0zm_81.min.js} +130 -130
- package/dist/{needle-engine.bundle-C4N-adas.umd.cjs → needle-engine.bundle-_RVu0BLh.umd.cjs} +131 -131
- package/dist/needle-engine.js +394 -393
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-61aXdqNz.umd.cjs → postprocessing-CVb_x9YY.umd.cjs} +1 -1
- package/dist/{postprocessing-D9jDHD0U.js → postprocessing-ORx-0eCx.js} +1 -1
- package/dist/{postprocessing-Be9Ds4NK.min.js → postprocessing-Ywv5oKkX.min.js} +1 -1
- package/dist/three-examples-BX_Sktc9.min.js +501 -0
- package/dist/{three-examples-BihZ_R96.js → three-examples-CNexix3E.js} +2436 -2781
- package/dist/{three-examples-Ce6Th3bv.umd.cjs → three-examples-DWxXVnws.umd.cjs} +21 -21
- package/dist/{vendor-BRpzuoJE.min.js → vendor-C43vobGc.min.js} +37 -37
- package/dist/{vendor-p_xp9KuJ.js → vendor-Z4SPrTcP.js} +2402 -2047
- package/dist/vendor-xfQ8tKF3.umd.cjs +1121 -0
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +12 -12
- package/lib/engine/engine_addressables.js +30 -23
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +1 -3
- package/lib/engine/engine_animation.js +15 -9
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_feature_flags.d.ts +3 -0
- package/lib/engine/engine_feature_flags.js +6 -0
- package/lib/engine/engine_feature_flags.js.map +1 -0
- package/lib/engine/engine_loaders.js +15 -11
- package/lib/engine/engine_loaders.js.map +1 -1
- package/lib/engine/engine_mainloop_utils.d.ts +2 -1
- package/lib/engine/engine_mainloop_utils.js +16 -4
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/extensions/extensions.js +2 -2
- package/lib/engine/extensions/extensions.js.map +1 -1
- package/lib/engine/js-extensions/Object3D.js +19 -0
- package/lib/engine/js-extensions/Object3D.js.map +1 -1
- package/lib/engine-components/Animation.js +2 -1
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimationUtilsAutoplay.js +1 -6
- package/lib/engine-components/AnimationUtilsAutoplay.js.map +1 -1
- package/lib/engine-components/DropListener.d.ts +17 -12
- package/lib/engine-components/DropListener.js +34 -31
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/LookAtConstraint.d.ts +5 -1
- package/lib/engine-components/LookAtConstraint.js +8 -0
- package/lib/engine-components/LookAtConstraint.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +5 -7
- package/lib/engine-components/OrbitControls.js +12 -11
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/export/usdz/Extension.d.ts +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +7 -0
- package/lib/engine-components/export/usdz/USDZExporter.js +8 -1
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.d.ts +4 -2
- package/lib/engine-components/webxr/WebXRImageTracking.js +117 -81
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/package.json +3 -2
- package/plugins/vite/alias.js +45 -23
- package/src/engine/api.ts +2 -1
- package/src/engine/engine_addressables.ts +44 -33
- package/src/engine/engine_animation.ts +17 -9
- package/src/engine/engine_feature_flags.ts +8 -0
- package/src/engine/engine_loaders.ts +18 -13
- package/src/engine/engine_mainloop_utils.ts +21 -6
- package/src/engine/extensions/extensions.ts +2 -2
- package/src/engine/js-extensions/Object3D.ts +25 -2
- package/src/engine-components/Animation.ts +1 -1
- package/src/engine-components/AnimationUtilsAutoplay.ts +1 -6
- package/src/engine-components/DropListener.ts +40 -31
- package/src/engine-components/LookAtConstraint.ts +9 -1
- package/src/engine-components/OrbitControls.ts +19 -16
- package/src/engine-components/export/usdz/Extension.ts +1 -1
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +1 -1
- package/src/engine-components/export/usdz/USDZExporter.ts +21 -12
- package/src/engine-components/webxr/WebXRImageTracking.ts +138 -90
- package/dist/three-examples-DKY9Nfge.min.js +0 -501
- package/dist/vendor-Ja-vKV-a.umd.cjs +0 -1121
package/plugins/vite/alias.js
CHANGED
|
@@ -26,9 +26,7 @@ const packages_to_resolve = {
|
|
|
26
26
|
'three/nodes': (res, packageName, index, path) => {
|
|
27
27
|
return "three/examples/jsm/nodes/Nodes.js";
|
|
28
28
|
},
|
|
29
|
-
'three':
|
|
30
|
-
return path.resolve(projectDir, 'node_modules', 'three');
|
|
31
|
-
},
|
|
29
|
+
'three': "auto-resolve",
|
|
32
30
|
|
|
33
31
|
// Handle all previous imports where users did import using @needle-engine/src
|
|
34
32
|
'@needle-tools/engine/src': (res, packageName, index, path) => {
|
|
@@ -61,6 +59,9 @@ const packages_to_resolve = {
|
|
|
61
59
|
'three-mesh-bvh': "auto-resolve",
|
|
62
60
|
'postprocessing': "auto-resolve",
|
|
63
61
|
'@dimforge/rapier3d-compat': "auto-resolve",
|
|
62
|
+
|
|
63
|
+
'@needle-tools/gltf-progressive': "auto-resolve",
|
|
64
|
+
'@needle-tools/materialx': "auto-resolve",
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
/**
|
|
@@ -76,10 +77,13 @@ export const needleViteAlias = (command, config, userSettings) => {
|
|
|
76
77
|
let outputDebugFile = null;
|
|
77
78
|
function log(...msg) {
|
|
78
79
|
console.log(...msg);
|
|
79
|
-
if (debug
|
|
80
|
+
if (debug) logToFile(...msg);
|
|
81
|
+
}
|
|
82
|
+
function logToFile(...msg) {
|
|
83
|
+
outputDebugFile?.write(msg.join(" ") + "\n");
|
|
80
84
|
}
|
|
81
85
|
if (debug) {
|
|
82
|
-
const outputFilePath = path.resolve(projectDir, 'node_modules/.
|
|
86
|
+
const outputFilePath = path.resolve(projectDir, 'node_modules/.needle/needle.alias.log');
|
|
83
87
|
const outputDirectory = path.dirname(outputFilePath);
|
|
84
88
|
if (!existsSync(outputDirectory)) mkdirSync(outputDirectory, { recursive: true });
|
|
85
89
|
outputDebugFile = createWriteStream(outputFilePath, { flags: "a" });
|
|
@@ -105,6 +109,18 @@ export const needleViteAlias = (command, config, userSettings) => {
|
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
|
|
113
|
+
// is needle engine a local package?
|
|
114
|
+
// This will cause local changes to not be reflected anymore...
|
|
115
|
+
const localEnginePath = path.resolve(projectDir, 'node_modules/', '@needle-tools/engine', 'node_modules');
|
|
116
|
+
if (existsSync(localEnginePath)) {
|
|
117
|
+
config.optimizeDeps ??= {};
|
|
118
|
+
config.optimizeDeps.exclude ??= [];
|
|
119
|
+
config.optimizeDeps.exclude.push('@needle-tools/engine');
|
|
120
|
+
log("[needle-alias] Detected local @needle-tools/engine package: will exclude it from optimization");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
108
124
|
if (debug) {
|
|
109
125
|
const testResults = [];
|
|
110
126
|
for (const name in aliasDict) {
|
|
@@ -118,6 +134,7 @@ export const needleViteAlias = (command, config, userSettings) => {
|
|
|
118
134
|
},
|
|
119
135
|
}
|
|
120
136
|
|
|
137
|
+
/** @type {string|undefined} */
|
|
121
138
|
let lastImporter = "";
|
|
122
139
|
/** This plugin logs all imports. This helps to find cases where incorrect folders are found/resolved. */
|
|
123
140
|
|
|
@@ -140,15 +157,16 @@ export const needleViteAlias = (command, config, userSettings) => {
|
|
|
140
157
|
// verbose logging for all imports
|
|
141
158
|
if (lastImporter !== importer) {
|
|
142
159
|
lastImporter = importer;
|
|
143
|
-
|
|
160
|
+
logToFile(`[needle-alias] Resolving: ${importer} (file${options?.ssr ? ", SSR" : ""})`);
|
|
144
161
|
}
|
|
145
|
-
|
|
162
|
+
logToFile(`[needle-alias] → ${id}`);
|
|
146
163
|
return;
|
|
147
164
|
},
|
|
148
165
|
}
|
|
149
166
|
if (debug) return [debuggingPlugin, aliasPlugin];
|
|
150
167
|
return [aliasPlugin];
|
|
151
168
|
|
|
169
|
+
|
|
152
170
|
/**
|
|
153
171
|
* Adds a path resolver to the alias dictionary.
|
|
154
172
|
* @param {string} name - The name of the package to resolve.
|
|
@@ -163,25 +181,29 @@ export const needleViteAlias = (command, config, userSettings) => {
|
|
|
163
181
|
const callback = typeof value === "function" ? value : null;
|
|
164
182
|
|
|
165
183
|
let fullpath = path.resolve(projectDir, 'node_modules', name);
|
|
166
|
-
if (!existsSync(fullpath))
|
|
167
|
-
|
|
184
|
+
// if (!existsSync(path.resolve(fullpath, "package.json")))
|
|
185
|
+
{
|
|
186
|
+
const pathInEngine = path.resolve(projectDir, 'node_modules', "@needle-tools/engine", "node_modules", name);
|
|
187
|
+
if (existsSync(pathInEngine)) {
|
|
188
|
+
fullpath = pathInEngine;
|
|
189
|
+
}
|
|
168
190
|
}
|
|
169
191
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
console.warn(`[needle-alias] \"${path}\" was requested and resolved to \"${fullpath}\"`);
|
|
183
|
-
}
|
|
192
|
+
const workingDirectory = `${process.cwd()}/`;
|
|
193
|
+
const pathExists = existsSync(fullpath);
|
|
194
|
+
|
|
195
|
+
aliasDict[name] = (packageName, index, path) => {
|
|
196
|
+
if (callback !== null) {
|
|
197
|
+
const overrideResult = callback(fullpath, packageName, index, path);
|
|
198
|
+
if (typeof overrideResult === "string")
|
|
199
|
+
if (existsSync(overrideResult)) {
|
|
200
|
+
if (debug && overrideResult !== packageName) log(`[needle-alias] Resolved \"${path}\" → \"${overrideResult.substring(workingDirectory.length).replaceAll("\\", "/")}\"`);
|
|
201
|
+
return overrideResult;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
184
204
|
|
|
205
|
+
if (pathExists) {
|
|
206
|
+
if (debug && path !== packageName) log(`[needle-alias] Resolved \"${path}\" → \"${fullpath.substring(workingDirectory.length).replaceAll("\\", "/")}\"`);
|
|
185
207
|
return fullpath;
|
|
186
208
|
}
|
|
187
209
|
}
|
package/src/engine/api.ts
CHANGED
|
@@ -33,6 +33,7 @@ export * from "./engine_context.js";
|
|
|
33
33
|
export * from "./engine_context_registry.js";
|
|
34
34
|
export * from "./engine_coroutine.js"
|
|
35
35
|
export * from "./engine_create_objects.js";
|
|
36
|
+
export * from "./engine_feature_flags.js"
|
|
36
37
|
export * from "./engine_gameobject.js";
|
|
37
38
|
export { Gizmos } from "./engine_gizmos.js"
|
|
38
39
|
export * from "./engine_gltf.js";
|
|
@@ -42,7 +43,7 @@ export { InstancingUtil } from "./engine_instancing.js";
|
|
|
42
43
|
export { hasCommercialLicense, hasIndieLicense, hasProLicense } from "./engine_license.js";
|
|
43
44
|
export * from "./engine_lifecycle_api.js";
|
|
44
45
|
export { NeedleEngineModelLoader } from "./engine_loaders.callbacks.js";
|
|
45
|
-
export { loadAsset, loadSync,parseSync } from "./engine_loaders.js";
|
|
46
|
+
export { loadAsset, loadSync, parseSync } from "./engine_loaders.js";
|
|
46
47
|
export * from "./engine_math.js";
|
|
47
48
|
export { MODULES as NEEDLE_ENGINE_MODULES } from "./engine_modules.js";
|
|
48
49
|
export * from "./engine_networking.js";
|
|
@@ -8,7 +8,7 @@ import { BlobStorage } from "./engine_networking_blob.js";
|
|
|
8
8
|
import { registerPrefabProvider, syncInstantiate, SyncInstantiateOptions } from "./engine_networking_instantiate.js";
|
|
9
9
|
import { SerializationContext, TypeSerializer } from "./engine_serialization_core.js";
|
|
10
10
|
import { Context } from "./engine_setup.js";
|
|
11
|
-
import type { GLTF, IComponent, IGameObject, SourceIdentifier } from "./engine_types.js";
|
|
11
|
+
import type { GLTF, IComponent, IGameObject, Model, SourceIdentifier } from "./engine_types.js";
|
|
12
12
|
|
|
13
13
|
const debug = getParam("debugaddressables");
|
|
14
14
|
|
|
@@ -138,21 +138,35 @@ export class AssetReference {
|
|
|
138
138
|
return ref;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
private static currentlyInstantiating: Map<string, number> = new Map<string, number>();
|
|
142
141
|
|
|
143
|
-
/** @returns true if this is an AssetReference instance */
|
|
144
|
-
get isAssetReference() { return true; }
|
|
145
142
|
|
|
146
|
-
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
readonly isAssetReference = true;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* This is the loaded asset root object. If the asset is a glb/gltf file this will be the {@link three#Scene} object.
|
|
150
|
+
*/
|
|
151
|
+
get rawAsset(): any { return this._rawAsset; }
|
|
152
|
+
|
|
153
|
+
/** The loaded asset root
|
|
154
|
+
*/
|
|
147
155
|
get asset(): Object3D | null {
|
|
148
|
-
return this._glbRoot ?? this.
|
|
156
|
+
return this._glbRoot ?? (this._rawAsset?.scene || null);
|
|
149
157
|
}
|
|
150
158
|
|
|
151
|
-
protected set asset(val:
|
|
152
|
-
|
|
159
|
+
protected set asset(val: Object3D | null) {
|
|
160
|
+
if (val) {
|
|
161
|
+
this._rawAsset = {
|
|
162
|
+
animations: val.animations,
|
|
163
|
+
scene: val,
|
|
164
|
+
scenes: [val],
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else this._rawAsset = null;
|
|
153
168
|
}
|
|
154
169
|
|
|
155
|
-
private _loading?: PromiseLike<any>;
|
|
156
170
|
|
|
157
171
|
/** The url of the loaded asset (or the asset to be loaded)
|
|
158
172
|
* @deprecated use url */
|
|
@@ -177,12 +191,7 @@ export class AssetReference {
|
|
|
177
191
|
(this._url.startsWith("http") || this._url.startsWith("blob:") || this._url.startsWith("www.") || this._url.includes("/"));
|
|
178
192
|
}
|
|
179
193
|
|
|
180
|
-
|
|
181
|
-
* This is the loaded asset root object. If the asset is a glb/gltf file this will be the {@link three#Scene} object.
|
|
182
|
-
*/
|
|
183
|
-
get rawAsset(): any { return this._asset; }
|
|
184
|
-
|
|
185
|
-
private _asset: any;
|
|
194
|
+
private _rawAsset: Model | null = null;
|
|
186
195
|
private _glbRoot?: Object3D | null;
|
|
187
196
|
private _url: string;
|
|
188
197
|
private _urlName: string;
|
|
@@ -207,7 +216,7 @@ export class AssetReference {
|
|
|
207
216
|
else {
|
|
208
217
|
this._urlName = uri;
|
|
209
218
|
}
|
|
210
|
-
|
|
219
|
+
|
|
211
220
|
if (asset !== null) this.asset = asset;
|
|
212
221
|
registerPrefabProvider(this._url, this.onResolvePrefab.bind(this));
|
|
213
222
|
}
|
|
@@ -226,6 +235,8 @@ export class AssetReference {
|
|
|
226
235
|
return !this.asset || (this.asset as any).__destroyed === true || isDestroyed(this.asset) === true;
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
private _loadingPromise: Promise<Model | null | undefined> | null = null;
|
|
239
|
+
|
|
229
240
|
/**
|
|
230
241
|
* @returns `true` if the asset has been loaded (via preload) or if it exists already (assigned to `asset`) */
|
|
231
242
|
isLoaded() { return this._rawBinary || this.asset !== undefined }
|
|
@@ -242,7 +253,7 @@ export class AssetReference {
|
|
|
242
253
|
this.asset = null;
|
|
243
254
|
this._rawBinary = undefined;
|
|
244
255
|
this._glbRoot = null;
|
|
245
|
-
this.
|
|
256
|
+
this._loadingPromise = null;
|
|
246
257
|
if (Context.Current) {
|
|
247
258
|
Context.Current.addressables.unregisterAssetReference(this);
|
|
248
259
|
}
|
|
@@ -267,16 +278,14 @@ export class AssetReference {
|
|
|
267
278
|
/** Loads the asset and creates one instance (assigned to `asset`)
|
|
268
279
|
* @returns the loaded asset
|
|
269
280
|
*/
|
|
270
|
-
async loadAssetAsync(prog?: ProgressCallback | null) {
|
|
271
|
-
if (debug)
|
|
272
|
-
console.log("loadAssetAsync", this.url);
|
|
281
|
+
async loadAssetAsync(prog?: ProgressCallback | null): Promise<Object3D | null> {
|
|
282
|
+
if (debug) console.log("[AssetReference] loadAssetAsync", this.url);
|
|
273
283
|
if (!this.mustLoad) return this.asset;
|
|
274
284
|
|
|
275
285
|
if (prog) this._progressListeners.push(prog);
|
|
276
|
-
|
|
277
|
-
if (this.
|
|
278
|
-
|
|
279
|
-
return this._loading.then(_ => this.asset);
|
|
286
|
+
|
|
287
|
+
if (this._loadingPromise !== null) {
|
|
288
|
+
return this._loadingPromise.then(_ => this.asset);
|
|
280
289
|
}
|
|
281
290
|
const context = Context.Current;
|
|
282
291
|
// TODO: technically we shouldnt call awake only when the object is added to a scene
|
|
@@ -285,31 +294,30 @@ export class AssetReference {
|
|
|
285
294
|
// console.log("START LOADING");
|
|
286
295
|
if (this._rawBinary) {
|
|
287
296
|
if (!(this._rawBinary instanceof ArrayBuffer)) {
|
|
288
|
-
console.error("Failed loading
|
|
297
|
+
console.error("[AssetReference] Failed loading – Invalid data. Must be of type ArrayBuffer. " + (typeof this._rawBinary));
|
|
289
298
|
return null;
|
|
290
299
|
}
|
|
291
|
-
this.
|
|
300
|
+
this._loadingPromise = getLoader().parseSync(context, this._rawBinary, this.url, null);
|
|
292
301
|
this.raiseProgressEvent(new ProgressEvent("progress", { loaded: this._rawBinary.byteLength, total: this._rawBinary.byteLength }));
|
|
293
302
|
}
|
|
294
303
|
else {
|
|
295
304
|
if (debug) console.log("Load async", this.url);
|
|
296
|
-
this.
|
|
305
|
+
this._loadingPromise = getLoader().loadSync(context, this.url, this.url, null, prog => {
|
|
297
306
|
this.raiseProgressEvent(prog);
|
|
298
307
|
});
|
|
299
308
|
}
|
|
300
|
-
|
|
309
|
+
this._loadingPromise.finally(() => this._loadingPromise = null);
|
|
310
|
+
const res = await this._loadingPromise;
|
|
301
311
|
// clear all progress listeners after download has finished
|
|
302
312
|
this._progressListeners.length = 0;
|
|
303
313
|
this._glbRoot = this.tryGetActualGameObjectRoot(res);
|
|
304
|
-
this._loading = undefined;
|
|
305
314
|
if (res) {
|
|
306
315
|
// Make sure the loaded roots all have a reference to this AssetReference
|
|
307
316
|
// that was originally loading it.
|
|
308
317
|
// We need this when the loaded asset is being disposed
|
|
309
318
|
// TODO: we have to prevent disposing resources that are still in use
|
|
310
319
|
res[$assetReference] = this;
|
|
311
|
-
if (this._glbRoot)
|
|
312
|
-
this._glbRoot[$assetReference] = this;
|
|
320
|
+
if (this._glbRoot) this._glbRoot[$assetReference] = this;
|
|
313
321
|
if (this.asset) this.asset[$assetReference] = this;
|
|
314
322
|
|
|
315
323
|
// we need to handle the pre_setup callsbacks before instantiating
|
|
@@ -317,7 +325,7 @@ export class AssetReference {
|
|
|
317
325
|
processNewScripts(context);
|
|
318
326
|
|
|
319
327
|
if (res.scene !== undefined) {
|
|
320
|
-
this.
|
|
328
|
+
this._rawAsset = res;
|
|
321
329
|
}
|
|
322
330
|
return this.asset;
|
|
323
331
|
}
|
|
@@ -352,6 +360,9 @@ export class AssetReference {
|
|
|
352
360
|
}
|
|
353
361
|
}
|
|
354
362
|
|
|
363
|
+
|
|
364
|
+
private static readonly currentlyInstantiating: Map<string, number> = new Map<string, number>();
|
|
365
|
+
|
|
355
366
|
private async onInstantiate(opts?: Object3D | IInstantiateOptions | SyncInstantiateOptions | null, networked: boolean = false, saveOnServer?: boolean): Promise<Object3D | null> {
|
|
356
367
|
const context = Context.Current;
|
|
357
368
|
|
|
@@ -544,7 +555,7 @@ export class ImageReference {
|
|
|
544
555
|
console.error("Can not load texture without url");
|
|
545
556
|
return failedTexturePromise;
|
|
546
557
|
}
|
|
547
|
-
|
|
558
|
+
|
|
548
559
|
if (!this.loader) this.loader = new TextureLoader();
|
|
549
560
|
this.loader.setCrossOrigin("anonymous");
|
|
550
561
|
return this.loader.loadAsync(this.url).then(res => {
|
|
@@ -2,6 +2,7 @@ import { AnimationAction, AnimationClip, AnimationMixer, Object3D, PropertyBindi
|
|
|
2
2
|
|
|
3
3
|
import type { Context } from "./engine_context.js";
|
|
4
4
|
import { GLTF, IAnimationComponent, Model } from "./engine_types.js";
|
|
5
|
+
import { TypeStore } from "./engine_typestore.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Registry for animation related data. Use {@link registerAnimationMixer} to register an animation mixer instance.
|
|
@@ -88,12 +89,15 @@ export class AnimationUtils {
|
|
|
88
89
|
* This method will look for objects in the scene that have animations and assign them to the correct objects.
|
|
89
90
|
* @param file The GLTF file to assign the animations from
|
|
90
91
|
*/
|
|
91
|
-
static
|
|
92
|
+
static autoplayAnimations(file: Object3D | Pick<Model, "animations" | "scene">): Array<IAnimationComponent> | null {
|
|
92
93
|
if (!file || !file.animations) {
|
|
93
94
|
console.debug("No animations found in file");
|
|
94
|
-
return;
|
|
95
|
+
return null;
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
const scene = "scene" in file ? file.scene : file as Object3D;
|
|
99
|
+
const animationComponents = new Array<IAnimationComponent>();
|
|
100
|
+
|
|
97
101
|
for (let i = 0; i < file.animations.length; i++) {
|
|
98
102
|
const animation = file.animations[i];
|
|
99
103
|
if (!animation.tracks || animation.tracks.length <= 0) {
|
|
@@ -103,12 +107,12 @@ export class AnimationUtils {
|
|
|
103
107
|
for (const t in animation.tracks) {
|
|
104
108
|
const track = animation.tracks[t];
|
|
105
109
|
const parsedPath = PropertyBinding.parseTrackName(track.name);
|
|
106
|
-
let obj = PropertyBinding.findNode(
|
|
110
|
+
let obj = PropertyBinding.findNode(scene, parsedPath.nodeName);
|
|
107
111
|
if (!obj) {
|
|
108
112
|
const objectName = track["__objectName"] ?? track.name.substring(0, track.name.indexOf("."));
|
|
109
113
|
// let obj = gltf.scene.getObjectByName(objectName);
|
|
110
114
|
// this finds unnamed objects that still have tracks targeting them
|
|
111
|
-
obj =
|
|
115
|
+
obj = scene.getObjectByProperty('uuid', objectName);
|
|
112
116
|
|
|
113
117
|
if (!obj) {
|
|
114
118
|
// console.warn("could not find " + objectName, animation, gltf.scene);
|
|
@@ -116,19 +120,23 @@ export class AnimationUtils {
|
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
let animationComponent = findAnimationGameObjectInParent(obj);
|
|
123
|
+
let animationComponent = findAnimationGameObjectInParent(obj) || findAnimationGameObjectInParent(scene);
|
|
120
124
|
if (!animationComponent) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
const anim = TypeStore.get("Animation");
|
|
126
|
+
animationComponent = scene.addComponent(anim);
|
|
127
|
+
if (!animationComponent) {
|
|
128
|
+
console.error("Failed creating Animation component: No 'Animation' component found in TypeStore");
|
|
129
|
+
break;
|
|
124
130
|
}
|
|
125
|
-
animationComponent = opts.createAnimationComponent(file.scene, animation)
|
|
126
131
|
}
|
|
132
|
+
animationComponents.push(animationComponent);
|
|
127
133
|
if (animationComponent.addClip) {
|
|
128
134
|
animationComponent.addClip(animation);
|
|
129
135
|
}
|
|
130
136
|
}
|
|
131
137
|
}
|
|
138
|
+
return animationComponents;
|
|
139
|
+
|
|
132
140
|
function findAnimationGameObjectInParent(obj): IAnimationComponent | null {
|
|
133
141
|
if (!obj) return null;
|
|
134
142
|
const components = obj.userData?.components;
|
|
@@ -61,7 +61,7 @@ export async function onCreateLoader(url: string, context: Context): Promise<Cus
|
|
|
61
61
|
switch (type) {
|
|
62
62
|
case "unsupported":
|
|
63
63
|
return null;
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
default:
|
|
66
66
|
case "unknown":
|
|
67
67
|
{
|
|
@@ -287,15 +287,6 @@ async function onAfterLoaded(loader: Loader | CustomLoader, context: Context, gl
|
|
|
287
287
|
gltfId = gltfId.split("?")[0];
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
// assign animations of loaded glTF to all scenes
|
|
291
|
-
if ("scenes" in model) {
|
|
292
|
-
for (const scene of model.scenes) {
|
|
293
|
-
if (scene && !scene.animations?.length) {
|
|
294
|
-
scene.animations = [...model.animations];
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
290
|
// E.g. fbx material cleanup
|
|
300
291
|
postprocessLoadedFile(loader, model);
|
|
301
292
|
|
|
@@ -357,13 +348,27 @@ function checkIfUserAttemptedToLoadALocalFile(url: string) {
|
|
|
357
348
|
/**
|
|
358
349
|
* Postprocess the loaded file. This is used to apply any custom postprocessing to the loaded file.
|
|
359
350
|
*/
|
|
360
|
-
function postprocessLoadedFile(loader: object,
|
|
351
|
+
function postprocessLoadedFile(loader: object, model: Model) {
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
// assign animations of loaded glTF to all scenes
|
|
355
|
+
if ("scenes" in model) {
|
|
356
|
+
for (const scene of model.scenes) {
|
|
357
|
+
if (scene && !scene.animations?.length) {
|
|
358
|
+
for (const anim of model.animations) {
|
|
359
|
+
if (!scene.animations.includes(anim)) {
|
|
360
|
+
scene.animations.push(anim);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
361
366
|
|
|
362
367
|
if (loader instanceof FBXLoader || loader instanceof OBJLoader) {
|
|
363
368
|
|
|
364
|
-
let obj: Object3D | Model =
|
|
369
|
+
let obj: Object3D | Model = model;
|
|
365
370
|
if (!(obj instanceof Object3D)) {
|
|
366
|
-
obj = (
|
|
371
|
+
obj = (model as GLTF).scene || model.scenes.find(s => s);
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
obj.traverse((child) => {
|
|
@@ -3,6 +3,7 @@ import { CubeCamera, Object3D, Scene, WebGLCubeRenderTarget } from 'three';
|
|
|
3
3
|
import { isDevEnvironment } from "./debug/index.js";
|
|
4
4
|
import * as constants from "./engine_constants.js";
|
|
5
5
|
import { ContextRegistry } from "./engine_context_registry.js";
|
|
6
|
+
import { NEEDLE_ENGINE_FEATURE_FLAGS } from './engine_feature_flags.js';
|
|
6
7
|
import { isActiveSelf } from './engine_gameobject.js';
|
|
7
8
|
import { safeInvoke } from "./engine_generic_utils.js";
|
|
8
9
|
import type { IComponent, IContext } from './engine_types.js';
|
|
@@ -31,7 +32,7 @@ export function processNewScripts(context: IContext) {
|
|
|
31
32
|
if (debug)
|
|
32
33
|
console.log("Register new components", context.new_scripts.length, [...context.new_scripts], context.alias ? ("element: " + context.alias) : context["hash"], context);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
if (context.new_scripts_pre_setup_callbacks.length > 0) {
|
|
36
37
|
for (const cb of context.new_scripts_pre_setup_callbacks) {
|
|
37
38
|
if (!cb) continue;
|
|
@@ -39,7 +40,7 @@ export function processNewScripts(context: IContext) {
|
|
|
39
40
|
}
|
|
40
41
|
context.new_scripts_pre_setup_callbacks.length = 0;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
+
|
|
43
44
|
if (context.new_scripts.length <= 0) return;
|
|
44
45
|
|
|
45
46
|
// TODO: update all the code from above to use this logic
|
|
@@ -269,8 +270,22 @@ export function isNeedleXRSessionEventReceiver(script: any, mode: XRSessionMode
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
|
|
273
|
+
let needsUpdate = true;
|
|
274
|
+
export function markHierarchyDirty() {
|
|
275
|
+
needsUpdate = true;
|
|
276
|
+
}
|
|
277
|
+
|
|
272
278
|
/** @internal */
|
|
273
|
-
export function updateIsActive(obj?: Object3D) {
|
|
279
|
+
export function updateIsActive(obj?: Object3D, force: boolean = false) {
|
|
280
|
+
|
|
281
|
+
if (NEEDLE_ENGINE_FEATURE_FLAGS.experimentalSmartHierarchyUpdate) {
|
|
282
|
+
|
|
283
|
+
if (!force) {
|
|
284
|
+
if (!needsUpdate) return;
|
|
285
|
+
}
|
|
286
|
+
needsUpdate = false;
|
|
287
|
+
}
|
|
288
|
+
|
|
274
289
|
if (!obj) obj = ContextRegistry.Current.scene;
|
|
275
290
|
if (!obj) {
|
|
276
291
|
console.trace("Invalid call - no current context.");
|
|
@@ -295,9 +310,9 @@ function updateIsActiveInHierarchyRecursiveRuntime(go: Object3D, activeInHierarc
|
|
|
295
310
|
return false;
|
|
296
311
|
}
|
|
297
312
|
|
|
298
|
-
const
|
|
313
|
+
const activeSelf = isActiveSelf(go);
|
|
299
314
|
if (activeInHierarchy) {
|
|
300
|
-
activeInHierarchy =
|
|
315
|
+
activeInHierarchy = activeSelf;
|
|
301
316
|
// IF we update activeInHierarchy within a disabled hierarchy we need to check the parent
|
|
302
317
|
if (activeInHierarchy && go.parent && level === 0) {
|
|
303
318
|
const parent = go.parent;
|
|
@@ -321,7 +336,7 @@ function updateIsActiveInHierarchyRecursiveRuntime(go: Object3D, activeInHierarc
|
|
|
321
336
|
go[constants.activeInHierarchyFieldName] = activeInHierarchy;
|
|
322
337
|
|
|
323
338
|
if (debugHierarchy)
|
|
324
|
-
console.warn("ACTIVE CHANGE", go.name,
|
|
339
|
+
console.warn("ACTIVE CHANGE", go.name, activeSelf, go.visible, activeInHierarchy, "changed?" + changed, go);
|
|
325
340
|
if (allowEventCall) {
|
|
326
341
|
perComponent(go, comp => {
|
|
327
342
|
if (activeInHierarchy) {
|
|
@@ -22,11 +22,11 @@ const debug = getParam("debugextensions");
|
|
|
22
22
|
|
|
23
23
|
// lazily import the GLTFAnimationPointerExtension in case it doesnt exist (e.g. using vanilla three)
|
|
24
24
|
let GLTFAnimationPointerExtension: any;
|
|
25
|
-
const KHR_ANIMATIONPOINTER_IMPORT = import("three
|
|
25
|
+
const KHR_ANIMATIONPOINTER_IMPORT = import("@needle-tools/three-animation-pointer").then(async mod => {
|
|
26
26
|
GLTFAnimationPointerExtension = mod.GLTFAnimationPointerExtension;
|
|
27
27
|
return GLTFAnimationPointerExtension;
|
|
28
28
|
}).catch(e => {
|
|
29
|
-
console.warn("Failed to import GLTFLoaderAnimationPointer. Please use @needle-tools/three for full KHR_animation support", e);
|
|
29
|
+
console.warn("Failed to import GLTFLoaderAnimationPointer. Please use @needle-tools/three-animationpointer for full KHR_animation support", e);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Object3D, Quaternion, Vector3 } from "three";
|
|
2
2
|
import { TransformControlsGizmo } from "three/examples/jsm/controls/TransformControls.js";
|
|
3
3
|
|
|
4
|
-
import { addComponent,
|
|
4
|
+
import { addComponent, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, removeComponent } from "../../engine/engine_components.js";
|
|
5
5
|
import { destroy, isActiveSelf, setActive } from "../../engine/engine_gameobject.js";
|
|
6
6
|
import {
|
|
7
7
|
getTempVector,
|
|
@@ -15,7 +15,9 @@ import {
|
|
|
15
15
|
setWorldScale
|
|
16
16
|
}
|
|
17
17
|
from "../../engine/engine_three_utils.js";
|
|
18
|
-
import type { ComponentInit, Constructor, ConstructorConcrete, HideFlags,IComponent as Component, IComponent } from "../../engine/engine_types.js";
|
|
18
|
+
import type { ComponentInit, Constructor, ConstructorConcrete, HideFlags, IComponent as Component, IComponent } from "../../engine/engine_types.js";
|
|
19
|
+
import { NEEDLE_ENGINE_FEATURE_FLAGS } from "../engine_feature_flags.js";
|
|
20
|
+
import { markHierarchyDirty } from "../engine_mainloop_utils.js";
|
|
19
21
|
import { applyPrototypeExtensions, registerPrototypeExtensions } from "./ExtensionUtils.js";
|
|
20
22
|
|
|
21
23
|
|
|
@@ -150,6 +152,27 @@ export function apply(object: Object3D) {
|
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
if (NEEDLE_ENGINE_FEATURE_FLAGS.experimentalSmartHierarchyUpdate) {
|
|
156
|
+
|
|
157
|
+
const addFn = Object3D.prototype.add;
|
|
158
|
+
Object3D.prototype.add = function (...args: any) {
|
|
159
|
+
markHierarchyDirty();
|
|
160
|
+
return addFn.apply(this, args);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const attachFn = Object3D.prototype.attach;
|
|
164
|
+
Object3D.prototype.attach = function (...args: any) {
|
|
165
|
+
markHierarchyDirty();
|
|
166
|
+
return attachFn.apply(this, args);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const removeFn = Object3D.prototype.remove;
|
|
170
|
+
Object3D.prototype.remove = function (...args: any) {
|
|
171
|
+
markHierarchyDirty();
|
|
172
|
+
return removeFn.apply(this, args);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
153
176
|
|
|
154
177
|
Object3D.prototype["SetActive"] = function (active: boolean) {
|
|
155
178
|
this.visible = active;
|
|
@@ -67,7 +67,7 @@ export class Animation extends Behaviour implements IAnimationComponent {
|
|
|
67
67
|
get isAnimationComponent(): boolean { return true; }
|
|
68
68
|
addClip(clip: AnimationClip) {
|
|
69
69
|
if (!this.animations) this.animations = [];
|
|
70
|
-
this.animations.push(clip);
|
|
70
|
+
if (!this.animations.includes(clip)) this.animations.push(clip);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
2
2
|
|
|
3
3
|
import { AnimationUtils } from "../engine/engine_animation.js";
|
|
4
|
-
import { addComponent } from "../engine/engine_components.js";
|
|
5
4
|
import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
|
|
6
5
|
import { Animation } from "./Animation.js";
|
|
7
6
|
import { Animator } from "./Animator.js";
|
|
@@ -29,11 +28,7 @@ ContextRegistry.registerCallback(ContextEvent.ContextCreated, args => {
|
|
|
29
28
|
return undefined;
|
|
30
29
|
}, true);
|
|
31
30
|
if (hasAnimation !== true) {
|
|
32
|
-
AnimationUtils.
|
|
33
|
-
createAnimationComponent: (obj, _clip) => {
|
|
34
|
-
return addComponent(obj, Animation);
|
|
35
|
-
},
|
|
36
|
-
});
|
|
31
|
+
AnimationUtils.autoplayAnimations(file.file as GLTF);
|
|
37
32
|
}
|
|
38
33
|
}
|
|
39
34
|
}
|