Camera direction
// camera direction
v3f d(0.f, 0.f, 1.f);
d = -math::mulBasis(d, m_editorCamera->m_viewMatrix);
printf("%f %f %f\n", d.x, d.y, d.z);
					
Orbit camera 🪐
void yyOrbitCamera_onUpdate(yyCamera* camera)
{
  math::makePerspectiveRHMatrix(
    camera->m_projectionMatrix,
    camera->m_fov,
    camera->m_aspect,
    camera->m_near,
    camera->m_far);

  v3f newCameraPosition = v3f(0.f, camera->m_objectBase.m_localPosition.w, 0.f);

  Mat4 MX(Quat(camera->m_objectBase.m_rotation.x, 0.f, 0.f));
  Mat4 MY(Quat(0.f, camera->m_objectBase.m_rotation.y, 0.f));

  newCameraPosition = math::mul(newCameraPosition, (MY * MX));
  newCameraPosition += v3f(
    camera->m_objectBase.m_localPosition.x, 
    camera->m_objectBase.m_localPosition.y, 
    camera->m_objectBase.m_localPosition.z);

  Mat4 T;
  T.m_data[3] = newCameraPosition;

  Mat4 P(Quat(v4f(-camera->m_objectBase.m_rotation.x + math::degToRad(90.f), 0.f, 0.f, 1.f)));
  Mat4 Y(Quat(v4f(0.f, -camera->m_objectBase.m_rotation.y, 0.f, 1.f)));
  Mat4 R(Quat(v4f(0.f, 0.f, -camera->m_objectBase.m_rotation.z, 1.f)));
  camera->m_viewMatrix = (R*(P * Y)) * T;
}

void PanMove(const v2f& mouseDelta, f32 deltaTime) {
  v4f vec(
    mouseDelta.x * deltaTime,
    0.f,
    -mouseDelta.y * deltaTime,
    0.f);
  Mat4 MX(Quat(m_objectBase.m_rotation.x, 0.f, 0.f));
  Mat4 MY(Quat(0.f, m_objectBase.m_rotation.y, 0.f));
  //Mat4 MZ(Quat(0.f, 0.f, m_objectBase.m_rotation.z)); // не знаю как это учесть
  vec = math::mul(vec, MY * MX);
  m_objectBase.m_localPosition += vec;
}

void Rotate(const v2f& mouseDelta, f32 deltaTime) {
  m_objectBase.m_rotation.x += mouseDelta.y * deltaTime;
  m_objectBase.m_rotation.y += -mouseDelta.x * deltaTime;
}

void Zoom(f32 value) {
  m_objectBase.m_localPosition.w -= value;
  if (m_objectBase.m_localPosition.w < 0.01f)
    m_objectBase.m_localPosition.w = 0.01f;
}

void ChangeFOV(f32 mouseDeltaX, f32 deltaTime) {
  m_fov += mouseDeltaX * deltaTime;
  if (m_fov < 0.01f)
    m_fov = 0.01f;
  if (m_fov > math::PI)
    m_fov = math::PI;
}

void RotateZ(f32 mouseDeltaX, f32 deltaTime) {
  m_objectBase.m_rotation.z += mouseDeltaX * deltaTime;
}
void Reset() {
  m_near = 0.01f;
  m_far = 1000.f;
  m_fov = math::degToRad(90.f);
  m_aspect = 800.f / 600.f;

  m_objectBase.m_localPosition = v4f(0.f, 0.f, 0.f, 5.f);
  m_objectBase.m_rotation = v3f(math::degToRad(-45.f), 0.f, 0.f);
}

/// TEST

m_editorCamera->Update();
if (g_demo->m_inputContext->m_isMMBHold)
{
  if (g_demo->m_inputContext->IsKeyHold(yyKey::K_LALT))
    m_editorCamera->Rotate(g_demo->m_inputContext->m_mouseDelta, g_demo->m_dt);
  else
    m_editorCamera->PanMove(g_demo->m_inputContext->m_mouseDelta, g_demo->m_dt);
}

if (g_demo->m_inputContext->IsKeyHold(yyKey::K_LALT))
{
  if (g_demo->m_inputContext->m_isLMBHold)
    m_editorCamera->RotateZ(g_demo->m_inputContext->m_mouseDelta.x, g_demo->m_dt);
  if (g_demo->m_inputContext->m_isRMBHold)
    m_editorCamera->ChangeFOV(g_demo->m_inputContext->m_mouseDelta.x, g_demo->m_dt);
}

if(g_demo->m_inputContext->m_wheelDelta)
  m_editorCamera->Zoom(g_demo->m_inputContext->m_wheelDelta);
					
