/*
 =======================================================================================
  Name        : Culling.c
  Author      : vincent lhullier            Date :15/06/98
  Description : culling functions
 =======================================================================================
 */
 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#include "acp_base.h"
#include "Geo.h"
#include "Gli.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
/*
 =======================================================================================
	External function prototype 
 =======================================================================================
 */
 
/*
 * retrieve current matrix for function that avoid MulMatrixMatrix
 */
extern POS_tdstCompletePosition *HIE_hRetrieveCurrentMatrix();
 
 
 
#ifdef __cplusplus
extern "C" 
{
#endif
 
/*
 =======================================================================================
	static global var
 =======================================================================================
 */
MTH_tdxReal				        GEO_g_xZFar = 0.0f;
MTH_tdxReal				        GEO_g_xZFarTransparencyZone = 10.0f;
MTH_tdxReal                     GEO_g_xOoZFarTransparencyZone = 0.1f;
MTH_tdxReal				        GEO_g_xZFarTransparencyLevel;
 
static MTH3D_tdstVector				gs_stGlobalNormPlaneLeft;
static MTH3D_tdstVector				gs_stGlobalNormPlaneRight;
static MTH3D_tdstVector				gs_stGlobalNormPlaneUp;
static MTH3D_tdstVector				gs_stGlobalNormPlaneDown;
static MTH3D_tdstVector				gs_stGlobalNormPlaneNear;
static MTH3D_tdstVector				gs_stCameraPosInGlobal;
 
/*
 =======================================================================================
	Access function
 =======================================================================================
 */
 
MTH_tdxReal GEO_xGetZFar( void )                            { return GEO_g_xZFar; }
void        GEO_vSetZFar( MTH_tdxReal _xZ )                 { GEO_g_xZFar = _xZ; }
 
MTH_tdxReal GEO_xGetZFarTransparencyZone( void )            { return GEO_g_xZFarTransparencyZone; }
void        GEO_vSetZFarTransparencyZone( MTH_tdxReal _xZ ) 
{  
    GEO_g_xZFarTransparencyZone = _xZ; 
    if (_xZ)
        GEO_g_xOoZFarTransparencyZone = MTH_M_xInv( _xZ ); 
}
 
MTH_tdxReal GEO_xGetZFarTransparencyLevel( void )           { return GEO_g_xZFarTransparencyLevel; }
 
  
/*
 =======================================================================================
	Local functions
 =======================================================================================
 */
 
/**********************************************************************************************/
/* local function !!*/
/* use GEO_lCullingSphere or GEO_lCullingSphereNoMMM instead !!*/
/**/
/* Name: GEO_lMainCullingSphere*/
/* Goal: Return 1 if the sphere is visible with the viewport's camera, 0 else.*/
/* Code: Philippe Vimont*/
/* modif : Fabien Bole-Feysot*/
/* entry :	the viewport to test*/
/*			the center of the sphere, in object's local axis system*/
/*			the radius of the sphere, not including any scale or so*/
/*			the matrix that must be used to compute coords in the camera axis system*/
/*			the matrix that must be used to compute coords in the world axis system*/
/**********************************************************************************************/
long GEO_lMainCullingSphere
(
	GLD_tdstViewportAttributes	*p_stVpt ,
	MTH3D_tdstVector			*p_stShereCenter , 
	MTH_tdxReal					xRadius,
	POS_tdstCompletePosition	*p_stMatrix,
	POS_tdstCompletePosition	*p_stMatrixW,
	long _lCullingResult
)
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  GLI_tdstSpecificAttributesFor3D	*p_stSpecifAttrib3D;
/* XB 05/05/99 */
/*  MTH3D_tdstVector				stMin,stMax,stMinG,stMaxG; */
/* End XB 05/05/99 */
	MTH3D_tdstVector				stCenter;
	GLI_tdxHandleToCamera			hCamera;	
	MTH_tdxReal						xDotProduct;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
	if (!POS_fn_ulIsNotScaledMatrix ( p_stMatrix ))
  {
		/* the matrix is scaled...*/
		/* so compute a parallel box around the sphere (in global coords) and call the cullingbox function*/
		/*
		stMin.xX = p_stShereCenter -> xX - xRadius;
		stMin.xY = p_stShereCenter -> xY - xRadius;
		stMin.xZ = p_stShereCenter -> xZ - xRadius;
		stMax.xX = p_stShereCenter -> xX + xRadius;
		stMax.xY = p_stShereCenter -> xY + xRadius;
		stMax.xZ = p_stShereCenter -> xZ + xRadius;
		*/
		/* here, stMin & stMax are not scaled and in local coordinates system compute them in global coordinates*/
		/*
		POS_fn_vMulMatrixVertex (&stMinG,p_stMatrixW,&stMin);    
		POS_fn_vMulMatrixVertex (&stMaxG,p_stMatrixW,&stMax);    
		*/
		/* and cull this parallel box*/
		/*
		return GEO_lCullingBox (p_stVpt,&stMinG,&stMaxG, _lCullingResult);
		*/

		/* the previous box isn't ligned in global axis system */
		xRadius = MTH_M_xMul(xRadius,POS_fn_xGetMaxScale(p_stMatrix));
  }
 
	
	/* the matrix is not scaled, so perform plan rejection for sphere*/
	p_stSpecifAttrib3D = (GLI_tdstSpecificAttributesFor3D *) p_stVpt -> p_vSpecificToXD;
	hCamera = p_stSpecifAttrib3D->p_stCam;
 
	/* compute sphere center in camera coords*/
	POS_fn_vMulMatrixVertex (&stCenter,p_stMatrix,p_stShereCenter);    
			
	_lCullingResult |= GEO_C_lCullingIntersect;
	/* if the culling result against the near border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingNear) )
	{
		if ( stCenter.xZ + xRadius < hCamera->xNear )
		{
			return GEO_C_lCullingOut;
		}
 
		xDotProduct = MTH_M_xSub(stCenter.xZ, xRadius);
		/* if all points are beyond the near plane, the sphere is inside relatively to this plane*/
		if ( MTH_M_bGreater(xDotProduct, hCamera->xNear) )
		{
			_lCullingResult |= GEO_C_lCullingNear;
		}
	}
 
	/* if the culling result against the far border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingFar) )
	{
		if ( MTH_M_bDifferentZero(GEO_g_xZFar) ) 
		{
			if ( xDotProduct > GEO_g_xZFar )
			{
				GEO_g_xZFarTransparencyLevel = MTH_C_ZERO;
				return GEO_C_lCullingOut;
			}
     
			if ( xDotProduct > GEO_g_xZFar - GEO_g_xZFarTransparencyZone )
			{
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE - ((stCenter.xZ - xRadius - GEO_g_xZFar + GEO_g_xZFarTransparencyZone) * GEO_g_xOoZFarTransparencyZone);
			}
			else
			{
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
				/* if all points are in front of the far plane, the sphere is inside relatively to this plane*/
				_lCullingResult |= GEO_C_lCullingFar;
			}
		}
		else
		{
			GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
			/* if all points are in front of the far plane, the sphere is inside relatively to this plane*/
			_lCullingResult |= GEO_C_lCullingFar;
		}
	}
	else
	{
		GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
	}
 
	/* if the culling result against the left border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingLeft) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneRight); /*left and right plane normal car names are inversed!*/
		if (xDotProduct > xRadius + hCamera->xDistPlaneLeft) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneLeft - xRadius)
		{
			_lCullingResult |= GEO_C_lCullingLeft;
		}
	}
 
	/* if the culling result against the right border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingRight) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneLeft); /*left and right plane normal car names are inversed!*/
		if (xDotProduct > xRadius + hCamera->xDistPlaneRight) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneRight - xRadius)
		{
			_lCullingResult |= GEO_C_lCullingRight;
		}
	}
 
	/* if the culling result against the up border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingUp) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneUp);
		if (xDotProduct > xRadius + hCamera->xDistPlaneUp) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneUp - xRadius)
		{
			_lCullingResult |= GEO_C_lCullingUp;
		}
	}
 
	/* if the culling result against the down border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingDown) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneDown);
		if (xDotProduct > xRadius + hCamera->xDistPlaneDown) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneDown - xRadius)
		{
			_lCullingResult |= GEO_C_lCullingDown;
		}
	}
 
	return _lCullingResult;
}
 
 
/*
 =======================================================================================
	Exported functions
 =======================================================================================
 */
 
