ThreeJS: Eliminar objeto de la escena

9 minutos de lectura

avatar de usuario de harman052
Harman052

Estoy usando ThreeJS para desarrollar una aplicación web que muestra una lista de entidades, cada una con el botón correspondiente “Ver” y “Ocultar”; p.ej nombre de la entidad Ver Ocultar. Cuando el usuario hace clic Vista botón, se llama a la siguiente función y la entidad se dibuja en la pantalla con éxito.

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);
}

Y al hacer clic Esconder botón, se llama a la siguiente función:

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

El problema es que la entidad no se elimina de la pantalla una vez cargada cuando Esconder se hace clic en el botón. ¿Qué puedo hacer para hacer Esconder botón para trabajar?

Hice un pequeño experimento. yo añadí scene.remove(object.name); justo después de scene.add(object); dentro addEntity función y como resultado, cuando se hace clic en el botón “Ver”, no se dibuja ninguna entidad (como se esperaba), lo que significa que scene.remove(object.name); funcionó bien dentro addEntity. Pero aún no puedo descubrir cómo usarlo en removeEntity (objeto).

Además, verifiqué el contenido de scene.children y muestra: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Código completo: http://devplace.in/~harman/model_display1.php.html

Por favor pregunte, si necesita más detalles. Probé con rev-59-dev y rev-60 de ThreeJS.

Gracias. 🙂

  • ¿Quizás olvidaste actualizar la escena (renderizarla)? ¿Existe una actualización de procesamiento (requestAnimationFrame loop o renderer.render(…))?

    – Antón Krutikov

    21 de agosto de 2013 a las 12:46

  • Lo intenté llamando a animate() (update() y render() se llaman dentro de animate()) después scene.remove(object.name); en removeEntity(object), pero sin cambios. 🙁

    – harman052

    21 de agosto de 2013 a las 15:39

Avatar de usuario de Darryl_Lehmann
Darryl_Lehmann

Creo que ver su uso para el código addEntity y removeEntity sería útil, pero mi primer pensamiento es ¿realmente está configurando object.name? Pruebe en su cargador justo antes de scene.add(object); algo como esto:

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

Lo que podría estar sucediendo es que el “nombre” predeterminado para un Object3D es “”, por lo que cuando llama a su función removeEntity falla debido a que el nombre de los objetos de la escena es “”

Además, me doy cuenta de que pasas object.name a tu cargador. ¿Es aquí donde está almacenando la URL del recurso? Si es así, recomendaría usar el método .userData incorporado de Object3D para almacenar esa información y mantener el campo de nombre para identificar la escena.

Editar: respuesta al código recién agregado

Lo primero que debe tener en cuenta es que no es una gran idea tener “/” en el nombre de su objeto, parece funcionar bien, pero nunca se sabe si algún algoritmo decidirá escapar de esa cadena y romper su proyecto.

El segundo elemento es ahora que he visto su código, es realmente sencillo lo que está pasando. Su función de eliminación está intentando eliminar por nombre, necesita un Object3D para eliminar. Prueba esto:

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

Aquí ves que busco tu Object3D en los Tres.js Scene pasando la etiqueta de su objeto name atributo.

  • En realidad, el argumento “objeto” pasado a addEntity y removeEntity es una palabra clave predeterminada de JavaScript que elige el “nombre” de la entidad en cuyo botón “Ver” correspondiente se hizo clic (addEntity y removeEntity son funciones de llamada al hacer clic), por lo que object.name contiene en realidad el nombre de la entidad. scene.add(object) funciona bien ya que puedo ver los objetos dibujados en la pantalla. El problema es solo con scene.remove(object.name) ya que no muestra ningún error ni ningún resultado.

    – harman052

    21 de agosto de 2013 a las 15:51

  • Ya veo, no estoy seguro del problema exacto, pero la función scene.remove en Three.js está buscando un Object3D.name que puede o no ser equivalente. Por desgracia, creo que, para mayor claridad, es posible que se necesite un poco más de código para ver la construcción del objeto y el uso de su código de agregar eliminar objeto. De lo contrario, haría console.log the Mesh.name en su función de agregar y quitar la función y ver qué resultados obtiene.

    – Darryl_Lehmann

    21 de agosto de 2013 a las 16:20

  • Acabo de editar la pregunta agregando más detalles y mencioné el enlace para completar el código. Por favor, compruebe. Gracias.

    – harman052

    22 de agosto de 2013 a las 5:42

  • Lo intenté como me sugeriste, no pasó nada. También comprobé poniendo scene.remove( scene.getObjectByName(object.name) ); en alerta () y obtuvo “indefinido”. Como no sé mucho sobre ThreeJS, descubrí que los objetos tienen una historia en scene.children. Cada objeto que agregamos a la escena se agrega al final de scene.children. Entonces escribí el siguiente código: var lastIndex = scene.children.length - 1; endElement = scene.children[lastIndex]; scene.remove(endElement); Esto elimina el último elemento que dibujé. Ahora, ¿pueden ayudarme a obtener la ID del elemento de scene.children con referencia al nombre del objeto?

    – harman052

    22 de agosto de 2013 a las 16:28

  • +1 para las sugerencias y la información sobre el atributo de nombre, lo que es importante es que debe asegurarse de que cada objeto en la escena tenga un nombre único al usar este método.

    – kon psicología

    27/03/2014 a las 21:43

