0
\$\begingroup\$

What I am trying to do is to create a Rubik's Cube game using LWJGL in Java. I have a lot of it done already, the only problem is the rotating. It just isn't working. I've tried multiple ways of rotating the cube but none of them seem to be working. Here's an image of what I've done so far:
No Rotation
It works fine as you can see. Now if I rotate the front slice, it also works well.
F' Rotation
It seems to be messing up when the rotation is more that one. What I mean by that is when I rotate "F R D", this is front, right, and down. Like so:
F R D Rotation
But the problem occurs when I rotate the front side one more time, the rotation completely messes up:
F R D F
As you can see, the cube rotates the wrong way. I don't know how to fix this.

What I've tried is calling three separate GL11.glRotatef( ... ) functions but that hasn't worked. I've tried storing the rotations in a Matrix4f and then GL11.glMultMatrix( ... ) and that has had the same output. I finally gave up and settled on this code here:

GL11.glPushMatrix();
GL11.glTranslatef(pos.offsetX * 1.1F, pos.offsetY * 1.1F, pos.offsetZ * 1.1F);
GL11.glRotatef(realRotation.x * 90F, 1F, 0F, 0F);
GL11.glRotatef(realRotation.y * 90F, 0F, 1F, 0F);
GL11.glRotatef(realRotation.z * 90F, 0F, 0F, 1F);
for (int i = 0; i < 6; i++)
{
    if (quads[i] != null)
    {
        quads[i].renderQuad();
    }
}
GL11.glPopMatrix();

I am at a loss now. If you need the variables and code up there defined: realRotation is a javax.vecmath.Point3i instance. All it has are the values from 0 to 3. It will never be greater than three, or less that zero. This is also the same reason why I am multiplying the values by 90 to rotate it 90 degrees each time. The quads array is just for rendering, it doesn't matter and has not caused a problem. Now the pos variable is an enum value that I have created to keep track of all the pieces in the game and where they are. Again, probably not the problem, but I don't know. It just might be the problem.

EDIT #1: I still haven't found any answer to the problem. The only other idea that I have gotten is that I can manually set the rotation to what it is actually supposed to be. I assumed that this will be EXTREMELY tedious and inefficient, so I've left that as a last resort. If I'm incorrect about this method, please feel free to let me know. I have a deadline for this project and I haven't gotten any progress beyond this.

EDIT #2: I have had an (I think) epiphany! I believe the problem for this peculiar rotational behavior is the cause of the Gimbal Lock. This is when an object is rotated in an axis, and the other axis pretty much becomes useless because of the previous rotation. This is explained very well with this GIF:
Gimbal Lock with a Plane
As you can see, once the plane is pitched to a 90 degree angle, roll and yaw are seemingly the same because of the pitch's rotation. On the wikipedia page, it is stated that a solution to this would be to use Quaternions, or so. I have not worked with Quaternions too much and still would like to hear if anyone has anything to say about this. I'm just guessing that this is the problem.

EDIT #3: After hours of studying and looking for an explanation as to what is going on. I have come up with nothing. I have still not solved the problem and I think I might have even made it worse. The code I have to render each cube is as follows:

GL11.glPushMatrix();
GL11.glTranslatef(pos.offsetX * 1.1F, pos.offsetY * 1.1F, pos.offsetZ * 1.1F);
//GL11.glRotatef(realRotation.z * 90F, 0F, 0F, 1F); // Roll
//GL11.glRotatef(realRotation.x * 90F, 1F, 0F, 0F); // Pitch
//GL11.glRotatef(realRotation.y * 90F, 0F, 1F, 0F); // Yaw

Quaternion roll = new Quaternion();
roll.setFromAxisAngle(new Vector4f(0F, 0F, -1F, rotation.z * 90F * DEG_TO_RAD));
roll.normalise();

Quaternion pitch = new Quaternion();
pitch.setFromAxisAngle(new Vector4f(-1F, 0F, 0F, rotation.x * 90F * DEG_TO_RAD));
pitch.normalise();

Quaternion yaw = new Quaternion();
yaw.setFromAxisAngle(new Vector4f(0F, -1F, 0F, rotation.y * 90F * DEG_TO_RAD));
yaw.normalise();

Quaternion change = Quaternion.mul(Quaternion.mul(roll, pitch, null), yaw, null);

FloatBuffer buff = BufferUtils.createFloatBuffer(16);
toFloatBuffer(change, buff);
GL11.glMultMatrix(buff);

for (int i = 0; i < 6; i++)
{
    if (quads[i] != null)
    {
        quads[i].renderQuad();
    }
}
GL11.glPopMatrix();

This is the toFloatBuffer method: dest.clear();

float x = quat.x;
float y = quat.y;
float z = quat.z;
float w = quat.w;

float x2 = x * x;
float y2 = y * y;
float z2 = z * z;
float xy = x * y;
float xz = x * z;
float yz = y * z;
float wx = w * x;
float wy = w * y;
float wz = w * z;

