forked from phenom/healer-man
87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
import type {
|
|
Material,
|
|
Mesh,
|
|
Object3D,
|
|
Skeleton,
|
|
SkinnedMesh,
|
|
Texture,
|
|
} from "three";
|
|
|
|
export interface Object3DDisposalOptions {
|
|
readonly geometries?: boolean;
|
|
readonly materials?: boolean;
|
|
readonly textures?: boolean;
|
|
readonly skeletons?: boolean;
|
|
}
|
|
|
|
export interface Object3DDisposalCounts {
|
|
readonly geometries: number;
|
|
readonly materials: number;
|
|
readonly textures: number;
|
|
readonly skeletons: number;
|
|
}
|
|
|
|
function materialTextures(material: Material): readonly Texture[] {
|
|
const textures = new Set<Texture>();
|
|
for (const value of Object.values(material)) {
|
|
if (value && typeof value === "object" && "isTexture" in value) {
|
|
textures.add(value as Texture);
|
|
}
|
|
}
|
|
const uniforms = (material as Material & {
|
|
readonly uniforms?: Readonly<Record<string, { readonly value?: unknown }>>;
|
|
}).uniforms;
|
|
for (const uniform of Object.values(uniforms ?? {})) {
|
|
const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value];
|
|
for (const value of values) {
|
|
if (value && typeof value === "object" && "isTexture" in value) {
|
|
textures.add(value as Texture);
|
|
}
|
|
}
|
|
}
|
|
return [...textures];
|
|
}
|
|
|
|
/** Disposes shared Three.js GPU resources exactly once per object graph. */
|
|
export function disposeObject3DResources(
|
|
root: Object3D,
|
|
options: Object3DDisposalOptions = {},
|
|
): Object3DDisposalCounts {
|
|
const disposeGeometries = options.geometries ?? true;
|
|
const disposeMaterials = options.materials ?? true;
|
|
const disposeTextures = options.textures ?? true;
|
|
const disposeSkeletons = options.skeletons ?? true;
|
|
const geometries = new Set<Mesh["geometry"]>();
|
|
const materials = new Set<Material>();
|
|
const textures = new Set<Texture>();
|
|
const skeletons = new Set<Skeleton>();
|
|
|
|
root.traverse((node) => {
|
|
const mesh = node as Mesh;
|
|
if (mesh.isMesh && disposeGeometries) geometries.add(mesh.geometry);
|
|
if (mesh.isMesh && (disposeMaterials || disposeTextures)) {
|
|
const meshMaterials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
for (const material of meshMaterials) {
|
|
if (disposeMaterials) materials.add(material);
|
|
if (disposeTextures) {
|
|
for (const texture of materialTextures(material)) textures.add(texture);
|
|
}
|
|
}
|
|
}
|
|
const skinnedMesh = node as SkinnedMesh;
|
|
if (skinnedMesh.isSkinnedMesh && disposeSkeletons) skeletons.add(skinnedMesh.skeleton);
|
|
});
|
|
|
|
for (const skeleton of skeletons) skeleton.dispose();
|
|
for (const material of materials) material.dispose();
|
|
for (const geometry of geometries) geometry.dispose();
|
|
for (const texture of textures) texture.dispose();
|
|
|
|
return {
|
|
geometries: geometries.size,
|
|
materials: materials.size,
|
|
textures: textures.size,
|
|
skeletons: skeletons.size,
|
|
};
|
|
}
|