Program Listing for File FlyThroughCamera.cpp

Return to documentation for file (Source\Camera\Src\FlyThroughCamera.cpp)

#include "Camera/FlyThroughCamera.h"
#include "Math/Transform.h"
#include "Math/Geometry.h"
#include "Core/Events.h"

namespace Azura {
namespace {
const Vector3f UNIT_LOOK  = Vector3f(0, 0, 1);
const Vector3f UNIT_RIGHT = Vector3f(1, 0, 0);
const Vector3f UNIT_UP    = Vector3f(0, 1, 0);
} // namespace

FlyThroughCamera::FlyThroughCamera(U32 width, U32 height)
  : Camera(width, height),
    m_cachedMouseX(width / 2.0f),
    m_cachedMouseY(height / 2.0f) {
  FlyThroughCamera::Recompute();
}

void FlyThroughCamera::Recompute() {
  const Matrix4f transform = Matrix4f::FromRotationMatrix(Matrix4f::RotationY(m_theta)) *
                             Matrix4f::FromRotationMatrix(Matrix3f::RotationX(m_phi));

  m_look  = (transform * Vector4f(UNIT_LOOK, 0)).xyz();
  m_up    = (transform * Vector4f(UNIT_UP, 0)).xyz();
  m_right = (transform * Vector4f(UNIT_RIGHT, 0)).xyz();

  m_ref = m_look.Normalized();
  m_ref += m_eye;

  m_view = Transform::LookAt(m_ref, m_eye, m_up);
  m_proj = Transform::Perspective(m_fovY, m_aspect, m_nearClip, m_farClip);

  m_viewProj    = m_proj * m_view;
  m_invViewProj = m_viewProj.Inverse();
}

void FlyThroughCamera::OnMouseEvent(MouseEvent mouseEvent) {
  if (mouseEvent.m_internalType != MouseEventType::MouseUpdate) {
    return;
  }

  const float currentX = mouseEvent.m_eventX;
  const float currentY = mouseEvent.m_eventY;

  const float diffX = currentX - m_cachedMouseX;
  const float diffY = currentY - m_cachedMouseY;

  RotateAboutRight(diffY * m_sensitivity * 2.0f);
  RotateAboutUp(diffX * m_sensitivity * 2.0f);

  Recompute();
}

void FlyThroughCamera::OnKeyEvent(KeyEvent keyEvent) {
  if (keyEvent.m_internalType == KeyEventType::KeyPress) {
    switch (keyEvent.m_key) {
      case KeyboardKey::W:
        m_moveForwardFactor = 1;
        break;

      case KeyboardKey::S:
        m_moveForwardFactor = -1;
        break;

      case KeyboardKey::D:
        m_moveRightFactor = 1;
        break;

      case KeyboardKey::A:
        m_moveRightFactor = -1;
        break;

      case KeyboardKey::Unmapped:
      case KeyboardKey::Esc:
      default:
        break;
    }
  } else if (keyEvent.m_internalType == KeyEventType::KeyRelease) {
    switch (keyEvent.m_key) {
      case KeyboardKey::W:
        m_moveForwardFactor = 0;
        break;

      case KeyboardKey::S:
        m_moveForwardFactor = 0;
        break;

      case KeyboardKey::D:
        m_moveRightFactor = 0;
        break;

      case KeyboardKey::A:
        m_moveRightFactor = 0;
        break;

      case KeyboardKey::Unmapped:
      case KeyboardKey::Esc:
      case KeyboardKey::Up:
      case KeyboardKey::Down:
      case KeyboardKey::Left:
      case KeyboardKey::Right:
      case KeyboardKey::T:
      case KeyboardKey::Y:
      default:
        break;
    }
  }
}

void FlyThroughCamera::SetTranslationStepSize(float amount) {
  m_stepSize = amount;
}

void FlyThroughCamera::RotateAboutUp(float degrees) {
  m_theta += Math::ToRadians(degrees);

  if (m_theta >= Math::TWO_PI) {
    m_theta = m_theta - Math::TWO_PI;
  }

  if (m_theta < 0) {
    m_theta = Math::TWO_PI + m_theta;
  }
}

void FlyThroughCamera::SetAngleAboutRight(float ndcY) {
  const float deg = ndcY * 89 * -1;
  m_phi           = Math::ToRadians(deg);
}

void FlyThroughCamera::RotateAboutRight(float degrees) {
  m_phi += Math::ToRadians(degrees);

  if (m_phi >= Math::PI_OVER2) {
    m_phi = Math::PI_OVER2 - Math::ONE_DEGREE_RADIAN;
  }

  if (m_phi <= -Math::PI_OVER2) {
    m_phi = -Math::PI_OVER2 + Math::ONE_DEGREE_RADIAN;
  }
}

void FlyThroughCamera::TranslateAlongLook(float amt) {
  const Vector3f translation = m_look * amt;

  m_eye += translation;
  m_ref += translation;
}

void FlyThroughCamera::TranslateAlongRight(float amt) {
  const Vector3f translation = m_right * amt;

  m_eye += translation;
  m_ref += translation;
}

void FlyThroughCamera::Update(float timeDelta) {
  const float distance = m_stepSize * timeDelta;

  bool needsRecompute = false;

  if (m_moveForwardFactor != 0) {
    TranslateAlongLook(distance * m_moveForwardFactor);
    needsRecompute = true;
  }

  if (m_moveRightFactor != 0) {
    TranslateAlongRight(distance * m_moveRightFactor);
    needsRecompute = true;
  }

  if (needsRecompute) {
    Recompute();
  }
}

} // namespace Azura