/*
 =======================================================================================
  Name        : VisuaSet.c
  Author      : philippe vimont
  Description : manage visual set structure
 =======================================================================================
 */
 
#include "Gli_st.h"
#include "GLI_Defn.h"
#include "VisuS_st.h"
#include "light_st.h"
#include "PvObj_st.h"
#include "matstack.h"
#include "VisuSet.h"
#include "proj.h"
#include "linear.h"
#include "vpt3D.h"
#include "camera.h"
#include "Object.h"
#include "pvobj_st.h"
#include "PCS.h"
#include "PO.h"
#include "IPO.h"
#include "ISI.h"
//#include "light.h"
 
/* See "ACP_Visualsets.DOC" for more */
#ifdef __cplusplus
extern "C" 
{
#endif
 
/*
 =======================================================================================
    externals
 =======================================================================================
 */

extern GLI_tdstInternalGlobalValuesFor3dEngine *GLI_BIG_GLOBALS;

/*
 =======================================================================================
    Creation
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : create a visual set
  lNbLOD              -> number of Level Of Detail
 ----------------------------------------------------------------------------------------
 */
GEO_tdstVisualSet *GLI_p_stCreateVisualSet( long lNbLOD )
{
    GEO_tdstVisualSet   *p_stLocalVS;
    ACP_tdxIndex        xLodCounter;
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_CPAMalloc(p_stLocalVS ,GEO_tdstVisualSet * ,sizeof ( GEO_tdstVisualSet ) ,E_uwGEONotEnoughtMemory );
    if ( p_stLocalVS == NULL) 
        return NULL;    
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_CPAMalloc(p_stLocalVS -> d_xThresholdsTable ,MTH_tdxReal *,sizeof ( MTH_tdxReal  ) * lNbLOD,E_uwGEONotEnoughtMemory );
    if ( p_stLocalVS -> d_xThresholdsTable == NULL) 
        return NULL;
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_CPAMalloc(p_stLocalVS -> d_p_stLodDefinitions ,GEO_tdstGeometricObject ** ,sizeof ( GEO_tdstGeometricObject * ) * lNbLOD,E_uwGEONotEnoughtMemory );
    if ( p_stLocalVS -> d_p_stLodDefinitions  == NULL) 
        return NULL;
        
    p_stLocalVS -> xNbLodDefinitions = (ACP_tdxIndex)lNbLOD;
        
    for (xLodCounter = 0; xLodCounter < lNbLOD; xLodCounter ++)
    {
        p_stLocalVS -> d_xThresholdsTable[xLodCounter] = 0.0;
        p_stLocalVS -> d_p_stLodDefinitions[xLodCounter] = NULL;
    }
        
    return (p_stLocalVS);
};
 
/*
 ----------------------------------------------------------------------------------------
  Description   : create a visual set in the TMP memory block
  lNbLOD              -> number of Level Of Detail
 ----------------------------------------------------------------------------------------
 */
GEO_tdstVisualSet *GLI_p_stCreateTMPVisualSet ( long lNbLOD)
{
    GEO_tdstVisualSet *p_stLocalVS;
    ACP_tdxIndex xLodCounter;
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_TMPMalloc(p_stLocalVS ,GEO_tdstVisualSet * ,sizeof ( GEO_tdstVisualSet ) );
    if ( p_stLocalVS == NULL) 
        return(NULL);
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_TMPMalloc(p_stLocalVS -> d_xThresholdsTable ,MTH_tdxReal *,sizeof ( MTH_tdxReal  ) * lNbLOD );
    if ( p_stLocalVS -> d_xThresholdsTable == NULL) 
        return(NULL);
        
    MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeVisualSet , NULL);
    GEO_M_TMPMalloc(p_stLocalVS -> d_p_stLodDefinitions ,GEO_tdstGeometricObject ** ,sizeof ( GEO_tdstGeometricObject * ) * lNbLOD );
    if ( p_stLocalVS -> d_p_stLodDefinitions  == NULL) 
        return(NULL);
        
    p_stLocalVS -> xNbLodDefinitions = (ACP_tdxIndex)lNbLOD;
        
    for (xLodCounter = 0; xLodCounter < lNbLOD; xLodCounter ++)
    {
        p_stLocalVS -> d_xThresholdsTable[xLodCounter] = 0.0;
        p_stLocalVS -> d_p_stLodDefinitions[xLodCounter] = NULL;
    }
        
    return (p_stLocalVS);
};
 
/*
 =======================================================================================
    Access functions
 =======================================================================================
 */

 /*
 ----------------------------------------------------------------------------------------
  Description   : set / get a LOD of a visual set object
 ----------------------------------------------------------------------------------------
 */
