
/**********************************************************
 *                                                        *
 *                   Morphing                             *
 *                                                        *
 * This file contains the main functions for the morphing *
 *                                                        *
 * Author : Ubi China - Alexis Vaisse                     *
 *                                                        *
 **********************************************************/

#ifndef U64 //AR980226 These functions are in u_morph.c for U64
#define HieFriend
#define PO_FRIEND
#define ENV_FRIEND
#define D_3dData_StructureDefine
#define	D_ObjsTbls_Define
#include "ACP_base.h"
#include "GAM/ToolsCPA.h"
#include "gam.h"

#include "GMT.h"
#include "GEO.h"
#include "GLI.h"
#include "PCS.h"
#include "COL.h"

#include "SPO.h"

#include "PO.h"


#include "mec.h"
#include "prt.h"
#include "fil.h"
#include "snd.h"
//#include "fon.h"
#include "ipt.h"
//#include "env.h"
//#include "srf.h"
#include "sct.h"
// ANNECY - MT 08/05/98 {
#include "TMP.h"
// END ANNECY MT }

#include "x:/cpa/tempgrp/gliglou/multidrv/inc/visus_st.h"

//End XB

#include "mor.h"

/* Variables */

int                 MaxMorphTask = -1;              // The last morph task used. -1 if none
MOR_tdstMorphStruct MorphList [MAX_MORPH_TASKS];    // List of the objects being morphed


/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_xPrepareMorph
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Launch a morphing task
 *-----------------------------------------------------------------------------*/
ACP_tdxIndex MOR_fn_xPrepareMorph (HIE_tdxHandleToSuperObject _hActorSuperObject ,
                                   ACP_tdxIndex _xStartIndexInObjectTable , ACP_tdxIndex _xEndIndexInObjectTable   ,
								   int _iNbMillisecond , int _iStart , int _iEnd ,
                                   unsigned char _ucMorphType , ACP_tdxIndex _xChannelIndex)
{
	ACP_tdxIndex	xMorphListIndex;
	ACP_tdxIndex    j;
	ACP_tdxIndex    xFreePosition=-1;
	MOR_tdstMorphStruct  * mp;
	PO_tdstPhysicalObject* pStartPhysicalObject;
	PO_tdstPhysicalObject* pEndPhysicalObject;
    PO_tdstPhysicalObject* pNewPhysicalObject;

    _xStartIndexInObjectTable --;    // The user enters index >= 1
    _xEndIndexInObjectTable   --;    // and internal indexes are >= 0

    // Check if there is already a morphing in the given channel of the given actor
	xMorphListIndex = fn_hMorphListIndex (_hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->hMorphChannelList,_xChannelIndex);
	if(xMorphListIndex != -1 && MorphList [xMorphListIndex] . bInUse == TRUE)  return -1;

	// Find an empty position in the list
	for(j=0 ; j<MAX_MORPH_TASKS ; j++)
	{
		if(MorphList[j] . bInUse == FALSE)
		{
			xFreePosition = j;
			break;
		}
	}
	if(xFreePosition == -1)  return -1;    // The list is full

	// Check of the two objects have the same number of points
	pStartPhysicalObject = (PO_tdxHandleToPhysicalObject) _hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->h_CurrentObjectsTable->d_stObjectsTable[_xStartIndexInObjectTable].h_Target;
	pEndPhysicalObject   = (PO_tdxHandleToPhysicalObject) _hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->h_CurrentObjectsTable->d_stObjectsTable[_xEndIndexInObjectTable].h_Target;

	if (GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet) != GLI_lGetVisualSetNumbertOfLOD(pEndPhysicalObject->_hVisualSet))
		return -1;    // The physical objects don't have the same number of geometric objects

	for(j=0 ; j<GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet); j++)
	{
		if (GLI_hGetVisualSetGeometricForADistance(pStartPhysicalObject->_hVisualSet,j)->xNbPoints !=
            GLI_hGetVisualSetGeometricForADistance(pEndPhysicalObject->_hVisualSet,j) -> xNbPoints)
			return -1;    // The geometric objects don't have the same number of points
	}

	// Fill the element in the list

	mp = MorphList + xFreePosition;
	mp -> bInUse                   = TRUE;

        // Copy the parameters
    mp -> xChannelIndex            = _xChannelIndex;
	mp -> hActorSuperObject        = _hActorSuperObject;
	mp -> xStartIndexInObjectTable = _xStartIndexInObjectTable;
	mp -> xEndIndexInObjectTable   = _xEndIndexInObjectTable;

	mp -> iNbMillisecond           = _iNbMillisecond * 10;
	mp -> iStart                   = _iStart         * 10;
	mp -> iEnd                     = _iEnd           * 10;
    mp -> iCurrent                 = _iStart         * 10;
	mp -> ucMorphType              = _ucMorphType;
    mp -> iStep                    = mp -> iStart <= mp -> iEnd ? 10 : -10;

	    // Store the original physical object
    mp -> pOriginalPhysicalObject  = pStartPhysicalObject;

	// Create a new physical object
