Thanks for pointing that out, somehow it completely went over my head, I guess cause I wasn’t using ShaderMaterial
s at the time, and yes, iterating over the Object.values
is better than using a hard-coded list, another great suggestion, so thanks again.
Here is the updated code, also removed the disposeMediaElement
method for the sake of simplicity.
import { Object3D, BufferGeometry, Material, Texture, ShaderMaterial } from "three"
/**
* Dispose of all Object3D`s nested Geometries, Materials and Textures
*
* @param object Object3D, BufferGeometry, Material or Texture
* @param disposeMedia If set to true will dispose of the texture image or video element, default false
*/
function deepDispose(
object: Object3D | BufferGeometry | Material | Texture
) {
const dispose = (object: BufferGeometry | Material | Texture) =>
object.dispose();
;
const disposeObject = (object: any) => {
if (object.geometry) dispose(object.geometry);
if (object.material)
traverseMaterialsTextures(object.material, dispose, dispose);
};
if (object instanceof BufferGeometry || object instanceof Texture)
return dispose(object);
if (object instanceof Material)
return traverseMaterialsTextures(object, dispose, dispose);
disposeObject(object);
if (object.traverse) object.traverse((obj) => disposeObject(obj));
}
/**
* Traverse material or array of materials and all nested textures
* executing there respective callback
*
* @param material Three js Material or array of material
* @param materialCallback Material callback
* @param textureCallback Texture callback
*/
function traverseMaterialsTextures(
material: Material | Material[],
materialCallback?: (material: any) => void,
textureCallback?: (texture: any) => void
) {
const traverseMaterial = (mat: Material) => {
if (materialCallback) materialCallback(mat);
if(!textureCallback) return;
Object.values(mat)
.filter((value) => value instanceof Texture)
.forEach((texture) => textureCallback(texture)
);
if((mat as ShaderMaterial).uniforms)
Object.values((mat as ShaderMaterial).uniforms)
.filter(({ value }) => value instanceof Texture)
.forEach(({ value }) => textureCallback(value))
};
if (Array.isArray(material)) {
material.forEach((mat) => traverseMaterial(mat));
} else traverseMaterial(material);
}
export { deepDispose }