/*
 ***************************************************************************************
  Name: GLI_fn_vComputeCameraParametersForCulling
  Goal: pre compute camera parameters for faster culling
		compute clipping plane normal in global system axis to avoid object
		transformation from global to camera system axis
  Code: 
 ***************************************************************************************
 */
void GEO_fn_vComputeCameraParametersForCulling( GLD_tdstViewportAttributes *p_stVpt, MTH_tdxReal _xZFar )
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	POS_tdstCompletePosition		stMatrixInverse;
	register GLI_tdxHandleToCamera			hCamera;
	register MTH3D_tdstMatrix *hInvTransformMatrix;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
	hCamera = ((GLI_tdstSpecificAttributesFor3D *) p_stVpt->p_vSpecificToXD)->p_stCam;
 
	/* STM - camera matrix should _never_ be scaled*/
	assert (hCamera->stMatrix.ulType <= POS_C_ulRotationMatrixFlag);
 
	/*invert the camera's position and get an handle on the rotation*/
	POS_fn_vInvertIsoMatrix( &stMatrixInverse, &hCamera->stMatrix);
	hInvTransformMatrix = &stMatrixInverse.stTransformMatrix;
	/*near plane (we use stCameraPosInGlobal as a temporary vector...)*/
	MTH3D_M_vSetVectorElements( &gs_stCameraPosInGlobal, MTH_C_ZERO, MTH_C_ZERO, MTH_C_MinusONE );
	MTH3D_M_vMulMatrixVectorWithoutBuffer( &gs_stGlobalNormPlaneNear, hInvTransformMatrix, &gs_stCameraPosInGlobal );
	/*position*/
	MTH3D_M_vCopyVector( &gs_stCameraPosInGlobal, &stMatrixInverse.stTranslationVector );
	/*left plane (the relative left and right planes are inversed in the camera...)*/
	MTH3D_M_vMulMatrixVectorWithoutBuffer( &gs_stGlobalNormPlaneLeft, hInvTransformMatrix, &hCamera->stNormPlaneRight );
	/*right plane (the relative left and right planes are inversed in the camera...)*/
	MTH3D_M_vMulMatrixVectorWithoutBuffer( &gs_stGlobalNormPlaneRight, hInvTransformMatrix, &hCamera->stNormPlaneLeft );
	/*upper plane*/
	MTH3D_M_vMulMatrixVectorWithoutBuffer( &gs_stGlobalNormPlaneUp, hInvTransformMatrix, &hCamera->stNormPlaneUp );
	/*lower plane*/
	MTH3D_M_vMulMatrixVectorWithoutBuffer( &gs_stGlobalNormPlaneDown, hInvTransformMatrix, &hCamera->stNormPlaneDown );
 
	GEO_g_xZFar = _xZFar;
}
 
  
/**********************************************************************************************/
/* Name: GEO_lCullingGlobalSphere*/
/* Goal: Return 1 if the sphere is visible with the viewport's camera, 0 else.*/
/* Code: Philippe Vimont*/
/* modif : Fabien Bole-Feysot*/
/* entry :	the viewport to test*/
/*			the center of the sphere, in global axis system*/
/*			the radius of the sphere, not including any scale or so*/
/**/
/*	if you plan to use this function during the drawing process, then look at the*/
/*	GEO_lCullingSphereNoMMM below, which is faster.*/
/**********************************************************************************************/
#ifndef _FIRE_DEADCODE_U64_ /* Added by RUC */
long GEO_lCullingGlobalSphere
(
	GLD_tdstViewportAttributes	*_p_stVpt,
	MTH3D_tdstVector			*_p_stSphereCenter,
 	MTH_tdxReal					_xRadius,
	long _lCullingResult
)
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  GLI_tdstSpecificAttributesFor3D	*p_stSpecifAttrib3D;
	GLI_tdxHandleToCamera			hCamera;	
	MTH_tdxReal						xDotProduct;
	MTH3D_tdstVector stCenter;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
	/* the matrix is not scaled, so perform plan rejection for sphere*/
	p_stSpecifAttrib3D = (GLI_tdstSpecificAttributesFor3D *) _p_stVpt -> p_vSpecificToXD;
	hCamera = p_stSpecifAttrib3D->p_stCam;
 
	/* compute sphere center in camera coords*/
	POS_fn_vMulMatrixVertex (&stCenter,&hCamera->stMatrix,_p_stSphereCenter);
 
	_lCullingResult |= GEO_C_lCullingIntersect;
	/* if the culling result against the near border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingNear) )
	{
		if (stCenter.xZ + _xRadius < hCamera->xNear) 
		{
			return GEO_C_lCullingOut;
		}
 
		xDotProduct = stCenter.xZ - _xRadius;
		if (xDotProduct > hCamera->xNear)
		{
			/* if all points are beyond the near plane, the sphere is inside relatively to this plane*/
			_lCullingResult |= GEO_C_lCullingNear;
		}
	}
 
	/* if the culling result against the far border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingFar) )
	{
		if ( MTH_M_bDifferentZero(GEO_g_xZFar) ) 
		{
			if (stCenter.xZ - _xRadius > GEO_g_xZFar )
				return GEO_C_lCullingOut;
     
			if ( stCenter.xZ - _xRadius > GEO_g_xZFar - GEO_g_xZFarTransparencyZone )
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE - ((stCenter.xZ - _xRadius - GEO_g_xZFar + GEO_g_xZFarTransparencyZone) * GEO_g_xOoZFarTransparencyZone);
			else
			{
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
				/* if all points are in front of the far plane, the sphere is inside relatively to this plane*/
				_lCullingResult |= GEO_C_lCullingFar;
			}
		}
		else
		{
			GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
			/* if all points are in front of the far plane, the sphere is inside relatively to this plane*/
			_lCullingResult |= GEO_C_lCullingFar;
		}
	}
	else
	{
		GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
	}
 
	/* if the culling result against the left border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingLeft) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneRight); /*left and right plane normal car names are inversed!*/
		if (xDotProduct > _xRadius + hCamera->xDistPlaneLeft) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneLeft - _xRadius)
		{
			_lCullingResult |= GEO_C_lCullingLeft;
		}
	}
 
	/* if the culling result against the right border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingRight) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneLeft); /*left and right plane normal car names are inversed!*/
		if (xDotProduct > _xRadius + hCamera->xDistPlaneRight) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneRight - _xRadius)
		{
			_lCullingResult |= GEO_C_lCullingRight;
		}
	}
 
	/* if the culling result against the up border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingUp) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneUp);
		if (xDotProduct > _xRadius + hCamera->xDistPlaneUp) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneUp - _xRadius)
		{
			_lCullingResult |= GEO_C_lCullingUp;
		}
	}
 
	/* if the culling result against the down border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingDown) )
	{
		xDotProduct = MTH3D_M_xDotProductVector(&stCenter, &hCamera->stNormPlaneDown);
		if (xDotProduct > _xRadius + hCamera->xDistPlaneDown) 
		{
			return GEO_C_lCullingOut;
		}
		if (xDotProduct < hCamera->xDistPlaneDown - _xRadius)
		{
			_lCullingResult |= GEO_C_lCullingDown;
		}
	}
 
	return _lCullingResult;
}
#endif /* _FIRE_DEADCODE_U64_ */ /* Added by RUC */
/* Name: GEO_lCullingSphere*/
/* Goal: Return 1 if the sphere is visible with the viewport's camera, 0 else.*/
/* Code: Philippe Vimont*/
/* modif : Fabien Bole-Feysot*/
/* entry :	the viewport to test*/
/*			the center of the sphere, in object's local axis system*/
/*			the radius of the sphere, not including any scale or so*/
/*			the object's matrix (that must be used to compute coords in the global axis system)*/
/**/
/*	if you plan to use this function during the drawing process, then look at the*/
/*	GEO_lCullingSphereNoMMM below, which is faster.*/
/**********************************************************************************************/
#ifndef _FIRE_DEADCODE_U64_ /* Added by RUC */
long GEO_lCullingSphere 
(
	GLD_tdstViewportAttributes	*p_stVpt,
	MTH3D_tdstVector			*p_stSphereCenter,
 	MTH_tdxReal					xRadius,
	POS_tdstCompletePosition	*p_stMatrix,
	long _lCullingResult
)
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	POS_tdstCompletePosition		stTempMatrix;
	GLI_tdstSpecificAttributesFor3D *p_stSpecifAttrib3D;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	
	p_stSpecifAttrib3D = (GLI_tdstSpecificAttributesFor3D*) p_stVpt -> p_vSpecificToXD;
	
	/* compute the camera * local matrix*/
  POS_fn_vMulMatrixMatrix (&stTempMatrix,&p_stSpecifAttrib3D->p_stCam->stMatrix,p_stMatrix);
 
	/* call the sphere culling main function*/
	return GEO_lMainCullingSphere(p_stVpt,p_stSphereCenter,xRadius,&stTempMatrix,p_stMatrix,_lCullingResult);
}
#endif /* _FIRE_DEADCODE_U64_ */ /* Added by RUC */
 
