For those who might be interested, here is the example cleanup code that removes GLTF model (gltf_obj
) from the scene and also tries to clear GPU.
It is a slightly modified / simplified version of the function from my GLTF Viewer which can be modified and/or simplified further but should cover the general idea of cleaning and disposing, depending of whether only a model is being removed from the scene or cleaning the whole scene (in which case try traversing the scene instead).
async function scene_cleanup() {
let texture_maps = [ 'map', 'aoMap', 'alphaMap', 'bumpMap', 'displacementMap', 'envMap', 'lightMap', 'emissiveMap', 'normalMap',
'metalnessMap', 'roughnessMap', 'anisotropyMap', 'clearcoatMap', 'clearcoatNormalMap', 'clearcoatRoughnessMap',
'iridescenceMap', 'iridescenceThicknessMap', 'sheenColorMap', 'sheenRoughnessMap', 'specularMap',
'specularColorMap', 'specularIntensityMap', 'thicknessMap', 'transmissionMap'
];
// Remove GLTF model from the scene
scene.remove( gltf_obj );
renderer.clear();
// If animation_mixer was declared and created uncomment the following lines
//if (animation_mixer) {
// animation_mixer.stopAllAction();
// for ( let i = 0; i < animations.length; i++ ) {
// animation_mixer.uncacheAction( animations[ i ] );
// animation_mixer.uncacheClip( animations[ i ] );
// }
// animation_mixer.uncacheRoot( animation_mixer.getRoot() );
//}
if (gltf_obj.skeleton && gltf_obj.skeleton.boneTexture)
gltf_obj.skeleton.boneTexture.dispose();
gltf_obj.traverse( function( child ) {
if (child.isMesh || child.isPoints || child.isLine)) {
if (child.skeleton && child.skeleton.boneTexture)
child.skeleton.boneTexture.dispose();
if (child.material) {
if (Array.isArray( child.material )) {
child.material.forEach( mtl => {
if (mtl.uniforms) {
Object.keys( mtl.uniforms ).forEach( ( key ) => {
if (mtl.uniforms[ key ].value) {
let kv = mtl.uniforms[ key ].value;
if (Array.isArray( kv ) && kv.length > 0) {
kv.forEach( val => {
if (val.type && val.type === 1009) val.dispose();
});
} else {
if (kv.type && kv.type === 1009) kv.dispose();
}
}
});
} else {
for (const prop in mtl) {
texture_maps.forEach( tex_map => {
if (prop === tex_map) {
if (mtl[ prop ]) mtl[ prop ].dispose();
}
});
};
}
mtl.dispose();
});
} else {
if (child.material.uniforms) {
Object.keys( child.material.uniforms ).forEach( ( key ) => {
if (child.material.uniforms[ key ].value) {
let kv = child.material.uniforms[ key ].value;
if (Array.isArray( kv ) && kv.length > 0) {
kv.forEach( val => {
if (val.type && val.type === 1009) val.dispose();
});
} else {
if (kv.type && kv.type === 1009) kv.dispose();
}
}
});
} else {
for (const prop in child.material) {
texture_maps.forEach( tex_map => {
if (prop === tex_map) {
if (child.material[ prop ]) child.material[ prop ].dispose();
}
});
};
}
child.material.dispose();
}
}
child.geometry.dispose();
}
});
// If cleaning the whole scene uncomment the following lines
//renderer.dispose();
//while (scene.children.length > 0) {
// scene.remove( scene.children[ 0 ] );
//}
}
This code will not be cleaning geometries / textures related to environment (they seem to be somehow cached for subsequent use).
If using a console then consider logging the renderer info so you can see how many geometries / textures are being used:
console.log( 'Memory: ', renderer.info.memory );
console.log( 'Render: ', renderer.info.render );