/*
 =======================================================================================
  Name        : Linear.c
  Description : compute serial operation on verticex
 =======================================================================================
 */

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#define MTH_LOW
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 #include"gli_st.h"
#include "GLI_Defn.h"
#include "vertex.h"
#include "light_st.h"
#include "PvObj_st.h"
#include "camera.h"
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
#ifdef __cplusplus
extern "C" 
{
#endif
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/*
 =======================================================================================
    Globals & Externals & Protos 
 =======================================================================================
 */
extern void GLI_vSinusEffetOn3DVertex( MTH3D_tdstVector * p_stVertex3D );

/*
 =======================================================================================
    Macros
 =======================================================================================
 */
#define GLI_M_SWAP(a,b)\
    {\
        fSwapValue = (a);\
        (a) = (b);\
        (b) = fSwapValue ;\
    }\

 
/*
 =======================================================================================
    Functions
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : serial dot product : do a dot product for a list of source point
                    source point are MTH3D_tdstVector (ie 3 float)
 ----------------------------------------------------------------------------------------
 */
void GLI_xSerialDotProduct(long lNbOfVertex, MTH3D_tdstVector *p_stSource, float *p_stDest, MTH3D_tdstVector *p_stMultiplicator, long lIncrement)
{
    float *p_stLastResult;

    p_stLastResult = p_stDest + lNbOfVertex;
    
    for ( ; p_stDest < p_stLastResult; p_stSource++ )
        *p_stDest++ = MTH_M_xRealToFloat( MTH3D_M_xDotProductVector( p_stSource,p_stMultiplicator) ) ;
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : serial dot product : do a dot product for a list of point
                source point are Aligned3DVector (ie 4 float)
 ----------------------------------------------------------------------------------------
 */
void GLI_xSerialDotProductAligned ( long lNbOfVertex, GLI_tdstAligned3DVector *p_stSource, float *p_stDest, MTH3D_tdstVector *p_stMultiplicator, long lIncrement) 
{
    float *p_stLastResult;

    p_stLastResult = p_stDest + lNbOfVertex;

    for ( ; p_stDest < p_stLastResult; p_stSource++ )
        *p_stDest++ = MTH_M_xRealToFloat ( MTH3D_M_xDotProductVector( (MTH3D_tdstVector *)p_stSource, p_stMultiplicator) ) ;
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : multiply a list of vertex with a matrix
            source vertices are MTH3D_tdstVector (ie 3 float)
            target vertices are Aligned3DVector (ie 4 float)
 ----------------------------------------------------------------------------------------
 */
void GLI_xSerialLinearOp( long lNbOfVertex, MTH3D_tdstVector *p_stSource, GLI_tdstAligned3DVector *p_stDest, POS_tdstCompletePosition *p_stMatrix)
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    MTH3D_tdstVector        stX,stY,stZ,stTrans;
    GLI_tdstAligned3DVector *p_stLastDest ;
    float                   fSwapValue;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
        
    POS_fn_vGetTransformMatrix ( p_stMatrix,&stX,&stY,&stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);
    POS_fn_vGetTranslationVector( p_stMatrix,&stTrans);
    p_stLastDest = p_stDest + lNbOfVertex;
    for ( ; p_stDest < p_stLastDest; p_stDest++, p_stSource++)
    {
        p_stDest->xX = MTH3D_M_xDotProductVector ( &stX , p_stSource ) + stTrans.xX;
        p_stDest->xY = MTH3D_M_xDotProductVector ( &stY , p_stSource ) + stTrans.xY;
        p_stDest->xZ = MTH3D_M_xDotProductVector ( &stZ , p_stSource ) + stTrans.xZ;
    }
}

/*
 ----------------------------------------------------------------------------------------
  Description   : new version of above function, parameters are in big structure
 ----------------------------------------------------------------------------------------
 */
void GLI_xNewSerialLinearOp ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    MTH3D_tdstVector        *p_stSource ;
    float                   fSwapValue;
    MTH3D_tdstVector        stX,stY,stZ,stTrans;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    
    POS_fn_vGetTransformMatrix ( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix, &stX, &stY, &stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);
    POS_fn_vGetTranslationVector( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stTrans);

    p_stDest        = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_stSource      = GLI_BG->p_stObj->d_stListOfPoints ;
    p_stLastDest    = p_stDest + GLI_BG->p_stObj->xNbPoints;

    for ( ; p_stDest < p_stLastDest ; p_stDest++ , p_stSource++ )
    {
        p_stDest->xX = MTH3D_M_xDotProductVector( &stX, p_stSource ) + stTrans.xX;
        p_stDest->xY = MTH3D_M_xDotProductVector( &stY, p_stSource ) + stTrans.xY;
        p_stDest->xZ = MTH3D_M_xDotProductVector( &stZ, p_stSource ) + stTrans.xZ;
    }
}

/*
 ----------------------------------------------------------------------------------------
  Description   : projection of 3D point that are in camera system axis
 ----------------------------------------------------------------------------------------
 */
void GLI_xNewSerialProjOp ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    GLI_tdstAligned2DVector *p_st2DDest;
    float fScaleX,fScaleY;
    long lNewX;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    *(float * )&lNewX = 8000.0f;
    
    fScaleX = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xX ;
    fScaleY = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xY ;
    
    p_stDest     = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_st2DDest   = GLI_BG->GLI_ScreenPoint;
    p_stLastDest = p_stDest + GLI_BG->p_stObj->xNbPoints;
    
    if (GLI_BG -> lClippingModeMask & GLI_C_ClipMaskZ)
    {
        for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_st2DDest++ )
        {
            if ( *(long *)&p_stDest->xZ < *(long *)&GLI_BG->p_stCurrentCamera->xNear)
            {
                *(long *)&p_st2DDest -> xOoZ |= 0x80000000;
                *((unsigned long *)&p_st2DDest->xX) = lNewX | ((*((unsigned long *)&p_stDest->xX) & 0x80000000) ^ 0x80000000);
                *((unsigned long *)&p_st2DDest->xY) = lNewX | ((*((unsigned long *)&p_stDest->xY) & 0x80000000) ^ 0x80000000);
                continue ;
            }
            p_st2DDest->xOoZ  = MTH_M_xInvLow(p_stDest->xZ ) ;
            p_st2DDest->xX    = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest->xX * fScaleX * p_st2DDest->xOoZ ;
            p_st2DDest->xY    = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest->xY * fScaleY * p_st2DDest->xOoZ ;
            p_st2DDest->xOoZ *= GLI_C_xZClippingNear;
        }
    }
    else
    {
        for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_st2DDest++ )
        {
            p_st2DDest->xOoZ  = MTH_M_xInvLow(p_stDest->xZ ) ;
            p_st2DDest->xX    = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest->xX * fScaleX * p_st2DDest->xOoZ ;
            p_st2DDest->xY    = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest->xY * fScaleY * p_st2DDest->xOoZ ;
            p_st2DDest->xOoZ *= GLI_C_xZClippingNear;
        }
    }
}
 
