


#include "PBMatrix.h"
#include "CAABB.h"
#include "CPlane.h"

#define FRUSTUM_PLANES		6
#define DEBUG_PLANES			1

class CVideo3DDoc;

//////////////////////////////////////////////////////////////////////////
class CCamera
{
public:

	CCamera() 
	{
		m_vPos.Clear();
		m_vAngles.Clear();
	}
	~CCamera() {}

	void Init(int w,int h,ftype fFov,ftype fZMax,ftype fZMin)
	{
		m_fFov=fFov;		
		m_nViewport[0]=0;m_nViewport[2]=w;
		m_nViewport[1]=0;m_nViewport[3]=h;

		m_fZMin=fZMin;
		m_fZMax=fZMax;

		// projection ratio (1.0 for square pixels)
		ftype fTw=(ftype)(w);
		ftype	fTh=(ftype)(h);
		m_fProjectionRatio = fTh/fTw;		
	}
	void CalcMatrices(CVideo3DDoc *pDoc);

	const bool IntersectAABB(const CAABBd &aAABBox) const
	{
		ftype fDot1,fVal;
		CPlanef *pFrusPtr=(CPlanef *)&m_Planes[0]; 

		//move to world space
		vector3f vMins=aAABBox.m_vMins+aAABBox.m_vWorldSpacePos;
		vector3f vMaxs=aAABBox.m_vMaxs+aAABBox.m_vWorldSpacePos;

		for (int k=0;k<FRUSTUM_PLANES;k++,pFrusPtr++) // loop while there are active planes
		{				
			fDot1=pFrusPtr->m_fDist;		

			fVal=pFrusPtr->m_vNormal.x;
			if (fVal<=0) 			
				fDot1+=fVal*vMins.x; 							
			else 		
				fDot1+=fVal*vMaxs.x;							

			fVal=pFrusPtr->m_vNormal.y;
			if (fVal<=0)
				fDot1+=fVal*vMins.y; 
			else 			
				fDot1+=fVal*vMaxs.y;

			fVal=pFrusPtr->m_vNormal.z;
			if (fVal<=0) 
				fDot1+=fVal*vMins.z; 
			else 
				fDot1+=fVal*vMaxs.z;		

			if (fDot1<0) 
				return (false);				  		
		} 

		// The AABB is either partially or fully visible
		return (true);	
	}

	//the renderer is responsable to call this function 'cos
	//the view matrix is obtained from the OpenGL/DX Matrix
	//this function computes also frustum planes
	//plane normals are pointing inside	
	/////////////////////////////////////////////////////////////////
	void	SetCameraMatrix(ftype *pProjMatrix,ftype *pViewMatrix)
	{
		memcpy(m_dGLProjMatrix,pProjMatrix,sizeof(ftype)*16);
		memcpy(m_dGLViewMatrix,pViewMatrix,sizeof(ftype)*16);
		//combine the two matrices		
		m_Matrix.Identity();
		m_Matrix.MultMatrixf(pProjMatrix);
		m_Matrix.MultMatrixf(pViewMatrix);

		vector3f vNormal;

		// RIGHT clipping plane
		vNormal.x = m_Matrix.cell(3)-m_Matrix.cell(0);
		vNormal.y = m_Matrix.cell(7)-m_Matrix.cell(4);
		vNormal.z = m_Matrix.cell(11)-m_Matrix.cell(8);
		m_Planes[3].Set(vNormal,m_Matrix.cell(15)-m_Matrix.cell(12));		

		// LEFT clipping plane
		vNormal.x = m_Matrix.cell(3)+m_Matrix.cell(0);
		vNormal.y = m_Matrix.cell(7)+m_Matrix.cell(4);
		vNormal.z = m_Matrix.cell(11)+m_Matrix.cell(8);
		m_Planes[2].Set(vNormal,m_Matrix.cell(15)+m_Matrix.cell(12));		

		// BOTTOM clipping plane
		vNormal.x = m_Matrix.cell(3)+m_Matrix.cell(1);
		vNormal.y = m_Matrix.cell(7)+m_Matrix.cell(5);
		vNormal.z = m_Matrix.cell(11)+m_Matrix.cell(9);
		m_Planes[4].Set(vNormal,m_Matrix.cell(15)+m_Matrix.cell(13));		

		// TOP clipping plane
		vNormal.x = m_Matrix.cell(3)-m_Matrix.cell(1);
		vNormal.y = m_Matrix.cell(7)-m_Matrix.cell(5);
		vNormal.z = m_Matrix.cell(11)-m_Matrix.cell(9);
		m_Planes[5].Set(vNormal,m_Matrix.cell(15)-m_Matrix.cell(13));		

		// FAR clipping plane
		vNormal.x = m_Matrix.cell(3)-m_Matrix.cell(2);
		vNormal.y = m_Matrix.cell(7)-m_Matrix.cell(6);
		vNormal.z = m_Matrix.cell(11)-m_Matrix.cell(10);
		m_Planes[1].Set(vNormal,m_Matrix.cell(15)-m_Matrix.cell(14));		

		// NEAR clipping plane
		vNormal.x = m_Matrix.cell(3)+m_Matrix.cell(2);
		vNormal.y = m_Matrix.cell(7)+m_Matrix.cell(6);
		vNormal.z = m_Matrix.cell(11)+m_Matrix.cell(10);
		m_Planes[0].Set(vNormal,m_Matrix.cell(15)+m_Matrix.cell(14));		

		//they dont really need to be normalized - its just to
		//simplify debugging
#ifdef DEBUG_PLANES
		for (int k=0;k<FRUSTUM_PLANES;k++)
			m_Planes[k].Normalize();
#endif
	}

