Too bad. I don't really wonder it does not work, but doing it right likely is some more work.
I'll post my own camera code below. That's just some hacky stuff so i can navigate through scene, but it behaves like anybody would expect.
Maybe the trick is to store euler angles, and reconstruct the matrix from that on every change:
euler = orientation;
world = glm::eulerAngleZ(orientation[2]);
world *= glm::eulerAngleY(orientation[1]);
world *= glm::eulerAngleX(orientation[0]);
The order of rotation is important to get the expected FPS game behavior. For me it's ZYX, and Y is my up axis.
On mouse input or key presses, i change the euler angles and then recalculate the whole matrix.
Then there is the fact i use the inverse of the resulting matrix to setup camera projection. I'm not sure if this is necessary for you too.
Maybe you'r current approach would work if you do it like this:
matrix temp = inverse(current camera)
// apply rotations to temp, trying both multiplication orders
current camera = inverse (temp)
In practice i often need to try 4 cases, resulting from uncertainty about inversion and multiplication order. (To defend myself, my uncertainty came from using different math libs with different conventions)
Inverting a matrix means for example: matrix a transforms stuff from world to object space, then inverse(a) transforms from object to world space.
For a 3x3 rotation matrix, inversion is equivalenmt to transpose which is easy to do just by swapping rows and columns:
void TransposeMat3x3 (vec *dst, vec[3] src)
{
for (int i=0; i<3; i++) for (int j=0; j<3; j++)
dst[i][j] = src[j][i];
}
(if your vectors allow to index dimension by number)
struct Camera
{
glm::mat4 world;
glm::mat4 worldInverse;
glm::mat4 projection;
glm::vec3 euler;
float fov;
float firstPersonSpeed;
float firstPersonSensibility;
float nearClip;
float farClip;
float aspect;
void Init (bool fromFile = true)
{
#if 1 // persp
fov = 60.0f;
firstPersonSpeed = 1.0f;
firstPersonSensibility = 0.1f;
aspect = 1.0f;
nearClip = 0.01f;
farClip = 100.0f;
#else // ortho
fov = -2.5f;
firstPersonSpeed = 7.0f;
firstPersonSensibility = 0.03f;
aspect = 1.0f;
nearClip = 10;
farClip = 13;
#endif
world = glm::mat4();
worldInverse = glm::mat4();
euler = glm::vec3();
UpdateProjection ();
UpdateWorld (glm::vec3(1, 0, 7), glm::vec3(0, 0, 0)); // front
}
Camera ()
{
Init();
LoadState("C:\\dev\\pengII\\data\\camDef.bin");
}
~Camera ()
{
SaveState("C:\\dev\\pengII\\data\\camDef.bin");
}
void UpdateWorld (const glm::vec3 position, const glm::vec3 orientation)
{
glm::vec3 pos = position;
euler = orientation;
world = glm::eulerAngleZ(orientation[2]);
world *= glm::eulerAngleY(orientation[1]);
world *= glm::eulerAngleX(orientation[0]);
world[3] = glm::vec4 (pos, 1.0f);
worldInverse = glm::inverse (world);
}
void MoveFirstPerson (const glm::vec3 translation, glm::vec3 rotation)
{
UpdateWorld (
glm::vec3(world[3] + world * glm::vec4(translation * firstPersonSpeed, 0.0f)),
euler + rotation * firstPersonSensibility * 0.01f);
}
void UpdateProjection ()
{
float fovY = fov / 180.0f * float(M_PI);
if (fov > 0.0f)
{
if (farClip <= 0)
projection = glm::infinitePerspective (fovY, aspect, nearClip);
else
projection = glm::perspective (fovY, aspect, nearClip, farClip);
}
else
projection = glm::ortho (fov*aspect, -fov*aspect, fov, -fov, -nearClip, farClip);
projection[1] *= -1.f;
glm::mat4 mvp;
mvp = projection * worldInverse * glm::mat4(1.f);
*((glm::mat4*)simpleVis.MVP) = mvp;
}
void SaveState(char *filename)
{
FILE* file = fopen(filename, "wb");
if (file)
{
fwrite(this, sizeof(Camera), 1, file);
fclose(file);
}
};
void LoadState(char *filename)
{
FILE* file = fopen(filename, "rb");
if (file)
{
fread(this, sizeof(Camera), 1, file);
fclose(file);
}
};
};