Search code examples
javacamerarotationlwjgl

Problems implementing roll in 6DoF


I am using a camera that has a yaw, a pitch, and a roll. When yaw == 0 the camera is looking down the -z axis(yaw == 90 is positive x), when pitch == 270 the camera is looking up(pitch == 0 is looking straight), and when roll == 180 the camera is upside down.

The camera's yaw, pitch, and roll values are never less than zero or greater than 360(when any value approaches 0 or 360 when it passes that amount it is automatically moved to the 'other side').

I have implemented 3DoF and it works quite nicely; however, when I implemented 6DoF, everything appears to work until the roll is around 90 or 270, then strange things occur to the up and right vectors(forward always seems to work because roll rotates around that axis?)

The scene I am rendering is just a bunch of blocks(in minecraft-style chunks) and I am always able to move forward/backward and use the forward vector to target a block so I know that the forward vector is done.

Here is my initGL:

public void initGL() {
    GL11.glEnable(GL11.GL_TEXTURE_2D);
    GL11.glShadeModel(GL11.GL_SMOOTH);
    GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    GL11.glClearDepth(1.0);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LEQUAL);

    GL11.glMatrixMode(GL11.GL_PROJECTION);
    GL11.glLoadIdentity();

    GLU.gluPerspective(fov, ((float) Display.getWidth()) / ((float) Display.getHeight() != 0 ? Display.getHeight() : 1), 0.1f, 100.0f);//fov is 45.0f

    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
}

Here is where I rotate and translate to my camera's view:

public final void lookThrough() {
    GL11.glRotatef(this.roll, 0.0f, 0.0f, 1.0f);
    GL11.glRotatef(this.pitch, 1.0f, 0.0f, 0.0f);
    GL11.glRotatef(this.yaw, 0.0f, 1.0f, 0.0f);
    GL11.glTranslatef(-this.position.x, -this.position.y, -this.position.z);
}

And here are my six degrees of freedom calculations:

public static final double  zeroRad         = Math.toRadians(0);
public static final double  ninetyRad       = Math.toRadians(90);
public static final double  oneEightyRad    = Math.toRadians(180);
public static final double  twoSeventyRad   = Math.toRadians(270);

public static final strictfp void updateLookVectorsIn6DoF(Vector3f yawPitchAndRoll, Vector3f forward, Vector3f up, Vector3f right) {
    final double yaw = Math.toRadians(yawPitchAndRoll.getX());
    final double pitch = Math.toRadians(yawPitchAndRoll.getY());
    final double roll = Math.toRadians(yawPitchAndRoll.getZ());

    final float sinYaw = ((float) Math.sin(yaw));
    final float cosYaw = ((float) Math.cos(yaw));

    final float sinYaw90 = ((float) Math.sin(yaw + ninetyRad));
    //final float sinYaw180 = ((float) Math.sin(yaw + oneEightyRad));
    final float cosYaw270 = ((float) Math.cos(yaw - ninetyRad));

    final float sinRoll = ((float) Math.sin(roll));
    final float cosRoll = ((float) Math.cos(roll));
    //final float sinRoll180 = ((float) Math.sin(roll + oneEightyRad));

    final float cosPitch90 = ((float) Math.cos(pitch + ninetyRad));
    //final float cosPitch270 = ((float) Math.cos(pitch + twoSeventyRad));
    final float sinPitch90 = ((float) Math.sin(pitch + ninetyRad));
    final float sinPitch270 = ((float) Math.sin(pitch - ninetyRad));

    //Forward:(No roll because roll goes around the Z axis and forward movement is in that axis.)
    float x = sinYaw * ((float) Math.cos(pitch));
    float y = -((float) Math.sin(pitch));
    float z = cosYaw * ((float) Math.cos(pitch - oneEightyRad));
    forward.set(x, y, z);

    //cos(90) = 0, cos(180) = -1, cos(270) = 0, cos(0) = 1
    //sin(90) = 1, sin(180) = 0, sin(270) = -1, sin(0) = 0

    //Up: Strange things occur when roll is near 90 or 270 and yaw is near 0 or 180
    x = -(sinYaw * cosPitch90) * cosRoll - (sinRoll * sinYaw90);
    y = -sinPitch270 * cosRoll;
    z = (cosYaw * cosPitch90) * cosRoll + (sinRoll * cosYaw270);
    up.set(x, y, z);
    //Right: Strange things occur when roll is near 90 or 270 and pitch is near 90 or 270
    x = (cosRoll * sinYaw90) - (sinRoll * (sinYaw * cosPitch90));
    y = 0 - (sinRoll * sinPitch90);//This axis works fine
    z = (cosRoll * cosYaw270) + (sinRoll * (sinYaw * cosPitch90));
    right.set(x, y, z);
}

