18

I tried to make a function to remove all objects from scene in one shoot but it removes only one object for invocation.

GeometryModel.prototype.clearScene = function(scene) {
var i;
for(i=0; i < scene.children.length; i++){
     obj = scene.children[i];
     scene.remove(obj);
  }
}

another solution I tried and that works is this:

scene.children={};

but I am not sure if it is correct.

Kalnode
  • 9,386
  • 3
  • 34
  • 62
Stefano Maglione
  • 3,946
  • 11
  • 49
  • 96
  • 1
    Often reloading will cause a HEAP mem leaks see my question http://stackoverflow.com/questions/37762961/three-js-proper-removing-object-from-scene-still-reserved-in-heap – Martin Jun 11 '16 at 18:46

5 Answers5

28

You have to do the opposite:

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

because in each iteration the .children array changes once you do a .remove() from the start and the indexing of that array changes.

If you want to understand it better, unroll the for loop and follow what the index into the array is.

else42.de
  • 465
  • 5
  • 23
gaitat
  • 12,449
  • 4
  • 52
  • 76
  • It is not a solution, it will cause a HEAP mem leaks see my question http://stackoverflow.com/questions/37762961/three-js-proper-removing-object-from-scene-still-reserved-in-heap – Martin Jun 11 '16 at 18:46
  • Is'nt while loop much easier. – mithun Oct 19 '16 at 04:17
  • @Martin as the other SO answer says it is largely a matter of the garbage collection. – gaitat Oct 19 '16 at 12:30
14

You can accomplish that with while :

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

Explanations :

object.children.length return true if object.children.length is not 0, if it's equal to 0 it return false.

So you just have to remove the first child element as long as object has children.

Tiago Castro
  • 835
  • 2
  • 10
  • 23
csblo
  • 1,571
  • 13
  • 20
  • 1
    Where is the difference ? – csblo Dec 13 '16 at 10:00
  • Sometimes especially with large quantities the object will not be removed on first .remove() iteration (i think slower removing of materials atc..) then this is good for this cases. Problem will be when object cannot be removed for some reason. – Martin Sep 16 '20 at 14:30
6

A preferred method is using the scene's traverse function. All objects have this function and will do a depth-first search through the parent's children.

Here's a snippet from Mr. Doob himself.

scene.traverse( function ( object ) {
    if ( object instanceof THREE.Mesh ) {
        var geometry = object.geometry;
        var matrixWorld = object.matrixWorld;

        ...

    }
});

And here's a bit from r82's source:

traverse: function ( callback ) {
    callback( this );
    var children = this.children;
    for ( var i = 0, l = children.length; i < l; i ++ ) {
        children[ i ].traverse( callback );
    }
}

You can also use traverseVisible in your case:

scene.traverseVisible(function(child) {
   if (child.type !== 'Scene') {
      scene.remove(child);
   }
});
austin_ce
  • 1,063
  • 15
  • 28
3

The existing answer is good, I just want to provide a fuller response for those running into this same issue. When I'm using hot module reloading with Three.js, I often want to recreate all objects other than the plane and camera. To do that I do the following:

export const reload = (/* updatedDependencies */) => {
  console.info('Canceling the run loop...')
  cancelAnimationFrame(runLoopIdentifier) // the return value of `requestAnimationFrame`, stored earlier

  console.info('Removing all children...')
  for (let i = scene.children.length - 1; i >= 0 ; i--) {
    let child = scene.children[ i ];

    if ( child !== plane && child !== camera ) { // plane & camera are stored earlier
      scene.remove(child);
    }
  }

  while (renderer.domElement.lastChild) // `renderer` is stored earlier
    renderer.domElement.removeChild(renderer.domElement.lastChild)

  document.removeEventListener( 'DOMContentLoaded', onDOMLoad )
  conicalDendriteTreeSegments = require('./plantae/conical-dendrite-trees').default

  initializeScene() // re-add all your objects
  runLoopIdentifier = startRenderRunLoop() // render on each animation frame

  console.info('Reload complete.')
}
james_womack
  • 10,028
  • 6
  • 55
  • 74
  • It is not a solution, it will cause a HEAP mem leaks see my question http://stackoverflow.com/questions/37762961/three-js-proper-removing-object-from-scene-still-reserved-in-heap – Martin Jun 11 '16 at 18:46
0

This solution seems to work better than traversing (according to the documentation of Three.js it is not recommended to use the traverse function to make scene graph changes). Also do not forget to dispose geometries in order to release the memory. It releases first the nodes that are deep in the graph so as to avoid memory leaks.

// Remove all objects
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