/*
 ----------------------------------------------------------------------------------------
  Description   : turned and project object vertex to get 2D coords
 ----------------------------------------------------------------------------------------
 */
void GLI_xNewSerialLinearProjOpZClip ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    GLI_tdstAligned2DVector *p_st2DDest;
    MTH3D_tdstVector        *p_stSource ;
    MTH3D_tdstVector        stX, stY, stZ, stTrans;
    float                   fSwapValue, fScaleX, fScaleY, fTmpZ;
    long                    lNewX;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    *(float * )&lNewX = 8000.0f;

    fScaleX = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xX;
    fScaleY = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xY;

    POS_fn_vGetTransformMatrix ( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix, &stX, &stY, &stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);

    POS_fn_vGetTranslationVector( &GLI_BG -> gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stTrans);

    p_stDest     = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_st2DDest   = GLI_BG->GLI_ScreenPoint;
    p_stSource   = GLI_BG->p_stObj->d_stListOfPoints ;
    p_stLastDest = p_stDest + GLI_BG->p_stObj->xNbPoints;

    for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_stSource++ , p_st2DDest++ )
    {
        p_stDest->xX = MTH3D_M_xDotProductVector( &stX , p_stSource ) + stTrans.xX;
        p_stDest->xY = MTH3D_M_xDotProductVector( &stY , p_stSource ) + stTrans.xY;
        p_stDest->xZ = MTH3D_M_xDotProductVector( &stZ , p_stSource ) + stTrans.xZ;

        if ( *(long *)&p_stDest->xZ < *(long *)&GLI_BG -> p_stCurrentCamera -> xNear)
        {
			/* point is before the near plane, so */
			/* turn OoZ into negative value */
            *(long *)&p_st2DDest -> xOoZ |= 0x80000000;
			/* and put X and Y to +/- 8000.0f according to the sign of the 3dPoint coordinates*/
            *((unsigned long *)&p_st2DDest ->xX) = lNewX | ((*((unsigned long *)&p_stDest->xX) & 0x80000000) ^ 0x80000000);
            *((unsigned long *)&p_st2DDest ->xY) = lNewX | ((*((unsigned long *)&p_stDest->xY) & 0x80000000) ^ 0x80000000);
            continue ;
        }

        fTmpZ = MTH_M_xInvLow(p_stDest->xZ);
        p_st2DDest->xX   = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest->xX * fScaleX * fTmpZ;
        p_st2DDest->xY   = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest->xY * fScaleY * fTmpZ;
        p_st2DDest->xOoZ = fTmpZ * GLI_C_xZClippingNear;
    }
}
 
