0

I know there are plenty of SO questions asking similar things about removing everything from the scene, however everything I have tried doesn't work the way I was hoping. There also doesn't seem to be a definitive way of removing everything from the scene (in the docs or anything like that), and so I am hoping that someone can tell me the correct way to do it.

Below is the function I am currently using to remove everything from the scene:

function removeAll() {
    while (scene.children.length > 0) {
        scene.remove(scene.children[0]);
        if (scene.children[0] == THREE.Mesh || scene.children[0] == THREE.Object3D || scene.children[0] == THREE.Group) {
            scene.children[0].dispose();
            scene.children[0].geometry.dispose();
            scene.children[0].material.dispose();
        }
    }
}

This works okay, and seems to visually remove everything from the scene.

My problem is that if I call this function and then re-call the function that creates the scene, or a function that creates a new scene (see context section, I have multiple), there are more objects in the scene than there were after the first call of the scene creation function (init()) - checked with renderer.info in the console.

I also notice that after calling the removeAll() function and then reloading the scene, the more I do this the laggier the scene gets, which I assume is because not everything is being removed properly.

So I ask:

What is the correct way to remove everything from the scene.

For context:

I have a HTML menu where the user can choose which scene they want to "skip" to. I need to remove everything when they do this and then reload only the objects for that particular scene. I have all of this functionality working, the only problem is with the removal of the objects before the new scene loads.

JJ Gerrish
  • 812
  • 1
  • 10
  • 25

1 Answers1

2

The big thing that I see is that you're only disposing of the top level scene children. If those objects have children themselves then they are not necessarily being disposed of properly.

Honestly the best way to ensure you clean everything up would just be to dispose of the renderer and make a new one and dump the scene and let it be GCed.

If you want to stick to trying to use a method to clear everything out then you'll need to recurse into the children and dispose of any geometries, materials, and textures that they might have.

You can find a very comprehensive dispose function in this related post. The only change that I would make to it is to modify diposeHierarchy to detach the objects from one another like so:

function disposeHierarchy (node, callback)
{
    for (var i = node.children.length - 1; i >= 0; i--)
    {
        var child = node.children[i];
        disposeHierarchy (child, callback);
        callback (child);
        node.remove(child);
    }
}
Smilliam
  • 99
  • 1
  • 5
  • I feel like this is almost working, except that I get the error: `root.material.dispose is not a function` in the console. If I remove this line the rest of the function works but it does not clear 100% of the objects still. I'm running the function as `removeAll(scene)` - hope that's right. Thanks! – JJ Gerrish May 18 '18 at 08:12
  • Also, how would I go about disposing the renderer? I tried calling `renderer.dispose()` in my `removeAll()` function but that doesn't seem to do anything. – JJ Gerrish May 18 '18 at 13:32
  • I have also just noticed that when I remove and then load a new scene, a brand new `` element is created but the old one is still there, could that be part of the problem? – JJ Gerrish May 18 '18 at 13:36
  • Fixed the multiple canvas issue by using the same THREE.WebGLRenderer instead of creating a new one for each scene so ignore that one – JJ Gerrish May 18 '18 at 13:50
  • What version of three are you using? – Smilliam May 18 '18 at 16:18
  • I'm currently using r91 – JJ Gerrish May 18 '18 at 16:19
  • That's curious. Have you overwritten or otherwise used the `.material` property of any of your `object3d`'s? If it's a three material then it should definitely have a [dispose](https://threejs.org/docs/#api/materials/Material.dispose) function. [Here](https://stackoverflow.com/a/33199591/5249889) is a related post that has a much more comprehensive remove function. Also note the answer right below it that takes advantage of `Scene#traverse`. – Smilliam May 18 '18 at 16:31
  • I have cloned the material of 1 object, changed the colour of a few (with setHex) and made one material .DoubleSide, but they are the only times I've modified a .material of an Object3D – JJ Gerrish May 18 '18 at 16:34
  • Did you try the version in the post I linked in my previous comment? That is a much more comprehensive dispose function. If you're still getting the same issue, you'll need to fire up the debugger and find out where and what object in your scene graph is causing the error. It's difficult to diagnose without that extra context. – Smilliam May 21 '18 at 16:50
  • Yeah I tried it and I didn't get the error with `material.dispose()` but it still didn't remove everything. From debugging I have noticed that the arrays that contain loaded .obj objects are doubling in amount each time, e.g. I have an array of building objects that has 60 buildings, when I reload the page it has 120 which leads me to believe it's something to do with either .obj objects not being deleted properly... For context I am loading each object once, cloning it (the amount I need e.g. 60 times) and then pushing it to an array. – JJ Gerrish May 22 '18 at 08:05
  • All of those objects are `THREE.Group()` objects and I feel like the problem is with trying to delete these. I tried adding a section for this to the other SO answer you linked which was basically `if (node instanceof THREE.Group) { removeAll(node) }` but I just got another error – JJ Gerrish May 22 '18 at 08:47
  • After you dispose of all of the geometries and materials, you still need to detach everything from the scene. `Object3d#remove` should be able to do that for you. If you're using the `dispose` function in the previous link, then modifying `disposeHierarchy` to something along these lines might get you there. – Smilliam May 22 '18 at 16:05
  • Edit: looks like code markdown doesn't work well in comments. In the `disposeHeirarchy` method, add in `node.remove(child)` after the callback. – Smilliam May 22 '18 at 16:05
  • Okay awesome that worked like 99% apart from the fact that I still get the error that I can't dispose materials... It works well enough for what I need although it would be nice to get rid of the materials! Sorry for the super long comments thread, if you want to write up an answer again with the correct function I'll accept it! (or edit this one) – JJ Gerrish May 23 '18 at 08:22
  • I've updated the main answer to link to the other post and show the modification to `disposeHierarchy`. w.r.t. the material issue, the only thing that I can think of off the top of my head is that since the material is shared, the first dispose call to it will clear it out causing subsequent calls to fail. However, in my experience, those subsequent calls fail silently so that you console isn't blowing up with errors. – Smilliam May 23 '18 at 16:08
  • Great thanks, yeah it's really strange, going to have to look into it more but it's fine for what I need :) – JJ Gerrish May 23 '18 at 16:27