
#include "camera.h"

const float pi2=D3DX_PI * 2.0f;

//=============================================================================
void CCamera::Create( IDirect3DDevice8** pDevice )
{
	m_pD3DDevice = *pDevice;

	
	// I'll set an initial position to a little back away from the origin
	m_PosX = m_PosY = 0.0f;
	m_PosZ = -4.0f;
	// Our intial rotational values
	m_RotX = m_RotY = m_RotZ = 0.0f;
	
	D3DXMatrixTranslation(&m_matTranslation, -m_PosX, -m_PosY, -m_PosZ);
	
	D3DXMATRIX mRX, mRY, mRZ;
	D3DXMatrixRotationX(&mRX, -m_RotX);
	D3DXMatrixRotationY(&mRY, -m_RotY);
	D3DXMatrixRotationZ(&mRZ, -m_RotZ);

	m_matRotation = mRZ;
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRY);
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRX);


	UpdateCamera();


	D3DXMATRIX  matProj;
	D3DXMatrixPerspectiveFovLH(&matProj,  D3DX_PI/4,
							    600.0f / 400.0f,  1.0f,2000.0f );

	m_pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

//=============================================================================
void CCamera::CreateFirstPerson()
{
	  m_bMovedSinceLastUpdate = true;
      m_vPosition = D3DXVECTOR3(0.0f,0.0f,-45.0f);  // start at the origin
      m_vFacing = D3DXVECTOR3(0.0f,0.0f,1.0f);    // facing down the positive Z axis
      m_vRight = D3DXVECTOR3(1.0f,0.0f,0.0f);     // with the positive X axis to the right
      m_vUp = D3DXVECTOR3(0.0f,1.0f,0.0);         // and the positive Y axis up
      m_fRotAboutUp = m_fRotAboutRight = m_fRotAboutFacing = 0.0f;

      D3DXMatrixIdentity(&m_matView); // initialise the view matrix to the identity
}

//=============================================================================
HRESULT CCamera::FirstPersonUpdate()
{
	HRESULT hr;
    D3DXMATRIX matTotal; // used to speed up the process by performing less matrix
                         // multiplications.

    // the matrices which represent the rotation about each of our 3 axes
    D3DXMATRIX matRotAboutUp, matRotAboutRight, matRotAboutFacing;

    // bail if we haven't moved since the last update
    if (!m_bMovedSinceLastUpdate)
	{
       return S_OK;
    }

    // get the rotation matrices for each rotation   
    D3DXMatrixRotationAxis(&matRotAboutRight,&m_vRight,m_fRotAboutRight);
    D3DXMatrixRotationAxis(&matRotAboutUp,&m_vUp,m_fRotAboutUp);
    D3DXMatrixRotationAxis(&matRotAboutFacing,&m_vFacing,m_fRotAboutFacing);

    // concatenate them to form a total rotation matrix
    D3DXMatrixMultiply(&matTotal,&matRotAboutUp,&matRotAboutRight);
    D3DXMatrixMultiply(&matTotal,&matRotAboutFacing,&matTotal);
   
    // transform 2 of the vectors by this matrix and get the third using the cross product
    D3DXVec3TransformCoord(&m_vRight,&m_vRight,&matTotal);   
    D3DXVec3TransformCoord(&m_vUp,&m_vUp,&matTotal);   
    D3DXVec3Cross(&m_vFacing,&m_vRight,&m_vUp);

    // are these two still perpandicular?
    if (fabs(D3DXVec3Dot(&m_vUp,&m_vRight)) > 0.01)
	{
		// these suckers aren't perpandicular anymore - get another using the cross product
        D3DXVec3Cross(&m_vUp,&m_vFacing,&m_vRight);
	}

    // remember to normalise vectors before putting them into the view matrix
    D3DXVec3Normalize(&m_vRight,&m_vRight);
    D3DXVec3Normalize(&m_vUp,&m_vUp);
    D3DXVec3Normalize(&m_vFacing,&m_vFacing);

    // used to compute the bottom row of the view matrix
    float fView41,fView42,fView43;
    fView41 = -D3DXVec3Dot(&m_vRight,&m_vPosition);
    fView42 = -D3DXVec3Dot(&m_vUp,&m_vPosition);
    fView43 = -D3DXVec3Dot(&m_vFacing,&m_vPosition);

    m_matView = D3DXMATRIX( m_vRight.x, m_vUp.x, m_vFacing.x, 0.0f,
                            m_vRight.y, m_vUp.y, m_vFacing.y, 0.0f,
                            m_vRight.z, m_vUp.z, m_vFacing.z, 0.0f,
                            fView41, fView42,    fView43,     1.0f
                           );


    m_fRotAboutUp = m_fRotAboutRight = m_fRotAboutFacing = 0.0f;      

    // update the device - if we fail then return the error code
    hr = m_pD3DDevice->SetTransform(D3DTS_VIEW,&m_matView);
    if (FAILED(hr))
	{
		return hr;

		m_bMovedSinceLastUpdate = false;

		return S_OK;
	}

	return S_OK;
}

