@needle-tools/engine
Version:
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.
101 lines (77 loc) • 3.44 kB
text/typescript
import { Mesh, Object3D } from "three";
import { type GLTF, type GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
import { getParam } from "../engine_utils.js";
const $loadingId = Symbol("gltf-loader-internal-usage-tracker");
const debug = getParam("debugusers");
export class InternalUsageTrackerPlugin implements GLTFLoaderPlugin {
get name(): string {
return "NEEDLE_internal_usage_tracker";
}
static isLoading(object: object) {
return InternalUsageTrackerPlugin._loadingProcesses > 0;
return object[$loadingId] !== undefined;
}
private static _loadingProcesses = 0;
private readonly parser: GLTFParser;
private readonly _getDependency: any;
private readonly _loadingId: string;
private _loadedObjects: Set<object> = new Set();
constructor(parser: GLTFParser) {
this.parser = parser;
this._getDependency = this.parser.getDependency;
this._loadingId = Date.now().toString()
}
beforeRoot() {
InternalUsageTrackerPlugin._loadingProcesses++;
const self = this;
// Patch parser get dependency to track all objects that have been loaded or created
const getDependency = this._getDependency;
this.parser.getDependency = function (type: string, index: number) {
const promise = getDependency.call(this, type, index);
promise.then((result) => {
if (result) {
self._loadedObjects.add(result);
result[$loadingId] = self._loadingId;
}
return result;
});
return promise;
};
return null;
}
afterRoot(_result: GLTF) {
InternalUsageTrackerPlugin._loadingProcesses--;
// reset original method
this.parser.getDependency = this._getDependency;
// Cleanup usage of objects that have not been used in a scene
for (const loaded of this._loadedObjects) {
delete loaded[$loadingId];
if (loaded instanceof Object3D) {
if (!loaded.parent) {
if (loaded instanceof Mesh) {
// we need to delay this for other plugins to use the mesh
// TODO: do we even need to do this?
setTimeout(() => {
if (debug) console.warn("> GLTF LOADER: Mesh not used in scene!", loaded);
loaded.material = null;
loaded.geometry = null;
}, 1000)
}
}
}
}
return null;
}
// private readonly _creatingNodeMesh: Map<number, CreateNodeMesh> = new Map();
// createNodeMesh(_nodeIndex: number): CreateNodeMesh | null {
// // if (!this.parser) return null;
// // let process = this._creatingNodeMesh.get(nodeIndex);
// // if (process) return process;
// // process = this.parser.createNodeMesh(nodeIndex)?.then((mesh) => {
// // console.log("createNodeMesh", nodeIndex, mesh);
// // return mesh;
// // }) as CreateNodeMesh;
// // this._creatingNodeMesh.set(nodeIndex, process);
// // return process;
// }
}