/*======================================================================*/
/* 3DLib (E3d)								*/
/*									*/
/* Model hierarchy-related functions					*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Dec-20 23:55:35					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdarg.h>		// For varargs
#include <stdio.h>
#include <sys/types.h>

#include <ELists.h>
#include <EMalloc.h>
#include <EStrings.h>

#ifndef _E3D_h
#include <E3D/E3D.h>
#endif

#ifndef _E3DFace_h
#include <E3D/Face.h>
#endif

#ifndef _E3DMaterial_h
#include <E3D/Material.h>
#endif

#ifndef _E3DMatrix_h
#include <E3D/Matrix.h>
#endif

#ifndef _E3DModel_h
#include <E3D/Model.h>
#endif

#ifndef _E3DPolygon_h
#include <E3D/Polygon.h>
#endif

#ifndef _E3DPanel_h
#include <E3D/Panel.h>
#endif

#ifndef _E3DScene_h
#include <E3D/Scene.h>
#endif

#ifndef _E3DSpline_h
#include <E3D/Spline.h>
#endif


//========================================================================================
// Free a Model hierarchy
//
// Argument
//  E3dModel* LRootModel       Pointer to the Root of the hierarchy to be freed
//
// Description
//  This function parses through the given hierarchy and frees the memory associated
//  with each node and its Geometries.
//========================================================================================
void E3d_ModelHrcFree(E3dModel* LRootModel, EBool LAllTypes)
{
 E3dModel*	LModel;
 E3dModel*	LTModel;

//Printf("ModelHrcFree() %p [%s]\n", LRootModel, LRootModel->Name);fflush(stdout);

 for(LModel=LRootModel;LModel;)
 {
  if((LModel->Type==E3dMDL_NORMAL)||LAllTypes)
  {
   LTModel=LModel->Next;

   E3d_ModelFree(LModel);
   LModel=LTModel;
  }
 }
}


//================================================
// Collect Materials used in a hierarchy
//================================================
E3dMaterial** E3d_ModelHrcCollectMaterials(E3dModel* LRootModel, unsigned int* LNumOfMaterialsPtr)
{
 E3dModel*	LModel;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMaterial**	LMaterials;
 E3dMaterial*	LMaterial;
 unsigned int	LNumOfMaterials;
 unsigned int	LGmNum, LGmCnt;


 LMaterials=NULL;
 LNumOfMaterials=0;

 for(LModel=LRootModel;LModel;LModel=LModel->Next)
 {
  LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;

  for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
  {
   if((LGeometry=LGeometries[LGmCnt])!=NULL)
   {
    switch(LGeometry->GeoType)
    {
     caseE3dMESH():
      {
       unsigned int	LGCnt, LGNum;
       E3dMesh*		LMesh;
       E3dPolyGroup*	LPolyGroup;
       E3dPolyGroup**	LPolyGroups;

       LMesh=(E3dMesh*)LGeometry;

       if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'
       LPolyGroups=LMesh->PolyGroups;
       LGNum=LMesh->NumOfPolyGroups;

       for(LGCnt=0;LGCnt<LGNum;LGCnt++)
       {
	LPolyGroup=LPolyGroups[LGCnt];

	LMaterial=LPolyGroup->Material;

        if(LMaterial) ELst_AddPointerChk((void***)(&LMaterials), &LNumOfMaterials, LMaterial);
       }
      }
     break;

     case E3dGEO_FACE:
      {
       E3dFace*	LFace=(E3dFace*)LGeometry;

       if(LFace->Material) ELst_AddPointerChk((void***)(&LMaterials), &LNumOfMaterials, LFace->Material);
      }
     break;
    }
   }
  }
 }

 *LNumOfMaterialsPtr=LNumOfMaterials;
 return(LMaterials);
}


//========================================
// Duplicate a Model hierarchy
//========================================
E3dModel* E3d_ModelHrcDuplicate(E3dModel* LRootModel)
{
 E3dGeometry**		LGeometries;
 E3dGeometry**		LDGeometries;
 E3dModel*		LModel=LRootModel;

 E3dModel*		LDModel=NULL;
 E3dModel*		LDRootModel;
 E3dModel*		LDParentModel;
 E3dModel*		LDPrevModel;
 unsigned int		LC, LN, LMCnt;


 LDRootModel=NULL;

 if(LModel)
 {
  for(LMCnt=0, LDPrevModel=NULL, LDParentModel=NULL;LModel;LMCnt++)
  {
   if(LMCnt>0) LDPrevModel=LDModel;
   if((LDModel=E3d_ModelAllocate(LModel->Name))!=NULL)
   {
    if(LMCnt==0) LDRootModel=LDModel;
    if(LDPrevModel) LDPrevModel->Next=LDModel;
    LDModel->Parent=LDParentModel;
    LModel->UserData=LDModel;
    LDModel->Translation.X=LModel->Translation.X;LDModel->Translation.Y=LModel->Translation.Y;LDModel->Translation.Z=LModel->Translation.Z;
    LDModel->Scaling.X=LModel->Scaling.X;LDModel->Scaling.Y=LModel->Scaling.Y;LDModel->Scaling.Z=LModel->Scaling.Z;
    LDModel->Rotation.X=LModel->Rotation.X;LDModel->Rotation.Y=LModel->Rotation.Y;LDModel->Rotation.Z=LModel->Rotation.Z;

    if((LN=LModel->NumOfGeometries)!=0)
    {
// Allocate Geometry pointer array for new E3dModel
//
     if((LDGeometries=(E3dGeometry**)EMalloc(sizeof(E3dGeometry*)*LN))!=NULL)
     {
      LGeometries=LModel->Geometries;
      LModel->Geometries=LDGeometries;
      for(LC=0, LN=LModel->NumOfGeometries;LC<LN;LC++)
      {
       LDGeometries[LC]=LGeometries[LC];
       LGeometries[LC]->RefCnt+=1;		// Now one more E3dModel references this geometry
      }
     }
    }
   }
   else break;

//----------------------------------------------------------------
// Get the next Model in the hierarchy (depth first)
//----------------------------------------------------------------
   LModel->UserData=(E3dPointer)LDModel;
   if(LModel->Child)							// Does the current model have a child ? (1)
   {
    LModel=LModel->Child;						// (1Y) Yes, let the child be the next...
    LDParentModel=LDModel;						// The next model's parent is the current one
   }
   else									// (1N) No, does it have a sibling ? (2)
   {
    if(LModel->NextSibling) LModel=LModel->NextSibling;			// (2Y) Yes, let the sibling be the next...
    else								// (2N) No, go back upward in this branch until
    {									// we find a model that has a sibling
     while((LModel->NextSibling==NULL)&&(LModel->Parent!=NULL)) LModel=LModel->Parent;
     if(LModel)
     {
      LDParentModel=((E3dModel*)(LModel->UserData))->Parent;
      LModel=LModel->NextSibling;
     }
    }
   }
  }


//--------------------------------------------------------
// Run through again the Model hierarchy to set the
// hierarchy linking (only NextSibling and Child parts,
// for the Parent is already set in the previous loop)
//--------------------------------------------------------
  for(LModel=LRootModel, LDModel=LDRootModel;(LModel!=NULL)&&(LDModel!=NULL);LDModel=LDModel->Next)
  {
   if(LModel->NextSibling) LDModel->NextSibling=((E3dModel*)(LModel->NextSibling->UserData));
   else LDModel->NextSibling=NULL;
   if(LModel->Child) LDModel->Child=((E3dModel*)(LModel->Child->UserData));
   else LDModel->Child=NULL;

   if(LModel->Child) LModel=LModel->Child;
   else
   {
    if(LModel->NextSibling) LModel=LModel->NextSibling;
    else
    {
     while((LModel->NextSibling==NULL)&&(LModel->Parent!=NULL)) LModel=LModel->Parent;
     if(LModel) LModel=LModel->NextSibling;
    }
   }
  }
 }
 return(LDRootModel);
}


//========================================================
// Refresh material inheritances of a model hierarchy
//========================================================
void E3d_ModelHrcRefreshMaterialInheritance(E3dModel* LModel)
{
 E3dGeometry*	LGeometry;
 E3dGeometry**	LGeometries;
 unsigned int	LGmCnt, LGmNum;


 for(;LModel;LModel=LModel->Next)
 {
// Make sure the Geometries and sub-geometries have valid Materials for rendering
//
   LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
   for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
   {
    if((LGeometry=LGeometries[LGmCnt])!=NULL)
    {
     switch(LGeometry->GeoType)
     {
      caseE3dMESH():
       {
	E3dMesh*	LMesh=(E3dMesh*)LGeometry;
	unsigned int	LGCnt, LGNum;
	E3dPolyGroup**	LPolyGroups;
	E3dPolyGroup*	LPolyGroup;

	LGNum=LMesh->NumOfPolyGroups;LPolyGroups=LMesh->PolyGroups;
	for(LGCnt=0;LGCnt<LGNum;LGCnt++)
	{
	 LPolyGroup=LPolyGroups[LGCnt];
	 E3d_UpdateDrawingMaterial(LGeometry, LPolyGroup->Material, &(LPolyGroup->DrawingMaterial));
	}
       }
      break;

      case E3dGEO_FACE:
       {
	E3dFace*	LFace=(E3dFace*)LGeometry;

	E3d_UpdateDrawingMaterial(LGeometry, LFace->Material, &(LFace->DrawingMaterial));
       }
      break;
     }
    }
   }

 }
}


//================================================================================
// (Re)Map 2DTextures of Model hierarchy
//
// Argument
//  E3dModel*  LRootModel      Pointer to the Root of the hierarchy
//
// Description
//  This function parses through the given hierarchy and recomputes the 2DTexture
//  coordinates (S and T) based on the mapping-method and parameters.
//================================================================================
void E3d_ModelHrcMap2DTextures(E3dModel* LRootModel)
{
 E3dModel*	LModel=LRootModel;

 for(;LModel;)
 {
  E3d_ModelMap2DTextures(LModel);

  LModel=LModel->Next;
 }
}


//================================================================================================================
// Update all links and other internal data of a hierarchy
//
// Argument
//  E3dModel* LRootModel       Pointer to the Root of the hierarchy
//
// Description
//  This function prepares a hierarchy for efficient parsing and rendering: it walks through the Models of the
//  hierarchy and updates the <I><B>Prev</B></I>, <I><B>Next</B></I> and <I><B>PrevSibling</B></I> link fields
//  and other internal information on each Model, using only the <I><B>Parent</B></I>, <I><B>Child</B></I> and
//  <I><B>NextSibling</B></I> links.<BR>
//  The <I><B>Prev</B></I> and <I><B>Next</B></I> links will follow the "depth-first" order, and only apply
//  within that hierarchy.<BR>
//  See the diagram below:<BR><BR>
//  <IMG SRC="../images/HrcDiagram.png"><BR>
//  Note that hierarchies are <B>NOT</B> linked in any way, but a Scene keeps an array of its hierarchies.<BR>
//  When creating a new hierarchy, or changing the links in an existing one (topology change), the application
//  only has to set the <I><B>Parent</B></I>, <I><B>Child</B></I> and <I><B>NextSibling</B></I> links of the
//  Models, and then call this function with the root Model of the hierarchy.
//
// See also
//  E3d_ModelHrcRefreshMatrices
//================================================================================================================
void E3d_ModelHrcRefreshHierarchy(E3dModel* LRootModel)
{
 E3dModel*	LModel;
 E3dModel*	LTModel;
 E3dModel*	LPrevModel;
 E3dModel*	LParentModel;
 char		LPopN, LPushN;


 LPrevModel=NULL;
 for(LModel=LRootModel;LModel;)
 {
  LPopN=0;LPushN=0;
  LParentModel=LModel->Parent;

//------------------------------------------------------------------------
// Set speed-up matrix-stacking flags
//-------------------------------------------------------------------------------------------------------------------------------------------------
// Before-PUSH if the MODEL is the ROOT, or if the MODEL's parent has more than one children and the MODEL is nor the first, nor the last one
// Before-POP if the MODEL's parent has more than one children and the MODEL is not the first one
// After-PUSH if the MODEL's parent has more than one children
//-------------------------------------------------------------------------------------------------------------------------------------------------
  if(LParentModel==NULL) LPushN|=E3dMDL_BEFORE;
  else
  {
   if((LParentModel->Child->NextSibling!=NULL)&&(LParentModel->Child!=LModel))	// Parent has more than one child and LModel is not the first one
   {
    LPopN|=E3dMDL_BEFORE;
    if(LModel->NextSibling) LPushN|=E3dMDL_BEFORE;
   }
  }

  if(LPrevModel) LPrevModel->Next=LModel;
  LModel->Prev=LPrevModel;
  LPrevModel=LModel;
  if(LModel->NextSibling) LModel->NextSibling->PrevSibling=LModel;
  if((LModel->Child!=NULL)&&(LModel->Child->NextSibling!=NULL)) LPushN|=E3dMDL_AFTER;


//----------------------------------------------------------------
// Get the next model in the hierarchy (depth first)
//----------------------------------------------------------------
  LTModel=LModel;
  if(LModel->Child) LModel=LModel->Child;			// Does the current model have a child ? (1)
  else								// (1N) No, does it have a sibling ? (2)
  {
   if(LModel->NextSibling) LModel=LModel->NextSibling;		// (2Y) Yes, let the sibling be the next...
   else								// (2N) No, go back upward in this branch until
   {								// we find a model that has a sibling
    while((LModel->NextSibling==NULL)&&(LModel->Parent!=NULL)) LModel=LModel->Parent;
    if(LModel) LModel=LModel->NextSibling;
   }
  }

// After-POP if the MODEL is the last one in the hierarchy (at bottom-left)
//
  if(LModel==NULL) { LPopN|=E3dMDL_AFTER;LTModel->Next=NULL; }
  LTModel->PopN=LPopN;LTModel->PushN=LPushN;
 }
}


//================================================================================
// Compute transformation matrices of a Model hierarchy
//
// Argument
//  E3dModel* LBranchRootModel       Pointer to the Root of the tree or branch
//
// Description
//  Parses through the given hierarchy and recomputes the LocalToWorldMatrix
//  and LocalToWorldNormalMatrix on each node, using the transformation values
//  of that Model node.
//
// See also
//  E3d_ModelHrcRefreshHierarchy
//================================================================================
void E3d_ModelHrcRefreshMatrices(E3dModel* LBranchRootModel)
{
 E3dModel*	LModel;
 E3dMatrix	LMatrix, LLastMatrix, LMatrixStack[E3dMAX_HIERARCHY_DEPTH];
 E3d3DPosition	LScaleStack[E3dMAX_HIERARCHY_DEPTH];
 E3dCoordinate	LScaleX, LScaleY, LScaleZ;
 int		LMatrixPtr;


 LMatrixPtr=E3dMAX_HIERARCHY_DEPTH-1;
 E3d_MatrixLoadIdentity(LMatrix);
 LScaleX=LScaleY=LScaleZ=1.0;

// This will make sure that we stop after the branch is done (and not
// follow the "Next" links all the way to the end of the hierarchy)
//
 LModel=LBranchRootModel;
 do
 {
  E3d_MatrixCopy(LMatrix, LLastMatrix);
  if(LModel->PopN&E3dMDL_BEFORE)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LScaleX=LScaleStack[LMatrixPtr].X;
   LScaleY=LScaleStack[LMatrixPtr].Y;
   LScaleZ=LScaleStack[LMatrixPtr].Z;
   LMatrixPtr++;
  }
  if(LModel->PushN&E3dMDL_BEFORE)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
   LScaleStack[LMatrixPtr].X=LScaleX;
   LScaleStack[LMatrixPtr].Y=LScaleY;
   LScaleStack[LMatrixPtr].Z=LScaleZ;
  }

//  E3d_MatrixScale(LMatrix, LModel->Scaling.X, LModel->Scaling.Y, LModel->Scaling.Z);
  E3d_MatrixTranslate(LMatrix, LModel->Translation.X, LModel->Translation.Y, LModel->Translation.Z);

// Neutralize scaling in the Matrix before doing the rotation
//
  E3d_MatrixScale(LMatrix, 1.0/LScaleX, 1.0/LScaleY, 1.0/LScaleZ);

  if(LModel->Type==E3dMDL_JOINT)
  {
   E3dJointModel*	LJointModel=(E3dJointModel*)LModel;

   E3d_MatrixMult(LJointModel->JointOrientMatrix, LMatrix);
  }
  switch(LModel->RotationOrder)
  {
   case E3dXYZ:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dXZY:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;

   case E3dYXZ:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dYZX:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZYX:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZXY:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;
  }

  LScaleX*=LModel->Scaling.X;
  LScaleY*=LModel->Scaling.Y;
  LScaleZ*=LModel->Scaling.Z;

// Use matrix without scaling for transforming normals
//
// FIXME: this Matrix is only correct if the scaling is uniform (n, n, n)
//
  E3d_MatrixCopy(LMatrix, LModel->NormalLocalToWorldMatrix);
  LModel->NormalLocalToWorldMatrix[M30]=0.0;LModel->NormalLocalToWorldMatrix[M31]=0.0;LModel->NormalLocalToWorldMatrix[M32]=0.0;

// Set back scaling in the matrix
//
  E3d_MatrixScale(LMatrix, LScaleX, LScaleY, LScaleZ);
  E3d_MatrixCopy(LMatrix, LModel->LocalToWorldMatrix);

  if(E3d_MatricesEqual(LModel->LocalToWorldMatrix, LLastMatrix)&&(LModel->Parent!=NULL)) LModel->MatrixNew=FALSE;	// Root's matrix always has to be set
  else LModel->MatrixNew=TRUE;

  if(LModel->PushN&E3dMDL_AFTER)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
   LScaleStack[LMatrixPtr].X=LScaleX;
   LScaleStack[LMatrixPtr].Y=LScaleY;
   LScaleStack[LMatrixPtr].Z=LScaleZ;
  }
  if(LModel->PopN&E3dMDL_AFTER)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LScaleX=LScaleStack[LMatrixPtr].X;
   LScaleY=LScaleStack[LMatrixPtr].Y;
   LScaleZ=LScaleStack[LMatrixPtr].Z;
   LMatrixPtr++;
  }

  if((LModel=LModel->Next)==NULL) break;
 } while(LModel);
}


//========================================================================================
// Update the LocalToWorld Matrices of a Model hierarchy
//
// Arguments
//  E3dModel*     LModel           Pointer to the root Model of the hierarchy
//
// Description
//  This function walks through the given hierarchy following the Parent, Child and
//  NextSibling links (depth-first) and refreshes the local-to-world matrix field
//  (LocalToWorldMatrix) of the Model nodes.
//  E3d_ModelHrcRefreshHierarchy() should be called on a hierarchy after a change in
//  local transformation values (Scaling, Rotation, Translation) on the Model nodes.
//  This function ignores the Scaling values of the Model nodes, so it is much faster
//  than E3d_ModelHrcRefreshMatrices(), but it only works correctly with Models that
//  have "unit scaling" (1.0, 1.0, 1.0).
//
// See also
//  E3d_ModelHrcRefreshMatrices, E3d_ModelHrcRefreshHierarchy
//========================================================================================
void E3d_ModelHrcRefreshMatricesNoScaling(E3dModel* LRootModel)
{
 E3dModel*	LModel;
 E3dModel*	LParentModel;
 E3dMatrix	LMatrix, LLastMatrix, LMatrixStack[E3dMAX_HIERARCHY_DEPTH];
 int		LMatrixPtr;


 LMatrixPtr=E3dMAX_HIERARCHY_DEPTH-1;
 E3d_MatrixLoadIdentity(LMatrix);

 LModel=LRootModel;

// This will make sure that we stop after the branch is done (and
// not follow the "Next"s all the way to the end of the hierarchy)
//
 LParentModel=LModel->Parent;
 do
 {
  E3d_MatrixCopy(LMatrix, LLastMatrix);
  if(LModel->PopN&E3dMDL_BEFORE)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LMatrixPtr++;
  }
  if(LModel->PushN&E3dMDL_BEFORE)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
  }

  E3d_MatrixTranslate(LMatrix, LModel->Translation.X, LModel->Translation.Y, LModel->Translation.Z);

  if(LModel->Type==E3dMDL_JOINT)
  {
   E3dJointModel*	LJointNode=(E3dJointModel*)LModel;

   E3d_MatrixMult(LJointNode->JointOrientMatrix, LMatrix);
  }

  switch(LModel->RotationOrder)
  {
   case E3dXYZ:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dXZY:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;

   case E3dYXZ:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dYZX:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZYX:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZXY:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;
  }

  E3d_MatrixCopy(LMatrix, LModel->LocalToWorldMatrix);

  if(LModel->PushN&E3dMDL_AFTER)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
  }
  if(LModel->PopN&E3dMDL_AFTER)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LMatrixPtr++;
  }

  LModel=LModel->Next;
  if(LModel==NULL) break;
 } while(LModel->Parent!=LParentModel);
}


//================================================================================
// Freeze scaling on a hierarchy or a branch (sub-tree of a hierarchy)
//
// Argument
//  E3dModel* LBranchRootModel     Pointer to the Root of the branch
//
// Description
//  This function performs the scaling on the geometries of each node in the
//  tgiven hierarchy or branch, then sets the scaling values to 1.0.
//================================================================================
void E3d_ModelHrcFreezeScaling(E3dModel* LBranchRootModel)
{
 E3dModel*	LModel;
 E3dMatrix	LMatrix, LLastMatrix, LMatrixStack[E3dMAX_HIERARCHY_DEPTH];
 E3d3DPosition	LScaleStack[E3dMAX_HIERARCHY_DEPTH];
 E3dCoordinate	LScaleX, LScaleY, LScaleZ;

 E3dGeometry**	LUniqueGeometries=NULL;

 EBool*		LGeometryTransformed=NULL;
 int*		LGeometryIndexList=NULL;

 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMatrix	LScalingMatrix;
 int		LMatrixPtr;
 int		LGeoIndex;
 unsigned int	LGC, LGN;
 unsigned int	LNumOfAllGeometries=0, LNumOfUniqueGeometries=0, LAllGC=0;


 LMatrixPtr=E3dMAX_HIERARCHY_DEPTH-1;
 E3d_MatrixLoadIdentity(LMatrix);
 LScaleX=LScaleY=LScaleZ=1.0;

 E3d_MatrixLoadIdentity(LScalingMatrix);


 LModel=LBranchRootModel;
 do
 {
  LNumOfAllGeometries+=LModel->NumOfGeometries;
  if((LModel=LModel->Next)==NULL) break;
 } while(LModel);


 if(LNumOfAllGeometries) LGeometryIndexList=(int*)EMalloc(sizeof(int)*LNumOfAllGeometries);

 LModel=LBranchRootModel;
 do
 {
  LGN=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
  for(LGC=0;LGC<LGN;LGC++)
  {
   LGeometry=LGeometries[LGC];

   if(ELst_AddPointerChk((void***)(&LUniqueGeometries), &LNumOfUniqueGeometries, LGeometry)) LGeometryIndexList[LAllGC]=LNumOfUniqueGeometries-1;
   else LGeometryIndexList[LAllGC]=ELst_GetIndexOfPointer((void**)LUniqueGeometries, LNumOfUniqueGeometries, LGeometry);

   LAllGC++;
  }
  if((LModel=LModel->Next)==NULL) break;
 } while(LModel);


 if(LNumOfUniqueGeometries)
 {
  LGeometryTransformed=(EBool*)EMalloc(sizeof(EBool)*LNumOfUniqueGeometries);
  for(LGC=0;LGC<LNumOfUniqueGeometries;LGC++) LGeometryTransformed[LGC]=FALSE;
 }

 LAllGC=0;
// This will make sure that we stop after the branch is done (and not
// follow the "Next" links all the way to the end of the hierarchy)
//
 LModel=LBranchRootModel;
 do
 {
  E3d_MatrixCopy(LMatrix, LLastMatrix);
  if(LModel->PopN&E3dMDL_BEFORE)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LScaleX=LScaleStack[LMatrixPtr].X;
   LScaleY=LScaleStack[LMatrixPtr].Y;
   LScaleZ=LScaleStack[LMatrixPtr].Z;
   LMatrixPtr++;
  }
  if(LModel->PushN&E3dMDL_BEFORE)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
   LScaleStack[LMatrixPtr].X=LScaleX;
   LScaleStack[LMatrixPtr].Y=LScaleY;
   LScaleStack[LMatrixPtr].Z=LScaleZ;
  }

  LModel->Translation.X*=LScaleX;LModel->Translation.Y*=LScaleY;LModel->Translation.Z*=LScaleZ;
  E3d_MatrixTranslate(LMatrix, LModel->Translation.X, LModel->Translation.Y, LModel->Translation.Z);

  switch(LModel->RotationOrder)
  {
   case E3dXYZ:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dXZY:
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;

   case E3dYXZ:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
   break;

   case E3dYZX:
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZYX:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
   break;

   case E3dZXY:
    E3d_MatrixRotate(LMatrix, 'z', LModel->Rotation.Z);
    E3d_MatrixRotate(LMatrix, 'x', LModel->Rotation.X);
    E3d_MatrixRotate(LMatrix, 'y', LModel->Rotation.Y);
   break;
  }


  LScaleX*=LModel->Scaling.X;
  LScaleY*=LModel->Scaling.Y;
  LScaleZ*=LModel->Scaling.Z;

  LModel->Scaling.X=LModel->Scaling.Y=LModel->Scaling.Z=1.0;

  E3d_MatrixCopy(LMatrix, LModel->LocalToWorldMatrix);
  LScalingMatrix[M00]=LScaleX;
  LScalingMatrix[M11]=LScaleY;
  LScalingMatrix[M22]=LScaleZ;
  LGN=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
  for(LGC=0;LGC<LGN;LGC++)
  {
   LGeometry=LGeometries[LGC];

   LGeoIndex=LGeometryIndexList[LAllGC];

   if(!(LGeometryTransformed[LGeoIndex]))
   {
    E3d_GeometryTransform(LGeometry, LScalingMatrix, LScalingMatrix);
    LGeometryTransformed[LGeoIndex]=TRUE;
   }
   LAllGC++;
  }

  if(E3d_MatricesEqual(LModel->LocalToWorldMatrix, LLastMatrix)&&(LModel->Parent!=NULL)) LModel->MatrixNew=FALSE;	// Root's matrix always has to be set
  else LModel->MatrixNew=TRUE;

  if(LModel->PushN&E3dMDL_AFTER)
  {
   LMatrixPtr--;
   E3d_MatrixCopy(LMatrix, LMatrixStack[LMatrixPtr]);
   LScaleStack[LMatrixPtr].X=LScaleX;
   LScaleStack[LMatrixPtr].Y=LScaleY;
   LScaleStack[LMatrixPtr].Z=LScaleZ;
  }
  if(LModel->PopN&E3dMDL_AFTER)
  {
   E3d_MatrixCopy(LMatrixStack[LMatrixPtr], LMatrix);
   LScaleX=LScaleStack[LMatrixPtr].X;
   LScaleY=LScaleStack[LMatrixPtr].Y;
   LScaleZ=LScaleStack[LMatrixPtr].Z;
   LMatrixPtr++;
  }

  if((LModel=LModel->Next)==NULL) break;
 } while(LModel);

 if(LGeometryIndexList) EFree(LGeometryIndexList);
 if(LGeometryTransformed) EFree(LGeometryTransformed);
}


//================================================================================
// Make the current "pose" of a JointModel hierarchy its default pose
//
// Arguments
//  E3dModel* LModel             Root Model of the skeleton hierarchy
//
// Description
//  Makes the current pose of the Skeleton hierarchy its default pose by setting the
//  WorldToDefaultMatrix of each Model to the inverse of the Model's LocalToWorldMatrix
//
// See also
//  E3d_SkinMeshBindPose
//================================================================================
void E3d_ModelHrcSkeletonSetDefaultTransform(E3dModel* LModel)
{
// Go to the root of the hierarchy
//
 if(LModel)
 {
  E3dJointModel*	LJointModel;


  for(;LModel;LModel=LModel->Next)
  {
   if(LModel->Type==E3dMDL_JOINT)
   {
    LJointModel=(E3dJointModel*)LModel;

    E3d_MatrixInvert3x4(LModel->LocalToWorldMatrix, LJointModel->WorldToDefaultMatrix);
   }
  }
 }
}


//================================================================================
// Call the "Remove" procedures of the Models in the given hierarchy
//================================================================================
void E3d_ModelHrcCallRemoveProcs(E3dModel* LRootModel)
{
 E3dModel*	LModel;
 unsigned int	LC, LN;

// Call Class-specific RemoveProcs
//
 for(LModel=LRootModel;LModel;LModel=LModel->Next)
 {
  if(LModel->Info)
  {
   E3dModelInfo*	LInfo;
   E3dModelClass*	LClass;

   LN=LModel->NumOfInfoRecords;

   for(LC=0;LC<LN;LC++)
   {
    LInfo=LModel->Info[LC];

    LClass=LInfo->Class;
    if(LClass)
    {
     E3dModelProc	LRemoveProc=LClass->DestroyProc;

     if(LRemoveProc) LRemoveProc(LModel, LInfo);
    }
   }
  }
 }
}


//================================================================================
// Get the last Model-node of a branch (sub-tree of a hierarchy)
//
// Argument
//  E3dModel*  LBranchRootModel    Pointer to the Root of the branch
//
// Description
//  Parses through the given branch (in depth-first order) and gets the last Model-node
//  that belongs to this branch (that is: it is still a descendant of LBranchRootModel).
//
// Return value
//  The last Model-node of the given branch, or NULL in case of an error.
//================================================================================
E3dModel* E3d_ModelHrcBranchGetLastNode(E3dModel* LBranchRootModel)
{
 E3dModel*	LModel=LBranchRootModel;
 E3dModel*	LTModel;

 if(LModel->Child==NULL) return(LModel);

 for(;LModel;)
 {
//--------------------------------------------------------------------
// Get the next model in the hierarchy (from root to down and left)
//--------------------------------------------------------------------
  if(LModel->Child) LModel=LModel->Child;			// Does the current model have a child ? (1)
  else								// (1N) No, does it have a sibling ? (2)
  {
   if(LModel->NextSibling) LModel=LModel->NextSibling;	// (2Y) Yes, let the sibling be the next...
   else								// (2N) No, go back upward in this branch until
   {								// we find a model that has a sibling or until we hit LBranchRootModel
    LTModel=LModel;
    while(LModel->NextSibling==NULL)
    {
     if((LModel->Parent==NULL)||(LModel->Parent==LBranchRootModel)) return(LTModel);
     LModel=LModel->Parent;
    }
    if(LModel) LModel=LModel->NextSibling;
   }
  }
 }
 return(LModel);
}


//================================================================================
// Get todel-node that follows a branch (sub-tree of a hierarchy)
//
// Argument
//  E3dModel* LBranchRootModel     Pointer to the Root of the branch
//
// Description
//  Parses through the given branch (in depth-first order) and gets the Model-node
//  that comes after the last node of the given branch (the next one after the
//  last descendant of LBranchRootModel).
//
// Return value
//  The Model-node following the given branch (can be NULL).
//================================================================================
E3dModel* E3d_ModelHrcBranchGetNodeAfter(E3dModel* LBranchRootModel)
{
 E3dModel*	LModel=LBranchRootModel;
 E3dModel*	LTModel;


 if(LModel->Child==NULL) return(LModel->NextSibling);

 for(;LModel;)
 {
//--------------------------------------------------------------------
// Get the next model in the hierarchy (from root to down and left)
//--------------------------------------------------------------------
  if(LModel->Child) LModel=LModel->Child;			// Does the current model have a child ? (1)
  else								// (1N) No, does it have a sibling ? (2)
  {
   if(LModel->NextSibling) LModel=LModel->NextSibling;		// (2Y) Yes, let the sibling be the next...
   else								// (2N) No, go back upward in this branch until
   {								// we find a model that has a sibling or until we hit LBranchRootModel
    LTModel=LModel;
//printf("[%s] %08x\n", LTModel->Name, LTModel->Next);fflush(stdout);
    while(LModel->NextSibling==NULL)
    {
     if((LModel->Parent==NULL)||(LModel->Parent==LBranchRootModel)) return(LTModel->Next);
     LModel=LModel->Parent;
    }
    if(LModel) LModel=LModel->NextSibling;
   }
  }
 }

 if(LModel) LModel=LModel->Next;

 return(LModel);
}


//========================================================================
// Disconnect a Model Branch from its hierarchy (parent)
//========================================================================
E3dModel* E3d_ModelHrcBranchDisconnectFromParent(E3dModel* LModel, EBool LCompensateForTransforms)
{
 E3dModel*	LParent;

 LParent=LModel->Parent;
 if(LParent)
 {
  E3dModel*	LPrevSibling;
  E3dModel*	LNextSibling;
  E3dModel*	LNext;
  E3dModel*	LPrev;

  LPrevSibling=LModel->PrevSibling;
  LNextSibling=LModel->NextSibling;

  LPrev=LModel->Prev;
  LNext=E3d_ModelHrcBranchGetNodeAfter(LModel);

//printf("Nxt %s Next: %08x\n", LModel->Name, LNext);fflush(stdout);

  if(LParent->Child==LModel) LParent->Child=LNextSibling;

  if(LNextSibling) LNextSibling->PrevSibling=LModel->PrevSibling;

  if(LPrevSibling) LPrevSibling->NextSibling=LModel->NextSibling;


  if(LPrev) LPrev->Next=LNext;

  if(LNext) LNext->Prev=LPrev;

  LModel->Parent=NULL;
  LModel->NextSibling=NULL;
  LModel->PrevSibling=NULL;
  if(LModel->Child==NULL) LModel->Next=NULL;
  LModel->Prev=NULL; 

  return(LNext);
 }
 else return(NULL);
}


//========================================
// Tesselate a Model's geometries
//========================================
void E3d_ModelHrcCreateRenderablePolygons(E3dModel* LModel, EBool LDoConvex)
{
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 unsigned int	LGmCnt, LGmNum;

// Do triangulation of Polygons and store result in the 'Renderable Polygons'
// structures on each Polygon
//

 for(;LModel;LModel=LModel->Next)
 {
  LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
  for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
  {
   if((LGeometry=LGeometries[LGmCnt])!=NULL)
   {
    switch(LGeometry->GeoType)
    {
     caseE3dMESH():
      {
       E3dMesh*		LMesh=(E3dMesh*)LGeometry;
       unsigned int	LGCnt, LGNum, LC, LN;
       E3dPolyGroup*	LPolyGroup;
       E3dPolyGroup**	LPolyGroups;
       E3dPolygon*	LPolygon;

       LGNum=LMesh->NumOfPolyGroups;LPolyGroups=LMesh->PolyGroups;
       for(LGCnt=0;LGCnt<LGNum;LGCnt++)
       {
        LPolyGroup=LPolyGroups[LGCnt];
	LN=LPolyGroup->NumOfPolygons;LPolygon=LPolyGroup->Polygons;
	for(LC=0;LC<LN;LC++, LPolygon++)
	{
	 if(LPolygon->NumOfVertices>3) E3d_TriangulatePolygon(LMesh->Vertices, LPolygon, LDoConvex);
	 else
	 {
	  E3dITriangle*		LITriangle;
	  E3dVertexNode*	LVertexNodes;

	  LITriangle=NULL;
	  if(LPolygon->ITriangles)
	  {
	   if(LPolygon->NumOfTriangles!=1) { EFree(LPolygon->ITriangles);LPolygon->ITriangles=NULL;LPolygon->NumOfTriangles=0; }
	   else LITriangle=LPolygon->ITriangles;
	  }

	  if(LITriangle==NULL) LITriangle=(E3dITriangle*)EMalloc(sizeof(E3dITriangle));

	  if(LITriangle)
	  {
	   LPolygon->ITriangles=LITriangle;LPolygon->NumOfTriangles=1;
           LVertexNodes=LPolygon->VertexNodes;
	   LITriangle->VertexNodes[0]=LVertexNodes;
	   LITriangle->VertexNodes[1]=LVertexNodes+1;
	   LITriangle->VertexNodes[2]=LVertexNodes+2;
	  }
	 }
	}
       }
      }
     break;
    }
   }
  }
 }
}


//================================================================================
// Create edge list for the Meshes of the given Model hierarchy
//================================================================================
void E3d_ModelHrcCreateEdgesFromPolyData(E3dModel* LRootModel)
{
 E3dModel*	LModel;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 unsigned int	LGmCnt, LGmNum;

 for(LModel=LRootModel;LModel;)
 {
  LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
  for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
  {
   if((LGeometry=LGeometries[LGmCnt])!=NULL)
   {
    switch(LGeometry->GeoType)
    {
     caseE3dMESH():
      E3d_MeshCreateEdgesFromPolyData((E3dMesh*)LGeometry, NULL);
     break;
    }
   }
  }
  LModel=LModel->Next;
 }
}


//================================================================================
// Refresh rendering data of a Model hierarchy
// - For OpenGL: re-create OpenGL-optimized data sets
// - For custom displayer: notify display routine that the object(s) changed
//================================================================================
void E3d_ModelHrcUpdateForDisplay(E3dModel* LModel, unsigned int LFlags)
{
 E3dGeometry**	LGeometries=NULL;
 unsigned int	LNumOfGeometries=0, LNumOfGeometriesAllocated=0, LGmC, LGmN;
 EBool		LLightChanged=FALSE;


 E3d_ModelHrcRefreshMaterialInheritance(LModel);
 for(;LModel;)
 {
// Collect Geometries to make sure we don't update any of
// them more than once, even if there is instancing
//
  LGmN=LModel->NumOfGeometries;
  for(LGmC=0;LGmC<LGmN;LGmC++) ELst_AddPointerAChk((void***)(&LGeometries), &LNumOfGeometries, &LNumOfGeometriesAllocated, 16, LModel->Geometries[LGmC]);

  switch(LModel->Type)
  {
   case E3dMDL_LIGHT:
    {
     E3dLightInfo*	LLightInfo=(E3dLightInfo*)(LModel->Info[0]);
     E3dLight*		LLight=LLightInfo->Light;

     if(LLight)
     {
      LLight->Position.X=LModel->Translation.X;
      LLight->Position.Y=LModel->Translation.Y;
      LLight->Position.Z=LModel->Translation.Z;

      LLightChanged=TRUE;
     }
    }
   break;
  }

  LModel=LModel->Next;
 }

 if(LGeometries)
 {
  E3dGeometry*	LGeometry;
  unsigned int	LLODC, LLODN;

  for(LGmC=0;LGmC<LNumOfGeometries;LGmC++)
  {
   LGeometry=LGeometries[LGmC];

   E3d_GeometryUpdateForDisplay(LGeometry, LFlags);
   LLODN=LGeometry->NumOfLODs;
   for(LLODC=0;LLODC<LLODN;LLODC++) E3d_GeometryUpdateForDisplay(LGeometry->LODs[LLODC], LFlags);
  }
  EFree(LGeometries);
 }

#ifdef USEOpenGL
 if(LLightChanged)
 {
  E3d_SceneLightsRefreshGL(E3d_Scene);
  E3d_SceneLightsDefineGL(E3d_Scene);
 }
#endif	// USEOpenGL
}


//========================================================================
// Update the drawing Materials of the Geometries a hierarchy
//========================================================================
void E3d_ModelHrcUpdateMaterialsForDisplay(E3dModel* LModel)
{
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMaterial**	LMaterials=NULL;
 unsigned int	LNumOfMaterials=0;
 unsigned int	LC, LN;

 for(;LModel;LModel=LModel->Next)
 {
  LN=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
  for(LC=0;LC<LN;LC++)
  {
   if((LGeometry=LGeometries[LC])!=NULL)
   {
    switch(LGeometry->GeoType)
    {
     caseE3dMESH():
      {
       E3dMesh*		LMesh=(E3dMesh*)LGeometry;
       E3dPolyGroup*	LPolyGroup;
       E3dPolyGroup**	LPolyGroups;
       unsigned int	LGCnt;


       LPolyGroups=LMesh->PolyGroups;

       for(LGCnt=0;LGCnt<LMesh->NumOfPolyGroups;LGCnt++)
       {
        LPolyGroup=LPolyGroups[LGCnt];

	E3d_UpdateDrawingMaterial(LGeometry, LPolyGroup->Material, &(LPolyGroup->DrawingMaterial));

#ifdef USEOpenGL
// Collect DrawingMaterials first to make sure we update each one of them only once
//
	ELst_AddPointerChk((void***)(&LMaterials), &LNumOfMaterials, LPolyGroup->DrawingMaterial);
#endif // USEOpenGL
       }
      }
     break;

     case E3dGEO_FACE:
      {
       E3dFace*	LFace=(E3dFace*)LGeometry;

       E3d_UpdateDrawingMaterial(LGeometry, LFace->Material, &(LFace->DrawingMaterial));

#ifdef USEOpenGL
// Collect DrawingMaterials first to make sure we update each one of them only once
//
       ELst_AddPointerChk((void***)(&LMaterials), &LNumOfMaterials, LFace->DrawingMaterial);
#endif // USEOpenGL
      }
     break;
    }
   }
  }
 }

// Now update the collected DrawingMaterials
//
#ifdef USEOpenGL
 if(LMaterials)
 {
  for(LC=0;LC<LNumOfMaterials;LC++)
  {
   E3d_MaterialConvertToGL(LMaterials[LC]);
  }
  EFree(LMaterials);
 }
#endif // USEOpenGL
}


#ifdef USEOpenGL
//========================================================================================
// Calculate transformation Matrices of a Model hierarchy using GL Matrix operations
//========================================================================================
void E3d_ModelHrcRefreshMatricesGL(E3dModel* LRootModel)
{
 E3dModel*	LModel;
 E3dMatrix	LLastMatrix;
 E3dCoordinate	LActScaleX=1.0, LActScaleY=1.0, LActScaleZ=1.0;

 glPushMatrix();
 MglLoadMatrix(E3d_IdentityMatrix);
 for(LModel=LRootModel;LModel;LModel=LModel->Next)
 {
  MglGetFloatV(GL_MODELVIEW_MATRIX, LLastMatrix);
  if(LModel->PopN&E3dMDL_BEFORE) E3d_MatrixPopGL();
  if(LModel->PushN&E3dMDL_BEFORE) E3d_MatrixPushGL();

  MglTranslate(LModel->Translation.X, LModel->Translation.Y, LModel->Translation.Z);
  MglScale(1.0/LActScaleX, 1.0/LActScaleY, 1.0/LActScaleZ);

  switch(LModel->RotationOrder)
  {
   case E3dXYZ:
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
   break;

   case E3dXZY:
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
   break;

   case E3dYXZ:
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
   break;

   case E3dYZX:
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
   break;

   case E3dZYX:
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
   break;

   case E3dZXY:
    MglRotate(LModel->Rotation.Z, 0.0, 0.0, 1.0);
    MglRotate(LModel->Rotation.X, 1.0, 0.0, 0.0);
    MglRotate(LModel->Rotation.Y, 0.0, 1.0, 0.0);
   break;
  }

  LActScaleX*=LModel->Scaling.X;
  LActScaleY*=LModel->Scaling.Y;
  LActScaleZ*=LModel->Scaling.Z;

  MglScale(LActScaleX, LActScaleY, LActScaleZ);
  MglGetFloatV(GL_MODELVIEW_MATRIX, LModel->LocalToWorldMatrix);
  if(E3d_MatricesEqual(LModel->LocalToWorldMatrix, LLastMatrix)&&(LModel->Parent!=NULL)) LModel->MatrixNew=FALSE;	// Root's matrix always has to be set
  else LModel->MatrixNew=TRUE;

  if(LModel->PushN&E3dMDL_AFTER) E3d_MatrixPushGL();
  if(LModel->PopN&E3dMDL_AFTER) E3d_MatrixPopGL();
 }
 glPopMatrix();
}


#endif // USEOpenGL
