0
votes

My 3D models rotates around wrong axis. Everything except X-axis works. When I set angle of Y and Z to 0 then X works fine but if rotate it eg. 90 degrees around Z-axis and rotate around X after that it rotates around Z too. So when Y or Z is used X rotates around Z. So if I make my model rotate 90 degrees around Z and -90 degrees around X it stays completely still.

My function for transformation matrix. If I move matrix.translate(position); to under rotations my model rotates in huge circle (maybe around worlds origin).

public static Matrix4f createTransformationMatrix(Vector3f position, Vector3f rotation, Vector3f scale) {

    Matrix4f matrix = new Matrix4f();

    matrix.scale(scale);

    matrix.translate(position);

    matrix.rotate((float) Math.toRadians(rotation.x), new Vector3f(1, 0, 0));
    matrix.rotate((float) Math.toRadians(rotation.y), new Vector3f(0, 1, 0));
    matrix.rotate((float) Math.toRadians(rotation.z), new Vector3f(0, 0, 1));

    return matrix;
}

Conversion of matrix to floatbuffer so I can pass it to shaders:

public static FloatBuffer toFloatBuffer(Matrix4f matrix) {

    FloatBuffer buffer = BufferUtils.createFloatBuffer(16);

    buffer.put(matrix.m00());
    buffer.put(matrix.m01());
    buffer.put(matrix.m02());
    buffer.put(matrix.m03());
    buffer.put(matrix.m10());
    buffer.put(matrix.m11());
    buffer.put(matrix.m12());
    buffer.put(matrix.m13());
    buffer.put(matrix.m20());
    buffer.put(matrix.m21());
    buffer.put(matrix.m22());
    buffer.put(matrix.m23());
    buffer.put(matrix.m30());
    buffer.put(matrix.m31());
    buffer.put(matrix.m32());
    buffer.put(matrix.m33());

    buffer.flip();

    return buffer;
}

Usage in shader:

gl_Position = transformationMatrix * vec4(position, 1.0) * viewMatrix * projectionMatrix;

The models' zero point has been set to its origin and I am sure that all data send to those functions are correct. I have been debugging this for while now. I am using LWJGL 3.

1
If you combine rotations, then the second rotation is applied in the coordinate system after the first translation. Example: If you rotate 90 degrees around X, afterwards the local Y-axis points along the global Z-Axis and so on.BDL
Unrelated, but the transformation order in your shader looks wrong. Unless view and projection are transposed, it doesn't make sense to multiply them from the right side.BDL
@Rabbid76 that still doesn't solve my problem –Maineri
@Rabbid76 I really don't know what you mean but can't you just look at the code above. The input vector rotation just contains degrees (0-360). I think it's Gimbal Lock as somebody answered because the Y rotation input is 90. I just don't know how to fix it.Maineri
@Rabbid76 well there has to be some solution other people useMaineri

1 Answers

2
votes

I think you are facing the Gimbal Lock. That means, it matters in which order the rotations are applied, and then you may get strange results such as the results you have experienced.

To solve this problem, you will have to use Quaternions.

Basically, you will have to :

  • Create Rotation Quaternions and multiply them with other Rotation Quaternions.
  • You have to create a Rotation Quaternion that represents X-Axis-Rotation, and so on for Y and Z-Axis

  • You multiply them

  • Finally, you'll have to convert the resulting Quaternion into a matrix.

Read here about how to implement Rotation Quaternions in OpenGL ?

Edit :

Read here about how to convert a Quaternion to a Matrix : Convert Quaternion rotation to rotation matrix?

Here's my Camera class(unfortunately, it uses JOGL libraries like Quaternion, but should be portable to LWJGL. It provides scaling, moving, rotation around center in Euler or Quaternion way, as well as methods to retrieve pointing direction etc.