/**********************************************************************************************/
/* Name: GEO_lCullingSphereNoMMM*/
/* Goal: Return 1 if the sphere is visible with the viewport's camera, 0 else.*/
/* Code: Philippe Vimont*/
/* modif : Fabien Bole-Feysot*/
/* entry :	the viewport to test*/
/*			the center of the sphere, in object's local axis system*/
/*			the radius of the sphere, not including any scale or so*/
/*			the object's matrix (that must be used to compute coords in the global axis system)*/
/**/
/*	use this function only in the drawing process, because the matrix that */
/*  will be used for computing will be retrieved with the HIE_hRetrieveCurrentMatrix() function.*/
/**/
/*  if the HIE_hRetrieveCurrentMatrix() matrix is not up to date, use GEO_lCullingSphere instead*/
/**********************************************************************************************/
long GEO_lCullingSphereNoMMM 
(
	GLD_tdstViewportAttributes *p_stVpt ,
	MTH3D_tdstVector *p_stSphereCenter , 
	MTH_tdxReal xRadius,
	POS_tdstCompletePosition *p_stMatrix,
	long _lCullingResult
)
{
	return GEO_lMainCullingSphere(p_stVpt,p_stSphereCenter,xRadius,HIE_hRetrieveCurrentMatrix(),p_stMatrix,_lCullingResult);
}
 
 
/**********************************************************************************************/
/* Name: GEO_lCullingBox*/
/* Goal: Return 1 if the parallele box is visible with the viewport's camera, 0 else.*/
/* Code: Philippe Vimont*/
/* modif : Fabien Bole-Feysot (optimisation)*/
/* modif : Vincent Lhullier (optimisation )*/
/* entry :	the viewport to test*/
/*			the min point of the parallel box, in the world global axis system*/
/*			the max point of the parallel box, in the world global axis system*/
/**********************************************************************************************/
long GEO_lCullingBox
(
	GLD_tdstViewportAttributes *p_stVpt,
	MTH3D_tdstVector *p_stMin,
	MTH3D_tdstVector *p_stMax,
	long _lCullingResult 
)
{
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	MTH3D_tdstVector				stMinWithCamera, stMaxWithCamera;
	MTH_tdxReal						xCompute, xValue0, xValue1, xValue2, xValue3, xValue4, xValue5;
	GLI_tdxHandleToCamera			hCamera;	
	ACP_tdxBool bComputed = FALSE;
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
	hCamera = ((GLI_tdstSpecificAttributesFor3D *) p_stVpt -> p_vSpecificToXD) -> p_stCam;
 
	MTH3D_M_vSubVector( &stMinWithCamera, p_stMin, &gs_stCameraPosInGlobal );
	MTH3D_M_vSubVector( &stMaxWithCamera, p_stMax, &gs_stCameraPosInGlobal );
 
	_lCullingResult |= GEO_C_lCullingIntersect;
 
	/* if the culling result against the near border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingNear) )
	{
		xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneNear.xX;
		xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneNear.xY;
		xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneNear.xZ;
		xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneNear.xX;
		xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneNear.xY;
		xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneNear.xZ;
 
		xCompute = ((xValue0 < xValue3) ? xValue0 : xValue3) + ((xValue1 < xValue4) ? xValue1 : xValue4) + ((xValue2 < xValue5) ? xValue2 : xValue5);
 
		/*if all points are behind the near plane, the box is out*/
		if ( xCompute > - hCamera->xNear ) 
		{
			return GEO_C_lCullingOut;
		}
		/* if all points are beyond the near plane, the box is inside relatively to this plane*/
		xCompute = ((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5);
		if ( xCompute < - hCamera->xNear )
		{
			_lCullingResult |= GEO_C_lCullingNear;
		}
		bComputed = TRUE; /*the test against the plane normal is already computed*/
	}
 
 
	/* if the culling result against the far border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingFar) )
	{
		if ( MTH_M_bDifferentZero(GEO_g_xZFar) )
		{
			if ( !bComputed ) /* we did not test agains the near plane, so we need to compute the test against the far plane normal*/
			{
				xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneNear.xX;
				xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneNear.xY;
				xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneNear.xZ;
				xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneNear.xX;
				xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneNear.xY;
				xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneNear.xZ;
 
				xCompute = - (((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5));
			}
			else
			{
				xCompute = - xCompute;
			}
			/* if all points are beyond the far clipping plane, the box is out*/
			if ( xCompute > GEO_g_xZFar )
				return GEO_C_lCullingOut;
 
			GEO_g_xZFarTransparencyLevel = GEO_g_xZFar - GEO_g_xZFarTransparencyZone;
			if ( xCompute > GEO_g_xZFarTransparencyLevel )
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE - ((xCompute - GEO_g_xZFarTransparencyLevel) * GEO_g_xOoZFarTransparencyZone);
			else
			{
				GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
				/* if all points are in front of the far plane, the box is inside relatively to this plane*/
				_lCullingResult |= GEO_C_lCullingFar;
			}
		}
		else
		{
			GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
			/* since there is no far plane, the box is inside relatively to this plane*/
			_lCullingResult |= GEO_C_lCullingFar;
		}
	}
	else
	{
		GEO_g_xZFarTransparencyLevel = MTH_C_ONE;
	}
	
	/* if the culling result against the left border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingLeft) )
	{
		xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneLeft.xX;
		xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneLeft.xY;
		xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneLeft.xZ;
		xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneLeft.xX;
		xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneLeft.xY;
		xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneLeft.xZ;
 
		/*if all points on the left side of the left plane, the box is out*/
		xCompute = ((xValue0 < xValue3) ? xValue0 : xValue3) + ((xValue1 < xValue4) ? xValue1 : xValue4) + ((xValue2 < xValue5) ? xValue2 : xValue5);
		if ( xCompute > hCamera->xDistPlaneLeft )
		{
			return GEO_C_lCullingOut;
		}
		/*if all points are on the right side of the left plane, the box is inside relatively to this plane*/
		xCompute = ((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5);
		if (xCompute < hCamera->xDistPlaneLeft )
		{
			_lCullingResult |= GEO_C_lCullingLeft;
		}
	}
 
	/* if the culling result against the right border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingRight) )
	{
		xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneRight.xX;
		xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneRight.xY;
		xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneRight.xZ;
		xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneRight.xX;
		xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneRight.xY;
		xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneRight.xZ;
 
		/*if all points on the right side of the right plane, the box is out*/
		xCompute = ((xValue0 < xValue3) ? xValue0 : xValue3) + ((xValue1 < xValue4) ? xValue1 : xValue4) + ((xValue2 < xValue5) ? xValue2 : xValue5);
		if (xCompute > hCamera->xDistPlaneRight )
		{
			return GEO_C_lCullingOut;
		}
		/*if all points are on the left side of the right plane, the box is inside relatively to this plane*/
		xCompute = ((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5);
		if (xCompute < hCamera->xDistPlaneRight )
		{
			_lCullingResult |= GEO_C_lCullingRight;
		}
	}
 
	/* if the culling result against the up border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingUp) )
	{
		xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneUp.xX;
		xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneUp.xY;
		xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneUp.xZ;
		xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneUp.xX;
		xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneUp.xY;
		xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneUp.xZ;
 
		/*if all points are above the upper plane, the box is out*/
		xCompute = ((xValue0 < xValue3) ? xValue0 : xValue3) + ((xValue1 < xValue4) ? xValue1 : xValue4) + ((xValue2 < xValue5) ? xValue2 : xValue5);
		if (xCompute > hCamera->xDistPlaneUp )
		{
			return GEO_C_lCullingOut;
		}
		/*if all points are below the upper plane, the box is inside relatively to this plane*/
		xCompute = ((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5);
		if (xCompute < hCamera->xDistPlaneUp )
		{
			_lCullingResult |= GEO_C_lCullingUp;
		}
	}
		
 
	/* if the culling result against the down border is not known as in, compute it*/
	if ( !(_lCullingResult & GEO_C_lCullingDown) )
	{
		xValue0 = stMinWithCamera.xX * gs_stGlobalNormPlaneDown.xX;
		xValue1 = stMinWithCamera.xY * gs_stGlobalNormPlaneDown.xY;
		xValue2 = stMinWithCamera.xZ * gs_stGlobalNormPlaneDown.xZ;
		xValue3 = stMaxWithCamera.xX * gs_stGlobalNormPlaneDown.xX;
		xValue4 = stMaxWithCamera.xY * gs_stGlobalNormPlaneDown.xY;
		xValue5 = stMaxWithCamera.xZ * gs_stGlobalNormPlaneDown.xZ;
 
		/*if all points are under the lower plane, the box is out*/
		xCompute = ((xValue0 < xValue3) ? xValue0 : xValue3) + ((xValue1 < xValue4) ? xValue1 : xValue4) + ((xValue2 < xValue5) ? xValue2 : xValue5);
		if (xCompute > hCamera->xDistPlaneDown )
		{
			return GEO_C_lCullingOut;
		}
		/*if all points are above the lower plane, the box is inside relatively to this plane*/
		xCompute = ((xValue0 > xValue3) ? xValue0 : xValue3) + ((xValue1 > xValue4) ? xValue1 : xValue4) + ((xValue2 > xValue5) ? xValue2 : xValue5);
		if (xCompute < hCamera->xDistPlaneDown )
		{
			_lCullingResult |= GEO_C_lCullingDown;
		}
	}
 
	return _lCullingResult;
}
 
 
/*
 * OLD CULLING
 
// DONT REMOVE SPACE ********************************************************************************************
// Name: GLI_lCullingBox
// Goal: Return 1 if the parallele box is visible with the viewport's camera, 0 else.
// Code: Philippe Vimont
// modif : Fabien Bole-Feysot (optimisation)
// entry :	the viewport to test
//			the min point of the parallel box, in the world global axis system
//			the max point of the parallel box, in the world global axis system
// DONT REMOVE SPACE ********************************************************************************************
long GLI_lNormalCullingBox
	(
		GLD_tdstViewportAttributes *p_stVpt ,
		MTH3D_tdstVector *p_stMin ,
		MTH3D_tdstVector *p_stMax 
	)
{
	GLI_tdstAligned3DVector a8_stVertexTrans [8], *p_stFastPointer;
	
	GLI_tdstSpecificAttributesFor3D *p_stSpecifAttrib3D;
	MTH3D_tdstVector stDiago, stOrigin;	
	MTH3D_tdstVector stIp, stJp, stKp;
	POS_tdstCompletePosition  *p_stPos;
	MTH3D_tdstMatrix *p_stMatrix;
	GLI_tdstAligned2DVector a8_st2DVertex [8] ;
	ACP_tdxIndex xCounter;
	MTH_tdxReal xNearPlane;		
 
	p_stSpecifAttrib3D = (GLI_tdstSpecificAttributesFor3D *) p_stVpt -> p_vSpecificToXD;
	p_stPos = &p_stSpecifAttrib3D->p_stCam->stMatrix;
	p_stMatrix = &p_stPos->stTransformMatrix;
	xNearPlane = p_stSpecifAttrib3D->p_stCam->xNear;
 
	
	// build the diagonal vector
	MTH3D_M_vSubVector(&stDiago,p_stMax,p_stMin);
	
	// compute the new box origin point
	POS_fn_vMulMatrixVertex (&stOrigin,p_stPos,p_stMin);
	
	// compute the new Ip,Jp,Kp vectors
	stIp.xX = MTH_M_xMul( p_stMatrix->stCol_0.xX, stDiago.xX);
	stIp.xY = MTH_M_xMul( p_stMatrix->stCol_0.xY, stDiago.xX);
	stIp.xZ = MTH_M_xMul( p_stMatrix->stCol_0.xZ, stDiago.xX);
	stJp.xX = MTH_M_xMul( p_stMatrix->stCol_1.xX, stDiago.xY);
	stJp.xY = MTH_M_xMul( p_stMatrix->stCol_1.xY, stDiago.xY);
	stJp.xZ = MTH_M_xMul( p_stMatrix->stCol_1.xZ, stDiago.xY);
	stKp.xX = MTH_M_xMul( p_stMatrix->stCol_2.xX, stDiago.xZ);
	stKp.xY = MTH_M_xMul( p_stMatrix->stCol_2.xY, stDiago.xZ);
	stKp.xZ = MTH_M_xMul( p_stMatrix->stCol_2.xZ, stDiago.xZ);
	
	
	// Build The Cube from its new origin and new base vectors
	MTH3D_M_vCopyVector(&a8_stVertexTrans[0],&stOrigin);
	MTH3D_M_vAddVector(&a8_stVertexTrans[1],&stOrigin,&stIp);	//i
	MTH3D_M_vAddVector(&a8_stVertexTrans[2],&stOrigin,&stJp);	//j
	MTH3D_M_vAddVector(&a8_stVertexTrans[3],&stOrigin,&stKp);	//k
	MTH3D_M_vAddVector(&a8_stVertexTrans[4],&a8_stVertexTrans[1],&stJp);	//i+j
	MTH3D_M_vAddVector(&a8_stVertexTrans[5],&a8_stVertexTrans[1],&stKp);	//i+k
	MTH3D_M_vAddVector(&a8_stVertexTrans[6],&a8_stVertexTrans[2],&stKp);	//j+k
	MTH3D_M_vAddVector(&a8_stVertexTrans[7],&a8_stVertexTrans[4],&stKp);	//i+j+k
	
	// Fast Cull on Z
	xCounter = 8;	// count the points that are not visible
	
	for (p_stFastPointer = a8_stVertexTrans+7; p_stFastPointer != a8_stVertexTrans-1; p_stFastPointer--)
	{
		if (xNearPlane >= p_stFastPointer->xZ)
		{
			// this point is invisible !
			xCounter--;
			p_stFastPointer->xZ = xNearPlane; // in case we must project this point...
		}
	}
	if (!xCounter)
	{
		// every point is invisible !
		return 0;
	}
	
	// part of the cube is in front of the camera, so project
	GLI_xSerialProjection (p_stSpecifAttrib3D -> p_stCam ,8,a8_stVertexTrans,a8_st2DVertex);
	// correct 3DFX bug
	g_stCurLLInterface.p_fnSerialSnap2DVertices (a8_st2DVertex,8);
	// and clip
	return g_stCurLLInterface.p_fnCullListOfPoints (p_stVpt , 8 , a8_st2DVertex);
}
* END OLD CULLING
*/
 
