2
\$\begingroup\$

I am making a Rubik's cube program. That will simulate a Rubik's cube. I am having problems with gimbal lock. I need to know how to avoid it. To my understanding I can either multiply the x, y, z rotation matrices in a specific order to acquire the rotation I am trying to get. Or I can use a quaternion which is like rotating around a single user defined axis instead of the axis x, y, or z. I can't really understand visually how I can get the resulting axis from two other axis. (x, y, z) How can I get the resulting axis for my quaternion from my Euler rotations? Will it just result in gimbal lock again?

One of the big problems is. I don't know what rotations are going to be made. I need to make it general purpose.

Edit: Little bit more documentation of my problem:

My Euler matrix multiplication looks like this

    rotate = XMMatrixRotationX(content.mesh[meshToRotate[i]].rotation.x) * XMMatrixRotationY(content.mesh[meshToRotate[i]].rotation.y) * XMMatrixRotationZ(radians);
    content.mesh[meshToRotate[i]].rotation.z = radians;

As a gimbal lock would suggest the last axis in the rotation gets the problem. When you attempt to rotate the front or back face you get problems.

enter image description here

\$\endgroup\$
8
  • 2
    \$\begingroup\$ Quaternions. Enjoy trying to get your puny meat brain to understand them! (Mine can't) \$\endgroup\$ Commented Jan 15, 2016 at 21:37
  • 1
    \$\begingroup\$ @Draco18s These questions helped me understand them: What is a quaternion? and Why do people use quaternions? and How can you visualize a quaternion? \$\endgroup\$
    – Anko
    Commented Jan 15, 2016 at 21:44
  • \$\begingroup\$ @Anko Good stuff, I'll have to take a look sometime. Generally though I can manage through euler (Unity3D handles things internally as quaternions, so as long as I let it recalc the eulers, gimbal lock is prevented). \$\endgroup\$ Commented Jan 15, 2016 at 21:46
  • \$\begingroup\$ So I guess a better statement for my question is, I need to not somehow unconcatenate one of the axis' after a 3rd axis is used on the Rubik's cube \$\endgroup\$ Commented Jan 15, 2016 at 22:02
  • 1
    \$\begingroup\$ @Anko There's a special case here in the sense that this is a square tile puzzle and there's a trick to avoid gimbal locks that does not apply to other scenarios. \$\endgroup\$ Commented Jan 16, 2016 at 7:19

1 Answer 1

3
\$\begingroup\$

In the case of a puzzle like this you can animate the rotation then reset the model back to its original position but with the cube's stickers rotated.

Look at it like cheating with a real puzzle by moving the stickers.

You'll have to make it possible to change the stickers' colors. This can be done with material colors or a dynamic texture.

And this way you can reduce it to 2 animations: center 90' rotation & end 90' rotation.

You flip/rotate the cube and stickers to animate the opposite end or on a different axis, reverse the animation to turn in the other direction.

The model (Mesh) always comes back to its original position after animating the 90 degree rotation, it's the stickers that change places. This way we completely avoid gimbal locks.


Another way is to use rotation matrices.

Each cube cells (the 3x3x3 elements of the puzzle) have 6 faces, up to 3 of them with stickers and their own rotation matrix. Using the face normal multiplied by the rotation matrix of the cell you can figure out which sticker points where as only 1 axis in the normal will ever have a non-zero value +/- 1.0 in X, Y or Z.

And there will (should) only ever be 9 stickers pointing to the same direction.

You can use the cell's positions to figure out which ones to rotate.

Since the cube cells will only ever be rotated to 0, 90, 180, or 270 at the end of the animation we can fix the cumulative rotation errors by doing:

if (matrix[r][c] > 0.5) {

    matrix[r][c] = 1; 

} else if (matrix[r][c] < -0.5) {

    matrix[r][c] = -1; 

} else {

    matrix[r][c] = 0;

}

For each cell (r=row, c=column) to eradicate any rounding errors once the animation is done.

Usually comparing float values to exact values is a bad idea but in this case we're fixing the matrices to be exactly -1.0, 0.0, or 1.0, without any translation and anything (N) multiplied by those numbers always end up exactly -N, 0 or +N so we can safely compare them.

\$\endgroup\$
8
  • \$\begingroup\$ While this technically answers the question and I'll probably end up using the technique described, I am still itching to know how I could make it work using quaternions. \$\endgroup\$ Commented Jan 16, 2016 at 23:35
  • \$\begingroup\$ You can convert angle-axis to a quaternion as described on euclideanspace.com/maths/geometry/rotations/conversions/… and multiply quaternions together to "accumulate" rotations mathworks.com/help/aeroblks/… but since this is a cube you can use matrices and fix rounding errors once the rotation is done: Any 0-90-180-270 degree rotation matrix will only have 0.0 or 1.0 in each cell so it's easy to fix a matrix like this. \$\endgroup\$ Commented Jan 17, 2016 at 1:23
  • \$\begingroup\$ if (matrix[r][c] > 0.5) matrix[r][c] = 1; else if (matrix[r][c] < -0.5) matrix[r][c] = -1; else matrix[r][c] = 0; \$\endgroup\$ Commented Jan 17, 2016 at 1:24
  • \$\begingroup\$ But how can I convert Euler rotations to axis angle without running into gimbal lock? \$\endgroup\$ Commented Jan 17, 2016 at 1:33
  • 1
    \$\begingroup\$ @AndrewWilson The trick is that you can't just store three axis-angle totals and combine them, because as explained in this answer, rotations don't combine commutatively the way sums do. So instead you'd store something like an orientation matrix or quaternion as your persistent state. You can apply each new twist as an angle-axis rotation of this base state, and then save the result as the new orientation state. \$\endgroup\$
    – DMGregory
    Commented Jan 17, 2016 at 3:36

Not the answer you're looking for? Browse other questions tagged .