Warm tip: This article is reproduced from serverfault.com, please click

Three.js: Proper way to add and remove child objects using THREE.SceneUtils.attach/detach functions

发布于 2014-04-30 10:24:49

Using three.js, and adapting instructions from West Langley's post provided here: Three.js: Adding and Removing Children of Rotated Objects, I set up a WebGL scene to which five cube meshes are added. Initially, all objects are children of the scene, then, I attach them to the fifth "parentCube" cube and translate it 100 units along the Y-Axis thereby translating the other four cubes and subsequently detach them.

After that, I want to independently translate the "parentCube" cube (previously the parent of the four cubes) back to the origin, however, when I perform that translation, the other four cube meshes also translate with the former parent cube mesh, even when I detached them.

This may be a very basic question, but how can I independently translate "parentCube" without affecting the position of the other cubes considering all of the above details? Where am I going wrong with the detachment? Any help would be appreciated. Thank you :)

Here's the code sample which I use to perform all of the above:

        //Create parentCube mesh
        var parentCube = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 10, 10, 10), new THREE.MeshBasicMaterial({ color: 0xa1ff11, wireframe: true }));
        scene.add(parentCube);

        //...create materials for the child cubes....

        //create child cube mesh
        for(var i = 0; i < 4; i++)
            cubeMesh[i] = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 30, 30, 30), materials[i]);

        //--> Set child cube world positions before the attachment to parentCube mesh
        cubeMesh[0].position.set((100 / 2),(100 / 2),(100 / 2));
        cubeMesh[1].position.set(-(100 / 2),(100 / 2),(100 / 2));
        cubeMesh[2].position.set(-(100 / 2),-(100 / 2),(100 / 2));
        cubeMesh[3].position.set((100 / 2),-(100 / 2),(100 / 2));

        //Add child cubes to the scene
        for(var i = 0; i < cubeMesh.length; i++)
            scene.add(cubeMesh[i]);

        //attach child cubeMesh[i] to parentCube mesh
        for(var i = 0; i < 4; i++)
            THREE.SceneUtils.attach(cubeMesh[i], scene, parentCube);

        //--> Set positions of child elements after attachment to parentCube
        cubeMesh[0].position.set((100 / 2),(100 / 2),(100 / 2));
        cubeMesh[1].position.set(-(100 / 2),(100 / 2),(100 / 2));
        cubeMesh[2].position.set(-(100 / 2),(100 / 2),-(100 / 2));
        cubeMesh[3].position.set((100 / 2),(100 / 2),-(100 / 2));

        //translate parentCube
        parentCube.position.set(0,150,0);
        parentCube.updateMatrixWorld();

        //Attempt to detach child objects from parentCube
        //And make them children of the scene
        for(var i = 0; i < 4; i++)
        {
            cubeMesh[i].updateMatrixWorld();
            THREE.SceneUtils.detach(cubeMesh[i], parentCube, scene);
        }

        //Attempt to translate parentCube back to origin
        parentCube.position.set(0,0,0);
    }
Questioner
The_Obfuscator
Viewed
0
vals 2014-05-04 16:57:37

your line of code

   THREE.SceneUtils.detach(cubeMesh[i], scene, parentCube);

should be

   THREE.SceneUtils.detach(cubeMesh[i], parentCube, scene);

I have done a demo with your example, and what I believe should be te correct approach.

HTML

<body>
    <button onclick="attachChild();">attach</button>
    <button onclick="detachChild();">dettach</button>
</body>

JavaScript

var camera, scene, renderer;
var geometry, material1, material2;
var parentCube;
var cubeMesh = [];
var cameraControls;
var attached = true;

window.onload = function() {
    init();
    animate();
}

function init() {

    camera = new THREE.PerspectiveCamera(75, 2, 1, 10000);
    camera.position.z = 400;
    camera.position.y = 100;

    scene = new THREE.Scene();

    geometry = new THREE.BoxGeometry(200, 200, 200);
    material1 = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true  });

    //Create parentCube mesh
    parentCube = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 1, 1, 1), material1);
    scene.add(parentCube);

    //...create materials for the child cubes....
    material2 = new THREE.MeshBasicMaterial({        color: 0x00ff00   });
    //create child cube mesh
    cubeMesh[0] = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 1, 1, 1), material2);
    cubeMesh[1] = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 1, 1, 1), material2);
    cubeMesh[2] = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 1, 1, 1), material2);
    cubeMesh[3] = new THREE.Mesh(new THREE.CubeGeometry(100, 100, 100, 1, 1, 1), material2);

        //--> Set child cube world positions before the attachment to parentCube mesh
    cubeMesh[0].position.set(100,100,0);
    cubeMesh[1].position.set(-100,100,0);
    cubeMesh[2].position.set(-100,-100,0);
    cubeMesh[3].position.set(100,-100,0);

        //Add child cubes to the scene
    for (var i = 0; i < 4; i++)
        parentCube.add(cubeMesh[i]);


   //translate parentCube
   parentCube.position.set(0,50,0);


  renderer = new THREE.CanvasRenderer();
    renderer.setSize(600, 300);

    document.body.appendChild(renderer.domElement);

        // CONTROLS
    cameraControls = new THREE.OrbitControls(camera);
    cameraControls.addEventListener( 'change', render );


}

function animate() {
    cameraControls.update();
    requestAnimationFrame(animate);
    parentCube.rotation.z += 0.01;
    render ();
}

function render () {
    renderer.render(scene, camera);

}

function attachChild () {
    if (attached) {
        alert ("already attached");
    } else {
        for (var i = 0; i < 4; i++)
            THREE.SceneUtils.attach(cubeMesh[i], scene, parentCube);
        attached = true;
    }
}

function detachChild () {
    if ( ! attached) {
        alert ("not attached");
    } else {
        for (var i = 0; i < 4; i++)
            THREE.SceneUtils.detach(cubeMesh[i], parentCube, scene);
        attached = false;
    }
}

Notice specially that I add the children directly to the parent (not the scene), and that I consider them attached from the beginning. After that, detaching and reattaching them works as expected

How could this be used for a Rubik's cube simulation ?

I would just create all the cubes added to the scene.

Then, to perform a move, you have to

  1. identify the pivot cube (the one at the center of the face)
  2. identify the surrounding cubes
  3. attach those to the pivot
  4. rotate the pivot
  5. detach the cubes