-1
\$\begingroup\$

How do we rotate the camera around x axis 360 degrees, without having to flip the axis, meaning without having the strange flip after we go over +-90 degrees on x axis? Mine behaves exactly the same way spherical coordinates would, I'm using euler angles.

The video of the strange effect can be viewed here.

Here's my code, rotation happens in dragTPSInput().

#pragma once

#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>

#include "InputHandler.h"
#include "Vector2D.h"

class Camera 
{
public:

    glm::vec3 m_position;

    glm::vec3 m_front;
    glm::vec3 m_up;
    glm::vec3 m_right;

    glm::vec3 m_worldup;

    float m_yaw;
    float m_pitch;

    float m_zoom;

    bool m_enableInput;

    // Mouse
    float m_mouseSensitivity;

    enum class direction { in, out, right, left };

    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), 
        glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), 
        float yaw = -90.0f,
        float pitch = 0.0,
        float zoom = 1.0f,
        float mouseSensitivity = 0.25f) :
        m_front(glm::vec3(0.0f, 0.0f, -1.0f)), 
        m_enableInput(false)
    {
        m_position = position;

        m_yaw = yaw;
        m_pitch = pitch;

        m_worldup = up;

        m_zoom = zoom;
        m_mouseSensitivity = mouseSensitivity;

        updateCameraVectors();
    }

    void move(direction d)
    {
        if (d == direction::in)
            m_position -= m_front;

        if (d == direction::out)
            m_position += m_front;

        if(d == direction::right)
            m_position += glm::normalize(glm::cross(m_front, m_up)) * m_mouseSensitivity;

        if(d == direction::left)
            m_position -= glm::normalize(glm::cross(m_front, m_up)) * m_mouseSensitivity;
    }

    void keyboardInput()
    {
        // todo: give the user ability to adjust input keys

        if (_inHandler->onKeyDown(SDL_SCANCODE_W)) {
            move(direction::in);
        }
        if (_inHandler->onKeyDown(SDL_SCANCODE_S)) {
            move(direction::out);
        }
        if (_inHandler->onKeyDown(SDL_SCANCODE_D)) {
            move(direction::right);
        }
        if (_inHandler->onKeyDown(SDL_SCANCODE_A)) {
            move(direction::left);
        }
    }

    void dragTPSInput(Vector2D* mouseMoveDiff)
    {
        glm::quat rot = glm::angleAxis(glm::radians(-mouseMoveDiff->getY()), glm::vec3(1, 0, 0));
        rot = rot * glm::angleAxis(glm::radians(-mouseMoveDiff->getX()), glm::vec3(0, 1, 0));

        glm::mat4 rotMatrix = glm::mat4_cast(rot);

        glm::vec4 pos = glm::vec4(m_position.x, m_position.y, m_position.z, 1.0f);

        pos = rotMatrix * pos;

        m_position.x = pos.x;
        m_position.y = pos.y;
        m_position.z = pos.z;

        // If I'm not mistaking the code above does the same as:
        // m_position = glm::rotate(m_position, -mouseMoveDiff->getY() * m_mouseSensitivity, glm::vec3(1, 0, 0));
        // m_position = glm::rotate(m_position, -mouseMoveDiff->getX() * m_mouseSensitivity, glm::vec3(0, 1, 0));

        updateCameraVectors();
    }

    void onInput(bool drag = true, bool scroll = true, bool keyboard = false)
    {
        if (drag)
            if (_inHandler->getMouseButtonState(_inHandler->mouse_buttons::LEFT))
                if(_inHandler->isMouseMovig())
                    dragTPSInput(_inHandler->getMouseMoveDiff());

        if (scroll)
            scrollInput();

        if (keyboard)
            keyboardInput();
    }

    // Returns the view matrix calculated using Eular Angles and the LookAt Matrix
    glm::mat4 getViewMatrix()
    {
        return glm::lookAt(m_position, m_front, m_up);
    }

private:
    void updateCameraVectors()
    {
        // Calculate the new Front vector
        glm::vec3 front;
        front.x = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
        front.y = sin(glm::radians(m_pitch));
        front.z = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));

        m_front = glm::normalize(front);

        // Also re-calculate the Right and Up vector
        // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        m_right = glm::normalize(glm::cross(m_front, m_worldup)); 

        m_up = glm::normalize(glm::cross(m_right, m_front));
    }

    InputHandler* _inHandler = TheInputHandler::Instance();
};

I tried googling, but I couldn't find a proper answer, guessing I don't know what exactly to Google for. I tried with 'rotate 360 around x axis' but I found no proper answers.

\$\endgroup\$
  • 1
    \$\begingroup\$ This code chooses the camera's right & up directions solely as a function of its look direction and a constant world up. Unfortunately because of the Hairy Ball Theorem, we can't extend this to the whole sphere of possible directions: we always have a singularity somewhere (here, the poles at ±90°). So you'll need to decide on a new policy for how to manage the camera's twist, depending on the gameplay needs this camera fulfills. Try describing what you want to happen when the camera pitches past ±90° and we can suggest ways to achieve it. \$\endgroup\$ – DMGregory Oct 14 '17 at 14:09
  • \$\begingroup\$ Also, please visit this page to have your accounts merged. This will allow you to edit your question and comment on it and its answers. \$\endgroup\$ – Alexandre Vaillancourt Oct 14 '17 at 19:09

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Browse other questions tagged or ask your own question.