23

I am trying to find ways of clearing all the objects in a scene without destroying the scene itself. I know that naming the object is one way and then when we want to delete the object, we just "get" it by its name. However, I want to find a quick way to clear a scene of all the objects in it, regardless of their names. Is there a easy way to do it? Thanks!

6 Answers6

27

You can traverse the child objects of the scene and remove them one by one.

As suggested in the comments, this should be done in reverse order to not modify the elements that you're iterating over.

while(scene.children.length > 0){ 
    scene.remove(scene.children[0]); 
}

Note: This is just a quick and dirty clearing of the object hierarchy. If you plan on doing this a lot you risk running in to memory leaks with the code above because the renderer has references to the objects materials, textures and geometries. A complete clean of the scene is more complicated and there are plenty other questions that goes in to more detail:

micnil
  • 4,705
  • 2
  • 28
  • 39
  • 5
    this solution is incorrect, because you're iterating over array which modified in loop, so some elements may be skipped (e.g. when you have two elements in children), please replace with: ```while(scene.children.length > 0){ scene.remove(scene.children[0]); }``` – Alleo Mar 23 '17 at 11:22
22

I have a more concise way of doing this. I noticed that the remove method of Object3D accepts more than one parameter for object removal. This allows us to use the entire children array by modifying the call to use each element as individual parameters by taking advantage of the built-in apply method for functions. This works like so:

scene.remove.apply(scene, scene.children);
Jonathan Gray
  • 2,509
  • 15
  • 20
16

Traversing all children and call dispose on their geometry, material and texture. The code below is my solution.

function clearThree(obj){
  while(obj.children.length > 0){ 
    clearThree(obj.children[0]);
    obj.remove(obj.children[0]);
  }
  if(obj.geometry) obj.geometry.dispose();

  if(obj.material){ 
    //in case of map, bumpMap, normalMap, envMap ...
    Object.keys(obj.material).forEach(prop => {
      if(!obj.material[prop])
        return;
      if(obj.material[prop] !== null && typeof obj.material[prop].dispose === 'function')                                  
        obj.material[prop].dispose();                                                      
    })
    obj.material.dispose();
  }
}   

clearThree(scene);
robotosail
  • 23
  • 1
  • 7
欧阳维杰
  • 1,608
  • 1
  • 14
  • 22
  • 1
    I see "Cannot read property 'dispose' of null" error message if obj.material[prop] is null. I have resolved issue by replacing "if ( typeof obj.material[prop].dispose === 'function' )" line to "if ( obj.material[prop] !== null && typeof obj.material[prop].dispose === 'function' )" – Andrej Oct 01 '19 at 02:04
  • If `obj.material[prop]` were null then that would be caught by the `if(!obj.material[prop])` check (since null is a falsy value). In fact that previous if/return can be removed and the `obj.material[prop] !== null` can be shortened to `obj.material[prop]` to handle all cases efficiently. – Jonathan Gray Jul 15 '21 at 18:59
1

Also the following method of clearing the scene worked for me:

const n = scene.children.length - 1; 
for (var i = n; i > -1; i--) {
    scene.remove(scene.children[i]); 
} 

Iterating the items in the reversed order, it will not get the issue with the current array index.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Adrian
  • 60
  • 2
0

Be sure to remove all things, especially texture maps.

    function removeObjectsWithChildren(obj){

        if(obj.children.length > 0){
            for (var x = obj.children.length - 1; x>=0; x--){
                removeObjectsWithChildren( obj.children[x]);
            }
        }

        if (obj.geometry) {
            obj.geometry.dispose();
        }

        if (obj.material) {
            if (obj.material.length) {
                for (let i = 0; i < obj.material.length; ++i) {

    
                    if (obj.material[i].map) obj.material[i].map.dispose();
                    if (obj.material[i].lightMap) obj.material[i].lightMap.dispose();
                    if (obj.material[i].bumpMap) obj.material[i].bumpMap.dispose();
                    if (obj.material[i].normalMap) obj.material[i].normalMap.dispose();
                    if (obj.material[i].specularMap) obj.material[i].specularMap.dispose();
                    if (obj.material[i].envMap) obj.material[i].envMap.dispose();

                    obj.material[i].dispose()
                }
            }
            else {
                if (obj.material.map) obj.material.map.dispose();
                if (obj.material.lightMap) obj.material.lightMap.dispose();
                if (obj.material.bumpMap) obj.material.bumpMap.dispose();
                if (obj.material.normalMap) obj.material.normalMap.dispose();
                if (obj.material.specularMap) obj.material.specularMap.dispose();
                if (obj.material.envMap) obj.material.envMap.dispose();

                obj.material.dispose();
            }
        }

        obj.removeFromParent();

        return true;
    }
Dimitrios Ververidis
  • 1,118
  • 1
  • 9
  • 33
-1

scene.clear() is what you need. Here is example code, make it become a function first, then call the function every time you want to change object.

function loadscene(path)
{
    scene.clear();
    loader.load(path,function(gltf)
    {
        const model = gltf.scene;
        model.position.set(0,0,0);
        model.rotation.set(0,0,0);
        model.scale.set(1,1,1);
        scene.add(model);
        animate();
    }, 
    undefined, function(e)
    {
        console.error(e);
    });
}
ForteD
  • 41
  • 4