public class Camera {
    public Matrix4 matrix;
    public Quaternion rotation;
    public Vector3 position;
    public Vector3 scale;
    public Vector3 center;
    public Vector2 frictionx;
    public Vector2 frictiony;
    public Vector2 frictionz;
    public Camera() {
        matrix=new Matrix4();
        rotation=new Quaternion();
        position=new Vector3(0f,0f,0f);
        scale=new Vector3(1f,1f,1f);
        center=new Vector3(0f,0f,0f);
        frictionx=new Vector2(0,0);
        frictiony=new Vector2(0,0);
        frictionz=new Vector2(0,0);
    }
    public float tryFric(float a, float b) {
        try {
            float r=a/b;
            if (r == Float.NaN) {
                return 0;
            }
            return r;
        }
        catch (Exception e) {
            return 0;
        }
    }
    public void getFrictions(Vector3 pointing) {
        frictionx.x=tryFric(pointing.y,pointing.x);
        frictionx.y=tryFric(pointing.z,pointing.x);
        frictiony.x=tryFric(pointing.x,pointing.y);
        frictiony.y=tryFric(pointing.z,pointing.y);
        frictionz.x=tryFric(pointing.x,pointing.z);
        frictionz.y=tryFric(pointing.y,pointing.z);
    }
    public Vector3 getPointing(Vector3 vec) {
        float[] in=new float[] {vec.x,vec.y,vec.z};
        float[] out=new float[3];
        rotation.conjugate();
        rotation.rotateVector(out, 0, in, 0);
        rotation.conjugate();
        Vector3 p=new Vector3(out[0],out[1],out[2]);
        getFrictions(p);
        return p;
    }
    public void move(float x, float y, float z) {
        position.x+=x;
        position.y+=y;
        position.z+=z;
    }
    public void setPosition(float x, float y, float z) {
        position.x=x;
        position.y=y;
        position.z=z;
    }
    public void moveCenter(float x, float y, float z) {
        center.x+=x;
        center.y+=y;
        center.z+=z;
    }
    public void setCenter(float x, float y, float z) {
        center.x=x;
        center.y=y;
        center.z=z;
    }
    public void scale(float x, float y, float z) {
        scale.x*=x;
        scale.y*=y;
        scale.z*=z;
    }
    public void setScale(float x, float y, float z) {
        scale.x=x;
        scale.y=y;
        scale.z=z;
    }
    public void rotateQuaternion(float angle, float x, float y, float z) {
        Quaternion rotationy=new Quaternion();
        rotationy.rotateByAngleY(angle*y);
        rotation.mult(rotationy);
        Quaternion rotationx=new Quaternion();
        rotationx.rotateByAngleX(angle*x);
        rotation.mult(rotationx);
        Quaternion rotationz=new Quaternion();
        rotationz.rotateByAngleZ(angle*z);
        rotation.mult(rotationz);
        rotation.normalize();
    }
    public void rotateEuler(float angle, float x, float y, float z) {
        rotation.rotateByEuler(angle*x, angle*y, angle*z);
    }
    public Vector3 getPointing() {
        Matrix4 as_matrix=new Matrix4();
        as_matrix.loadIdentity();
        as_matrix.rotate(rotation);
        float[] out=new float[3];
        float[] in=new float[] {0,0,-1};
        as_matrix.multVec(in, out);
        Vector3 pointing=new Vector3(out[0],out[1],out[2]);
        return pointing;
    }
    public Matrix4 getMatrix() {
        Matrix4 rot_as_mat=new Matrix4();
        rot_as_mat.loadIdentity();
        rot_as_mat.translate(center.x, center.y, center.z);
        rot_as_mat.rotate(rotation);
        Matrix4 result=new Matrix4();
        result.loadIdentity();
        result.scale(scale.x, scale.y, scale.z);
        result.multMatrix(rot_as_mat);
        result.translate(position.x,position.y,position.z);
        matrix=result;
        return result;
    }
}

Hope it helps !

Note :

  • You may have to experiment with the rotation quaternion multiplication order to achieve different results