
//Camera variables...
double nearPlaneXMin, nearPlaneXMax, nearPlaneYMin, nearPlaneYMax, nearPlaneZ;
double farPlaneXMin, farPlaneXMax, farPlaneYMin, farPlaneYMax, farPlaneZ;
double tanFov;

void Camera::computeGameViewVolumePoints (double fovy, double aspect, double zNear, double zFar) {
	//Note: same parameters as gluPerspective.
	//Taken from Mesa Libraries to gain access to near/far plane bounds.
	#define M_PI 3.14159265358979323846

	tanFov = tan (fovy * M_PI / 360.0);

	nearPlaneYMax = zNear * tanFov;
	nearPlaneYMin = -nearPlaneYMax;
	nearPlaneXMin = nearPlaneYMin * aspect;
	nearPlaneXMax = nearPlaneYMax * aspect;

	farPlaneYMax = zFar * tanFov;
	farPlaneYMin = -farPlaneYMax;
	farPlaneXMin = farPlaneYMin * aspect;
	farPlaneXMax = farPlaneYMax * aspect;

	nearPlaneZ = zNear;
	farPlaneZ = zFar;
}

//Camera variables...
Plane leftLocalPlane; Plane bottomLocalPlane; Plane rightLocalPlane;
Plane topLocalPlane; Plane frontLocalPlane; Plane backLocalPlane;

void Camera::computeViewVolumePlanes () {
	//Plane equation of the form N. ([x,y,z] - P) = 0 which
	//leads to coefficients {A,B,C,D} = {Nx, Ny, Nz, -N.P}.

    Vector upVector (0.0, 1.0, 0.0);
    Vector downVector (0.0, -1.0, 0.0);
    Vector leftVector (-1.0, 0.0, 0.0);
    Vector rightVector (1.0, 0.0, 0.0);
    Vector inVector (0.0, 0.0, -1.0);
    Vector outVector (0.0, 0.0, 1.0);

	Vector bottomLeftToCamera (-nearPlaneXMin, -nearPlaneYMin, -nearPlaneZ);
	Vector bottomRightToCamera (-nearPlaneXMax, -nearPlaneYMin, -nearPlaneZ);
	Vector topLeftToCamera (-nearPlaneXMin, -nearPlaneYMax, -nearPlaneZ);
	Vector topRightToCamera (-nearPlaneXMax, -nearPlaneYMax, -nearPlaneZ);
	Vector nearCenter (0.0, 0.0, -nearPlaneZ);
	Vector farCenter (0.0, 0.0, -farPlaneZ);

	Vector leftNormal = upVector.cross (bottomLeftToCamera); leftNormal.normalize ();
	Vector bottomNormal = leftVector.cross (bottomRightToCamera); bottomNormal.normalize ();
	Vector rightNormal = downVector.cross (topRightToCamera); rightNormal.normalize ();
	Vector topNormal = rightVector.cross (topLeftToCamera); topNormal.normalize ();

	leftLocalPlane.set (leftNormal, leftNormal.dot (bottomLeftToCamera));
	bottomLocalPlane.set (bottomNormal, bottomNormal.dot (bottomRightToCamera));
	rightLocalPlane.set (rightNormal, rightNormal.dot (topRightToCamera));
	topLocalPlane.set (topNormal, topNormal.dot (topLeftToCamera));
	frontLocalPlane.set (inVector, inVector.dot (nearCenter));
	backLocalPlane.set (outVector, outVector.dot (farCenter));
}

inline void Plane::unnormalizedTransform (const Plane &plane, const Transformation &t) {
	//Transform 3 points and return the new plane constructed from these points.
	normal = plane.normal * t - Point (0.0, 0.0, 0.0) * t;
	minusPDotN = -(normal.dot (plane.normal * (-plane.minusPDotN) * t));
}

inline void Camera::cameraToWorld (Plane &output, Plane &input) {
	output.unnormalizedTransform (input, this->localToWorldTransformation);
}

//Camera variables...
Plane leftWorldPlane; Plane bottomWorldPlane; Plane rightWorldPlane;
Plane topWorldPlane; Plane frontWorldPlane; Plane backWorldPlane;

void Camera::computeWorldPlanes () {
	cameraToWorld (leftWorldPlane, leftLocalPlane);
	cameraToWorld (bottomWorldPlane, bottomLocalPlane);
	cameraToWorld (rightWorldPlane, rightLocalPlane);
	cameraToWorld (topWorldPlane, topLocalPlane);
	cameraToWorld (frontWorldPlane, frontLocalPlane);
	cameraToWorld (backWorldPlane, backLocalPlane);
}