Files
healer-man/src/scene/threeResourceDisposal.ts
T
2026-08-14 15:56:39 -04:00

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,
};
}