void GLI_vSetVisualSetLOD ( GEO_tdstVisualSet *p_stVisualSet, long lNumberOfLodToSet, MTH_tdxReal xThreshold, GEO_tdstGeometricObject *p_stObject)
{
    if (lNumberOfLodToSet < p_stVisualSet->xNbLodDefinitions)
    {
        p_stVisualSet -> d_xThresholdsTable[lNumberOfLodToSet] = xThreshold;
        p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSet] = p_stObject;
    }
}
    
void GLI_vGetVisualSetLOD ( GEO_tdstVisualSet *p_stVisualSet, long lNumberOfLodToGet, MTH_tdxReal *p_xThreshold, GEO_tdstGeometricObject **h_stObject )
{
    if (lNumberOfLodToGet < p_stVisualSet -> xNbLodDefinitions)
    {
        *p_xThreshold = p_stVisualSet -> d_xThresholdsTable[lNumberOfLodToGet] ;
        *h_stObject = p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToGet] ;
    }
}

GEO_tdstGeometricObject *GLI_hGetVisualSetGeometricForADistance( GEO_tdstVisualSet *p_stVSE, MTH_tdxReal xThreshold )
{
    long lLod;
        
    for (lLod = 0; lLod < p_stVSE->xNbLodDefinitions - 1;  lLod ++)
    {
        if (p_stVSE->d_xThresholdsTable[ lLod + 1 ] > xThreshold) 
            break;
    }
    return (p_stVSE->d_p_stLodDefinitions[lLod]);
}
 
    
/*
 ----------------------------------------------------------------------------------------
  Description   : get number of LOD
 ----------------------------------------------------------------------------------------
 */
long GLI_lGetVisualSetNumbertOfLOD ( GEO_tdstVisualSet *p_stVisualSet )
{
    return (p_stVisualSet->xNbLodDefinitions);
}
 
 
/*
 =======================================================================================
   Display
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : send object to display, with distance to camera computation, choice of LOD, 
                    fog check, and RLI
 ----------------------------------------------------------------------------------------
 */
void GLI_lSendVisualSetToViewportWithRLI2
( 
    struct GLD_tdstViewportAttributes_  *p_stVpt ,
    GEO_tdstVisualSet                   *p_stVisualSet,
    MTH3D_tdstVector                    *p_xCenter,
    long                                lDrawModeMask,
    ACP_tdxHandleToRadiosity            hISI
)
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    ACP_tdxIndex lNumberOfLodToSend;
    MTH_tdxReal xDistance;
    MTH3D_tdstVector stLocal;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
        
    GLI_xSetRLIForNextObjectToDraw ( NULL , NULL );
        
    /* if visualset has only one LOD, and no fog or infinite fog distance, then display the LOD 0 */
	if (p_stVisualSet -> xNbLodDefinitions == 1 && ( GLI_BIG_GLOBALS->xFogIsOn == 0 || GLI_BIG_GLOBALS->p_stActiveFog->xInfinite >= 50000.0f ) )
    {
        lNumberOfLodToSend = 0;
    }
    else
    {
        /*
         * compute distance from camera to center of object
         */
        POS_fn_vMulMatrixVertex ( &stLocal, g_p_stCurrentMatrix,p_xCenter );
        POS_fn_vMulMatrixVertex ( &stLocal, &(( GLI_tdstSpecificAttributesFor3D *)p_stVpt->p_vSpecificToXD)->p_stCam->stMatrix , &stLocal);
        xDistance = MTH3D_M_xNormVector( &stLocal ) ;
 
        /* if fog is on and object is hidden by fog, then no display */
		if (GLI_BIG_GLOBALS->xFogIsOn && xDistance >= GLI_BIG_GLOBALS->p_stActiveFog->xInfinite)
            return;
        
        /* find number of LOD to use according to distance */
		for (lNumberOfLodToSend = 0; lNumberOfLodToSend < p_stVisualSet -> xNbLodDefinitions - 1; lNumberOfLodToSend ++)
        {
            if (p_stVisualSet -> d_xThresholdsTable [ lNumberOfLodToSend+1 ] > xDistance) 
                break;
        }
    }
    
    /* look if it exist a RLI for the chosen LOD, if yes, set it */
    if ((hISI) && (lNumberOfLodToSend<=ISI_fn_xGetNbLOD(hISI)))
    {
        GLI_xSetRLIForNextObjectToDraw (ISI_fn_dGetVertexRLILOD(hISI,lNumberOfLodToSend), p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend]);
    }
        
    /* look if it exist a Geometric Object for the chosen LOD (???), if yes, send it to display */
    if (p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend] != NULL)
	{
        GLI_xSendObjectToViewportWithLights ( p_stVpt ,p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend],lDrawModeMask);
    }
}
 
