//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  This file contains the definition of the CCamera class.             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
#include "popassert.hpp"
#include "portable_gl.h"
#include <cmath>
#include <GL/glu.h>

#include "CCamera.hpp"




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : CCamera (default constructor)                            //
//                                                                      //
//  Comments :                                                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
CCamera::CCamera() :
  m_position(1, 1, 1),
  m_target(0, 0, 0),
  m_up(0, 0, 1),
  m_moveSpeed(MAKE_REAL(0.001)),
  m_rotateSpeed(MAKE_REAL(0.001))
{
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : CamLookAt()                                              //
//                                                                      //
//  Comments : This function sets the OpenGL modelview matrix to what   //
//             is needed to represent the camera position.              //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
void CCamera::CamLookAt(void)
{
  gluLookAt(m_position.x, m_position.y, m_position.z,
            m_target.x, m_target.y, m_target.z,
            m_up.x, m_up.y, m_up.z);
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : Rotate()                                                 //
//                                                                      //
//  Comments : This function rotates the camera around the point        //
//             towards which the camera is looking, around a given      //
//             axis and of a given angle.                               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
void CCamera::Rotate(real_t angle, const CVector& axis)
{
  CVector newDir;
  CVector temp = m_position - m_target;

  real_t Cos = cos(angle);
  real_t Sin = sin(angle);

  // Rotation using matrix multiplication
  newDir.x  = (Cos + (1 - Cos) * axis.x * axis.x)          * temp.x;
  newDir.x += ((1 - Cos) * axis.x * axis.y - axis.z * Sin) * temp.y;
  newDir.x += ((1 - Cos) * axis.x * axis.z + axis.y * Sin) * temp.z;

  newDir.y  = ((1 - Cos) * axis.x * axis.y + axis.z * Sin) * temp.x;
  newDir.y += (Cos + (1 - Cos) * axis.y * axis.y)          * temp.y;
  newDir.y += ((1 - Cos) * axis.y * axis.z - axis.x * Sin) * temp.z;

  newDir.z  = ((1 - Cos) * axis.x * axis.z - axis.y * Sin) * temp.x;
  newDir.z += ((1 - Cos) * axis.y * axis.z + axis.x * Sin) * temp.y;
  newDir.z += (Cos + (1 - Cos) * axis.z * axis.z)          * temp.z;

  m_position = m_target + newDir;
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : HorizontalRotate()                                       //
//                                                                      //
//  Comments : This function rotates the camera around the horizontal   //
//             axis passing throught the camera position.               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
void CCamera::HorizontalRotate(real_t delta)
{
  CVector horAxis = (m_target - m_position) ^ m_up;
  horAxis.normalize();
  Rotate(delta*m_rotateSpeed, horAxis);
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : VerticalRotate()                                         //
//                                                                      //
//  Comments : This function rotates the camera around the up vector.   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
void CCamera::VerticalRotate(real_t delta)
{
  Rotate(delta*m_rotateSpeed, m_up);
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  Class    : CCamera                                                  //
//                                                                      //
//  Function : Move()                                                   //
//                                                                      //
//  Comments : This function moves the camera along the direction       //
//             towards which it looks.                                  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////
void CCamera::Move(real_t delta)
{
  CVector direction = m_target - m_position;
  m_position = m_position + direction / direction.norm() * delta * m_moveSpeed;
}