	/////////////////////////////////////////////////////////////////
	const bool IsPointVisible(const vector3f &vPos,unsigned int dwInClipMask) const
	{
		CPlanef *pFrusPtr=(CPlanef *)&m_Planes[0]; 

		unsigned int mk	= 1;
		ftype fDist;

		while (mk<=dwInClipMask) // loop while there are active planes
		{				
			if (dwInClipMask & mk) //if clip plane is active
			{	
				fDist=pFrusPtr->m_vNormal*vPos+pFrusPtr->m_fDist;
				if (fDist<0)
					return (false);
			}

			mk+=mk;			// mk = (1<<iter)
			pFrusPtr++;	// next plane

		} //mk<=dwInClipMask

		return (true);
	}

	//////////////////////////////////////////////////////////////////////////
	vector3f GetForwardVector() const
	{
		//calculate forward vector  
		vector3f vForward(m_Matrix.cell(2),m_Matrix.cell(6),m_Matrix.cell(10)); 
		vForward.Normalize();	
		return (vForward);
	}

	//////////////////////////////////////////////////////////////////////////
	vector3f GetUpVector() const
	{
		//calculate up vector  
		vector3f vUp(m_Matrix.cell(1),m_Matrix.cell(5),m_Matrix.cell(9)); 
		vUp.Normalize();	
		return (vUp);
	}

	//////////////////////////////////////////////////////////////////////////
	vector3f GetRightVector() const
	{
		//calculate right vector  
		//vector3f vRight(m_Matrix.cell(3),m_Matrix.cell(7),m_Matrix.cell(11)); 
		vector3f vRight(m_Matrix.cell(0),m_Matrix.cell(4),m_Matrix.cell(8)); 
		vRight.Normalize();	
		return (vRight);
	}

	CPlanef		m_Planes[FRUSTUM_PLANES];
	int				m_nViewport[4];
	ftype			m_fFov;
	ftype			m_fZMin,m_fZMax;

	ftype			m_fProjectionRatio;

	vector3f	m_vPos;
	vector3f  m_vAngles;
	CMatrixf	m_ProjMatrix;
	CMatrixf	m_Orig;
	CMatrixf	m_MatrixView;

	CMatrixf	m_Matrix;
	CMatrixf	m_MatrixInverted;	

	double		m_dGLProjMatrix[16],m_dGLViewMatrix[16];
	
	int				m_lastX,m_lastY;
};