/*
 ----------------------------------------------------------------------------------------
  Description   : send object to display, with distance to camera computation, choice of LOD, 
                    fog check, but no RLI
 ----------------------------------------------------------------------------------------
 */
void GLI_lSendVisualSetToViewportBV2( struct GLD_tdstViewportAttributes_ *p_stVpt, GEO_tdstVisualSet *p_stVisualSet, MTH3D_tdstVector *p_xCenter, MTH_tdxReal xRadiusOfSphere, long lDrawModeMask)
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    long                lNumberOfLodToSend;
    MTH3D_tdstVector    stLocal;
    MTH_tdxReal         xDistance;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
    GLI_xSetRLIForNextObjectToDraw ( NULL , NULL );
 
    /* if visualset has only one LOD, and no fog or infinite fog distance, then display the LOD 0 */
    if (p_stVisualSet -> xNbLodDefinitions == 1 && ( GLI_BIG_GLOBALS->xFogIsOn == 0 || GLI_BIG_GLOBALS->p_stActiveFog->xInfinite >= 50000.0f ) )
    {
        lNumberOfLodToSend = 0;
    }
    else
    {
        /*
         * compute distance from camera to center of object
         */
        POS_fn_vMulMatrixVertex ( &stLocal, g_p_stCurrentMatrix,p_xCenter );
        POS_fn_vMulMatrixVertex ( &stLocal, &(( GLI_tdstSpecificAttributesFor3D *)p_stVpt->p_vSpecificToXD)->p_stCam->stMatrix , &stLocal);
        xDistance = MTH3D_M_xNormVector( &stLocal ) ;
 
        /* if fog is on and object is hidden by fog, then no display */
        if (GLI_BIG_GLOBALS->xFogIsOn && xDistance >= GLI_BIG_GLOBALS->p_stActiveFog->xInfinite)
            return;
        
        /* find number of LOD to use according to distance */
        for (lNumberOfLodToSend = 0; lNumberOfLodToSend < p_stVisualSet -> xNbLodDefinitions - 1; lNumberOfLodToSend ++)
        {
            if (p_stVisualSet -> d_xThresholdsTable [ lNumberOfLodToSend+1 ] > xDistance) 
                break;
        }
    }
 
    /* look if it exist a Geometric Object for the chosen LOD (???), if yes, send it to display */
    if (p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend] != NULL) 
	{
	    GLI_xSendObjectToViewportWithLights (p_stVpt, p_stVisualSet->d_p_stLodDefinitions[lNumberOfLodToSend], lDrawModeMask);
    }
}
 
/*
 ----------------------------------------------------------------------------------------
  Description   : send object to display, with absolute distance, choice of LOD
 ----------------------------------------------------------------------------------------
 */
void GLI_lSendVisualSetToViewport( struct GLD_tdstViewportAttributes_ *p_stVpt, GEO_tdstVisualSet *p_stVisualSet, MTH_tdxReal xDistance, long lDrawModeMask)
{
    long lNumberOfLodToSend;
        
    /* reset RLI : no RLI for this function */
	GLI_xSetRLIForNextObjectToDraw ( NULL , NULL );
        
	/* find the right LOD for the given dstance */
    for (lNumberOfLodToSend = 0; lNumberOfLodToSend < p_stVisualSet -> xNbLodDefinitions - 1; lNumberOfLodToSend ++)
    {
        if (p_stVisualSet -> d_xThresholdsTable [ lNumberOfLodToSend+1 ] > xDistance) 
            break;
    }
 
    /* LOD found, check if exist and send to drawing */
	if (p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend] != NULL) 
	{
        GLI_xSendObjectToViewportWithLights ( p_stVpt, p_stVisualSet -> d_p_stLodDefinitions[lNumberOfLodToSend], lDrawModeMask);
    }
}

/*
 ----------------------------------------------------------------------------------------
  Description   : send object to display, with distance to camera computation, choice of LOD
 ----------------------------------------------------------------------------------------
 */
