1

I am trying to build a working Rubikscube with ThreeJS. Now I have a problem with rotating the sides. At the moment, I am adding the smaller cubes to the Rubikscube and create it like this:

const rubikscube = new THREE.Group();
for (var i = 0; i < 27; i++) {
  // 3x3x3 (0,0,0) is on the top left corner
  var cube = createPartOfCube(i, scene);
  cube.name = i;
  cube.position.x = (i % 3) * gap;
  cube.position.y = Math.floor(i / 9) * gap;
  cube.position.z = (Math.floor(i / 3) % 3) * gap;
  rubikscube.add(cube);
}
scene.add(rubikscube);

And this all works fine until I try to rotate, e.g. the right side. I select the pieces on the right side and add them to their own group. Now, when I try to rotate the group, it's rotating around the x axes. Here is the method I want to use for moving a side (just ignore the bool and eval part, its just for getting the right pieces):

  function move(direction) {
    var bool = moves[direction];

    var pivot = new THREE.Group();
    pivot.updateMatrixWorld();

    for (var i = 0; i < 27; i++) {
      if (eval(format(bool, i))) {
        pivot.add(scene.getObjectByName(i));
      }
    }
    scene.add(pivot);
    animateMove(pivot);
  }

And animating it:

  function animateMove(pivot) {
    requestAnimationFrame(() => {
      animateMove(pivot);
    });
    // missing part
    renderer.render(scene, camera);
  }

What I've tried:

I have tried different methods, to rotate it the right way but nothing worked and moving the cube is also not the right answer.

One thing I tried was on this thread, but when I tried to rotate it this way, the side just moved, so it's still rotating around the x-axes.

Minimal reproducible example

function main() {
  const scene = new THREE.Scene();
  const renderer = new THREE.WebGLRenderer();
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  const gap = 1.1;

  scene.add(new THREE.AxesHelper(100));
  scene.background = new THREE.Color('white');

  camera.position.z = 5;

  renderer.setSize(window.innerWidth, window.innerHeight);

  const rubikscube = new THREE.Group();
  for (var i = 0; i < 27; i++) {
    // 3x3x3 (0,0,0) is on the top left corner
    var cube = createPartOfCube(i, scene);
    cube.name = i;
    cube.position.x = (i % 3) * gap;
    cube.position.y = Math.floor(i / 9) * gap;
    cube.position.z = (Math.floor(i / 3) % 3) * gap;
    rubikscube.add(cube);
  }

  scene.add(rubikscube);
  animate();
}

function animate() {
  requestAnimationFrame(animate);
  const controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.target.set(1, 1, 1);
  controls.update();

  var movingIds = [2, 5, 8, 11, 14, 17, 20, 23, 26]; // IDs of the pieces needed to rotate
  var group = new THREE.Group();
  movingIds.forEach((i) => {
    group.add(scene.getObjectByName(i));
  });

  scene.add(group);
  animateMove(group);
}

function animateMove(group) {
  requestAnimationFrame(() => {
    animateMove(group);
  });
  group.rotation.x = 2; // Wrong part I need help with
  renderer.render(scene, camera);
}

function createPartOfCube() {
  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material = new THREE.MeshBasicMaterial({ color: 'black' });
  var cube = new THREE.Mesh(geometry, material);
  return cube;
}
main();

I hope that someone understands my issue and helps me to solve it. Thanks!

Frederick
  • 450
  • 4
  • 22
  • Not sure how the group affects things, but, essentially, first find where the center currently is. Translate by that amount in the opposite direction (T1) - this will place the center at (0, 0, 0), Apply the rotation (R), then translate back to position (T2). Concatenate (multiply) these matrices to get the desired transform: T2 * R * T1. In that order. Or, alternatively, create the cube so that its center is already at (0, 0, 0), rotate, then apply any other transforms you want *after* that. – Filip Milovanović Jan 27 '22 at 19:28
  • "Something interesting I noticed was, that the position of the group I created was always on (0, 0, 0) even when it, moved to another coordinate to rotate" - did you move *the group itself*, or the things that are inside it? – Filip Milovanović Jan 27 '22 at 19:31
  • No, I moved the cube to the center, as you mentioned. Then I made the group for the rotating pieces and I rotated them. But instead of only rotating, they rotated and were moved to where the side previously was, but the position didn't change (says ThreeJS) – Frederick Jan 27 '22 at 19:33
  • Okay, never mind I fixed the moving part, I only changed the position of the parent, not the child but wanted to rotate the child. But its still rotating around the x axes, I will try to do what you mentioned – Frederick Jan 27 '22 at 19:36

1 Answers1

1

All rotations take place around the object's point of origin. So if your object's position is at (0, 0, 0), then that's going to be the pivot point.

I recommend you nest your cube pieces into a THREE.Group() each, that way you can keep the pivot point constant at (0, 0, 0) for all 27 pieces. Then you can displace each piece inside its group to the desired position:

const geom = new THREE.BoxGeometry(1, 1, 1);
const Piece = new THREE.Mesh(geom, mat);
const Group = new THREE.Group();

// Nest Piece inside of its Group
Group.add(Piece);

// Parent Group position is at (0, 0, 0)
// Inner piece position is at (-1, -1, -1)
Piece.position.set(-1, -1, -1);

// Now we can apply rotations to the parent group 
// and the child will maintain its distance from the center
Group.rotation.set(Math.PI / 2, 0, 0);

You'll want to repeat this for all values of [-1, 0, +1] for X,Y,Z. That's 3^3 = 27 pieces.

M -
  • 26,908
  • 11
  • 49
  • 81
  • You can see this principle in action [here](https://playcanv.as/p/V9B7k46n/). – M - Jan 27 '22 at 19:43
  • Thanks, but when I am using rotation.set(Math.PI / 2); The object just disappears. And I am not sure if I haven't tried that. I will provide a little more detail about my functions in a minute – Frederick Jan 27 '22 at 19:50
  • @Frederick Whoops, that's my mistake. I was rushing my answer, [rotation.set() needs `x, y, z` values](https://threejs.org/docs/?q=Group#api/en/math/Euler.set), not just one. Fixed. – M - Jan 27 '22 at 19:52
  • Thanks, but its still the same issue, its rotated but around the x axes. I am not sure if I did it wrong, but I centred the cube and applied the rotation to the group. – Frederick Jan 27 '22 at 19:58
  • @Frederick I'd need to see a working demo then. It might be how your geometry is being generated. If your geometry has any translations then its pivot point will be hard to predict. Or it could be anything else. Hard to tell without a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – M - Jan 27 '22 at 20:00
  • I have added it, does it help? – Frederick Jan 27 '22 at 20:25
  • @Frederick Yes, the first thing I notice is that if you `console.log()` your cube positions, you'll see that they iterate between `[0, 1, 2]`, not between `[-1, 0, 1]` like I suggested. Like I said, all rotations will spin around the origin, in this case `(0, 0, 0)`. That's why it's important to have half the cube on one side of the pivot point. Right now you're keeping the entire cube in the positive side of each axis. – M - Jan 27 '22 at 20:29
  • Okay, thanks that works. My issue was, that I thought I already tried doint it, but I did it like described here https://stackoverflow.com/questions/28848863/threejs-how-to-rotate-around-objects-own-center-instead-of-world-center?noredirect=1&lq=1 with another box and that didnt work. But just calculating it myself did it. Thank you – Frederick Jan 27 '22 at 20:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/241493/discussion-between-frederick-and-marquizzo). – Frederick Jan 28 '22 at 15:51