// Author : Chris Tolley
// Date   : 3/9/2004
// viewFrustum.cpp contains the class definition for a view frustum
// class to be used with OpenGL.  The view frustum is the thing in OpenGL that
// is the area that can be seen on the screen by the camera.

#pragma warning( disable : 4244 )
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>
#include <math.h>
#include "viewFrustum.h"

// Initialize the frustum data
float viewFrustum::frustum_[6][4] = { 0,0,0,0,
									  0,0,0,0,
									  0,0,0,0,
									  0,0,0,0,
									  0,0,0,0,
									  0,0,0,0 };

// Constructor
viewFrustum::viewFrustum()
{}


// Destructor
viewFrustum::~viewFrustum()
{}


// Calculates the view frustum.  This is best called at the end of the
// camera's update function.
void viewFrustum::calculateFrustum()
{
	// Get the projection matrix
	float proj[16];
	glGetFloatv( GL_PROJECTION_MATRIX, proj );

	// Get the modelview matrix
	float modl[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, modl );

	// Combine the two matrices
	float clip[16];
	matrixMultiply(modl, proj, clip );

	// Get the numbers for the right plane
	frustum_[0][0] = clip[ 3] - clip[ 0];
	frustum_[0][1] = clip[ 7] - clip[ 4];
	frustum_[0][2] = clip[11] - clip[ 8];
	frustum_[0][3] = clip[15] - clip[12];

	// Get the numbers for the left plane
	frustum_[1][0] = clip[ 3] + clip[ 0];
	frustum_[1][1] = clip[ 7] + clip[ 4];
	frustum_[1][2] = clip[11] + clip[ 8];
	frustum_[1][3] = clip[15] + clip[12];

	// Get the numbers for the bottom plane
	frustum_[2][0] = clip[ 3] + clip[ 1];
	frustum_[2][1] = clip[ 7] + clip[ 5];
	frustum_[2][2] = clip[11] + clip[ 9];
	frustum_[2][3] = clip[15] + clip[13];

	// Get the numbers for the top plane
	frustum_[3][0] = clip[ 3] - clip[ 1];
	frustum_[3][1] = clip[ 7] - clip[ 5];
	frustum_[3][2] = clip[11] - clip[ 9];
	frustum_[3][3] = clip[15] - clip[13];

	// Get the numbers for the far plane
	frustum_[4][0] = clip[ 3] - clip[ 2];
	frustum_[4][1] = clip[ 7] - clip[ 6];
	frustum_[4][2] = clip[11] - clip[10];
	frustum_[4][3] = clip[15] - clip[14];

	// Get the numbers for the near plane
	frustum_[5][0] = clip[ 3] + clip[ 2];
	frustum_[5][1] = clip[ 7] + clip[ 6];
	frustum_[5][2] = clip[11] + clip[10];
	frustum_[5][3] = clip[15] + clip[14];


	// Normalize all the results
	for( int n = 0; n < 6; ++n )
	{
		double t = sqrt( frustum_[n][0] * frustum_[n][0] + 
						frustum_[n][1] * frustum_[n][1] +
						frustum_[n][2] * frustum_[n][2] );

		frustum_[n][0] = frustum_[n][0] / t;
		frustum_[n][1] = frustum_[n][1] / t;
		frustum_[n][2] = frustum_[n][2] / t;
		frustum_[n][3] = frustum_[n][3] / t;
	}// End for n
}// End viewFrustum::calculateFrustum


// Returns true if point x,y,z is inside the view frustum
bool viewFrustum::isInFrustum( const float x, const float y, const float z )
{
	// Check to see which side of each of the six planes the point is on
	// If the point is on the correct side of all 6 planes, then the point
	// is inside the view frustum
	for( int n = 0; n < 6; ++n )
	{
		if( frustum_[n][0] * x +
			frustum_[n][1] * y +
			frustum_[n][2] * z + frustum_[n][3] <= 0 )
		{
			// If it is on the wrong side of any of the planes, return false.
			return false;
		}// End if
	}// End for n

	// At this point we can return true;
	return true;
}// End view Frustum::isInFrustum( 3 floats )


// Returns true if point is inside the view frustum
bool viewFrustum::isInFrustum( const float point[3] )
{
	// Check to see which side of each of the six planes the point is on
	// If the point is on the correct side of all 6 planes, then the point
	// is inside the view frustum
	for( int n = 0; n < 6; ++n )
	{
		if( frustum_[n][0] * point[0] +
			frustum_[n][1] * point[1] +
			frustum_[n][2] * point[2] + frustum_[n][3] <= 0 )
		{
			// If it is on the wrong side of any of the planes, return false.
			return false;
		}// End if
	}// End for n

	// At this point we can return true;
	return true;
}// End view Frustum::InInFrustum( float* )


