68

I'm using ThreeJS to develop a web application that displays a list of entities, each with corresponding "View" and "Hide" button; e.g. entityName View Hide. When user clicks View button, following function is called and entity drawn on screen successfully.

function loadOBJFile(objFile){            
    /* material of OBJ model */                                          
    var OBJMaterial = new THREE.MeshPhongMaterial({color: 0x8888ff});
    var loader = new THREE.OBJLoader();
    loader.load(objFile, function (object){
        object.traverse (function (child){
            if (child instanceof THREE.Mesh) {
                child.material = OBJMaterial;
            }
        });
        object.position.y = 0.1;
        scene.add(object);
    });     
}

function addEntity(object) {
    loadOBJFile(object.name);
}

And on clicking Hide button, following function is called:

function removeEntity(object){
    scene.remove(object.name);
}

The problem is, entity is not removed from screen once loaded when Hide button is clicked. What can I do to make Hide button to work?

I did small experiment. I added scene.remove(object.name); right after scene.add(object); within addEntity function and as result, when "View" button clicked, no entity drawn (as expected) meaning that scene.remove(object.name); worked just fine within addEntity. But still I'm unable to figure out how to use it in removeEntity(object).

Also, I checked contents of scene.children and it shows: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Complete code: http://devplace.in/~harman/model_display1.php.html

Please ask, if more detail is needed. I tested with rev-59-dev and rev-60 of ThreeJS.

Thanks. :)

FunkySayu
  • 7,641
  • 10
  • 38
  • 61
