10

I've successfully imported a .dae scene via ColladaLoader.

The problem is, I need to switch between several .dae files.

I can't seem to implement the dispose method properly.

        dae.traverse(function(obj) {

            console.log('unloading ' + obj.id);

            scene.remove(obj);

            if(obj.geometry)
                obj.geometry.dispose();
            if(obj.material)
                obj.material.dispose();
            if(obj.mesh)
                obj.mesh.dispose();
            if(obj.texture)
                obj.texture.dispose();

        });

        scene.remove(dae);

What could I be possibly doing wrong?

Thanks so much in advance!


EDIT:

Here's the entire code.

    var renderer = null;
    var scene = null;
    var camera = null;
    var controls = null;
    var dae = null;
    //var loader = null;

    function init() {


        renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } );
        renderer.setSize( 800, 600 );

        var elem = $('.main3d')[0];
        elem.appendChild( renderer.domElement );

        scene = new THREE.Scene();

        camera = new THREE.PerspectiveCamera( 20, 800/600, 1, 1000 );
        camera.position.set( 0, -100, 50 );
        //camera.lookAt( scene.position );
        controls = new THREE.TrackballControls( camera, renderer.domElement );

        var light = new THREE.AmbientLight( 0xffffff ); // soft white light
        scene.add( light );

        threeAnimate();


    }

    function load(url) {
        loader = new THREE.ColladaLoader();

            loader.load(url, function (collada) {
                dae = collada.scene;
                scene.add(dae);

            });

    }

    function unload() {

        dae.traverse(function(obj) {

            console.log('unloading ' + obj.id);

            scene.remove(obj);

            if(obj.geometry)
                obj.geometry.dispose();
            if(obj.material)
                obj.material.dispose();
            if(obj.mesh)
                obj.mesh.dispose();
            if(obj.texture)
                obj.texture.dispose();

        });

        scene.remove(dae);

    }

    var animFrame = null;
    function animate()  {

        animFrame = requestAnimationFrame( threeAnimate );
        renderer.render( scene, camera );
        controls.update();

    }

4 Answers4

31

This should do the job:

function disposeNode (node)
{
    if (node instanceof THREE.Mesh)
    {
        if (node.geometry)
        {
            node.geometry.dispose ();
        }

        if (node.material)
        {
            if (node.material instanceof THREE.MeshFaceMaterial)
            {
                $.each (node.material.materials, function (idx, mtrl)
                {
                    if (mtrl.map)               mtrl.map.dispose ();
                    if (mtrl.lightMap)          mtrl.lightMap.dispose ();
                    if (mtrl.bumpMap)           mtrl.bumpMap.dispose ();
                    if (mtrl.normalMap)         mtrl.normalMap.dispose ();
                    if (mtrl.specularMap)       mtrl.specularMap.dispose ();
                    if (mtrl.envMap)            mtrl.envMap.dispose ();
                    if (mtrl.alphaMap)          mtrl.alphaMap.dispose();
                    if (mtrl.aoMap)             mtrl.aoMap.dispose();
                    if (mtrl.displacementMap)   mtrl.displacementMap.dispose();
                    if (mtrl.emissiveMap)       mtrl.emissiveMap.dispose();
                    if (mtrl.gradientMap)       mtrl.gradientMap.dispose();
                    if (mtrl.metalnessMap)      mtrl.metalnessMap.dispose();
                    if (mtrl.roughnessMap)      mtrl.roughnessMap.dispose();

                    mtrl.dispose ();    // disposes any programs associated with the material
                });
            }
            else
            {
                if (node.material.map)              node.material.map.dispose ();
                if (node.material.lightMap)         node.material.lightMap.dispose ();
                if (node.material.bumpMap)          node.material.bumpMap.dispose ();
                if (node.material.normalMap)        node.material.normalMap.dispose ();
                if (node.material.specularMap)      node.material.specularMap.dispose ();
                if (node.material.envMap)           node.material.envMap.dispose ();
                if (node.material.alphaMap)         node.material.alphaMap.dispose();
                if (node.material.aoMap)            node.material.aoMap.dispose();
                if (node.material.displacementMap)  node.material.displacementMap.dispose();
                if (node.material.emissiveMap)      node.material.emissiveMap.dispose();
                if (node.material.gradientMap)      node.material.gradientMap.dispose();
                if (node.material.metalnessMap)     node.material.metalnessMap.dispose();
                if (node.material.roughnessMap)     node.material.roughnessMap.dispose();

                node.material.dispose ();   // disposes any programs associated with the material
            }
        }
    }
}   // disposeNode

function disposeHierarchy (node, callback)
{
    for (var i = node.children.length - 1; i >= 0; i--)
    {
        var child = node.children[i];
        disposeHierarchy (child, callback);
        callback (child);
    }
}

and you use it

disposeHierarchy (YOUR_OBJECT3D, disposeNode);
gaitat
  • 12,449
  • 4
  • 52
  • 76
  • Wow! That's pretty comprehensive! I'll check it out in a bit. Thank you for taking the time to answer! – だらんぎん じょん Oct 19 '15 at 05:13
  • I also want to know if the answer is based on your own research or if you have any sources. Thanks again! – だらんぎん じょん Oct 19 '15 at 05:14
  • Awesome answer! I was able to release 700MB+ using the disposeHierarchy() function! Just awesome man. Thanks a lot. I hope this answer helps others too. – だらんぎん じょん Oct 19 '15 at 06:10
  • I posted another question (somewhat in relation to this one). It would be awesome if you could share your knowledge on the topic! Thanks in advance! – だらんぎん じょん Oct 19 '15 at 06:21
  • thanks for your comments. my answer is based on my own research and development. – gaitat Oct 19 '15 at 12:48
  • 1
    @gaitat there is no dispose method on light class – kishore Mar 18 '16 at 04:12
  • @gaitat we are not passing the main node to disposeNode i.e parent is it ok? – kishore Mar 18 '16 at 04:49
  • All the lines of code with `node = undefined;` are useless. All it's doing is setting the local reference to `undefined` which has no impact on anything in this example. – Pepe Apr 26 '16 at 04:27
  • THANK YOU from 2021... this is insane it isn't documented in ThreeJS. I have been working on an MMO for years and years, my client FPS has always sucked. I started to figure this out recently that things weren't adding up, after 8 days of research this function has restored my game to a playable state! I get ThreeJS's stance on material loading and stuff but damn, common, there needs to be some OPTIONS for this kind of stuff. Thank you for writing this. – Andy Dec 03 '21 at 16:29
  • @andy glad it helped. dont forget to award points for helpful answers. – gaitat Dec 03 '21 at 16:46