// These functions return true if the sphere is at least partly inside 
// the view frustum.  The sphere is made up of a center point and a
// radius point.  The radius point can be any point on the sphere, I just
// need it to calculate the distance from the center point.
bool viewFrustum::sphereInFrustum( const float cx, const float cy, const float cz,
							   	   const float rx, const float ry, const float rz )
{
	// Calculate the distance between center and radius
	double distance = sqrt( ( cx - rx ) * ( cx - rx ) +
							( cy - ry ) * ( cy - ry ) +
							( cz - rz ) * ( cz - rz ) );

	// Use the same formula above except that instead of being <= 0
	// the result has to be <= -distance
	for( int n = 0; n < 6; ++n )
	{
		if( frustum_[n][0] * cx +
			frustum_[n][1] * cy +
			frustum_[n][2] * cz + frustum_[n][3] <= -distance )
		{
			// If it is on the wrong side of any of the planes, return false.
			return false;
		}// End if
	}// End for n

	// If we reached this part, then we can return true
	return true;
}// End viewFrustum::sphereInFrustum( 6 floats )


bool viewFrustum::sphereInFrustum( const float center[3], 
								   const float radius[3] )
{
	// Calculate the distance between center and radius
	double distance = 
			sqrt( ( center[0] - radius[0] ) * ( center[0] - radius[0] ) +
				  ( center[1] - radius[1] ) * ( center[1] - radius[1] ) +
				  ( center[2] - radius[2] ) * ( center[2] - radius[2] ) );

	// Use the same formula above except that instead of being <= 0
	// the result has to be <= -distance
	for( int n = 0; n < 6; ++n )
	{
		if( frustum_[n][0] * center[0] +
			frustum_[n][1] * center[1] +
			frustum_[n][2] * center[2] + frustum_[n][3] <= -distance )
		{
			// If it is on the wrong side of any of the planes, return false.
			return false;
		}// End if
	}// End for n

	// If we reached this part, then we can return true
	return true;
}// End viewFrustum::sphereInFrustum( 2 float[3] )


// Returns the distance from the near plane of the view frustum. In
// other words, this function returns the distance of a point from 
// the camera.
float viewFrustum::distanceFromNear( const float point[3] )
{
	return abs( frustum_[5][0] * point[0] + 
				frustum_[5][1] * point[1] +
				frustum_[5][2] * point[2] + frustum_[5][3] );
}// End viewFrustum::distanceFromNear


float viewFrustum::distanceFromNear( const float x, 
							   const float y, const float z )
{
	return abs( frustum_[5][0] * x + 
				frustum_[5][1] * y +
				frustum_[5][2] * z + frustum_[5][3] );
}// End distanceFromNear



// Multiplies two matricies.  A matrix in this case is a 16 element array
// of floats.  Stores the product of one and two in result.  I am writing 
// this so that I will not need to depend on an implementation 
// of a matrix class
void viewFrustum::matrixMultiply( float* one, float* two, float* result )
{
	result[0] = one[0] * two[0] + one[1] * two[4] + 
				one[2] * two[8] + one[3] * two[12];

	result[1] = one[0] * two[1] + one[1] * two[5] + 
				one[2] * two[9] + one[3] * two[13];

	result[2] = one[0] * two[2]  + one[1] * two[6] + 
				one[2] * two[10] + one[3] * two[14];

	result[3] = one[0] * two[3]  + one[1] * two[7] + 
				one[2] * two[11] + one[3] * two[15];

	result[4] = one[4] * two[0] + one[5] * two[4] + 
				one[6] * two[8] + one[7] * two[12];

	result[5] = one[4] * two[1] + one[5] * two[5] + 
				one[6] * two[9] + one[7] * two[13];

	result[6] = one[4] * two[2]  + one[5] * two[6] + 
			 	one[6] * two[10] + one[7] * two[14];

	result[7] = one[4] * two[3]  + one[5] * two[7] + 
				one[6] * two[11] + one[7] * two[15];

	result[8] = one[8]  * two[0] + one[9]  * two[4] + 
				one[10] * two[8] + one[11] * two[12];

	result[9] = one[8]  * two[1] + one[9]  * two[5] + 
				one[10] * two[9] + one[11] * two[13];

	result[10] = one[8]  * two[2]  + one[9]  * two[6] + 
				 one[10] * two[10] + one[11] * two[14];

	result[11] = one[8]  * two[3]  + one[9]  * two[7] + 
				 one[10] * two[11] + one[11] * two[15];

	result[12] = one[12] * two[0] + one[13] * two[4] + 
				 one[14] * two[8] + one[15] * two[12];

	result[13] = one[12] * two[1] + one[13] * two[5] + 
				 one[14] * two[9] + one[15] * two[13];

	result[14] = one[12] * two[2]  + one[13] * two[6] + 
				 one[14] * two[10] + one[15] * two[14];

	result[15] = one[12] * two[3]  + one[13] * two[7] + 
				 one[14] * two[11] + one[15] * two[15];
}// End viewFrustum::matrixMultiply