Avatar de usuario de MJB
MJB

clearScene: function() {
    var objsToRemove = _.rest(scene.children, 1);
    _.each(objsToRemove, function( object ) {
          scene.remove(object);
    });
},

esto usa undescore.js para iterar sobre todos los niños (excepto el primero) en una escena (es parte del código que uso para borrar una escena). solo asegúrate de que prestar la escena al menos una vez después borrando, porque de lo contrario el lienzo no cambia! No hay necesidad de una bandera obj “especial” ni nada como esto.

Además, no elimina el objeto por su nombre, solo por el objeto en sí, por lo que llamar

scene.remove(object); 

en lugar de scene.remove(object.name);
puede ser suficiente

PD: _.each es una función de guión bajo.js

  • ¿Qué tiene de malo un for normal (let x of foo)? o un bucle for regular?

    – Shinzou

    21 de febrero de 2017 a las 12:53

  • por supuesto, puede usar un bucle for simple, no hay nada de malo en eso, simplemente he usado _.each, eso es todo.

    – MJB

    21 de febrero de 2017 a las 22:09

  • Solución perfectamente válida, pero el guión bajo es una dependencia adicional que abarrota el código base a menos que ya lo esté usando.

    – DOOMDUDEMX

    15 de julio de 2019 a las 8:55

  • Oh, estos tipos adictos a la biblioteca… Parecen olvidar que todas las bibliotecas JS no hacen nada más que usar… ¡JS simple, antiguo y nativo! 😉

    – Pedro Ferreira

    16 mayo 2020 a las 17:35

  • ¿Sabes que el estado de javascript era completamente diferente en 2013? Además de eso, las bibliotecas resuelven muchos problemas que ocurren regularmente y, por ejemplo, lodash, están bien probadas. No hay razón para implementar la lógica usted mismo. Además, si ya tiene una biblioteca incluida por la razón X, no hay razón para no reutilizar los módulos existentes para resolver otros problemas. Sin embargo, si importa jquery para acceder a un nodo dom usando una identificación, lo está haciendo mal.

    – MJB

    20 de mayo de 2020 a las 9:43


Avatar de usuario de Ibrahim W.
Ibrahim W.

Llegué tarde, pero después de leer las respuestas, es necesario aclarar más.

La función de eliminación que escribiste

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
}

Una buena práctica para eliminar objetos 3D de las escenas de Three.js

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;
}

  • por favor reemplace “objeto” a “objeto3D”

    – Andrej

    15 de diciembre de 2021 a las 2:58


  • Consulte también Atravesar a todos los elementos secundarios y llame a disponer en su geometría, material y textura.

    – Andrej

    15 de diciembre de 2021 a las 3:12

Si su elemento no está directamente en su escena, vuelva a Padre para eliminarlo

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

Avatar de usuario de Роман Зыков
Роман Зыков

ESTO FUNCIONA MUY BIEN. Lo probé, así que, ESTABLEZCA EL NOMBRE para cada objeto.

dar el nombre al objeto en el momento de la creación

    mesh.name="nameMeshObject";

y use esto si tiene que eliminar un objeto

    delete3DOBJ('nameMeshObject');



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

abrir una nueva escena, agregar objeto
abrir nueva escena, agregar objeto

eliminar un objeto y crear uno nuevo
eliminar objeto y crear nuevo

Avatar de usuario de Alex Melluzzo
Alex Melluzzo

Empecé a guardar esto como una función y lo llamé según sea necesario para cualquier reacción que lo requiera:

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

Ahora puede llamar a Remove(); función cuando corresponda.

Avatar de usuario de Sylvain Lugez
Sylvain Luguez

Cuando usas: scene.remove(objeto); ¡El objeto se elimina de la escena, pero la colisión con él todavía está habilitada!

Para eliminar también la colisión con el objeto, puede usar eso (para una matriz): objectsArray.splice(i, 1);

Ejemplo :

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); 

}

¿Ha sido útil esta solución?