6

I tweaked gaitat's already awesome answer to just use the now built in scene traverse function, to remove $ and also handle MultiMaterial. Why, oh why is there not a built in memory cleanup in THREE!!? Surely it should do it when you do scene.dispose(). I'm still trying to track down a couple more textures I'm using but don't seem to get dispose()ed according to renderer.info.memory.textures

this.disposeNode = function (parentObject) {

    parentObject.traverse(function (node) {
        if (node instanceof THREE.Mesh) {
            if (node.geometry) {
                node.geometry.dispose();
            }

            if (node.material) {

                if (node.material instanceof THREE.MeshFaceMaterial || node.material instanceof THREE.MultiMaterial) {
                    node.material.materials.forEach(function (mtrl, idx) {
                        if (mtrl.map) mtrl.map.dispose();
                        if (mtrl.lightMap) mtrl.lightMap.dispose();
                        if (mtrl.bumpMap) mtrl.bumpMap.dispose();
                        if (mtrl.normalMap) mtrl.normalMap.dispose();
                        if (mtrl.specularMap) mtrl.specularMap.dispose();
                        if (mtrl.envMap) mtrl.envMap.dispose();

                        mtrl.dispose();    // disposes any programs associated with the material
                    });
                }
                else {
                    if (node.material.map) node.material.map.dispose();
                    if (node.material.lightMap) node.material.lightMap.dispose();
                    if (node.material.bumpMap) node.material.bumpMap.dispose();
                    if (node.material.normalMap) node.material.normalMap.dispose();
                    if (node.material.specularMap) node.material.specularMap.dispose();
                    if (node.material.envMap) node.material.envMap.dispose();

                    node.material.dispose();   // disposes any programs associated with the material
                }
            }
        }
    });
}
deejbee
  • 1,148
  • 11
  • 17
  • 1
    This is why it is not _automagically_ removed. [Delocating heap objects...](https://github.com/mrdoob/three.js/issues/5175). The programer **has** to be involved, because it is not as straightforward as you think it is. In large enough projects, things get complicated and you end up reusing materials, geometries and even meshes between scenes (not kidding, you might end up doing all of the above). So deleting stuff so happily is only going to bring pain and sorrow. Having a learning curve and having to be involved as a programer keeps you from doing silly mistakes. – tfrascaroli Mar 06 '17 at 07:32
  • There is a pretty nice article now in the official docs about disposing things: https://threejs.org/docs/index.html?q=dispose#manual/en/introduction/How-to-dispose-of-objects – Maccesch Dec 23 '21 at 18:39
3

Building off the answers here, this code handles arrays of materials.

function disposeNode(parentObject) {
    parentObject.traverse(function (node) {
        if (node instanceof THREE.Mesh) {
            if (node.geometry) {
                node.geometry.dispose();
            }
            if (node.material) {
                var materialArray;
                if (node.material instanceof THREE.MeshFaceMaterial || node.material instanceof THREE.MultiMaterial) {
                    materialArray = node.material.materials;
                }
                else if(node.material instanceof Array) {
                    materialArray = node.material;
                }
                if(materialArray) {
                    materialArray.forEach(function (mtrl, idx) {
                        if (mtrl.map) mtrl.map.dispose();
                        if (mtrl.lightMap) mtrl.lightMap.dispose();
                        if (mtrl.bumpMap) mtrl.bumpMap.dispose();
                        if (mtrl.normalMap) mtrl.normalMap.dispose();
                        if (mtrl.specularMap) mtrl.specularMap.dispose();
                        if (mtrl.envMap) mtrl.envMap.dispose();
                        mtrl.dispose();
                    });
                }
                else {
                    if (node.material.map) node.material.map.dispose();
                    if (node.material.lightMap) node.material.lightMap.dispose();
                    if (node.material.bumpMap) node.material.bumpMap.dispose();
                    if (node.material.normalMap) node.material.normalMap.dispose();
                    if (node.material.specularMap) node.material.specularMap.dispose();
                    if (node.material.envMap) node.material.envMap.dispose();
                    node.material.dispose();
                }
            }
        }
    });
}
Paul E
  • 41
  • 3
1

After research on the web and couple of refactoring, here is what I came up with:

  disposeNode = ( node, recursive = false ) => {

    if ( !node ) return;

    if ( recursive && node.children)
      for ( const child of node.children )
        disposeNode( child , recursive );

    node.geometry && node.geometry.dispose();
    
    if ( !node.material ) return;

    const materials = node.material.length === undefined ? [ node.material ] : node.material

    for ( const material of materials ) {

        for ( const key in material ) {

          const value = material[key];

          if ( value && typeof value === 'object' && 'minFilter' in value )
            value.dispose();

        }

        material && material.dispose();    
        
    }

  }
JSmith
  • 4,519
  • 4
  • 29
  • 45