dest.put(1.0f - 2.0f * (y2 + z2));
dest.put(2.0f * (xy - wz));
dest.put(2.0f * (xz + wy));
dest.put(0.0f);
dest.put(2.0f * (xy + wz));
dest.put(1.0f - 2.0f * (x2 + z2));
dest.put(2.0f * (yz - wx));
dest.put(0.0f);
dest.put(2.0f * (xz - wy));
dest.put(2.0f * (yz + wx));
dest.put(1.0f - 2.0f * (x2 + y2));
dest.put(0.0f);
dest.put(0.0f);
dest.put(0.0f);
dest.put(0.0f);
dest.put(1.0f);

dest.rewind();

Again, this isn't working and I (once again) am absolutely clueless. The page I am looking at to figure out this stuff is this page. Don't even know if it's right. I got it from my Physics teacher who is also working on Quaternions. Once again, I have a deadline for this project and I would love it if I can get this done by then. I am willing to accept anything anyone says, I really need a lead on this. I keep hitting walls after walls.

\$\endgroup\$

migrated from stackoverflow.com Nov 24 '15 at 15:57

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ One word: Quaternions. Use them to store rotations. Convert them to matrixes when you need to transform vertices. \$\endgroup\$ – HolyBlackCat Nov 23 '15 at 14:03
  • \$\begingroup\$ If you want to move it to gamedev I'd suggest you edit the post already there - take time to write it as though you were writing the question afresh and merge the "edit:" style comments so that it flows naturally before flagging it there for reopening. \$\endgroup\$ – Flexo Nov 24 '15 at 15:08
  • \$\begingroup\$ Also please make sure that the code you show is the minimum complete amount needed to reproduce this problem. \$\endgroup\$ – Flexo Nov 24 '15 at 15:09
1
\$\begingroup\$

Quaternions can't suffer from gimbal lock; they are functionally identical to full matrix multiplations. However, the angles you use to generate them can be wrong.

The problem is that you are generating your rotations around fixed angles, in your case Cartesian unit vectors. You can't do that; your rotation axes must rotate along with everything else. Yours are static.

Whether you're doing this:

GL11.glRotatef(realRotation.x * 90F, 1F, 0F, 0F);
GL11.glRotatef(realRotation.y * 90F, 0F, 1F, 0F);
GL11.glRotatef(realRotation.z * 90F, 0F, 0F, 1F);

...or this:

roll.setFromAxisAngle(  new Vector4f( 0F, 0F,-1F, rotation.z * 90F * DEG_TO_RAD));
pitch.setFromAxisAngle( new Vector4f(-1F, 0F, 0F, rotation.x * 90F * DEG_TO_RAD));
yaw.setFromAxisAngle(   new Vector4f( 0F,-1F, 0F, rotation.y * 90F * DEG_TO_RAD));

Both are using fixed unit vectors.


You need relative axes in order to calculate the changes in rotation. roll must be calculated around a relative-forward vector, pitch must be rotated around relative-right, and yaw must be calculated around relative-up.

And on top of that, changing roll changes relative-up and relative-right, pitch changes relative-forward and relative-up, and yaw changes relative-forward and relative-right.


Alternately, you could simply store your current orientation as a full transform matrix. Then the number of manual operations would be reduced greatly. You would still need to calculate the additional rotations on the transformed (relative) axes, but you could use that same matrix to transform them.

\$\endgroup\$
  • \$\begingroup\$ I'm sorry but I'm really not following what you're saying, perhaps show some code or point me to a larger, more in-depth source? \$\endgroup\$ – Hashim Kayani Nov 24 '15 at 2:00
  • \$\begingroup\$ What don't you understand? \$\endgroup\$ – Seth Battin Nov 24 '15 at 2:02
  • \$\begingroup\$ What do you mean by relative axis? How would this affect anything in LWJGL? Also, the last paragraph, you say something about using Matrices instead, I would also like to know about that as well. I'm sorry if I'm sounding arrogant or un-willing. \$\endgroup\$ – Hashim Kayani Nov 24 '15 at 2:32
  • \$\begingroup\$ It has nothing to do with libgdx. When I say relative axes, I mean the unit vectors are being rotated away from their original positions. Say you "yaw" the model 90 degrees to the left. From that orientation, "roll" no longer happens around the unit-forward vector, because that's not the correct direction relative to the model. The model is now pointing to the left, and a roll would have to happen around a different vector. The axes of rotation need to be rotated, as well as the model itself. \$\endgroup\$ – Seth Battin Nov 24 '15 at 3:57
  • \$\begingroup\$ Ahhh, Thank makes much more sense now. I'll do that with Matrices and transform the vectors accordingly. Thank you. One more question, when I rotate, is there a specific order I should follow? Like first Yaw, then Pitch, then Roll, or does it not matter? \$\endgroup\$ – Hashim Kayani Nov 24 '15 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.