I did find a very similar question here, but it uses matrices and quaternions and I don't want to have to do that unless I absolutely have to(and I was careful to try to multiply roll pitch and yaw in the correct order): LWJGL - Problems implementing 'roll' in a 6DOF Camera using quaternions and a translation matrix


Solution

  • So I finally got the hang of the meaning of cos and sin(but don't ask me to teach it) and was able to get this working!

    Here is the new and improved code:

    public static final double  zeroRad         = Math.toRadians(0);
    public static final double  ninetyRad       = Math.toRadians(90);
    public static final double  oneEightyRad    = Math.toRadians(180);
    public static final double  twoSeventyRad   = Math.toRadians(270);
    
    public static final strictfp void updateLookVectorsIn6DoF(Vector3f yawPitchAndRoll, Vector3f forward, Vector3f up, Vector3f right) {
        final double yaw = Math.toRadians(yawPitchAndRoll.getX());
        final double pitch = Math.toRadians(yawPitchAndRoll.getY());
        final double roll = Math.toRadians(yawPitchAndRoll.getZ());
    
        final float sinYaw = ((float) Math.sin(yaw));
        final float cosYaw = ((float) Math.cos(yaw));
    
        final float sinYaw90 = ((float) Math.sin(yaw + ninetyRad));
        final float sinYaw270 = ((float) Math.sin(yaw - ninetyRad));//+ twoSeventyRad));
        final float cosYaw90 = ((float) Math.cos(yaw + ninetyRad));
        final float cosYaw180 = ((float) Math.cos(yaw + oneEightyRad));
        final float cosYaw270 = ((float) Math.cos(yaw - ninetyRad));//+ twoSeventyRad));
    
        final float sinRoll = ((float) Math.sin(roll));
        final float cosRoll = ((float) Math.cos(roll));
        final float cosRoll180 = ((float) Math.cos(roll + oneEightyRad));
    
        final float cosPitch90 = ((float) Math.cos(pitch + ninetyRad));
        final float sinPitch90 = ((float) Math.sin(pitch + ninetyRad));
        final float sinPitch270 = ((float) Math.sin(pitch - ninetyRad));
    
        //Forward:(No roll because roll goes around the Z axis and forward movement is in that axis.)
        float x = sinYaw * ((float) Math.cos(pitch));
        float y = -((float) Math.sin(pitch));
        float z = cosYaw * ((float) Math.cos(pitch - oneEightyRad));
        forward.set(x, y, z);
    
        //Multiply in this order: roll, pitch, yaw
        //cos(90) = 0, cos(180) = -1, cos(270) = 0, cos(0) = 1
        //sin(90) = 1, sin(180) = 0, sin(270) = -1, sin(0) = 0
    
        //hmm... gimbal lock, eh? No!
    
        //Up://
        x = (cosRoll180 * cosPitch90 * sinYaw) - (sinRoll * cosYaw180);
        y = -sinPitch270 * cosRoll;
        z = (cosRoll * cosPitch90 * cosYaw) + (sinRoll * sinYaw);
        up.set(x, y, z);
        //Right:
        x = (cosRoll * sinYaw90) - (sinRoll * cosPitch90 * cosYaw90);
        y = 0 - (sinRoll * sinPitch90);//This axis works fine
        z = (cosRoll * cosYaw270) + (sinRoll * cosPitch90 * sinYaw270);
        right.set(x, y, z);
    }