//
// 3DMath.h
//

#ifndef MATH_H
#define MATH_H

#include <math.h>
#include "metaobjects.h"

Boolean IsPowerOf2(int num);

#define OGLMath_RadiansToDegrees(x)	((float)((x) * 180.0f / PI))

		/* C FUNCTIONS */
		
#ifdef __cplusplus
extern "C" {
#endif

float CalcDistance(float x1, float y1, float x2, float y2);
float CalcDistance3D(float x1, float y1, float z1, float x2, float y2, float z2);


float OGLPoint3D_DistanceToPlane(const OGLPoint3D *point,const OGLPlaneEquation	*plane);
float OGLPoint2D_LineDistance(OGLPoint2D *point, float p1x, float p1y, float p2x, float p2y, float *t);


Boolean IntersectionOfLineSegAndPlane(const OGLPlaneEquation *plane, float v1x, float v1y, float v1z,
								 float v2x, float v2y, float v2z, OGLPoint3D *outPoint);

Boolean VectorsAreCloseEnough(const OGLVector3D *v1, const OGLVector3D *v2);
Boolean PointsAreCloseEnough(const OGLPoint3D *v1, const OGLPoint3D *v2);
Boolean ColorsAreCloseEnough(const OGLColorRGBA_Byte *c1, const OGLColorRGBA_Byte *c2);

float IntersectionOfYAndPlane_Func(float x, float z, const OGLPlaneEquation *p);
void OGLVector3D_Cross(const OGLVector3D *v1, const OGLVector3D	*v2, OGLVector3D *result);
void OGLMatrix4x4_Invert(const OGLMatrix4x4 *inMatrix, OGLMatrix4x4 *result);


Boolean IntersectLineSegments(float x1, float y1, float x2, float y2,
		                     float x3, float y3, float x4, float y4,
                             float *x, float *y);

void CalcLineNormal2D(float p0x, float p0y, float p1x, float p1y,
					 float	px, float py, OGLVector2D *normal);
Boolean CalcRayNormal2D(const OGLVector2D *vec, float p0x, float p0y,
					 float	px, float py, OGLVector2D *normal);

void ReflectVector2D(OGLVector2D *theVector, const OGLVector2D *N, OGLVector2D *outVec);
void ReflectVector3D(const OGLVector3D *vec, OGLVector3D *N, OGLVector3D *out);

float CalcVectorLength(OGLVector3D *v);
float CalcVectorLength2D(const OGLVector2D *v);
void OGLPoint3D_Transform(const OGLPoint3D *point3D, const OGLMatrix4x4 *matrix4x4,
								OGLPoint3D *result);
void OGLMatrix4x4_SetScale(OGLMatrix4x4 *m, float x, float y, float z);
void OGLMatrix4x4_SetRotate_X(OGLMatrix4x4	*m, float angle);
void OGLMatrix4x4_SetRotate_Y(OGLMatrix4x4	*m, float angle);
void OGLMatrix4x4_SetRotate_Z(OGLMatrix4x4	*m, float angle);
void OGLMatrix4x4_SetRotateAboutPoint(OGLMatrix4x4 *matrix4x4, const OGLPoint3D	*origin,
									float xAngle, float yAngle, float zAngle);
void OGLMatrix4x4_GetFrustumToWindow(const OGLSetupOutputType *setupInfo, OGLMatrix4x4 *m);

void OGLMatrix4x4_Multiply(const OGLMatrix4x4	*mA, const OGLMatrix4x4 *mB, OGLMatrix4x4	*result);
void OGLMatrix4x4_SetRotate_XYZ(OGLMatrix4x4 *m, float rx, float ry, float rz);
void OGLMatrix3x3_SetRotate(OGLMatrix3x3 *m, double angle);
void OGLMatrix3x3_SetIdentity(OGLMatrix3x3 *m);
void OGLPoint2D_Transform(const OGLPoint2D *p, const OGLMatrix3x3 *m, OGLPoint2D *result);
float OGLVector3D_Dot(const OGLVector3D	*v1, const OGLVector3D	*v2);
float OGLVector2D_Dot(const OGLVector2D	*v1,  const OGLVector2D	*v2);
float OGLVector2D_Cross(const OGLVector2D *v1, const OGLVector2D *v2);
void OGLMatrix4x4_Transpose(const OGLMatrix4x4 *matrix4x4, OGLMatrix4x4 *result);
void OGLVector3D_Normalize(const OGLVector3D *vector3D, OGLVector3D	*result);
void OGLVector3D_Transform(const OGLVector3D *vector3D,	const OGLMatrix4x4	*matrix4x4, OGLVector3D *result);
void OGLMatrix4x4_SetRotateAboutAxis(OGLMatrix4x4	*m, const OGLVector3D	*axis, float angle);

void OGLMatrix3x3_Multiply(const OGLMatrix3x3	*matrixA,
							const OGLMatrix3x3	*matrixB,
							OGLMatrix3x3		*result);

void OGLMatrix3x3_SetRotateAboutPoint(OGLMatrix3x3 *m, OGLPoint2D *origin, double angle);
void OGLVector2D_Transform(const OGLVector2D *vector2D, 
							const OGLMatrix3x3	*matrix3x3,
							OGLVector2D			*result);
void OGLMatrix4x4_SetTranslate(OGLMatrix4x4 *m, float x, float y, float z);
void OGLMatrix4x4_SetIdentity(OGLMatrix4x4 *m);
void OGLMatrix3x3_SetTranslate(OGLMatrix3x3 *m, float x, float y);

void OGLPoint3D_To4DTransformArray(const OGLPoint3D *inVertex, const OGLMatrix4x4  *matrix,
									OGLPoint4D *outVertex,  long numVertices);
void OGLPoint3D_TransformArray(const OGLPoint3D *inVertex, const OGLMatrix4x4  *matrix,
									OGLPoint3D *outVertex,  long numVertices);
void OGLPoint2D_TransformArray(const OGLPoint2D *inVertex, const OGLMatrix3x3  *matrix,
									OGLPoint2D *outVertex,  long numVertices);
void OGLVector3D_TransformArray(const OGLVector3D *inVectors, const OGLMatrix4x4 *m,
								 OGLVector3D *outVectors, int numVectors);


Boolean OGL_IsBBoxVisible(const OGLBoundingBox *bBox, OGLMatrix4x4	*localToWorld);
void OGLVector2D_Normalize(const OGLVector2D *vector2D, OGLVector2D	*result);
float OGLPoint3D_Distance(const OGLPoint3D *p1, const OGLPoint3D *p2);
float OGLPoint2D_Distance(const OGLPoint2D *p1, const OGLPoint2D *p2);
void ApplyFrictionToRotation(float f,OGLVector3D *d);
void ApplyFrictionToDeltasXZ(float f,OGLVector3D *d);

void OGLBoundingBox_Transform(OGLBoundingBox *inBox, OGLMatrix4x4 *m, OGLBoundingBox *outBox);


#ifdef __cplusplus
}
#endif