Chase camera
void ChaseCamera::update(const Mat4& chasisMatrix, f32 dt)
{
  m_camera->m_objectBase.UpdateBase();

  math::makePerspectiveRHMatrix(
    m_camera->m_projectionMatrix,
    m_camera->m_fov,
    m_camera->m_aspect,
    m_camera->m_near,
    m_camera->m_far);

  Mat4 V = chasisMatrix; // car body matrix
  auto bodyPosition = V[3];
  V[3] = v4f(0.f, 0.f, 0.f, 1.f);
  Quat Q = math::matToQuat(V);
  Q.normalize();

  m_orientation = Q.slerp(m_orientation, Q, 1.f * dt, 0.1f);

  Mat4 rot;
  math::makeRotationMatrix(rot, m_orientation); 

  // m_originalPosition - camera position
  v4f newPosition = math::mul(m_originalPosition, rot) + bodyPosition;

  math::makeLookAtRHMatrix(
    m_camera->m_viewMatrix,
    newPosition, 
    bodyPosition + v4f(0.f, m_originalTarget, 0.f, 0.f), // m_originalTarget - move target up\down
    m_camera->m_up
  );

  m_camera->Update(); // build frustum etc.
}
					
Camera for racing games
void camera_OnUpdate(Game_Camera* camera)
{
	Mat4 P;

	math::makePerspectiveRHMatrix(
		P,
		camera->GetFOV(),
		camera->GetAspect(),
		camera->GetNear(),
		camera->GetFar() );

	camera->SetProjection(P);
	
	Mat4 V = g_body->GetGlobalMatrix();
	V.invert();
	camera->SetView(V);
}
					
Flying camera
// For example, hold RMB and fly

// in main loop
if (g_inputContex->m_isRMBHold)
{
  m_activeCamera->rotate(g_inputContex->m_mouseDelta, deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_LSHIFT) || g_inputContex->isKeyHold(yyKey::K_RSHIFT))
    m_activeCamera->m_moveSpeed = m_activeCamera->m_moveSpeedDefault * 5.f;
  else
    m_activeCamera->m_moveSpeed = m_activeCamera->m_moveSpeedDefault;

  if (g_inputContex->isKeyHold(yyKey::K_W))
    m_activeCamera->moveForward(deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_S))
    m_activeCamera->moveBackward(deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_A))
    m_activeCamera->moveLeft(deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_D))
    m_activeCamera->moveRight(deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_E))
    m_activeCamera->moveUp(deltaTime);
  if (g_inputContex->isKeyHold(yyKey::K_Q))
    m_activeCamera->moveDown(deltaTime);
  
  auto cursorX = std::floor((f32)window.m_data->m_clientSize.x / 2.f);
  auto cursorY = std::floor((f32)window.m_data->m_clientSize.y / 2.f);
  g_inputContex->m_cursorCoordsOld.set(cursorX, cursorY);

  // move cursor to center of the window
  // this example not about it, so implement it by yourself
  yySetCursorPosition(cursorX, cursorY, window.m_data); 
}

/// CAMERA
void Camera::rotate(const v2f& mouseDelta, f32 dt)
{
	f32 speed = 4.4f;
	Mat4 RX;
	Mat4 RY;
	bool update = false;
	if (mouseDelta.x != 0.f)
	{
		update = true;
		RY.setRotation(Quat(v4f(0.f, math::degToRad(-mouseDelta.x) * dt * speed, 0.f, 0.f)));
	}
	if (mouseDelta.y != 0.f)
	{
		update = true;
		RX.setRotation(Quat(v4f(math::degToRad(-mouseDelta.y) * dt * speed, 0.f, 0.f, 0.f)));
	}

	if (update)
		m_camera->m_rotationMatrix = RX * m_camera->m_rotationMatrix * RY;
}
void Camera::moveLeft(f32 dt)
{
	_moveCamera(v4f(-m_moveSpeed * dt, 0.f, 0.f, 1.f));
}
void Camera::moveRight(f32 dt)
{
	_moveCamera(v4f(m_moveSpeed * dt, 0.f, 0.f, 1.f));
}
void Camera::moveUp(f32 dt)
{
	_moveCamera(v4f(0.f, m_moveSpeed * dt, 0.f, 1.f));
}
void Camera::moveDown(f32 dt)
{
	_moveCamera(v4f(0.f, -m_moveSpeed * dt, 0.f, 1.f));
}
void Camera::moveBackward(f32 dt)
{
	_moveCamera(v4f(0.f, 0.f, m_moveSpeed * dt, 1.f));
}
void Camera::moveForward(f32 dt)
{
	_moveCamera(v4f(0.f, 0.f, -m_moveSpeed * dt, 1.f));
}
void Camera::_moveCamera(v4f& vel)
{
	auto RotInv = m_camera->m_rotationMatrix;
	RotInv.invert();
	vel = math::mul(vel, RotInv);
	m_camera->m_objectBase.m_localPosition += vel; // m_localPosition is just vec4 for position
}

void camera_onUpdate(yyCamera* camera)
{
	math::makePerspectiveRHMatrix(
		camera->m_projectionMatrix,
		camera->m_fov,
		camera->m_aspect,
		camera->m_near,
		camera->m_far);

	auto V = math::mul(-camera->m_objectBase.m_localPosition, camera->m_rotationMatrix);
	camera->m_viewMatrix = camera->m_rotationMatrix;
	camera->m_viewMatrix[3] = V;
	camera->m_viewMatrix[3].w = 1.f;
}