// ANNECY - MT 08/05/98 {
	MMG_fn_vAddMemoryInfo (MMG_C_lTypeMOR , MMG_C_lSubTypePO , 0);
    MOR_M_pvMalloc (pNewPhysicalObject , PO_tdstPhysicalObject * , sizeof (PO_tdstPhysicalObject));
// END ANNECY MT }

	// Store it into the element of the list
	mp -> pMorphedPhysicalObject = pNewPhysicalObject;

	// Create its visual set
// ANNECY - MT 11/05/98 {
	pNewPhysicalObject -> _hVisualSet                      = GLI_p_stCreateTMPVisualSet (GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet));
// END ANNECY MT }
	pNewPhysicalObject -> _hVisualSet -> xLastDistance     = pStartPhysicalObject->_hVisualSet->xLastDistance;

	pNewPhysicalObject -> _hVisualSet -> xNbLodDefinitions = (short)GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet);

	// Copy the collision set
	pNewPhysicalObject -> _hCollideSet     = pStartPhysicalObject->_hCollideSet;
	pNewPhysicalObject -> _hBoundingVolume = pStartPhysicalObject->_hBoundingVolume;

	// Create the geometric objects
	for(j=0 ; j<GLI_lGetVisualSetNumbertOfLOD(pNewPhysicalObject->_hVisualSet) ; j++)
	{
		pNewPhysicalObject->_hVisualSet->d_xThresholdsTable[j]   = mp -> pOriginalPhysicalObject->_hVisualSet->d_xThresholdsTable[j];
		pNewPhysicalObject->_hVisualSet->d_p_stLodDefinitions[j] = MOR_fn_xCopyGeometricObjectForMorph (mp->pOriginalPhysicalObject->_hVisualSet->d_p_stLodDefinitions[j]);
	}

    // Put the morphed object into the list of morphed objects of the actor
	fn_vPutMorphElementInChannel (_hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData -> hMorphChannelList ,
                                  _xChannelIndex , xFreePosition);

    // Update MaxMorphTask (which contains the maximum index of the used elements in the list
    if (xFreePosition > MaxMorphTask)  MaxMorphTask = xFreePosition;

	return	xFreePosition;
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_xStopMorph
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Stop a morphing task
 *-----------------------------------------------------------------------------*/