harman052
  • 919
  • 3
  • 9
  • 14
  • Maybe you forgot update scene (rerender it)? Is there render update exists (requestAnimationFrame loop or renderer.render(...))? – Anton Krutikov Aug 21 '13 at 12:46
  • I tried by calling animate() (update() and render() are called within animate()) after `scene.remove(object.name);` in `removeEntity(object)`, but no change. :( – harman052 Aug 21 '13 at 15:39

13 Answers13

64

I think seeing your usage for addEntity and removeEntity code would be helpful, but my first thought is are you actually setting the object.name? Try in your loader just before scene.add(object); something like this:

object.name = "test_name";
scene.add(object);

What might be happening is the default "name" for an Object3D is "", so when you then call your removeEntity function it fails due to the scene objects name being ""

Also, I notice you pass in object.name to your loader? Is this where your storing the URL to the resource? If so, I would recommend using the Object3D's built in .userData method to store that information and keep the name field for scene identification purposes.

Edit: Response to newly added Code

First thing to note is it's not a great idea to have "/" in your object name, it seems to work fine but you never know if some algorithm will decide to escape that string and break your project.

Second item is now that I've seen your code, its actually straight forward whats going on. Your delete function is trying to delete by name, you need an Object3D to delete. Try this:

function removeEntity(object) {
    var selectedObject = scene.getObjectByName(object.name);
    scene.remove( selectedObject );
    animate();
}

Here you see I lookup your Object3D in the Three.js Scene by passing in your object tag's name attribute.

starball
  • 20,030
  • 7
  • 43
  • 238
Darryl_Lehmann
  • 2,178
  • 15
  • 21
  • Actually, the "object" argument passed to `addEntity` and `removeEntity` is a JavaScript default keyword that picks the "name" of entity whose corresponding "View" button clicked (addEntity and removeEntity are onclick callee functions), so `object.name` contains actually name of entity. scene.add(object) is working fine as I can see objects drawn on screen. Problem is only with scene.remove(object.name) as neither it shows any error nor any results. – harman052 Aug 21 '13 at 15:51
  • I see, I'm not sure the exact problem, but the scene.remove function in Three.js is looking for an Object3D.name which may or may not be equivalent. Alas, I think for clarity a bit more code may be needed to see the object construction and usage of your add remove object code. Otherwise, I'd console.log the Mesh.name in your add function and remove function and see what outputs you get. – Darryl_Lehmann Aug 21 '13 at 16:20
  • I just edited the question adding more detail and mentioned the link to complete code. Please check. Thanks. – harman052 Aug 22 '13 at 05:42
  • Thanks for that link to the code. I think I see the problem, have a look at my editted response above ^^ – Darryl_Lehmann Aug 22 '13 at 15:27
  • 1
    I just tried as you suggested, nothing happened. I also checked by putting `scene.remove( scene.getObjectByName(object.name) );` in alert() and got "undefined". Since I don't know much about ThreeJS, but I found that objects are storied in scene.children. Every object we add to scene get append to the end of scene.children. So I wrote following code: `var lastIndex = scene.children.length - 1; endElement = scene.children[lastIndex]; scene.remove(endElement);` This removes the last element I drew. Now can you help me to fetch the ID of element from scene.children with reference to object name? – harman052 Aug 22 '13 at 16:28
  • Sure try splitting my one liner into two lines, I've updated the answer above. – Darryl_Lehmann Aug 22 '13 at 18:31
  • :( It still gives "undefined". Don't you think we need to do something with "scene.children" array? I print the contents of scene.children as `alert(scene.children.toSource());` and I found that it has JSON data. Do I need to process this JSON data to fetch ID corresponding to the entity name(that I actually have) from it? – harman052 Aug 22 '13 at 22:59
  • I think what your referring to when your alerting "undefined" is the expected result as the scene.remove function returns nothing. I think your very close, the suggested code works fine on my use case, you just need to adapt it to your project. Did you try the above replacement to your function `removeEntity` – Darryl_Lehmann Aug 23 '13 at 13:37
  • One last thing, feel silly mentioning it after all we've been through :) In your "View"/"Hide" your calling `deleteEntity` but your function is called `removeEntity`, quick fix and it should work. – Darryl_Lehmann Aug 23 '13 at 14:31
  • 1
    +1 for the suggestions and the information about name attribute, what is important though is that you have to make sure every object in the scene has a unique name, when using this method. – kon psych Mar 27 '14 at 21:43
  • what if i have to remove a particular elem from object not the entire obj for example say there is a man model and I need to remove only hand from that?? – user2906608 Mar 20 '18 at 10:11
11
clearScene: function() {
    var objsToRemove = _.rest(scene.children, 1);
    _.each(objsToRemove, function( object ) {
          scene.remove(object);
    });
},

this uses undescore.js to iterrate over all children (except the first) in a scene (it's part of code I use to clear a scene). just make sure you render the scene at least once after deleting, because otherwise the canvas does not change! There is no need for a "special" obj flag or anything like this.

Also you don't delete the object by name, just by the object itself, so calling

scene.remove(object); 

instead of scene.remove(object.name); can be enough

PS: _.each is a function of underscore.js

MJB
  • 3,934
  • 10
  • 48
  • 72
  • 7
    What's wrong with a regular for(let x of foo)? or a regular for loop? – shinzou Feb 21 '17 at 12:53
  • of course you can use a simple for-loop, nothing wrong with that, I just happen to have used _.each, that is all. – MJB Feb 21 '17 at 22:09
  • 3
    Perfectly valid solution but underscore is an additional dependency which clutters up the codebase unless you are already using it. – DOOMDUDEMX Jul 15 '19 at 08:55
  • 3
    Oh these library addicted guys... They seem to forget that all JS libraries do nothing more than using... plain, old, native JS! ;-) – Pedro Ferreira May 16 '20 at 17:35
  • Are you aware that the state of javascript was completely different in 2013? On top of that, libraries solve a lot of regularly occurring problems and are, e.g. lodash, well tested. There is 0 reason to implement the logic yourself. Furthermore, if you already have a library included for reason X, there is no reason to not reuse existing modules to solve other problems. If you import jquery to access a dom node using an id, you are doing it wrong though. – MJB May 20 '20 at 09:43
10

I came in late but after reading the answers more clarification needs to be said.

The remove function you wrote

function removeEntity(object) {
    // scene.remove(); it expects as a parameter a THREE.Object3D and not a string
    scene.remove(object.name); // you are giving it a string => it will not remove the object
}

A good practice to remove 3D objects from Three.js scenes

function removeObject3D(object3D) {
    if (!(object3D instanceof THREE.Object3D)) return false;

    // for better memory management and performance
    if (object3D.geometry) object3D.geometry.dispose();

    if (object3D.material) {
        if (object3D.material instanceof Array) {
            // for better memory management and performance
            object3D.material.forEach(material => material.dispose());
        } else {
            // for better memory management and performance
            object3D.material.dispose();
        }
    }
    object3D.removeFromParent(); // the parent might be the scene or another Object3D, but it is sure to be removed this way
    return true;
}
Ibrahim W.
  • 615
  • 8
  • 20
4

If your element is not directly on you scene go back to Parent to remove it

  function removeEntity(object) {
        var selectedObject = scene.getObjectByName(object.name);
        selectedObject.parent.remove( selectedObject );
    }
Victor Santos
  • 509
  • 4
  • 5
4

THIS WORKS GREAT - I tested it so, please SET NAME for every object

give the name to the object upon creation

    mesh.name = 'nameMeshObject';

and use this if you have to delete an object

    delete3DOBJ('nameMeshObject');



    function delete3DOBJ(objName){
        var selectedObject = scene.getObjectByName(objName);
        scene.remove( selectedObject );
        animate();
    }

open a new scene , add object open new scene , add object

delete an object and create new delete object and create new

KirstieBallance
  • 1,238
  • 12
  • 26
2

I started to save this as a function, and call it as needed for whatever reactions require it:

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

Now you can call the Remove(); function where appropriate.

Alex Melluzzo
  • 73
  • 1
  • 5
2

When you use : scene.remove(object); The object is removed from the scene, but the collision with it is still enabled !

To remove also the collsion with the object, you can use that (for an array) : objectsArray.splice(i, 1);

Example :

for (var i = 0; i < objectsArray.length; i++) {
//::: each object ::://
var object = objectsArray[i]; 
//::: remove all objects from the scene ::://
scene.remove(object); 
//::: remove all objects from the array ::://
objectsArray.splice(i, 1); 

}

2

I improve Ibrahim code for removeObject3D, added some checks for geometry or material

removeObject3D(object) {
    if (!(object instanceof THREE.Object3D)) return false;
    // for better memory management and performance
    if (object.geometry) {
        object.geometry.dispose();
    }
    if (object.material) {
        if (object.material instanceof Array) {
            // for better memory management and performance
            object.material.forEach(material => material.dispose());
        } else {
            // for better memory management and performance
            object.material.dispose();
        }
    }
    if (object.parent) {
        object.parent.remove(object);
    }
    // the parent might be the scene or another Object3D, but it is sure to be removed this way
    return true;
}
Vít Zadina
  • 168
  • 8
1

this example might give you a different approach . I was trying to implement a similar feature in my project also with scene.remove(mesh). However disposal of geometry and material attributes of mesh worked for me! source

1

Use scene.remove(Object) The object you want to remove from the scene

It's Joker
  • 65
  • 10
1

To add more information to @Ibrahim W. answer about how to clean up threejs would be to also free up memory allocated to threejs objects.

Typically you would want to clean up like this:

// `scene` is threejs scene(new THREE.Scene())
scene.children.forEach(sceneObject => {
        if (!(sceneObject instanceof THREE.Object3D)) return;

        // Remove geometries to free GPU resources
        if (sceneObject.geometry) sceneObject.geometry.dispose();

        // Remove materials to free GPU resources
        if (sceneObject.material) {
            if (sceneObject.material instanceof Array) {
                sceneObject.material.forEach(material => material.dispose());
            } else {
                sceneObject.material.dispose();
            }
        }

        // Remove object from scene
        scene.remove(sceneObject); // OR sceneObject.removeFromParent()
    });

    scene.clear(); // Remove all other object children that aren't Object3D instance e.g DirectionalLight

    // Remove any registered listeners
    domTarget.removeEventListener("pointermove", onPointerMove);
    window.removeEventListener("resize", onWindowResize);
    domTarget.removeChild(renderer.domElement);

    // Manually clear webgl instance/context and free up CPU memory
    renderer.renderLists.dispose();
    renderer.forceContextLoss();
    renderer.context = null;
    renderer.domElement = null;
    renderer.dispose();
    renderer = null;
    scene = null;
    camera = null;
}

Clearing the WebglContext avoids running into a warning in the browser console:

Too many active WebGL contexts. Oldest context will be lost

And since most of the variables are created in the global scope, Javascript garbage collector may not free up the memory space allocated to these objects after the animation has been removed. Possibly they still have references. So setting them to null will enable the Garbage Collector to reclaim the memory space

hane Smitter
  • 516
  • 3
  • 11
0

I had The same problem like you have. I try this code and it works just fine: When you create your object put this object.is_ob = true

function loadOBJFile(objFile){            
    /* material of OBJ model */                                          
    var OBJMaterial = new THREE.MeshPhongMaterial({color: 0x8888ff});
    var loader = new THREE.OBJLoader();
    loader.load(objFile, function (object){
        object.traverse (function (child){
            if (child instanceof THREE.Mesh) {
                child.material = OBJMaterial;
            }
        });
        object.position.y = 0.1;
      // add this code
        object.is_ob = true;

        scene.add(object);
    });     
}

function addEntity(object) {
    loadOBJFile(object.name);
}

And then then you delete your object try this code:

function removeEntity(object){
    var obj, i;
            for ( i = scene.children.length - 1; i >= 0 ; i -- ) {
                obj = scene.children[ i ];
                if ( obj.is_ob) {
                    scene.remove(obj);

                }
            }
}

Try that and tell me if that works, it seems that three js doesn't recognize the object after added to the scene. But with this trick it works.

juanpscotto
  • 990
  • 1
  • 13
  • 32
  • 1
    I'm not sure what this is supposed to do, you just add a random flag. – MJB Dec 24 '13 at 01:37
  • You can use the name attribute that is present in all objects and defaults to ''. It doesn't have to be unique and you may have many classes of objects this way. – kon psych Mar 06 '14 at 02:21
-10

You can use this

function removeEntity(object) {
    var scene = document.querySelectorAll("scene");                               //clear the objects from the scene
    for (var i = 0; i < scene.length; i++) {                                    //loop through to get all object in the scene
    var scene =document.getElementById("scene");                                  
    scene.removeChild(scene.childNodes[0]);                                        //remove all specified objects
  }   
Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
Dabby
  • 5
  • 2
  • Ah, this is a pretty good place to get started with three.js https://threejs.org/docs/#manual/en/introduction/Creating-a-scene – taystack Dec 24 '18 at 08:53