//=============================================================================
void CCamera::MoveForward()
{
	  m_vPosition += 0.55f*m_vFacing;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::MoveBackward()
{
	  m_vPosition -= 0.55f*m_vFacing;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::MoveRight()
{
	  m_vPosition += 0.55f*m_vRight;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::MoveLeft()
{
	  m_vPosition -= 0.55f*m_vRight;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::MoveUp()
{
	  m_vPosition += 0.55f*m_vUp;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::MoveDown()
{
	  m_vPosition -= 0.55f*m_vUp;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::RotateRight()
{
	  m_fRotAboutUp += 0.02f;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::RotateLeft()
{
	  m_fRotAboutUp -= 0.02f;
      m_bMovedSinceLastUpdate = true;
}

//=============================================================================
void CCamera::Release()
{
	
}

//=============================================================================
void CCamera::Setup2dCamera()
{
	D3DXMATRIX matOrtho;	
	D3DXMATRIX matIdentity;
	
	//Setup the orthogonal projection matrix and the default world/view matrix
	D3DXMatrixOrthoLH(&matOrtho, 640.0f, 480.0f, 0.0f, 1.0f);
	D3DXMatrixIdentity(&matIdentity);

	m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matOrtho);
	m_pD3DDevice->SetTransform(D3DTS_WORLD, &matIdentity);
	m_pD3DDevice->SetTransform(D3DTS_VIEW, &matIdentity);

	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
	m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
}

//=============================================================================
void CCamera::Setup3dCamera()
{

	D3DXMATRIX matView;
    D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0.0f, 0, -4.0f),		    //Camera Position
                                 &D3DXVECTOR3(0.0f, 0.0f, 0.0f),		//Look At Position
                                 &D3DXVECTOR3(0.0f, 1.0f, 0.0f));		//Up Direction
    m_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

	// Clipping planes
    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.25f, 1.0f, 2000.0f);
    m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);


	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
}

//=============================================================================
void CCamera::UpdateCamera()
{
	// Combine it all
	D3DXMatrixMultiply(&m_matWorld, &m_matTranslation, &m_matRotation);
	m_pD3DDevice->SetTransform(D3DTS_VIEW,&m_matWorld); 
}

//=============================================================================
void CCamera::Move( float x, float y, float z)
{
		m_PosX = x;
		m_PosY = y;
		m_PosZ = z;

	D3DXMatrixTranslation(&m_matTranslation, -m_PosX, -m_PosY, -m_PosZ);

	UpdateCamera();
	
}

//=============================================================================
void CCamera::MoveRel( float x, float y, float z)
{
	Move( m_PosX + x,
		  m_PosY + y,
		  m_PosZ + z );
}

//=============================================================================
void CCamera::Rotate(float x, float y, float z)
{
	// Save our new rotation value.
	m_RotX = x;
	m_RotY = y;
	m_RotZ = z;

	D3DXMATRIX mRX, mRY, mRZ;
	D3DXMatrixRotationX(&mRX, -m_RotX);
	D3DXMatrixRotationY(&mRY, -m_RotY);
	D3DXMatrixRotationZ(&mRZ, -m_RotZ);

	m_matRotation = mRZ;
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRY);
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRX);

	UpdateCamera();

}

//=============================================================================
void CCamera::RotateRel(float x, float y, float z)
{
	// Save our new rotation value.
	m_RotX += x;
	if(m_RotX > D3DX_PI*2)
		m_RotX-=(float)(D3DX_PI*2);
	if(m_RotX < 0)
		m_RotX+=(float)(D3DX_PI*2);

	m_RotY += y;
	if(m_RotY > D3DX_PI*2)
		m_RotY-=(float)(D3DX_PI*2);
	if(m_RotY < 0)
		m_RotY+=(float)(D3DX_PI*2);

	m_RotZ += z;
	if(m_RotZ > D3DX_PI*2)
		m_RotZ-=(float)(D3DX_PI*2);
	if(m_RotZ < 0)
		m_RotZ+=(float)(D3DX_PI*2);

	D3DXMATRIX mRX, mRY, mRZ;
	D3DXMatrixRotationX(&mRX, -m_RotX);
	D3DXMatrixRotationY(&mRY, -m_RotY);
	D3DXMatrixRotationZ(&mRZ, -m_RotZ);

	m_matRotation = mRZ;
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRY);
	D3DXMatrixMultiply(&m_matRotation, &m_matRotation, &mRX);
}
//=============================================================================