ACP_tdxIndex MOR_fn_xStopMorph(HIE_tdxHandleToSuperObject _hActorSuperObject, ACP_tdxIndex _xChannelIndex)
{
	ACP_tdxIndex          xMorphListIndex;
	ACP_tdxIndex	      j;
	MOR_tdstMorphStruct * mp=NULL;

    // Check that the given channel of the given actor has a morphed object
	xMorphListIndex = fn_hMorphListIndex (_hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->hMorphChannelList, _xChannelIndex);
	if(xMorphListIndex == -1 || MorphList [xMorphListIndex] . bInUse == FALSE)
		return	-1;    // Not found

    // Find the element in the list
	for(j=0 ; j<=MaxMorphTask ; j++)
	{
		mp=MorphList+j;

		if (mp -> bInUse && mp -> hActorSuperObject == _hActorSuperObject && mp -> xChannelIndex == _xChannelIndex)
		     break;
        else mp = NULL;
	}

	if (mp == NULL)  return -1;    // Not found into the list

    // Replace the morphed object by the 'end' object in the hierarchy
    MOR_fn_bRemoveMorphedObjectFromHierarchy (mp -> hActorSuperObject , mp -> pMorphedPhysicalObject,
                                              (PO_tdxHandleToPhysicalObject) mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData ->
                                              h_CurrentObjectsTable -> d_stObjectsTable [mp -> xEndIndexInObjectTable] . h_Target);

    // Delete the created physical object
    MOR_fn_vDeletePhysicalObject (mp -> pMorphedPhysicalObject);
    mp -> pMorphedPhysicalObject = NULL;

    // Remove the element in the list (if we remove the last morph task, we can decreaze MaxMorphTask)
    MOR_fn_vCleanMorph (mp);
    if (j == MaxMorphTask)  MaxMorphTask --;

    // Remove the morphed object from the list of morphed objects of the actor
	fn_vRemoveChannelFromMorphList (_hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData -> hMorphChannelList , _xChannelIndex);

	return	0;    // No error
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_xMorphing
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Morph an object in a channel
 *-----------------------------------------------------------------------------*/
ACP_tdxIndex MOR_fn_xMorphing (HIE_tdxHandleToSuperObject _hActorSuperObject ,
                               ACP_tdxIndex _xStartIndexInObjectTable , ACP_tdxIndex _xEndIndexInObjectTable   ,
							   int _iRatio , ACP_tdxIndex _xChannelIndex)
{
	ACP_tdxIndex	xMorphListIndex;
	ACP_tdxIndex    j;
	MOR_tdstMorphStruct  * mp;
	PO_tdstPhysicalObject* pStartPhysicalObject;
	PO_tdstPhysicalObject* pEndPhysicalObject;
    PO_tdstPhysicalObject* pNewPhysicalObject;
    ACP_tdxBool            bCreateObject;

	// Check of the two objects have the same number of points
	pStartPhysicalObject = (PO_tdxHandleToPhysicalObject) _hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->h_CurrentObjectsTable->d_stObjectsTable[_xStartIndexInObjectTable].h_Target;
	pEndPhysicalObject   = (PO_tdxHandleToPhysicalObject) _hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->h_CurrentObjectsTable->d_stObjectsTable[_xEndIndexInObjectTable].h_Target;

	if (GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet) != GLI_lGetVisualSetNumbertOfLOD(pEndPhysicalObject->_hVisualSet))
		return -1;    // The physical objects don't have the same number of geometric objects

	for(j=0 ; j<GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet); j++)
	{
		if (pStartPhysicalObject -> _hVisualSet -> d_p_stLodDefinitions [j] -> xNbPoints !=
            pEndPhysicalObject   -> _hVisualSet -> d_p_stLodDefinitions [j] -> xNbPoints)
			return -1;    // The geometric objects don't have the same number of points
	}

    // Check if there is already a morphing in the given channel of the given actor
	xMorphListIndex = fn_hMorphListIndex (_hActorSuperObject->hLinkedObject.p_stCharacter->h_3dData->hMorphChannelList,_xChannelIndex);
    // If not, create the morphing task
	if (xMorphListIndex == -1 || MorphList [xMorphListIndex] . bInUse == FALSE)
    {
        xMorphListIndex = -1;
	    // Find an empty position in the list
	    for(j=0 ; j<MAX_MORPH_TASKS ; j++)
	    {
		    if(MorphList[j] . bInUse == FALSE)
		    {
			    xMorphListIndex = j;
			    break;
		    }
	    }
	    if(xMorphListIndex == -1)  
			return -1;    // The list is full

        bCreateObject = TRUE;
    }
    else bCreateObject = FALSE;

	// Fill the element in the list
	mp = MorphList + xMorphListIndex;
	mp -> bInUse                   = TRUE;

        // Copy the parameters
    mp -> xChannelIndex            = _xChannelIndex;
	mp -> hActorSuperObject        = _hActorSuperObject;
	mp -> xStartIndexInObjectTable = _xStartIndexInObjectTable;
	mp -> xEndIndexInObjectTable   = _xEndIndexInObjectTable;

	mp -> iNbMillisecond           = 100;    // 10 milliseconds < 1/60 second
	mp -> iEnd                     = _iRatio;
	mp -> iStart                   = mp -> iEnd - 1;
    mp -> iCurrent                 = mp -> iStart;
	mp -> ucMorphType              = MORPH_WAITATEND;
    mp -> iStep                    = 10;

	    // Store the original physical object
    mp -> pOriginalPhysicalObject  = pStartPhysicalObject;

    if (bCreateObject)
    {
    	// Create a new physical object
// ANNECY - MT 08/05/98 {
		MMG_fn_vAddMemoryInfo (MMG_C_lTypeMOR , MMG_C_lSubTypePO , 0);
        MOR_M_pvMalloc (pNewPhysicalObject , PO_tdstPhysicalObject * , sizeof (PO_tdstPhysicalObject));
// END ANNECY MT }

    	// Store it into the element of the list
    	mp -> pMorphedPhysicalObject = pNewPhysicalObject;

    	// Create its visual set
// ANNECY - MT 11/05/98 {
    	pNewPhysicalObject -> _hVisualSet                      = GLI_p_stCreateTMPVisualSet (GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet));