/*********** INTERSECTION OF Y AND PLANE ********************/
//
// INPUT:	
//			x/z		:	xz coords of point
//			p		:	ptr to the plane
//
//
// *** IMPORTANT-->  This function does not check for divides by 0!! As such, there should be no
//					"vertical" polygons (polys with normal->y == 0).
//

#define IntersectionOfYAndPlane(_x, _z, _p)	(((_p)->constant - (((_p)->normal.x * _x) + ((_p)->normal.z * _z))) / (_p)->normal.y)


/***************** ANGLE TO VECTOR ******************/
//
// Returns a normalized 2D vector based on a radian angle
//
// NOTE: +rotation == counter clockwise!
//

static inline void AngleToVector(float angle, OGLVector2D *theVector)
{
	theVector->x = -sin(angle);
	theVector->y = -cos(angle);
}



/******************** FAST RECIPROCAL **************************/
//
//   y1 = y0 + y0*(1 - num*y0), where y0 = recip_est(num)
//

#define FastReciprocal(_num,_out)		\
{										\
	float	_y0;						\
										\
	_y0 = __fres(_num);					\
	_out = _y0 * (1 - _num*_y0) + _y0;	\
}


/***************** FAST VECTOR LENGTH 3D/2D *********************/

#define VectorLength3D(_r, _inX, _inY, _inZ)	_r = sqrt((_inY*_inY) + (_inX*_inX) + (_inZ*_inZ));

#define VectorLength2D(_r, _inX, _inY)	_r = sqrt((_inY*_inY) + (_inX*_inX));




/***************** CALC FACE NORMAL: NOT NORMALIZED *********************/
//
// Returns the normal vector off the face defined by 3 points.
//

static inline void CalcFaceNormal_NotNormalized(const OGLPoint3D *p1, const OGLPoint3D *p2, const OGLPoint3D *p3, OGLVector3D *normal)
{
float		dx1,dx2,dy1,dy2,dz1,dz2;

	dx1 = (p1->x - p3->x);
	dy1 = (p1->y - p3->y);
	dz1 = (p1->z - p3->z);
	dx2 = (p2->x - p3->x);
	dy2 = (p2->y - p3->y);
	dz2 = (p2->z - p3->z);


			/* DO CROSS PRODUCT */
			
	normal->x =   dy1 * dz2 - dy2 * dz1;
	normal->y =   dx2 * dz1 - dx1 * dz2; 
	normal->z =   dx1 * dy2 - dx2 * dy1;			
}




/************* CALC QUICK DISTANCE ****************/
//
// Does cheezeball quick distance calculation on 2 2D points.
//

static inline float CalcQuickDistance(float x1, float y1, float x2, float y2)
{
float	diffX,diffY;

	diffX = fabs(x1-x2);
	diffY = fabs(y1-y2);

	if (diffX > diffY)
	{
		return(diffX + (0.375f*diffY));			// same as (3*diffY)/8
	}
	else
	{
		return(diffY + (0.375f*diffX));
	}

}

#endif









