Como calcular o ponto de vista para mover a câmera com o mouse no OpenGL / GLUT?

Isso vai ser confuso para eu explicar, então por favor, tenha paciência comig

Eu já implementei a maioria dos tipos de movimentos e rotações na minha classe de câmera, tudo está funcionando com o teclado, agora eu quero implementar o mouse. Capto o movimento do mouse assim:

#define SENSITIVITY 25.0f

void main(void) {
    (...)
    glutPassiveMotionFunc(processPassiveMotion);    
    glutWarpPointer(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
    glutSetCursor(GLUT_CURSOR_NONE);
    (...)
}

void processPassiveMotion(int x, int y) {
    int centerX = WINDOW_WIDTH / 2;
    int centerY = WINDOW_HEIGHT / 2;

    int deltaX = -1 * (x - centerX);
    int deltaY = -1 * (y - centerY);

    if(deltaX != 0 || deltaY != 0) {
        mainCamera.Rotate(deltaX / SENSITIVITY, deltaY / SENSITIVITY);

        glutWarpPointer(centerX, centerY);
    }
}

Depois de tudo o que li, acredito que isso é suficiente na minha situação. No entanto, devo declarar que primeiro tentei chamar oPitch() eYaw() funções da câmera, mas não foi possível, tive que criar uma função extra para girar os dois eixos "ao mesmo tempo"

Essa função de rotação é mais ou menos assim:

#define DEG2RAD(a) (a * (M_PI / 180.0f))
#define SINDEG(a)  sin(DEG2RAD(a))
#define COSDEG(a)  cos(DEG2RAD(a))

void Camera::Rotate(GLfloat angleX, GLfloat angleY) {
    Reference = NormalizeVector(
        Reference * COSDEG(angleY) + UpVector * SINDEG(angleY)
    );

    Reference = NormalizeVector(
        Reference * COSDEG(angleX) - RightVector * SINDEG(angleX)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
    RightVector = CrossProduct(&Reference, &UpVector);
}

OReference é a direção de visualização, o ponto em que a câmera está olhando. E como é um vetor normalizado, passa de -1,0 a 1,0. Esse vetor, ou ponto, é usado posteriormente com outro vetor Position, que é o local da câmera) para calcular a aparência real do ponto a ser usado emgluLookAt, como isso

void Camera::LookAt(void) {
    Vector3D viewPoint = Position + Reference;

    gluLookAt(
        Position.x, Position.y, Position.z,
        viewPoint.x, viewPoint.y, viewPoint.z,
        UpVector.x, UpVector.y, UpVector.z
    );
}

Todas as operações de vetor acima, como+, - e* estão sobrecarregados, é clar

Agora vou tentar descrever meu problema ...

A função de rotação acima funciona muito bem no sentido em que executa corretamente um tom e guinada usando o mouse. No entanto, essas rotações não se parecem com as dos jogos de tiro em primeira pessoa. Nesses jogos, quando se olha para o céu e depois para a esquerda / direita, espera-se continuar olhando para o céu. Imaginando que estamos dentro de uma esfera, um movimento como esse deveria "desenhar" um círculo na parte superior da esfer

Mas não é isso que acontece, porque não é isso que uma guinada faz. Um movimento de guinada gira em torno de um eixo arbitrário, que eu acho que é o vetor positivo nessa situação. Portanto, o problema está no movimento da guinada porque o tom parece funcionar be

Em outras palavras, meu código acima não pode manter o horizonte nivelado e é isso que deve acontecer, porque acontece nos jogos quando se olha para o céu e depois para a esquerda / direita, o horizonte está sempre nivelado. O mesmo não acontecerá com meu código, olho para cima e depois para esquerda / direita, e o horizonte ficará todo torcid

Eu me deixei claro o suficiente? Não sei como explicar melhor isso. :( Espero que seja o suficiente para qualquer um entender.

Não sei ao certo como posso corrigir esse problema ... Como posso olhar para a esquerda / direita corretamente depois de olhar para cima / baixo, mantendo o horizonte nivelado?

EDITAR

Meu código de função de rotação é obtido das funções Yaw e Pitch, que também existem, para que eu possa chamar essas rotações de forma independente. Para fins de referência, eu os adicionarei abaixo, juntamente com a função Roll (que provavelmente nunca usarei, mas, caso precise, está lá):

void Camera::Pitch(GLfloat angle) {
    Reference = NormalizeVector(
        Reference * COSDEG(angle) + UpVector * SINDEG(angle)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
}

void Camera::Yaw(GLfloat angle) {
    Reference = NormalizeVector(
        Reference * COSDEG(angle) - RightVector * SINDEG(angle)
    );

    RightVector = CrossProduct(&Reference, &UpVector);
}

void Camera::Roll(GLfloat angle) {
    RightVector = NormalizeVector(
        RightVector * COSDEG(angle) - UpVector * SINDEG(angle)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
}

questionAnswers(1)

yourAnswerToTheQuestion