c++camerarotationquaternionsdirectxmath

DirectX quaternion camera - rolling when changing pitch and yaw


Ok so, I'm currently making my own little engine using DirectX11. I got a lot of things working and decided to go back to my Transform class to make it a bit more polished. I changed my rotation from using pure euler angles to using quaternions, and that's when everything broke...

Now, whenever I rotate my camera (purely changing pitch and yaw right now) eventually my camera somehow gets rolled. Scouring the internet led me to switching up multiplication order of a bunch of matrices and quaternions, but that didn't really work out.

So yeah, hope you guys can help me out 🥲

Here's my camera movement code:

void FreeMovement::HandleRotation(float deltaTime)
{
    // Get data
    Transform* pTransform = GetGameObject()->GetTransform();
    InputManager* pInput = InputManager::GetInstance();

    Rotator cameraRotation = pTransform->GetLocalRotation();
    const float deltaAngle = m_RotateSpeed * deltaTime;

    // Handle input
    if (pInput->IsKeyPressed(VK_LEFT))
    {
        cameraRotation = Rotator::FromAxisAngle(Vector3::Up(), -deltaAngle) * cameraRotation;
    }
    if (pInput->IsKeyPressed(VK_RIGHT))
    {
        cameraRotation = Rotator::FromAxisAngle(Vector3::Up(), deltaAngle) * cameraRotation;
    }

    if (pInput->IsKeyPressed(VK_UP))
    {
        cameraRotation = Rotator::FromAxisAngle(Vector3::Right(), -deltaAngle) * cameraRotation;
    }
    if (pInput->IsKeyPressed(VK_DOWN))
    {
        cameraRotation = Rotator::FromAxisAngle(Vector3::Right(), deltaAngle) * cameraRotation;
    }

    // Set data
    cameraRotation.Normalize();
    pTransform->SetLocalRotation(cameraRotation);
}

The FromAxisAngle() and Rotator multiplication:

DirectX_Rotator DirectX_Rotator::FromAxisAngle(const Vector3& axis, float angle)
{
    return DirectX_Rotator{ DirectX::XMQuaternionRotationAxis(axis.GetVector(), angle * toRadians) };
}

DirectX_Rotator DirectX_Rotator::operator* (const DirectX_Rotator& other) const
{
    return DirectX_Rotator{ DirectX::XMQuaternionMultiply(GetVector(), other.GetVector()) };
}

There's also the Transform class and/or world- and view-matrix I can show if needed. Or more, just ask.

Btw, you might've noticed that I showed two different Rotator classes: "Rotator" and "DirectX_Rotator". But that's because I'm using a "using" keyword, in case I wanted to allow for different rendering APIs.


Solution

  • Ok, I fixed it by changing the camera movement code to the following:

    void FreeMovement::HandleRotation(float deltaTime)
    {
        // Get data
        Transform* pTransform = GetGameObject()->GetTransform();
        InputManager* pInput = InputManager::GetInstance();
    
        const float deltaAngle = m_RotateSpeed * deltaTime;
    
        // Handle input
        if (pInput->IsKeyPressed(VK_LEFT)) m_TotalYaw -= deltaAngle;
        if (pInput->IsKeyPressed(VK_RIGHT)) m_TotalYaw += deltaAngle;
    
        if (pInput->IsKeyPressed(VK_UP)) m_TotalPitch -= deltaAngle;
        if (pInput->IsKeyPressed(VK_DOWN)) m_TotalPitch += deltaAngle;
    
        // Set data
        pTransform->SetLocalRotation(Rotator::FromEuler(m_TotalPitch, m_TotalYaw, 0.f));
    }
    

    Honestly, it still doesn't really make sense to me why my previous piece of code didn't work.