D4NieLDev
D4NieLDev

Reputation: 300

Group children position is not saved after removing from group and adding to scene individually

I am trying to make a rubiks cube simulator with three.js so my goal now is to rotate the bottom and then rotate the middle layer. After I rotate the bottom layer and switch to the middle layer the bottom layer goes back in (the position after the rotation is not saved) and the middle layer snappes instantly.

This is my code:

import * as THREE from 'three'
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls';


const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas.webgl")});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const geometry = new THREE.BoxGeometry(1, 1, 1);
const materials = [
    new THREE.MeshBasicMaterial({color: 'white'}),
    new THREE.MeshBasicMaterial({color: 'yellow'}),
    new THREE.MeshBasicMaterial({color: 'red'}),
    new THREE.MeshBasicMaterial({color: 'darkorange'}),
    new THREE.MeshBasicMaterial({color: 'blue'}),
    new THREE.MeshBasicMaterial({color: 'green'}),
];
for(let i = 0; i < geometry.groups.length; i++)
    geometry.groups[i].materialIndex = i;

const cube = [];

for(let y = -1; y <= 1; y++){
    for(let x = -1; x <= 1; x++){
        for(let z = -1; z <= 1; z++){
            const mesh = new THREE.Mesh(geometry, materials);
            mesh.position.x = x;
            mesh.position.y = y;
            mesh.position.z = z;
            cube.push(mesh);
            scene.add(mesh);
        }
    }
}

const controls = new TrackballControls( camera, renderer.domElement );
controls.noPan = true;
controls.noZoom = true;
controls.rotateSpeed = 3;

camera.position.z = 7.5;
controls.update();

const face = new THREE.Group();
// get the bottom layer
for(let i = 0; i < 9; i++){
    scene.remove(cube[i]);
    face.add(cube[i]);
}

scene.add(face);
let moveCount = 0;
function animate() {
    requestAnimationFrame( animate );
    
    controls.update();

    if(moveCount < 1){
        if(face.rotation.y < THREE.MathUtils.degToRad(90)){
            face.rotation.y += THREE.MathUtils.degToRad(2);
        }
        else{
            changeFace();
            moveCount++;
        }
    }

    renderer.render( scene, camera );
}
function changeFace() {
    scene.add(...face.children);
    face.remove(...face.children);
    
    for(let i = 9; i < 18; i++){
        scene.remove(cube[i]);
        face.add(cube[i]);
    }
};

animate();

A gif to illustrate: enter image description here

Upvotes: 2

Views: 387

Answers (1)

M -
M -

Reputation: 28492

When you rotate a Mesh's parent, the mesh's rotation doesn't change. Only the parent's rotation changes. So when you take the mesh out of that parent, you're removing any transformations that it inherited.

You can use object.getWorldQuaternion() to store the world-space rotations into a quaternion, then apply them to the child mesh:

let tempQuaternion = new THREE.Quaternion();

for(let i = 9; i < 18; i++){
    // Read world rotations before removal
    cube[i].getWorldQuaternion(tempQuaternion);

    // Take the cube out of the group, place directly onto scene
    scene.add(cube[i]);

    // Now we re-apply the world rotations to the cube
    cube[i].quaternion.copy(tempQuaternion);
}

Keep in mind that a mesh can only have one parent at a time. So when adding it to a group, there's no need to remove it from the scene, and vice-versa.

// This line adds all children to scene, 
// and automatically removes each one from face when doing so
scene.add(...face.children);

// This line doesn't do anything because all children have already been removed
face.remove(...face.children);

Upvotes: 2

Related Questions