// END ANNECY MT }
    	pNewPhysicalObject -> _hVisualSet -> xLastDistance     = pStartPhysicalObject->_hVisualSet->xLastDistance;
    	pNewPhysicalObject -> _hVisualSet -> xNbLodDefinitions = (short)GLI_lGetVisualSetNumbertOfLOD(pStartPhysicalObject->_hVisualSet);

    	// Copy the collision set
        	pNewPhysicalObject -> _hCollideSet     = pStartPhysicalObject->_hCollideSet;
	    pNewPhysicalObject -> _hBoundingVolume = pStartPhysicalObject->_hBoundingVolume;

    	// Create the geometric objects
    	for(j=0 ; j<GLI_lGetVisualSetNumbertOfLOD(pNewPhysicalObject->_hVisualSet) ; j++)
    	{
		    pNewPhysicalObject->_hVisualSet->d_xThresholdsTable[j]   = mp -> pOriginalPhysicalObject->_hVisualSet->d_xThresholdsTable[j];
		    pNewPhysicalObject->_hVisualSet->d_p_stLodDefinitions[j] = MOR_fn_xCopyGeometricObjectForMorph (mp->pOriginalPhysicalObject->_hVisualSet->d_p_stLodDefinitions[j]);
	    }

        // Put the morphed object into the list of morphed objects of the actor
    	fn_vPutMorphElementInChannel (_hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData -> hMorphChannelList ,
                                      _xChannelIndex , xMorphListIndex);
    }

    // Update MaxMorphTask (which contains the maximum index of the used elements in the list
    if (xMorphListIndex > MaxMorphTask)  MaxMorphTask = xMorphListIndex;

	return	xMorphListIndex;
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vMorphTo
 *-----------------------------------------------------------------------------
 *  Status      : private function
 *  Description : Compute one frame of a morphed object
 *-----------------------------------------------------------------------------*/