/*
 ----------------------------------------------------------------------------------------
  Description   : turned and project object vertex to get 2D coords
                apply sinus effect on 3D transformed vertices
 ----------------------------------------------------------------------------------------
 */
void GLI_xSerialLinearProjOpZClipWithEffect ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    GLI_tdstAligned2DVector *p_st2DDest;
    MTH3D_tdstVector        *p_stSource ;
    MTH3D_tdstVector        stX,stY,stZ,stTrans;
    float                   fSwapValue,fScaleX,fScaleY, fTmpZ;
    long                    lNewX;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    *(float * )&lNewX = 8000.0f;

    fScaleX = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xX;
    fScaleY = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xY;

    POS_fn_vGetTransformMatrix ( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix, &stX, &stY, &stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);

    POS_fn_vGetTranslationVector( &GLI_BG -> gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stTrans);

    p_stDest     = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_st2DDest   = GLI_BG->GLI_ScreenPoint;
    p_stSource   = GLI_BG->p_stObj->d_stListOfPoints ;
    p_stLastDest = p_stDest + GLI_BG->p_stObj->xNbPoints;

    for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_stSource++ , p_st2DDest++ )
    {
        p_stDest->xX = MTH3D_M_xDotProductVector( &stX , p_stSource ) + stTrans.xX;
        p_stDest->xY = MTH3D_M_xDotProductVector( &stY , p_stSource ) + stTrans.xY;
        p_stDest->xZ = MTH3D_M_xDotProductVector( &stZ , p_stSource ) + stTrans.xZ;

        GLI_vSinusEffetOn3DVertex((MTH3D_tdstVector *)p_stDest) ;
 
        if ( *(long *)&p_stDest->xZ < *(long *)&GLI_BG -> p_stCurrentCamera -> xNear)
        {
            *(long *)&p_st2DDest -> xOoZ |= 0x80000000;
            *((unsigned long *)&p_st2DDest ->xX) = lNewX | ((*((unsigned long *)&p_stDest->xX) & 0x80000000) ^ 0x80000000);
            *((unsigned long *)&p_st2DDest ->xY) = lNewX | ((*((unsigned long *)&p_stDest->xY) & 0x80000000) ^ 0x80000000);
            continue ;
        }
        fTmpZ = MTH_M_xInvLow(p_stDest->xZ);
        p_st2DDest->xX = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest->xX * fScaleX * fTmpZ;
        p_st2DDest->xY = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest->xY * fScaleY * fTmpZ;
        p_st2DDest->xOoZ = fTmpZ * GLI_C_xZClippingNear;
    }
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : turned and project object vertex to get 2D coords
                    object is not Z clipped so do no test on Z
 ----------------------------------------------------------------------------------------
 */
