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