void GLI_lSendVisualSetToViewport2( struct GLD_tdstViewportAttributes_ *p_stVpt, GEO_tdstVisualSet *p_stVisualSet, MTH3D_tdstVector *p_xCenter, long lDrawModeMask)
{
    MTH3D_tdstVector stLocal;
    GLI_tdstSpecificAttributesFor3D *p_stSpecifAttrib3D;  
 
	/* put object center in camera axis system (p_xCenter is in local coordinates system */
    p_stSpecifAttrib3D = ( GLI_tdstSpecificAttributesFor3D *)p_stVpt->p_vSpecificToXD;
    /* put center in world axis system */
	POS_fn_vMulMatrixVertex ( &stLocal, g_p_stCurrentMatrix,p_xCenter );
	/* and in camera axis system */
    POS_fn_vMulMatrixVertex ( &stLocal, &(( GLI_tdstSpecificAttributesFor3D *)p_stVpt->p_vSpecificToXD)->p_stCam->stMatrix , &stLocal);

    /* and call display function with right distance */
    GLI_lSendVisualSetToViewport( p_stVpt ,p_stVisualSet  ,MTH3D_M_xNormVector( &stLocal ) ,lDrawModeMask);
}
 
/*
 =======================================================================================
  RLI
 =======================================================================================
 */
 
/*
 ----------------------------------------------------------------------------------------
  Description   : return one of RLI Table
 ----------------------------------------------------------------------------------------
 */
ACP_tdxHandleToRadiosity GLI_hGetRLI ( GEO_tdxHandleToVisualSet hVisualSet, unsigned char ucIndex ) 
{
    if ( ucIndex < hVisualSet->lNumberOfRLI )
        return hVisualSet->d_hRLI[ucIndex] ;
        
    return NULL;
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : compute a new RLI table that is a blend of two other table
 ----------------------------------------------------------------------------------------
 */
ACP_tdxHandleToRadiosity GLI_hComputeBlendRLI (GEO_tdxHandleToVisualSet hVisualSet, unsigned char ucIndex1, unsigned char ucIndex2, ACP_tdxIndex xNumISILOD, MTH_tdxReal xPercent) 
{
    if ( ucIndex1 < hVisualSet->lNumberOfRLI && ucIndex2 < hVisualSet->lNumberOfRLI ) 
    {
        return ISI_hComputeBlendRLI ( hVisualSet->d_hRLI[ucIndex1], hVisualSet->d_hRLI[ucIndex2], xNumISILOD, xPercent );
    }
    return NULL;
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : Set RLI table of a visual set
 ----------------------------------------------------------------------------------------
 */
void GLI_vVisualSetSetRLITable (GEO_tdxHandleToVisualSet hVisualSet, ACP_tdxHandleToRadiosity *d_hRLI, long lNumberOfTable) 
{
    if ( d_hRLI ) 
    {
        hVisualSet->d_hRLI = d_hRLI ;
        hVisualSet->lNumberOfRLI = lNumberOfTable ;
    }
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : Load the RLI section of the VSE of a charactere
 ----------------------------------------------------------------------------------------
 */
ACP_tdxHandleToRadiosity *GLI_vVisualSetLoadRLI (char *szSection, long *lNumberOfTable) 
{
    ACP_tdxHandleToRadiosity *d_hRLI ;
    SCR_tdst_Cxt_Values *p_stVal=NULL;
        
    p_stVal = SCR_fnp_st_RdL0_AnalyseSection(szSection, SCR_CDF_uw_Anl_Normal);
        
    d_hRLI = (ACP_tdxHandleToRadiosity *)SCR_M_ul_RdL0_ExtractLongValue(p_stVal,0);
    *lNumberOfTable = SCR_M_ul_RdL0_ExtractLongValue(p_stVal,1);
    return (d_hRLI);
}
    
/*
 ----------------------------------------------------------------------------------------
  Description   : script callback for RLI section
 ----------------------------------------------------------------------------------------
 */
SCR_tde_Anl_ReturnValue GLI_fn_xLoadRLI(SCR_tdst_File_Description *p_fFile, char *szAction, char *szParams[], SCR_tde_Anl_Action cType)
{
    long lNumberOfRLI ;
    ACP_tdxHandleToRadiosity *d_hRLI;
        
    switch (cType)
    {
        case SCR_EA_Anl_BeginSection:
            lNumberOfRLI = atol(szParams[1]) ;
            MMG_fn_vAddMemoryInfo (MMG_C_lTypeGLI , MMG_C_lSubTypeRLI , NULL);
            GEO_M_CPAMalloc(d_hRLI ,ACP_tdxHandleToRadiosity * ,sizeof ( ACP_tdxHandleToRadiosity ) ,E_uwGEONotEnoughtMemory );
            
            SCR_M_RdL0_SetSectionLong(0,0,(long)d_hRLI);
            SCR_M_RdL0_SetSectionLong(0,1,lNumberOfRLI);
            SCR_M_RdL0_SetContextLong(0,0,(long)d_hRLI);
            break;
    }
    return  SCR_ERV_Anl_NormalReturn;
}
   
#ifdef __cplusplus
} /*extern "C"*/
#endif