void GLI_xNewSerialLinearProjOpNoZClip ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    GLI_tdstAligned2DVector *p_st2DDest;
    MTH3D_tdstVector        *p_stSource ;
    MTH3D_tdstVector        stX, stY, stZ, stTrans;
    float                   fSwapValue, fScaleX, fScaleY, fTmpZ;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    fScaleX = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xX ;
    fScaleY = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xY ;

	POS_fn_vGetTransformMatrix ( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stX , &stY , &stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);
	/* here, stX contains the first line of the matrix */
	/* stY the second, and stz the third */
    POS_fn_vGetTranslationVector( &GLI_BG->gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stTrans);

    p_stDest     = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_st2DDest   = GLI_BG->GLI_ScreenPoint;
    p_stSource   = GLI_BG->p_stObj->d_stListOfPoints ;
    p_stLastDest = p_stDest + GLI_BG->p_stObj->xNbPoints;

    for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_stSource++ , p_st2DDest++ )
    {
	    /* compute object's points in camera coordinates */
		/* this is a faster version of the mullmatrixvertex*/
		/* note that it uses the specially-computed stX, stY and stZ */
        p_stDest->xZ = MTH3D_M_xDotProductVector( &stZ , p_stSource ) + stTrans.xZ;
        p_stDest->xX = MTH3D_M_xDotProductVector( &stX , p_stSource ) + stTrans.xX;
        p_stDest->xY = MTH3D_M_xDotProductVector( &stY , p_stSource ) + stTrans.xY;
        
		/* and scale the coordinates to let them fit in the viewport, with perspective correction */
        fTmpZ = MTH_M_xInvLow(p_stDest->xZ);
        p_st2DDest->xX = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest->xX * fScaleX * fTmpZ;
        p_st2DDest->xY = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest->xY * fScaleY * fTmpZ;
        p_st2DDest->xOoZ = fTmpZ * GLI_C_xZClippingNear;
    }
}
 
/*
 ----------------------------------------------------------------------------------------
  Description   : turned and project object vertex to get 2D coords
                    object is not Z clipped so do no test on Z
                    apply sinus effect on transformed vertices
 ----------------------------------------------------------------------------------------
 */
void GLI_xSerialLinearProjOpNoZClipWithEffect ( GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BG )
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned3DVector *p_stDest;
    GLI_tdstAligned3DVector *p_stLastDest;
    GLI_tdstAligned2DVector *p_st2DDest;
    MTH3D_tdstVector        *p_stSource ;
    MTH3D_tdstVector        stX,stY,stZ,stTrans;
    float                   fSwapValue,fScaleX,fScaleY, fTmpZ;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    fScaleX = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xX ;
    fScaleY = GLI_BG->p_stCurrentCamera->xScreen * GLI_BG->p_stCurrentCamera->stScale.xY ;
    
    POS_fn_vGetTransformMatrix ( &GLI_BG -> gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stX , &stY , &stZ);
    GLI_M_SWAP (stX.xY,stY.xX);
    GLI_M_SWAP (stX.xZ,stZ.xX);
    GLI_M_SWAP (stY.xZ,stZ.xY);
    POS_fn_vGetTranslationVector( &GLI_BG -> gs_st_CurrentMatrixMultiplyedWithCameraMatrix , &stTrans);
    
    p_stDest     = GLI_BG->GLI_TurnedScaledDisplecedPoint;
    p_st2DDest   = GLI_BG->GLI_ScreenPoint;
    p_stSource   = GLI_BG->p_stObj->d_stListOfPoints ;
    p_stLastDest = p_stDest + GLI_BG -> p_stObj -> xNbPoints;
    
    for ( ; p_stDest < p_stLastDest ; p_stDest ++ , p_stSource++ , p_st2DDest++ )
    {
        p_stDest->xZ = MTH3D_M_xDotProductVector ( &stZ , p_stSource )  + stTrans . xZ;
        p_stDest->xX = MTH3D_M_xDotProductVector ( &stX , p_stSource )  + stTrans . xX;
        p_stDest->xY = MTH3D_M_xDotProductVector ( &stY , p_stSource )  + stTrans . xY;
        
        GLI_vSinusEffetOn3DVertex((MTH3D_tdstVector *)p_stDest) ;
        
        fTmpZ = MTH_M_xInvLow(p_stDest->xZ);
        p_st2DDest -> xX = GLI_BG->p_stCurrentCamera->stTrans.xX + p_stDest -> xX * fScaleX * fTmpZ;
        p_st2DDest -> xY = GLI_BG->p_stCurrentCamera->stTrans.xY + p_stDest -> xY * fScaleY * fTmpZ;
        p_st2DDest -> xOoZ = fTmpZ * GLI_BG->p_stCurrentCamera->xNear ;
    }
}

#ifdef __cplusplus
} /*extern "C"*/
#endif