void MOR_fn_vMorphTo (MOR_tdstMorphStruct * mp)
{
	MTH_tdxReal             xRatio;
	ACP_tdxIndex            i,j;
	PO_tdstPhysicalObject   * phy1 , * phy2 , * phy3;
	GEO_tdstGeometricObject * geo1 , * geo2 , * geo3;

    // Compute the ratio (between 0 and 1)
	xRatio = MTH_M_xDiv (MTH_M_xLongToReal (mp -> iCurrent) , MTH_M_xLongToReal (mp -> iNbMillisecond));

    if (MTH_M_bGreater (xRatio , MTH_C_ONE ))  xRatio = MTH_C_ONE;
    else
    if (MTH_M_bLess    (xRatio , MTH_C_ZERO))  xRatio = MTH_C_ZERO;

    // Get the physical object : phy1 is the 'start' object, phy2 the 'end' object and phy3 the morphed object
	phy1 = (PO_tdxHandleToPhysicalObject) mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData ->
                                                h_CurrentObjectsTable -> d_stObjectsTable [mp -> xStartIndexInObjectTable] . h_Target;
	phy2 = (PO_tdxHandleToPhysicalObject) mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData ->
                                                h_CurrentObjectsTable -> d_stObjectsTable [mp -> xEndIndexInObjectTable  ] . h_Target;
	phy3 = mp -> pMorphedPhysicalObject;

    // For each geometric object of the physical object ...
	for(i=0 ; i<GLI_lGetVisualSetNumbertOfLOD(phy1->_hVisualSet) ; i++)
	{
		geo1 = phy1 -> _hVisualSet -> d_p_stLodDefinitions [i];
		geo2 = phy2 -> _hVisualSet -> d_p_stLodDefinitions [i];
		geo3 = phy3 -> _hVisualSet -> d_p_stLodDefinitions [i];

        // For each point of the geometric object ...
		for(j=0 ; j<geo3 -> xNbPoints ; j++)
		{
			MTH3D_M_vLinearInterpolVector (& geo3 -> d_stListOfPoints [j] , & geo1 -> d_stListOfPoints [j] ,
										   & geo2 -> d_stListOfPoints [j] , xRatio);
/* The calculation of the normals is done by the GEO_vEndModifyObject2 function
			MTH3D_M_vLinearInterpolVector (& geo3 -> d_stListOfPointsNormals [j] ,
										   & geo1 -> d_stListOfPointsNormals [j] ,
										   & geo2 -> d_stListOfPointsNormals [j] , xRatio);
*/
		}

        // Recompute the normals
        GEO_vEndModifyObject2 (geo3);
	}
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vCleanMorph
 *-----------------------------------------------------------------------------
 *  Status      : private function
 *  Description : Remove one element in the list of morph elements
 *-----------------------------------------------------------------------------*/
void MOR_fn_vCleanMorph (MOR_tdstMorphStruct * mp)
{
	if( mp -> bInUse == FALSE)  return;

	memset(mp , 0 , sizeof (MOR_tdstMorphStruct));

	mp -> bInUse = FALSE;
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vMorphAddInMainLoop
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Call each frame to compute the morphed objects
 *-----------------------------------------------------------------------------*/
void GEO_fn_vMorphAddInMainLoop (void)
{
	ACP_tdxIndex          i;
	MOR_tdstMorphStruct * mp;
    int                   iIncrement;

	for(i=0 ; i<=MaxMorphTask ; i++)
	{
		mp = MorphList + i;

		if(mp -> bInUse)
		{
            // Increaze the counter of the duration of the last frame (in millisecond)
            iIncrement      = g_stEngineStructure.stEngineTimer.ulUsefulDeltaTime * mp -> iStep;
            mp -> iCurrent += iIncrement;

            switch (mp -> ucMorphType)
            {
                case MORPH_STOPATEND:
                    // If we have reached the end of the morphing, we stop the morphing task
                    if(mp -> iCurrent > mp -> iEnd)
                    {
                        // Replace the morphed object by the 'end' object in the hierarchy
                        MOR_fn_bRemoveMorphedObjectFromHierarchy (mp -> hActorSuperObject, mp -> pMorphedPhysicalObject,
                                                                  (PO_tdxHandleToPhysicalObject) mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData ->
                                                                  h_CurrentObjectsTable -> d_stObjectsTable [mp -> xEndIndexInObjectTable] . h_Target);

                        // Delete the create physical object
                        MOR_fn_vDeletePhysicalObject (mp -> pMorphedPhysicalObject);

                        // Remove the morphed object from the list of morphed objects of the actor
                        fn_vRemoveChannelFromMorphList (mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData -> hMorphChannelList, mp -> xChannelIndex);

                        // Remove the element in the list (if we remove the last morph task, we can decreaze MaxMorphTask)
                        MOR_fn_vCleanMorph (mp);
                        if (i == MaxMorphTask)  MaxMorphTask --;

                        break;
                    }

                    // Compute the morphed object
                    MOR_fn_vMorphTo (mp);
                    break;

                case MORPH_WAITATEND:
                    // If we have reached the end of the morphing ...
		            if (mp -> iCurrent > mp -> iEnd)
                    {
                        // If the last computed frame was not the 'end' frame, we compute it
                        if (mp -> iCurrent - iIncrement < mp -> iEnd)  mp -> iCurrent = mp -> iEnd;
                        // Else we do nothing
                        else break;
                    }

                    // Compute the morphed object
			        MOR_fn_vMorphTo (mp);
                    break;

                case MORPH_ROLLBACKATEND:
                    // If we have reached the end of the first morph (from 'Start' to 'End')
		            if(mp -> iCurrent > mp -> iEnd)
                    {
                        if ( mp -> iCurrent < mp -> iEnd + (mp -> iEnd - mp -> iStart))
                              mp -> iCurrent = mp -> iEnd - (mp -> iCurrent - mp -> iEnd);
                        else  mp -> iCurrent = mp -> iEnd;

                        mp -> iStep = - abs (mp -> iStep);
                    }
                    else
                        // If we have reached the end of the second morph (from 'End' to 'Start')
                    if (mp -> iCurrent < mp -> iStart)
                    {
                        if (mp -> iCurrent > mp -> iStart - (mp -> iEnd - mp -> iStart))
                              mp -> iCurrent = mp -> iStart + (mp -> iStart - mp -> iCurrent);
                        else  mp -> iCurrent = mp -> iStart;

                        mp -> iStep = abs (mp -> iStep);
                    }

                    // Compute the morphed object
    	            MOR_fn_vMorphTo (mp);
	                break;
            }  // end switch
        }  // end if
    }  // end for
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vFirstInitMorphTaskList
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Initialization of the morphing module
 *-----------------------------------------------------------------------------*/
void  fn_vFirstInitMorphTaskList()
{
	memset (MorphList , 0 , sizeof (MOR_tdstMorphStruct) * MAX_MORPH_TASKS);
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vLastDeinitMorphTaskList
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Desinitialization of the morphing module
 *-----------------------------------------------------------------------------*/
void  fn_vLastDeinitMorphTaskList()
{
	ACP_tdxIndex i;
	for(i=0 ; i<=MaxMorphTask ; i++)
	{
		if(MorphList[i] . bInUse)
		{
			MOR_fn_vCleanMorph (MorphList + i);			
		}
	}
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_bIsChannelMorphed
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Return TRUE if there is a morphed object in the given channel of the given actor
 *-----------------------------------------------------------------------------*/
ACP_tdxBool MOR_fn_bIsChannelMorphed (HIE_tdxHandleToSuperObject _hActorSuperObject, ACP_tdxIndex _xChannelIndex)
{
    ACP_tdxIndex          i;
	MOR_tdstMorphStruct * mp;

    for (i = 0 ; i <= MaxMorphTask ; i ++)
    {
        mp = MorphList + i;

        if (mp -> bInUse && mp -> hActorSuperObject == _hActorSuperObject && mp -> xChannelIndex == _xChannelIndex)
        {
            // With the 'MORPH_WAITATEND' or 'MORPH_STOPATEND' type, the morphing is finished if we have reached the end
            if ((mp -> ucMorphType == MORPH_WAITATEND || mp -> ucMorphType == MORPH_STOPATEND) && mp -> iCurrent >= mp -> iEnd)
                 return FALSE;
            else return  TRUE;
        }
    }

    return FALSE;    // Not found
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_vRemoveAllMorphedObject
 *-----------------------------------------------------------------------------
 *  Status      : public function
 *  Description : Remove all the morphed objects (used when there is a 'Reinit the map' or when entering the editor)
 *-----------------------------------------------------------------------------*/
void MOR_fn_vRemoveAllMorphedObjects (void)
{
    ACP_tdxIndex            i;
	MOR_tdstMorphStruct   * mp;

    for (i = 0 ; i <= MaxMorphTask ; i ++)
    {
        mp = MorphList + i;

        if (mp -> bInUse)
        {
            // Replace the morphed object by the original physical object in the hierarchy
            MOR_fn_bRemoveMorphedObjectFromHierarchy (mp -> hActorSuperObject , mp -> pMorphedPhysicalObject , mp -> pOriginalPhysicalObject);

            // Remove the morphed object from the list of morphed objects of the actor
            if (mp->hActorSuperObject->hLinkedObject.p_stCharacter->h_StandardGame)
              fn_vRemoveChannelFromMorphList   (mp -> hActorSuperObject -> hLinkedObject . p_stCharacter -> h_3dData -> hMorphChannelList, mp -> xChannelIndex);

            // Delete the created physical object
            MOR_fn_vDeletePhysicalObject (mp->pMorphedPhysicalObject);

            // Remove the element in the list
            MOR_fn_vCleanMorph (mp);
        }
    }

    MaxMorphTask = -1;
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_bRemoveMorphedObjectFromHierarchy
 *-----------------------------------------------------------------------------
 *  Status      : Private function
 *  Description : Recursiv function to remove and replace an object in the hierarchy
 *-----------------------------------------------------------------------------*/
ACP_tdxBool MOR_fn_bRemoveMorphedObjectFromHierarchy (HIE_tdxHandleToSuperObject hSuperObject ,
                                                      PO_tdstPhysicalObject * p_stPhysicalObjectToRemove ,
                                                      PO_tdstPhysicalObject * p_stPhysicalObjectToPut)
{
    HIE_tdxHandleToSuperObject hChildSuperObject;
    ACP_tdxIndex               i;

    if (HIE_fn_hGetSuperObjectObject (hSuperObject) == p_stPhysicalObjectToRemove)
    {
        HIE_fn_vSetSuperObjectObjectAndType (hSuperObject , p_stPhysicalObjectToPut , HIE_C_ulPO);
        return TRUE;
    }

    HIE_M_ForEachChildOf (hSuperObject , hChildSuperObject , i)
    {
        if (MOR_fn_bRemoveMorphedObjectFromHierarchy (hChildSuperObject , p_stPhysicalObjectToRemove , p_stPhysicalObjectToPut))
            return TRUE;
    }

    return FALSE;
}

/*-----------------------------------------------------------------------------
 *  Name        : MOR_fn_hGetMorphedObject
 *-----------------------------------------------------------------------------
 *  Status      : Public function
 *  Description : Return the morphed object of an element in the list
 *-----------------------------------------------------------------------------*/
PO_tdxHandleToPhysicalObject MOR_fn_hGetMorphedObject (ACP_tdxIndex _xIndex)
{
    return MorphList [_xIndex] . pMorphedPhysicalObject;
}

/*----------------------------------------------------------------------------
--  Description : MOR_fn_vDoMorph
--  Execute the morphing operation corresponding to an index into the morph
--  task list
------------------------------------------------------------------------------
--  Methods : Use the morphto function
------------------------------------------------------------------------------
--  Input :		_xIndex : Index of the task
--  Output :    
------------------------------------------------------------------------------
--  Creation date : Mar 97		                Author: Alain Robin
------------------------------------------------------------------------------
--  Modifications :     
--  Modification date :                         Modification author : 
----------------------------------------------------------------------------*/
void MOR_fn_vDoMorph(ACP_tdxIndex _xIndex)
{
	MOR_tdstMorphStruct   * mp;

	mp = MorphList + _xIndex;
	MOR_fn_vMorphTo(mp);
}

#endif //U64 AR980226