/*
 * TEST FOR CULLING
// DONT REMOVE SPACE ********************************************************************************************
// Name: GLI_lCullingBox
// Goal: Return 1 if the parallele box is visible with the viewport's camera, 0 else.
// Code: Philippe Vimont
// modif : Fabien Bole-Feysot (optimisation)
// entry :	the viewport to test
//			the min point of the parallel box, in the world global axis system
//			the max point of the parallel box, in the world global axis system
// DONT REMOVE SPACE ********************************************************************************************
extern struct tdstEngineStructure_ g_stEngineStructure;
 
long GLI_lCullingBox
	(
		GLD_tdstViewportAttributes *p_stVpt ,
		MTH3D_tdstVector *p_stMin ,
		MTH3D_tdstVector *p_stMax 
	)
{
	long								lMyResult, lNormalResult;
	static unsigned long				lClippedByMeButNotByNormal = 0;
	static unsigned	long				lClippedByNormalButNotByMe = 0;
	static char							cReturnType = 0;
	static unsigned long				ulTrameNumber = 0xFF;
	static GLD_tdstViewportAttributes	*p_stLastVpt = NULL;
 
	if ( ( *(unsigned long*) ((char *) &g_stEngineStructure + 0xEA8 ) != ulTrameNumber) || (p_stVpt != p_stLastVpt) )
	{
		ulTrameNumber = *(unsigned long*) ((char *) &g_stEngineStructure + 0xEA8 );
		p_stLastVpt = p_stVpt;
		GLI_fn_vComputeCameraParametersForCulling( p_stVpt );
	}
 
	lMyResult = GLI_lMyCullingBox( p_stVpt , p_stMin , p_stMax  );
	lNormalResult = GLI_lNormalCullingBox( p_stVpt , p_stMin , p_stMax  );
 
	if ((lMyResult == 0) && (lNormalResult != 0))
		lClippedByMeButNotByNormal++;
	else if ((lMyResult != 0) && (lNormalResult == 0))
		lClippedByNormalButNotByMe++;
 
	switch (cReturnType)
	{
		case 0 : return lMyResult;
		case 1 : return lNormalResult;
	}
}
* END TEST FOR CULLING
*/
 
 
#ifdef __cplusplus
} /*extern "C"*/
#endif
