/*======================================================================*/
/* 3DLib								*/
/*									*/
/* Polygon and PolyGroup-related functions				*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Jan-01 23:51:36					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <assert.h>
#include <math.h>
#include <stdarg.h>		// For varargs
#include <stdio.h>
#include <stdlib.h>

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

#include <E3D/E3D.h>

#include <E3D/Math.h>

#include <E3D/Matrix.h>

#include <E3D/Scene.h>


E3dPolyGroupClass**	E3d_CustomPolyGroupClasses=NULL;
int			E3d_NumOfCustomPolyGroupClasses=0;


//----------------------------------------------------------------------------------------
// Polygon/TriangleStrip functions
//----------------------------------------------------------------------------------------

//========================================================================================
// Allocate VertexNodes for a Polygon
//
// Arguments
//  E3dPolygon*   LPolygon              Pointer to the Polygon structure
//  unsigned int  LNumOfVertexNodes     The number of VertexNodes to allocate
//
// Description
//  This function allocates VertexNodes for the given Polygon and initializes
//  the related fields:
// <PRE> LPolygon->NumOfVertices=0;             // Number of VertexNodes minus contour separators
// LPolygon->NumOfExteriorVertices=0;         // Number of Vertices in the exterior contour
// LPolygon->NumOfVertexNodes=LNumOfVertexNodes; // Total number of VertexNodes</PRE>
//
// Return value
//  A pointer to the allocated array of VertexNodes or NULL in case of an error
//
// See also
//  E3d_PolygonAddVertexNode
//========================================================================================
E3dVertexNode* E3d_PolygonVertexNodesAllocate(
                                     E3dPolygon* LPolygon,
                                     unsigned int LNumOfVertexNodes)
{
 E3dVertexNode*	LVertexNodes=NULL;

 if(LNumOfVertexNodes>0)
 {

#ifdef E_MEMDEBUG
 E_MemName="VertexNodes";
#endif // E_MEMDEBUG
  LVertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode)*LNumOfVertexNodes);
  if(LVertexNodes)
  {
   LPolygon->NumOfVertices=0;
   LPolygon->NumOfExteriorVertices=0;
   LPolygon->NumOfVertexNodes=LNumOfVertexNodes;
  }
  LPolygon->VertexNodes=LVertexNodes;
 }
 return(LVertexNodes);
}


//========================================================
// Allocate vertex nodes for a renderable Polygon
//========================================================
E3dVertexNode** E3d_RPolygonVertexNodesAllocate(E3dRPolygon* LPolygon,
                                                 unsigned int LNumOfVertexNodes)
{
 if(LNumOfVertexNodes>0)
 {
#ifdef E_MEMDEBUG
  E_MemName="RPolygon VertexNodes";
#endif // E_MEMDEBUG
  LPolygon->VertexNodes=(E3dVertexNode**)EMalloc(sizeof(E3dVertexNode*)*LNumOfVertexNodes);
  if(LPolygon->VertexNodes)
  {
   LPolygon->NumOfVertices=0;
  }
 }
 return(LPolygon->VertexNodes);
}


//================================================================================================================
// Add one VertexNode to a Polygon
//
// Arguments
//  E3dPolygon*    LPolygon          Pointer to the E3dPolygon structure
//  unsigned int   LVIdx             Vertex index
//  unsigned int   LWhere            Where to add the new VertexNode
//                 ...               List of vertex indices (int)
//
// Description
//  <P ALIGN="justify">This function adds a new VertexNode to the given Polygon.
//  The <TT>LWhere</TT> argument is the index of the VertexNode BEFORE which the the new one should be
//  inserted.<BR>
//  The value <TT>E3dVNODE_END</TT> for <TT>LWhere</TT> is recognized. It means to add the new VertexNode
//  after the last one.<BR>
//  <TT>LVIdx</TT> is an index to the Vertex array on the E3dMesh that the Polygon belongs to. This value will
//  be copied into the VertexID field of the new VertexNode.
//<BR>
//  A Polygon can have multiple contours: exterior and optional holes.
//  The exterior contour is always the first one and is defined with a counter-clockwise Vertex order, while
//  holes are defined with a clockwise Vertex order.<BR>
//  In the VertexNode list of a Polygon, negative VertexIDs separate the contours and these numbers give the
//  number of Vertices in the next contour (e.g.: -3 means that the next contour has 3 Vertices).
//  There is no such separator before the exterior contour, but instead the Polygon has a field called
//  <TT>NumOfExteriorVertices</TT>.</P>
//
// Example
//  This call inserts a VertexNode that uses the #42 Vertex of the Mesh, before the #5 VertexNode of LPolygon:<BR>
//  <PRE><FONT COLOR="000080" SIZE=+0.1> E3d_PolygonAddVertexNode(LPolygon, 42, 5);</FONT></PRE>
//
// Return value
//  A pointer to the new VertexNode, or NULL in case of an error.
//
// See also
//  E3d_PolygonsAllocate, E3d_PolygonInit, E3d_PolygonInitAsTriangle
//================================================================================================================
E3dVertexNode* E3d_PolygonAddVertexNode(E3dPolygon* LPolygon,
                                         int LVIdx,
                                         int LWhere)
{
 E3dVertexNode*	LVertexNode=NULL;
 E3dVertexNode*	LVertexNodes;

// We won't allow insertion of contour-separating negative numbers before the
// last VertexNode. They can only go at the end of the list
//
 if((LVIdx<=-1)&&(LWhere!=E3dVNODE_END)) return(NULL);

#ifdef E_MEMDEBUG
 E_MemName="VertexNodes";
#endif // E_MEMDEBUG

 if(LPolygon->VertexNodes==NULL) LVertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode));
 else
 {
  if((LVertexNodes=(E3dVertexNode*)ERealloc(LPolygon->VertexNodes, sizeof(E3dVertexNode)*(LPolygon->NumOfVertexNodes+1)))!=NULL)
  {
   LPolygon->VertexNodes=LVertexNodes;
  }
  else return(NULL);
 }

 if(LVertexNodes)
 {
  switch(LWhere)
  {
   case E3dVNODE_END:	LVertexNode=LVertexNodes+LPolygon->NumOfVertexNodes;break;

   default:
    LVertexNode=LVertexNodes+LWhere;

// If we insert before the last one, we have to make room
//
    if(LWhere<LPolygon->NumOfVertexNodes) memmove(LVertexNode+1, LVertexNode, sizeof(E3dVertexNode)*(LPolygon->NumOfVertexNodes-LWhere));
   break;
  }
 }

 if(LVertexNode)
 {
  LVertexNode->VertexID=LVIdx;
  if(LVIdx>-1)
  {
   LPolygon->NumOfVertices+=1;
   if((LWhere<(LPolygon->NumOfExteriorVertices+1))||(LWhere==E3dVNODE_END)) LPolygon->NumOfExteriorVertices+=1;
  }
  LPolygon->NumOfVertexNodes+=1;
 }
 LPolygon->VertexNodes=LVertexNodes;

 return(LVertexNode);
}


//========================================
// Default a Polygon
//========================================
void E3d_PolygonDefault(E3dPolygon* LPolygon)
{
 LPolygon->NumOfVertices=0;
 LPolygon->NumOfVertexNodes=0;
 LPolygon->NumOfExteriorVertices=0;
 LPolygon->VertexNodes=NULL;
 LPolygon->ITriangles=NULL;
 LPolygon->NumOfTriangles=0;
 LPolygon->Flags=0;
}


//========================================================================================================
// Allocate array of Polygons
//
// Arguments
//  unsigned int   LNumOfPolys       Number of Polygons to Allocate
//
// Description
//  This function allocates memory for an array of Polygons and initializes each Polygon in this array.
//
// Return value	
//  A pointer to the newly allocated array of Polygons, or NULL in case of an error.
//
// See also
//  E3d_PolygonInit, E3d_PolygonInitAsTriangle
//========================================================================================================
E3dPolygon* E3d_PolygonsAllocate(unsigned int LNumOfPolys)
{
 E3dPolygon*	LAPolygon=NULL;
 E3dPolygon*	LIPolygon;
 unsigned int	LC;

#ifdef E_MEMDEBUG
 E_MemName="Polygon";
#endif // E_MEMDEBUG

 if(LNumOfPolys>0) LAPolygon=(E3dPolygon*)EMalloc(sizeof(E3dPolygon)*LNumOfPolys);

 if(LAPolygon)
 {
  LIPolygon=LAPolygon;
  for(LC=0;LC<LNumOfPolys;LC++, LIPolygon++)
  {
   E3d_PolygonDefault(LIPolygon);
  }
 }
 return(LAPolygon);
}


//========================================================================================================
// Add one Polygon to a PolyGroup
//
// Arguments
//  E3dPolyGroup*  LPolyGroup        Pointer to the PolyGroup structure
//
// Description
//  This function adds a new Polygon to the given PolyGroup.
//
// Return value
//  A pointer to the new Polygon, or NULL in case of an error.
//
// See also
//  E3d_PolygonsAllocate, E3d_PolygonInit, E3d_PolygonInitAsTriangle
//========================================================================================================
E3dPolygon* E3d_PolyGroupAddPolygon(E3dPolyGroup* LPolyGroup)
{
 E3dPolygon*	LPolygons=LPolyGroup->Polygons;
 E3dPolygon*	LPolygonsT;
 E3dPolygon*	LPolygon=NULL;
 unsigned int	LNumOfPolys=LPolyGroup->NumOfPolygons;


 if(LPolygons==NULL)
 {
  if((LPolygons=(E3dPolygon*)EMalloc(sizeof(E3dPolygon)))!=NULL)
  {
   LPolyGroup->Polygons=LPolygons;
  }
  else return(NULL);
 }
 else
 {
  if((LPolygonsT=(E3dPolygon*)ERealloc(LPolygons, sizeof(E3dPolygon)*(LNumOfPolys+1)))!=NULL)
  {
   LPolyGroup->Polygons=LPolygons=LPolygonsT;
  }
  else return(NULL);
 }

 LPolygon=LPolygons+LNumOfPolys;

 if(LPolygons)
 {
  E3d_PolygonDefault(LPolygon);
  LPolyGroup->NumOfPolygons+=1;
 }

 return(LPolygon);
}


//========================================
// Add a Polygon to a Polygon array
//========================================
E3dPolygon* E3d_PolygonAddOneToArray(E3dPolygon* LPolygons, unsigned int LNumOfPolys, unsigned int* LNumOfPolysAllocatedPtr)
{
 unsigned int	LNumOfPolysAllocated;

 if(LNumOfPolysAllocatedPtr) LNumOfPolysAllocated=*LNumOfPolysAllocatedPtr;
 else LNumOfPolysAllocated=0;

 if(LPolygons==NULL)
 {
  if((LPolygons=E3d_PolygonsAllocate(E3dPOLYALLOC_INCREMENT))!=NULL)
  {
   if(LNumOfPolysAllocatedPtr) *LNumOfPolysAllocatedPtr=E3dPOLYALLOC_INCREMENT;
  }
  else return(NULL);
 }
 else
 {
  if(LNumOfPolys>=LNumOfPolysAllocated)
  {
   E3dPolygon*	LPolygonsT;

   if((LPolygonsT=(E3dPolygon*)ERealloc(LPolygons, sizeof(E3dPolygon)*(LNumOfPolys+E3dPOLYALLOC_INCREMENT)))!=NULL)
   {
    LPolygons=LPolygonsT;
    if(LNumOfPolysAllocatedPtr) *LNumOfPolysAllocatedPtr=LNumOfPolysAllocated+E3dPOLYALLOC_INCREMENT;
   }
   else return(NULL);
  }
 }

 if(LPolygons) E3d_PolygonDefault(LPolygons+LNumOfPolys);

 return(LPolygons);
}


//========================================
// Add n Polygons to a Polygon array
//========================================
E3dPolygon* E3d_PolygonAddNToArray(E3dPolygon* LPolygons, unsigned int LNumOfNewPolys, unsigned int LNumOfPolys, unsigned int* LNumOfPolysAllocatedPtr)
{
 unsigned int	LNumOfPolysAllocated;


 if(LNumOfPolysAllocatedPtr) LNumOfPolysAllocated=*LNumOfPolysAllocatedPtr;
 else LNumOfPolysAllocated=0;

 if(LPolygons==NULL)
 {
  if((LPolygons=E3d_PolygonsAllocate(LNumOfNewPolys))!=NULL)
  {
   if(LNumOfPolysAllocatedPtr) *LNumOfPolysAllocatedPtr=LNumOfNewPolys;
  }
  else return(NULL);
 }
 else
 {
  if(LNumOfPolys+LNumOfNewPolys>LNumOfPolysAllocated)
  {
   E3dPolygon*	LPolygonsT;

   if((LPolygonsT=(E3dPolygon*)ERealloc(LPolygons, sizeof(E3dPolygon)*(LNumOfPolys+LNumOfNewPolys)))!=NULL)
   {
    LPolygons=LPolygonsT;
    if(LNumOfPolysAllocatedPtr) *LNumOfPolysAllocatedPtr=LNumOfPolysAllocated+LNumOfNewPolys;
   }
   else return(NULL);
  }
 }

 if(LPolygons)
 {
  unsigned int	LC;
  E3dPolygon*	LPolygon=LPolygons+LNumOfPolys;

  for(LC=0;LC<LNumOfNewPolys;LC++,LPolygon++) E3d_PolygonDefault(LPolygon);
 }

 return(LPolygons);
}


//========================================
// Allocate renderable Polygons array
//========================================
E3dRPolygon* E3d_RPolygonsAllocate(unsigned int LNumOfPolys)
{
 E3dRPolygon*	LAPolygon=NULL;
 E3dRPolygon*	LIPolygon;
 unsigned int	LC;

 if(LNumOfPolys>0) LAPolygon=(E3dRPolygon*)EMalloc(sizeof(E3dRPolygon)*LNumOfPolys);
 if(LAPolygon)
 {
  LIPolygon=LAPolygon;
  for(LC=0;LC<LNumOfPolys;LC++, LIPolygon++)
  {
   LIPolygon->NumOfVertices=0;
   LIPolygon->VertexNodes=NULL;
  }
 }
 return(LAPolygon);
}


//========================================================================================================
// Initialize a Polygon's Flags and VertexNodes
//
// Arguments
//  E3dPolygon*    LPolygon             Pointer to the E3dPolygon structure
//  unsigned int   LNumOfVertexNodes    Number of vertex nodes
//                 ...                  List of vertex indices (int)
//
// Description
//  This function initializes the given Polygon by allocating VertexNodes for it, initializing them and
//  setting the Polygon's Flags.<BR>
//  The argument "..." is a variable length vertex index list. The vertex indices are of type: "int".
//  These indices refer to the Vertex array on the E3dMesh the given E3dPolygon belongs to.
//
//  In the vertex list, -1 separates the contours of the Polygon (exterior contours and holes).
//  The exterior contour is always the first one and is defined with a counter-clockwise Vertex	order.
//  Holes are defined with a clockwise Vertex order.
//
// Example
//  This call defines a rectangle of vertices: 0, 1, 2 and 3 with a triangle hole: 6, 5, 4:<BR>
//  <PRE><FONT COLOR="000080" SIZE=+0.1> E3d_PolygonInit(0, LPolygon, 0, 1, 2, 3, -1, 6, 5, 4);</FONT></PRE>
//
// Return value
//  TRUE if successful, FALSE in case of an error.
//
// See also
//  E3d_PolygonAllocate, E3d_PolygonsAllocate, E3d_PolygonInitAsTriangle, E3d_PolygonSet, E3d_PolygonSetAsTriangle
//========================================================================================================
EBool E3d_PolygonInit(E3dPolygon* LPolygon, unsigned int LNumOfVertexNodes, ...)
{
 va_list	LVertices;
 int		LVertexID;
 unsigned int	LVCnt, LNumOfVertices, LNumOfExteriorVertices;
 EBool		LIncExtVN;


 if(LNumOfVertexNodes>0)
 {
  LPolygon->VertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode)*LNumOfVertexNodes);

  LPolygon->Flags=0;
  if(LPolygon->VertexNodes)
  {
   va_start(LVertices, LNumOfVertexNodes);
    for(LVCnt=0, LNumOfVertices=0, LNumOfExteriorVertices=0, LIncExtVN=TRUE;LVCnt<LNumOfVertexNodes;LVCnt++)
    {
     LVertexID=va_arg(LVertices, int);
     LPolygon->VertexNodes[LVCnt].VertexID=LVertexID;
     if(LVertexID<=-1) LIncExtVN=FALSE;
     else LNumOfVertices++;
     LPolygon->VertexNodes[LVCnt].S=0.0;
     LPolygon->VertexNodes[LVCnt].T=0.0;
     if(LIncExtVN) LNumOfExteriorVertices++;
    }
   va_end(LVertices);
   LPolygon->NumOfVertices=LNumOfVertices;
   LPolygon->NumOfVertexNodes=LNumOfVertexNodes;
   LPolygon->NumOfExteriorVertices=LNumOfExteriorVertices;
   return(TRUE);
  }
 }
 return(FALSE);
}


//================================================================================
// Initialize Polygon as a triangle
//
// Arguments
//  E3dPolygon* LPolygon              The E3dPolygon to be initialized
//  int         LVtx0, LVtx1, LVtx2   Vertex indices (from the Mesh)
//
// Description
//  Takes a pointer to a Polygon and initializes it as a triangle:<BR>
//   - it sets the Polygon's flags,<BR> 
//   - allocates memory for 3 VertexNodes<BR>
//   - initializes the Vertex indices of the VertexNodes, using LVtx0, LVtx1 and LVtx2
//
// Return value
//  TRUE or FALSE depending on whether the Polygon was successfully initialized
//  or not.
//
// See also
//  E3d_PolygonInit, E3d_PolygonSet, E3d_PolygonSetAsTriangle
//================================================================================
EBool E3d_PolygonInitAsTriangle(E3dPolygon* LPolygon, int LVtx0, int LVtx1, int LVtx2)
{
 LPolygon->VertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode)*3);

 if(LPolygon->VertexNodes)
 {
  LPolygon->VertexNodes[0].VertexID=LVtx0;
  LPolygon->VertexNodes[1].VertexID=LVtx1;
  LPolygon->VertexNodes[2].VertexID=LVtx2;

  LPolygon->NumOfVertices=3;
  LPolygon->NumOfVertexNodes=3;
  LPolygon->NumOfExteriorVertices=3;
  return(TRUE);
 }  
 return(FALSE);
}


//========================================================================================================
// Change a Polygon's VertexNodes
//
// Arguments
//  E3dPolygon*    LPolygon             Pointer to the E3dPolygon structure
//  unsigned int   LNumOfVertexNodes    Number of vertex nodes
//                 ...                  List of vertex indices (int)
//
// Description
//  This function is just like E3d_PolygonInit, except it fitst checks whether VertexNodes are already
//  allocated for this Polygon. If yes and the number of VertexNodes allocated were equal to
//  LNumOfVertexNodes, it uses the old VertexNode array, otherwise it (re)allocates the VertexNodes
//  before initializing them.
//
// Return value
//  TRUE if successful, FALSE in case of an error.
//
// See also
//  E3d_PolygonAllocate, E3d_PolygonsAllocate, E3d_PolygonInit, E3d_PolygonInitAsTriangle
//========================================================================================================
EBool E3d_PolygonSet(E3dPolygon* LPolygon, unsigned int LNumOfVertexNodes, ...)
{
 va_list	LVertices;
 int		LVertexID;
 unsigned int	LVCnt, LNumOfVertices, LNumOfExteriorVertices;
 EBool		LIncExtVN;


 if(LNumOfVertexNodes>0)
 {
  if(LPolygon->NumOfVertexNodes!=LNumOfVertexNodes)
  {
   if(LPolygon->VertexNodes) EFree(LPolygon->VertexNodes);
   LPolygon->VertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode)*LNumOfVertexNodes);
  }

  LPolygon->Flags=0;
  if(LPolygon->VertexNodes)
  {
   va_start(LVertices, LNumOfVertexNodes);
    for(LVCnt=0, LNumOfVertices=0, LNumOfExteriorVertices=0, LIncExtVN=TRUE;LVCnt<LNumOfVertexNodes;LVCnt++)
    {
     LVertexID=va_arg(LVertices, int);
     LPolygon->VertexNodes[LVCnt].VertexID=LVertexID;
     if(LVertexID<=-1) LIncExtVN=FALSE;
     else LNumOfVertices++;
     LPolygon->VertexNodes[LVCnt].S=0.0;
     LPolygon->VertexNodes[LVCnt].T=0.0;
     if(LIncExtVN) LNumOfExteriorVertices++;
    }
   va_end(LVertices);
   LPolygon->NumOfVertices=LNumOfVertices;
   LPolygon->NumOfVertexNodes=LNumOfVertexNodes;
   LPolygon->NumOfExteriorVertices=LNumOfExteriorVertices;
   return(TRUE);
  }
 }
 return(FALSE);
}


//================================================================================
// Make a Polygon a triangle
//
// Arguments
//  E3dPolygon* LPolygon              The E3dPolygon to be initialized
//  int         LVtx0, LVtx1, LVtx2   Vertex indices (from the Mesh)
//
// Description
//  This function is just like E3d_PolygonInitAsTriangle, except it fitst checks
//  whether VertexNodes are already allocated for this Polygon. If yes and the
//  and the number of VertexNodes allocated were 3, it uses that VertexNode 
//  array, otherwise it (re)allocates the VertexNodes before initializing them.
//
// Return value
//  TRUE or FALSE depending on whether the Polygon was successfully initialized
//  or not.
//
// See also
//  E3d_PolygonInit, E3d_PolygonInitAsTriangle, E3d_PolygonSet
//================================================================================
EBool E3d_PolygonSetAsTriangle(E3dPolygon* LPolygon, int LVtx0, int LVtx1, int LVtx2)
{
 if(LPolygon->NumOfVertexNodes!=3)
 {
  if(LPolygon->VertexNodes) EFree(LPolygon->VertexNodes);
  LPolygon->VertexNodes=(E3dVertexNode*)EMalloc(sizeof(E3dVertexNode)*3);
 }

 LPolygon->Flags=0;

 if(LPolygon->VertexNodes)
 {
  LPolygon->VertexNodes[0].VertexID=LVtx0;
  LPolygon->VertexNodes[1].VertexID=LVtx1;
  LPolygon->VertexNodes[2].VertexID=LVtx2;

  LPolygon->NumOfVertices=3;
  LPolygon->NumOfVertexNodes=3;
  LPolygon->NumOfExteriorVertices=3;
  return(TRUE);
 }  
 return(FALSE);
}


//========================================================
// Recompute the normal of a Polygon
//========================================================
void E3d_PolygonRefreshNormal(E3dVertex* LVertices, E3dPolygon* LPolygon)
{
 E3dVertexNode*	LVertexNode;
 E3dVertex*	LVertex;
 E3dCoordinate	LX0, LY0, LZ0, LX1, LY1, LZ1, LX2, LY2, LZ2, LNormalX, LNormalY, LNormalZ;
 E3dCoordinate	r;
 unsigned int	LVCnt, LVertNum;


 if((LVertNum=LPolygon->NumOfExteriorVertices)>2)
 {
  LVertexNode=LPolygon->VertexNodes;
   LVertexNode=LPolygon->VertexNodes;
   if(LVertNum==3)
   {
    LVertex=LVertices+LVertexNode[0].VertexID;
    LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;
    LVertex=LVertices+LVertexNode[1].VertexID;
    LX1=LVertex->X;LY1=LVertex->Y;LZ1=LVertex->Z;
    LVertex=LVertices+LVertexNode[2].VertexID;
    LX2=LVertex->X;LY2=LVertex->Y;LZ2=LVertex->Z;

    LNormalX=-((LY2-LY1)*(LZ2-LZ0)-(LZ2-LZ1)*(LY2-LY0));
    LNormalY=(LX2-LX1)*(LZ2-LZ0)-(LZ2-LZ1)*(LX2-LX0);
    LNormalZ=-((LX2-LX1)*(LY2-LY0)-(LY2-LY1)*(LX2-LX0));
   }
   else
//----------------------------------------------------------------------------------------------------------------
// If the Polygon has more than 3 vertices, it can be concave, so to make sure we don't pick 3 vertices for
// the normal calculation which determine a reverse polygon, we have to run trough all the triangles of 3
// connected vertices and sum the normals of them, because even if a polygon is "strongly" concave, there are
// always more such triangles with the right orientation than ones with the wrong.
//----------------------------------------------------------------------------------------------------------------
   {
    LNormalX=0.0, LNormalY=0.0, LNormalZ=0.0;

    LVertNum--;
    for(LVCnt=0;LVCnt<LVertNum;LVCnt++)
    {
     LVertex=LVertices+LVertexNode[LVCnt].VertexID;
     LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;

     LVertex=LVertices+LVertexNode[LVCnt+1].VertexID;
     LX1=LVertex->X;LY1=LVertex->Y;LZ1=LVertex->Z;

     LNormalX-=(LZ0+LZ1)*(LY1-LY0);
     LNormalY+=(LZ0+LZ1)*(LX1-LX0);
     LNormalZ-=(LY0+LY1)*(LX1-LX0);
    }

// The last Vertex pair is the last one and the 0th one
//
    LVertex=LVertices+LVertexNode[LVCnt].VertexID;
    LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;
    LVertex=LVertices+LVertexNode[0].VertexID;
    LX1=LVertex->X;LY1=LVertex->Y;LZ1=LVertex->Z;

    LNormalX-=(LZ0+LZ1)*(LY1-LY0);
    LNormalY+=(LZ0+LZ1)*(LX1-LX0);
    LNormalZ-=(LY0+LY1)*(LX1-LX0);
   }

   r=sqrt(LNormalX*LNormalX+LNormalY*LNormalY+LNormalZ*LNormalZ);
   if(r>0.0) { r=1.0/r;LNormalX*=r;LNormalY*=r;LNormalZ*=r; }
   LPolygon->Normal.X=LNormalX;LPolygon->Normal.Y=LNormalY;LPolygon->Normal.Z=LNormalZ;
   LPolygon->Normal.Length=1.0;
  }
  else
  {
   LPolygon->Normal.X=0.0;LPolygon->Normal.Y=0.0;LPolygon->Normal.Z=0.0;
  }
}


//================================================================
// Set the Vertex normals of a Polygon to the Polygon's normal
//================================================================
void E3d_PolygonFlattenVertexNormals(E3dPolygon* LPolygon)
{
 E3dVertexNode*	LVertexNode=LPolygon->VertexNodes;
 unsigned int	LC, LN=LPolygon->NumOfVertexNodes;

 for(LC=0;LC<LN;LC++, LVertexNode++)
 {
  if(LVertexNode->VertexID>=0) LVertexNode->Normal=LPolygon->Normal;
 }
}


//========================================
// Create triangles for a Polygon
//========================================
void E3d_PolygonCreateTriangles(E3dVertex* LVertices, E3dPolygon* LPolygon, EBool LDoConvex)
{
 if(LPolygon->NumOfVertices>3) E3d_TriangulatePolygon(LVertices, 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;
  }
 }
}


//========================================
// Invert a Polygon
//========================================
void E3d_PolygonInvert(E3dPolygon* LPolygon)
{
 E3dVertexNode*	LVertexNodes=LPolygon->VertexNodes;


 if(LVertexNodes)
 {
  E3dVertexNode*	LNewVertexNodes=(E3dVertexNode*)EMalloc((LPolygon->NumOfVertexNodes)*sizeof(E3dVertexNode));


  if(LNewVertexNodes)
  {
   E3dVertexNode*	LVertexNode;
   E3dVertexNode*	LNewVertexNode;
   unsigned int		LC, LN, LVC, LVN=LPolygon->NumOfVertexNodes;


// Exterior
//
   LN=LPolygon->NumOfExteriorVertices;
   LVertexNode=LVertexNodes;
   LNewVertexNode=LNewVertexNodes+LN-1;
   for(LC=0;LC<LN;LC++, LVertexNode++, LNewVertexNode--)
   {
    memmove(LNewVertexNode, LVertexNode, sizeof(E3dVertexNode));
   }

// Holes
//
   if(LPolygon->NumOfVertices>LPolygon->NumOfExteriorVertices)
   {
// Start after the first contour separator
//
    LVC=LN;

    while(LVC<LVN)
    {
     LVertexNode=LVertexNodes+LVC;
     LNewVertexNodes[LVC].VertexID=LVertexNode->VertexID;	// Copy contour separator

     LVertexNode++;LVC++;	// Skip contour separator
     LC=LVC;
     LN=0;while((LVertexNode->VertexID>-1)&&(LVC<LVN)) { LVC++;LN++;LVertexNode++; }

     LVertexNode=LVertexNodes+LC;
     LNewVertexNode=LNewVertexNodes+LC+LN-1;
     for(LC=0;LC<LN;LC++, LVertexNode++, LNewVertexNode--)
     {
      memmove(LNewVertexNode, LVertexNode, sizeof(E3dVertexNode));
     }

    }
   }

   LPolygon->VertexNodes=LNewVertexNodes;
   EFree(LVertexNodes);

   LPolygon->Normal.X*=-1.0;
   LPolygon->Normal.Y*=-1.0;
   LPolygon->Normal.Z*=-1.0;
 #ifdef USEOpenGL
   LPolygon->GLNormal[E3dX]*=-1.0;
   LPolygon->GLNormal[E3dY]*=-1.0;
   LPolygon->GLNormal[E3dZ]*=-1.0;
 #endif // USEOpenGL

   LVertexNode=LPolygon->VertexNodes;
   for(LC=0;LC<LVN;LC++, LVertexNode++)
   {
    if(LVertexNode->VertexID>-1)
    {
     LVertexNode->Normal.X*=-1.0;
     LVertexNode->Normal.Y*=-1.0;
     LVertexNode->Normal.Z*=-1.0;
 #ifdef USEOpenGL
     LVertexNode->GLNormal[E3dX]*=-1.0;
     LVertexNode->GLNormal[E3dY]*=-1.0;
     LVertexNode->GLNormal[E3dZ]*=-1.0;
 #endif // USEOpenGL
    }
   }

   if(LPolygon->ITriangles) { EFree(LPolygon->ITriangles);LPolygon->ITriangles=NULL;LPolygon->NumOfTriangles=0; }
  }
 }
}


//========================================================================================
// Determine if a Polygon has a given Vertex index
//========================================================================================
EBool E3d_PolygonHasVertexIndex(E3dPolygon* LPolygon, int LVertexIndex)
{
 E3dVertexNode*	LVertexNodes=LPolygon->VertexNodes;
 unsigned int	LVC, LNumOfVertexNodes=LPolygon->NumOfVertexNodes;

 for(LVC=0;LVC<LNumOfVertexNodes;LVC++)
 {
  if(LVertexNodes[LVC].VertexID==LVertexIndex) return(TRUE);
 }
 return(FALSE);
}


//========================================================================================
// Determine if a Polygon has a given Edge
//========================================================================================
EBool E3d_PolygonHasEdge(E3dPolygon* LPolygon, E3dEdge* LEdge)
{
 E3dVertexNode*	LVertexNode=LPolygon->VertexNodes;
 E3dVertexNode*	LOtherVertexNode;
 unsigned int	LVC, LVN=LPolygon->NumOfVertexNodes;
 int		LEdgeStart=LEdge->Start, LEdgeEnd=LEdge->End;


 for(LVC=0;LVC<LVN;LVC++, LVertexNode++)
 {
  if(LVertexNode->VertexID==LEdgeStart)
  {
   LOtherVertexNode=E3dM_NextVertexNode(LPolygon->VertexNodes, LVertexNode, LVC, LVN);
   if(LOtherVertexNode->VertexID==LEdgeEnd)
   {
    return(TRUE);
   }
   else
   {
    LOtherVertexNode=E3dM_PrevVertexNode(LPolygon->VertexNodes, LVertexNode, LVC, LVN);
    if(LOtherVertexNode->VertexID==LEdgeEnd)
    {
     return(TRUE);
    }
   }
  }
 }

 return(FALSE);
}


//========================================================================================
// Allocate memory for triangle strips
//
// Argument
//  unsigned int LNumOfStrips      Number of strips to allocate
//
// Description
//  Allocates memory for the specified number of triangle strips and initializes them.
//
// Return value
//  A pointer to the allocated array of triangle strips or NULL in case of an error
//
// See also
//  E3d_PolyGroupFreeTriangleStrips
//========================================================================================
E3dTriangleStrip* E3d_TriangleStripsAllocate(unsigned int LNumOfStrips)
{
 E3dTriangleStrip*	LATriangleStrip;
 E3dTriangleStrip*	LITriangleStrip;
 unsigned int		LC;

 if(LNumOfStrips>0)
 {
#ifdef E_MEMDEBUG
 E_MemName="TriangleStrips";
#endif // E_MEMDEBUG

  LATriangleStrip=(E3dTriangleStrip*)EMalloc((size_t)(sizeof(E3dTriangleStrip)*LNumOfStrips));
  if(LATriangleStrip)
  {
   LITriangleStrip=LATriangleStrip;
   for(LC=0;LC<LNumOfStrips;LC++, LITriangleStrip++)
   {
    LITriangleStrip->NumOfVertices=0;
    LITriangleStrip->VertexNodes=NULL;
    LITriangleStrip->Flags=0;
   }
  }
  return(LATriangleStrip);
 }
 return(NULL);
}


//================================================================================
// Allocate vertex nodes for a triangle strip
//
// Arguments
//  E3dTriangleStrip* LTriangleStrip     Pointer to the Polygon structure
//  int               LNumOfVertexNodes  Number of vertex nodes to allocate
//
// Description
//  This function allocates a number of E3dVertexNode structures in an array
//  and initializes them.
//
// Return value
//  Pointer to the array of allocated vertex nodes or NULL in case of an error.
//
// See also
//  E3d_TriangleStripsAllocate
//================================================================================
E3dVertexNode* E3d_TriangleStripVertexNodeAllocate(
                                     E3dTriangleStrip* LTriangleStrip,
                                     int LNumOfVertices)
{
 E3dVertexNode*	LVertexNodes=NULL;

 if(LNumOfVertices>0)
 {
  LVertexNodes=(E3dVertexNode*)EMalloc((size_t)(sizeof(E3dVertexNode)*LNumOfVertices));
  if(LVertexNodes)
  {
   LTriangleStrip->NumOfVertices=LNumOfVertices;
  }
  LTriangleStrip->VertexNodes=LVertexNodes;
 }
 return(LVertexNodes);
}







//----------------------------------------------------------------------------------------
// PolyGroup functions
//----------------------------------------------------------------------------------------



//================================================================================================
// Allocate a PolyonGroup
//
// Description
//  This function allocates and initializes an E3dPolyGroup structure.
//
// Return value
//  Pointer to the allocated E3dPolyGroup structure or NULL in case of an error.
//
// See also
//  E3d_PolyGroupFree
//================================================================================================
E3dPolyGroup* E3d_PolyGroupAllocate()
{
 E3dPolyGroup*	LPolyGroup;

#ifdef E_MEMDEBUG
 E_MemName="PolyGroup";
#endif // E_MEMDEBUG

 if((LPolyGroup=(E3dPolyGroup*)EMalloc(sizeof(E3dPolyGroup)))!=NULL)
 {
  LPolyGroup->RefCnt=0;
  LPolyGroup->LockCnt=0;

  LPolyGroup->Name=NULL;

  LPolyGroup->NumOfInfoRecords=0;
  LPolyGroup->Info=NULL;

  LPolyGroup->Material=NULL;
  LPolyGroup->DrawingMaterial=NULL;

#ifdef USEOpenGL
//  LPolyGroup->GLMaterial=NULL;

  LPolyGroup->GLArrayVertexRGBA=NULL;
  LPolyGroup->GLArraySizeVertexRGBA=0;

  LPolyGroup->GLArrayVertexRGBAST=NULL;
  LPolyGroup->GLArraySizeVertexRGBAST=0;

  LPolyGroup->GLArrayVertexNormal=NULL;
  LPolyGroup->GLArraySizeVertexNormal=0;

  LPolyGroup->GLArrayVertexNormalST=NULL;
  LPolyGroup->GLArraySizeVertexNormalST=0;

  LPolyGroup->GLEdgeVertexIndexArray=NULL;
  LPolyGroup->GLTriangleVertexIndexArray=NULL;

  LPolyGroup->GLTriangleCount=0;
#endif // USEOpenGL

  LPolyGroup->NumOf2DTextures=0;	// In addition to the 2DTextures in the Material, a PolyGroup can have its own 2DTextures...
  LPolyGroup->Textures2D=NULL;		// ...that way we can share Materials without sharing their 2DTextures

  LPolyGroup->NumOfTextureMappers=0;	// For each 2DTexture in the Material and the PolyGoup, there is a Mapper
  LPolyGroup->TextureMappers=NULL;


  LPolyGroup->VertexNormalType=E3dNormalNONE;
  LPolyGroup->Flags=0;
  LPolyGroup->DiscontinuityAngle=60.0;

  LPolyGroup->Selected=FALSE;
  LPolyGroup->TexturesMapped=FALSE;

  LPolyGroup->NumOfEdges=0;
  LPolyGroup->Edges=NULL;

  LPolyGroup->CastsShadows=TRUE;
  LPolyGroup->ReceivesShadows=TRUE;

  LPolyGroup->DoubleSided=FALSE;
  LPolyGroup->Visible=TRUE;
  LPolyGroup->NumOfPolygons=0;
  LPolyGroup->Polygons=NULL;
  LPolyGroup->NumOfTriangleStrips=0;
  LPolyGroup->TriangleStrips=NULL;

  LPolyGroup->NumOfVertices=0;
  LPolyGroup->VertexColors=NULL;


// Render-triangles
//
  LPolyGroup->NumOfTriangles=0;
  LPolyGroup->Triangles=NULL;
  LPolyGroup->PolygonsBase=-1;
  LPolyGroup->TrianglesBase=-1;


  LPolyGroup->LightChangeCount=-1;
 }
 return(LPolyGroup);
}


//========================================================================================
// Free a PolyGroup
//
// Arguments
//  E3dPolyGroup*  LPolyGroup     The E3dPolyGroup structure to free
//
// Description
//  This function first decrements the reference count (RefCnt) of the given PolyGroup
//  structure (if it's not already zero).
//  After that, if RefCnt is still greater than zero, it means that something other
//  than the function calling E3d_PolyGroupFree() is still referring to this PolyGroup,
//  so E3d_PolyGroupFree() will simply return.
//  If RefCnt is zero, E3d_PolyGroupFree() will free all the Polygons and TriangleStrips
//  associated with this PolyGroup and the PolyGroup structure itself.
//
// See also
//  E3d_PolyGroupAllocate
//========================================================================================
void E3d_PolyGroupFree(E3dPolyGroup* LPolyGroup)
{
 if(LPolyGroup->RefCnt) LPolyGroup->RefCnt-=1;
 if(LPolyGroup->RefCnt) return;

 if(LPolyGroup->Name) EFree(LPolyGroup->Name);

 if(LPolyGroup->Edges) EFree(LPolyGroup->Edges);

 E3d_PolyGroupInfoRemoveAll(LPolyGroup);

 if(LPolyGroup->TextureMappers)
 {
  E3d_2DTextureMappersFree(LPolyGroup->TextureMappers, LPolyGroup->NumOfTextureMappers);
  LPolyGroup->NumOfTextureMappers=0;
  LPolyGroup->TextureMappers=NULL;
 }

 E3d_PolyGroupFreePolygons(LPolyGroup);
 E3d_PolyGroupFreeTriangleStrips(LPolyGroup);
 E3d_PolyGroupFreeRenderTriangles(LPolyGroup);


 if(LPolyGroup->Material) E3d_MaterialFree(LPolyGroup->Material);
 if(LPolyGroup->DrawingMaterial) E3d_MaterialFree(LPolyGroup->DrawingMaterial);

 if(LPolyGroup->VertexColors) EFree(LPolyGroup->VertexColors);

#ifdef USEOpenGL
 if(LPolyGroup->GLEdgeVertexIndexArray) EFree(LPolyGroup->GLEdgeVertexIndexArray);
 if(LPolyGroup->GLTriangleVertexIndexArray) EFree(LPolyGroup->GLTriangleVertexIndexArray);

// Free AGP arrays
//
#ifdef USE_NV_AGP
 if(LPolyGroup->GLArrayVertexRGBA) E3d_AGPFree(LPolyGroup->GLArrayVertexRGBA);
 if(LPolyGroup->GLArrayVertexRGBAST) E3d_AGPFree(LPolyGroup->GLArrayVertexRGBAST);
 if(LPolyGroup->GLArrayVertexNormal) E3d_AGPFree(LPolyGroup->GLArrayVertexNormal);
 if(LPolyGroup->GLArrayVertexNormalST) E3d_AGPFree(LPolyGroup->GLArrayVertexNormalST);
#endif	// USE_NV_AGP

#endif // USEOpenGL

 EFree(LPolyGroup);
}


//================================================================================
// Clone a PolyGroup
//
// Argument
//  E3dMesh* LMesh         Pointer to the PolyGroup structure to be cloned
//  int      LFlags        Flags determining what to clone / share
//
// Description
//  This function creates and exact copy of the given PolyGroup.
//  If LFlags has the flag E3dCLONE_MATERIALS set, the Material of the PolyGroup
//  will be cloned as well, otherwise the Material will be shared between the
//  original PolyGroup and the clone.
//
// Return value
//  Pointer to the new PolyGroup structure, or NULL in case of an error
//
// See also
//  E3d_PolyGroupAllocate, E3d_PolyGroupFree
//================================================================================
E3dPolyGroup* E3d_PolyGroupClone(E3dPolyGroup* LPolyGroup, int LFlags)
{
 E3dPolyGroup*	LNewPolyGroup=E3d_PolyGroupAllocate();
 unsigned int	LPNum=LPolyGroup->NumOfPolygons;


 if(LFlags&E3dCLONE_MATERIALS) LNewPolyGroup->Material=E3d_SceneMaterialClone(E3d_Scene, LPolyGroup->Material);
 else LNewPolyGroup->Material=LPolyGroup->Material;

 if(LNewPolyGroup->Material) LNewPolyGroup->Material->RefCnt+=1;


 LNewPolyGroup->VertexNormalType=LPolyGroup->VertexNormalType;
 LNewPolyGroup->Flags=LPolyGroup->Flags;
 LNewPolyGroup->DiscontinuityAngle=LPolyGroup->DiscontinuityAngle;
 LNewPolyGroup->DoubleSided=LPolyGroup->DoubleSided;

 if(LPNum)
 {
  E3dPolygon*		LPolygon=LPolyGroup->Polygons;
  E3dPolygon*		LDPolygon;
  E3dVertexNode*	LDVertexNodes;
  E3dVertexNode*	LDVertexNode;
  unsigned int		LVNum, LPCnt;

  if((LDPolygon=E3d_PolygonsAllocate(LPNum))!=NULL)
  {
   LNewPolyGroup->Polygons=LDPolygon;
   LNewPolyGroup->NumOfPolygons=LPNum;
   for(LPCnt=0;LPCnt<LPNum;LPCnt++, LPolygon++, LDPolygon++)
   {
    E3d_VectorCopy(&(LPolygon->Normal), &(LDPolygon->Normal));
    LVNum=LPolygon->NumOfVertexNodes;

    LDVertexNode=LDVertexNodes=E3d_PolygonVertexNodesAllocate(LDPolygon, LVNum);

    LDPolygon->NumOfExteriorVertices=LPolygon->NumOfExteriorVertices;
    LDPolygon->NumOfVertices=LPolygon->NumOfVertices;
    if(LDVertexNode)
    {
     LDPolygon->VertexNodes=LDVertexNode;

     memcpy(LDPolygon->VertexNodes, LPolygon->VertexNodes, sizeof(E3dVertexNode)*LVNum);

     if(LPolygon->ITriangles)
     {
      E3dVertexNode*	LVertexNodes=LPolygon->VertexNodes;
      E3dITriangle*	LITriangle=LPolygon->ITriangles;
      E3dITriangle*	LDITriangle;
      unsigned int	LC, LN=LPolygon->NumOfTriangles;


      LDPolygon->ITriangles=LDITriangle=(E3dITriangle*)EMalloc(sizeof(E3dITriangle)*LN);
      LDPolygon->NumOfTriangles=LPolygon->NumOfTriangles;

      for(LC=0;LC<LN;LC++, LITriangle++, LDITriangle++)
      {
       LDITriangle->VertexNodes[0]=LITriangle->VertexNodes[0]-LVertexNodes+LDVertexNodes;
       LDITriangle->VertexNodes[1]=LITriangle->VertexNodes[1]-LVertexNodes+LDVertexNodes;
       LDITriangle->VertexNodes[2]=LITriangle->VertexNodes[2]-LVertexNodes+LDVertexNodes;
      }
     }

    }
   }
  }
 }
 return(LNewPolyGroup);
}


//================================================
// Find a PolyGroupClass by its name
//================================================
E3dPolyGroupClass* E3d_PolyGroupClassFindByName(char* LName)
{
 E3dPolyGroupClass**	LClasses=E3d_CustomPolyGroupClasses;

 if(LClasses)
 {
  E3dPolyGroupClass*	LClass;
  unsigned int		LC, LN;

  for(LC=0, LN=E3d_NumOfCustomPolyGroupClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LName)) return(LClass);
  }
 }
 return(NULL);
}


//================================================
// Register a new PolyGroup class
//================================================
E3dPolyGroupClass* E3d_PolyGroupClassRegister(E3dPolyGroupClass* LClassTemplate)
{
 E3dPolyGroupClass**	LClasses=E3d_CustomPolyGroupClasses;
 E3dPolyGroupClass*	LClass;
 unsigned int		LC, LN;

 if(LClasses)
 {
  for(LC=0, LN=E3d_NumOfCustomPolyGroupClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LClassTemplate->Name))
   {
    LClass->StructSize=LClassTemplate->StructSize;
    LClass->EditProc=LClassTemplate->EditProc;
    LClass->DestroyProc=LClassTemplate->DestroyProc;
    LClass->Resources=LClassTemplate->Resources;

    return(LClass);
   }
  }
 }

 if(LClasses==NULL) E3d_CustomPolyGroupClasses=LClasses=(E3dPolyGroupClass**)EMalloc(sizeof(E3dPolyGroupClass*));
 else
 {
  if((LClasses=(E3dPolyGroupClass**)ERealloc(E3d_CustomPolyGroupClasses, sizeof(E3dPolyGroupClass*)*(E3d_NumOfCustomPolyGroupClasses+1)))!=NULL) E3d_CustomPolyGroupClasses=LClasses;
 }

 if(LClasses)
 {
  if((LClass=(E3dPolyGroupClass*)EMalloc(sizeof(E3dPolyGroupClass)))!=NULL)
  {
   LClasses[E3d_NumOfCustomPolyGroupClasses]=LClass;

   LClass->Name=EStrDup(LClassTemplate->Name);
   LClass->StructSize=LClassTemplate->StructSize;
   LClass->EditProc=LClassTemplate->EditProc;
   LClass->DestroyProc=LClassTemplate->DestroyProc;
   LClass->Resources=LClassTemplate->Resources;
   E3d_NumOfCustomPolyGroupClasses++;
  }
  return(LClass);
 }
 else return(NULL);
}


//================================================
// Deactivate a PolyGroupClass
//================================================
void E3d_PolyGroupClassDeactivate(E3dPolyGroupClass* LClass)
{
 E3dPolyGroupClass**	LClasses=E3d_CustomPolyGroupClasses;

 if(LClasses)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=E3d_NumOfCustomPolyGroupClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    LClass->EditProc=NULL;
    LClass->DestroyProc=NULL;
    LClass->Resources=NULL;
   }
  }
 }
}


//================================================
// Remove a PolyGroup class
//================================================
void E3d_PolyGroupClassRemove(E3dPolyGroupClass* LClass)
{
 E3dPolyGroupClass**	LClasses=E3d_CustomPolyGroupClasses;

 if(LClasses)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=E3d_NumOfCustomPolyGroupClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    if(LClass->Name) EFree(LClass->Name);
    EFree(LClass);
    if(LC<(LN-1)) memmove(LClass, LClass+1, sizeof(E3dPolyGroupClass*)*(LN-LC-1));
    E3d_NumOfCustomPolyGroupClasses-=1;
    if(E3d_NumOfCustomPolyGroupClasses==0) { EFree(E3d_CustomPolyGroupClasses);E3d_CustomPolyGroupClasses=NULL; }
    else E3d_CustomPolyGroupClasses=(E3dPolyGroupClass**)ERealloc(E3d_CustomPolyGroupClasses, sizeof(E3dPolyGroupClass*)*E3d_NumOfCustomPolyGroupClasses);
    return;
   }
  }
 }
}


//================================================
// Remove Info record from a PolyGroup
//================================================
int E3d_PolyGroupInfoRemove(E3dPolyGroup* LPolyGroup, E3dPolyGroupInfo* LInfo)
{
// Free Class-specific data
//
 if(LPolyGroup->Info)
 {
  unsigned int	LC, LN=LPolyGroup->NumOfInfoRecords;

  for(LC=0;LC<LN;LC++)
  {
   if(LInfo==LPolyGroup->Info[LC])
   {
    E3dPolyGroupClass*	LClass=LInfo->Class;

    if(LClass)
    {
     if(LClass->DestroyProc) LClass->DestroyProc(NULL, NULL, LPolyGroup, LInfo);
     EFree(LInfo);
     ELst_RemovePointer((void***)(&(LPolyGroup->Info)), &(LPolyGroup->NumOfInfoRecords), LInfo);
     return(1);
    }
   }
  }
 }
 return(0);
}


//========================================================
// Free PolyGroupInfo records of a PolyGroup
//========================================================
int E3d_PolyGroupInfoRemoveAll(E3dPolyGroup* LPolyGroup)
{
// Free Class-specific data
//
 if(LPolyGroup->Info)
 {
  E3dPolyGroupInfo*	LInfo;
  E3dPolyGroupClass*	LClass;
  unsigned int		LC, LN=LPolyGroup->NumOfInfoRecords;


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

   LClass=LInfo->Class;

   if(LClass)
   {
    if(LClass->DestroyProc) LClass->DestroyProc(NULL, NULL, LPolyGroup, LInfo);
    EFree(LInfo);
   }
  }

  EFree(LPolyGroup->Info);LPolyGroup->Info=NULL;LPolyGroup->NumOfInfoRecords=0;
  return(LN);
 }
 return(0);
}


//========================================================
// Add an Info record of a given Class to a PolyGroup
//========================================================
E3dPolyGroupInfo* E3d_PolyGroupInfoAdd(E3dPolyGroup* LPolyGroup, E3dPolyGroupClass* LClass)
{
 E3dPolyGroupInfo*	LInfo=(E3dPolyGroupInfo*)EMalloc(LClass->StructSize);

 if(LInfo)
 {
  ELst_AddPointer((void***)(&(LPolyGroup->Info)), &(LPolyGroup->NumOfInfoRecords), LInfo);
  LInfo->Class=LClass;
 }
 return(LInfo);
}


//========================================================
// Find Info record of a PolyGroup by its Class
//========================================================
E3dPolyGroupInfo* E3d_PolyGroupInfoByClass(E3dPolyGroup* LPolyGroup, E3dPolyGroupClass* LClass)
{
 if(LPolyGroup->Info)
 {
  unsigned int		LC, LN=LPolyGroup->NumOfInfoRecords;

  for(LC=0;LC<LN;LC++)
  {
   if(LPolyGroup->Info[LC]->Class==LClass) return(LPolyGroup->Info[LC]);
  }
 }
 return(NULL);
}




//================================================================================================
// Count selected Polygons in a PolyonGroup
//
// Arguments
//  E3dPolyGroup*  LPolyGroup     Pointer to the E3dPolyGroup structure
//
// Description
//  This function returns the number of selected Polygons in the given PolyGroup.
//
// Return value
//  The number of selected Polygons.
//
// See also
//  E3d_PolyGroupDeleteSelectedPolygons
//================================================================================================
int E3d_PolyGroupNumOfSelectedPolygons(E3dPolyGroup* LPolyGroup)
{
 E3dPolygon*	LPolygon;
 unsigned int	LC, LN, LNSelectedPolys;


 LN=LPolyGroup->NumOfPolygons;
 LNSelectedPolys=0;

// Count selected Polygons
//
 LPolygon=LPolyGroup->Polygons;
 for(LC=0;LC<LN;LC++, LPolygon++)
 {
  if(LPolygon->Flags&E3dPolyFlagSELECTED) LNSelectedPolys++;
 }

 return(LNSelectedPolys);
}


//================================================================================================
// Remove selected Polygons from a PolyonGroup
//
// Arguments
//  E3dPolyGroup*  LPolyGroup     Pointer to the E3dPolyGroup structure
//
// Description
//  This function removes the selected Polygons from the given PolyGroup by first copying the
//  unselected Polygons into a new array and replacing the old array with this array.
//  A Polygon is considered selected if its 'Flags' field has the 'E3dPolyFlagSELECTED' flag set.
//
// Return value
//  The number of Polygons removed.
//
// See also
//  E3d_PolyGroupFree
//================================================================================================
int E3d_PolyGroupDeleteSelectedPolygons(E3dPolyGroup* LPolyGroup)
{
 E3dPolygon*	LPolygons;
 E3dPolygon*	LPolygon;
 E3dPolygon*	LDPolygon;
 unsigned int	LC, LN, LNUnselectedPolys, LNSelectedPolys;


 LN=LPolyGroup->NumOfPolygons;
 LNUnselectedPolys=LNSelectedPolys=0;

// Count unselected Polygons
//
 LPolygon=LPolyGroup->Polygons;
 for(LC=0;LC<LN;LC++, LPolygon++)
 {
  if((LPolygon->Flags&E3dPolyFlagSELECTED)==0) LNUnselectedPolys++;
  else LNSelectedPolys++;
 }

 if(LNUnselectedPolys>0)
 {
// Remove the selected Polygons from the original PolyGroup by copying the
// unselected ones into a new array and replacing the old array with this array.
//
  if((LPolygons=E3d_PolygonsAllocate(LNUnselectedPolys))!=NULL)
  {
   LPolygon=LPolyGroup->Polygons;
   LDPolygon=LPolygons;
   LN=LPolyGroup->NumOfPolygons;
   for(LC=0;LC<LN;LC++, LPolygon++)
   {
    if((LPolygon->Flags&E3dPolyFlagSELECTED)==0)
    {
      memcpy(LDPolygon, LPolygon, sizeof(E3dPolygon));
      LDPolygon++;
    }
   }

// FIXME: free the Polygons too (VertexNodes) or store them for Undo!
//
   EFree(LPolyGroup->Polygons);
   LPolyGroup->Polygons=LPolygons;
   LPolyGroup->NumOfPolygons=LNUnselectedPolys;
  }
 }
 else
 {
// FIXME: Store for Undo!
//
  E3d_PolyGroupFreePolygons(LPolyGroup);
 }

 return(LNSelectedPolys);
}


//================================================================================
// Free the Polygons of a PolyGroup
//
// Argument
//  E3dPolyGroup* LPolyGroup       Pointer to the PolyGroup
//
// Description
//  Frees all Polygons in the specified PolyGroup.
//
// See also
//  E3d_PolyGroupFreeTriangleStrips
//================================================================================
void E3d_PolyGroupFreePolygons(E3dPolyGroup* LPolyGroup)
{
 E3dPolygon*	LPolygon=LPolyGroup->Polygons;
 unsigned int	LC, LN;


 if(LPolygon)
 {
  for(LC=0, LN=LPolyGroup->NumOfPolygons;LC<LN;LC++, LPolygon++)
  {
   E3dM_PolygonFree(LPolygon);
  }

  EFree(LPolyGroup->Polygons);LPolyGroup->Polygons=NULL;LPolyGroup->NumOfPolygons=0;
 }
}


//================================================================================
// Free the TriangleStrips of a PolyGroup
//
// Argument
//  E3dPolyGroup* LPolyGroup       Pointer to the PolyGroup 
//
// Description
//  Frees the triangle strips in the specified PolyGroup.
//
// See also
//  E3d_PolyGroupFreePolygons
//================================================================================
void E3d_PolyGroupFreeTriangleStrips(E3dPolyGroup* LPolyGroup)
{
 E3dTriangleStrip*	LTriangleStrip=LPolyGroup->TriangleStrips;

 if(LTriangleStrip)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=LPolyGroup->NumOfTriangleStrips;LC<LN;LC++, LTriangleStrip++)
  {
   if(LTriangleStrip->VertexNodes) EFree(LTriangleStrip->VertexNodes);
  }
  EFree(LPolyGroup->TriangleStrips);LPolyGroup->TriangleStrips=NULL;LPolyGroup->NumOfTriangleStrips=0;
 }
}


//================================================================================
// Free the Rendering Triangles of a PolyGroup
//
// Argument
//  E3dPolyGroup* LPolyGroup       Pointer to the PolyGroup
//
// Description
//  Frees the specified PolyGroup's RenderTriangles.
//
// See also
//  Scene/E3d_SceneGetRenderTriangles, Scene/E3d_SceneFreeRenderTriangles
//================================================================================
void E3d_PolyGroupFreeRenderTriangles(E3dPolyGroup* LPolyGroup)
{
 if(LPolyGroup->Triangles)
 {
  EFree(LPolyGroup->Triangles);
  LPolyGroup->Triangles=NULL;LPolyGroup->NumOfTriangles=0;
 }
}


//========================================================================================
// Create edge list for the given PolyGroup from its Polygon and Triangle strip data
//========================================================================================
void E3d_PolyGroupCreateEdgesFromPolyData(E3dPolyGroup* LPolyGroup, E3dVertexLink* LVertexLinks)
{
 E3dPolygon*		LPolygon;
 E3dTriangleStrip*	LTriangleStrip;
 E3dVertexNode*		LPolyVertNodes;
 E3dEdge*		LEdges;
 E3dEdge*		LNextEdge;
 unsigned short		LLNum;
 unsigned int*		LVLinks;
 unsigned int		LPCStart, LPNum;
 register int		LCVCnt, LVCnt, LVNum, LPCnt, LNEdgeNum, LECnt, LEdgeNum, LActStartV, LActEndV, LTmpV, LStartV, LEndV;
 EBool			LEFound, LEdgesOk;


 if(LPolyGroup->Edges) { EFree(LPolyGroup->Edges);LPolyGroup->Edges=NULL;LPolyGroup->NumOfEdges=0; }

 LNEdgeNum=0;

// Add up maximum possible number of Edges
//
 if((LPolygon=LPolyGroup->Polygons)!=NULL)
 {
  LPNum=LPolyGroup->NumOfPolygons;
  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LPolygon++) LNEdgeNum+=LPolygon->NumOfVertices;
 }

 if((LTriangleStrip=LPolyGroup->TriangleStrips)!=NULL)
 {
  LPNum=LPolyGroup->NumOfTriangleStrips;
  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LTriangleStrip++) LNEdgeNum+=LTriangleStrip->NumOfVertices*2-3;
 }
 if(LNEdgeNum==0) return;


// Allocate maximum possible number of edges (all polygons, all triangle strips all edges of the mesh)
//
 LPolyGroup->Edges=E3d_EdgesAllocate(LNEdgeNum);
 LEdgesOk=TRUE;
 LEdgeNum=0;
 LEdges=LPolyGroup->Edges;LNextEdge=LEdges;


// Initialize this separately, so the old-fashioned method can pick up at the
// PolyGroup and Polygon/TriangleStrip where the optimized method gave up
//
 LPCnt=0;

// If we have a valid Vertex link-list use it
//
 if(LVertexLinks)
 {
// Edges on Polygons/TriangleStrips
//
  if((LPolygon=LPolyGroup->Polygons)!=NULL)
  {
   LPNum=LPolyGroup->NumOfPolygons;

   for(LPCnt=0;LPCnt<LPNum;LPCnt++, LPolygon++)
   {
    LPolyVertNodes=LPolygon->VertexNodes;
    if(LPolyVertNodes)
    {
     LVNum=LPolygon->NumOfExteriorVertices;
//------------------------------------------------
// First (exterior) contour of the Polygon
//------------------------------------------------
     for(LVCnt=0;LVCnt<(LVNum-1);LVCnt++)		//  First n-1 edges of the exterior contour
     {
      LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LVCnt+1].VertexID;
      if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
      if((LLNum=LVertexLinks[LActStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
      LVLinks=LVertexLinks[LActStartV].Vertices;
      for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
      {
       if(LVLinks[LECnt]==LActEndV) { LEFound=TRUE;break; }
      }
      if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
     }
     if(!LEdgesOk) break;

// Last edge: Last vertex->first vertex
//
     LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[0].VertexID;
     if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
     if((LLNum=LVertexLinks[LActStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
     LVLinks=LVertexLinks[LActStartV].Vertices;
     for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
     {
      if(LVLinks[LECnt]==LActEndV) { LEFound=TRUE;break; }
     }
     if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }

     LVNum=LPolygon->NumOfVertexNodes;
//------------------------------------------------
// Other contours of the Polygon (holes)
//------------------------------------------------
     LVCnt+=2;
     while(LVCnt<(LVNum-1))
     {
      for(LCVCnt=LVCnt;(LVCnt<(LVNum-1))&&(LPolyVertNodes[LVCnt+1].VertexID!=-1);LVCnt++)
      {
       LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LVCnt+1].VertexID;
       if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Make LActStartV always be the lower one
       if((LLNum=LVertexLinks[LActStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
       LVLinks=LVertexLinks[LActStartV].Vertices;
       for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
       {
	if(LVLinks[LECnt]==LActEndV) { LEFound=TRUE;break; }
       }
       if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
      }
      if(!LEdgesOk) break;

// Last edge: Last vertex->first vertex
//
      LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LCVCnt].VertexID;
      if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
      if((LLNum=LVertexLinks[LActStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
      LVLinks=LVertexLinks[LActStartV].Vertices;
      for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
      {
       if(LVLinks[LECnt]==LActEndV) { LEFound=TRUE;break; }
      }
      if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
      if(LVCnt<(LVNum-1)) LVCnt+=2;
     }
    }
   }
  }

// Do Triangle strips
//
  if((LTriangleStrip=LPolyGroup->TriangleStrips)!=NULL)
  {
   LPNum=LPolyGroup->NumOfTriangleStrips;

   for(LPCnt=0;LPCnt<LPNum;LPCnt++, LTriangleStrip++)
   {
    LPolyVertNodes=LTriangleStrip->VertexNodes;
    if(LPolyVertNodes)
    {
     LVNum=LTriangleStrip->NumOfVertices;

// First edge of the TriangleStrip
//
     LActStartV=LPolyVertNodes[0].VertexID;LActEndV=LPolyVertNodes[1].VertexID;

// Make LStartV always the lower one
//
     if(LActStartV>LActEndV) { LStartV=LActEndV;LEndV=LActStartV; }
     else { LStartV=LActStartV;LEndV=LActEndV; }

     if((LLNum=LVertexLinks[LStartV].LinkedToNum)>=MAX_VLINKNUM) LEdgesOk=FALSE;
     else
     {
      LVLinks=LVertexLinks[LStartV].Vertices;
      for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
      {
       if(LVLinks[LECnt]==LEndV) { LEFound=TRUE;break; }
      }
      if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
     }


     for(LVCnt=2;LVCnt<LVNum;LVCnt++)
     {
      LActStartV=LPolyVertNodes[LVCnt-2].VertexID;LActEndV=LPolyVertNodes[LVCnt].VertexID;

// Make LStartV always the lower one
//
      if(LActStartV>LActEndV) { LStartV=LActEndV;LEndV=LActStartV; }
      else { LStartV=LActStartV;LEndV=LActEndV; }

      if((LLNum=LVertexLinks[LStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
      LVLinks=LVertexLinks[LStartV].Vertices;
      for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
      {
       if(LVLinks[LECnt]==LEndV) { LEFound=TRUE;break; }
      }
      if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }

      LActStartV=LPolyVertNodes[LVCnt-1].VertexID;

// Make LStartV always the lower one
//
      if(LActStartV>LActEndV) { LStartV=LActEndV;LEndV=LActStartV; }
      else { LStartV=LActStartV;LEndV=LActEndV; }

      if((LLNum=LVertexLinks[LStartV].LinkedToNum)>=MAX_VLINKNUM) { LEdgesOk=FALSE;break; }
      LVLinks=LVertexLinks[LStartV].Vertices;
      for(LECnt=0, LEFound=FALSE;LECnt<LLNum;LECnt++)
      {
       if(LVLinks[LECnt]==LEndV) { LEFound=TRUE;break; }
      }
      if(!LEFound) { LVLinks[LLNum]=LActEndV;LVertexLinks[LActStartV].LinkedToNum++;LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
     }
     if(!LEdgesOk) break;
    }
   }
  }

  if(LEdgesOk)
  {
   LPolyGroup->NumOfEdges=LEdgeNum;
   if(LEdgeNum<LNEdgeNum) LPolyGroup->Edges=(E3dEdge*)ERealloc(LPolyGroup->Edges, sizeof(E3dEdge)*LNEdgeNum);	// Free unnecessary edges
   return;
  }
 }

// If the optimized function above could not proceed (either because we didn't get a valid vertex
// link array or because there were vertices with more than MAX_VLINKNUM edges connected to them),
// do it the "old-fashioned" way
//

// Continue with the Polygon we left off at
//
 LPCStart=LPCnt;

 LPolygon=LPolyGroup->Polygons+LPCStart;LPNum=LPolyGroup->NumOfPolygons;

 for(LPCnt=LPCStart;LPCnt<LPNum;LPCnt++, LPolygon++)
 {
  LPolyVertNodes=LPolygon->VertexNodes;
  if(LPolyVertNodes)
  {
   LVNum=LPolygon->NumOfExteriorVertices;
//------------------------------------------------
// First (exterior) contour of the Polygon
//------------------------------------------------
   for(LVCnt=0;LVCnt<(LVNum-1);LVCnt++)		//  First n-1 edges of the exterior contour
   {
    LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LVCnt+1].VertexID;
    if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
    for(LECnt=0, LEFound=FALSE;LECnt<LEdgeNum;LECnt++)
    {
     if((LEdges[LECnt].Start==LActStartV)&&(LEdges[LECnt].End==LActEndV)) { LEFound=TRUE;break; }
    }
    if(!LEFound) { LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
   }

// Last edge: Last vertex->first vertex
//
   LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[0].VertexID;
   if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
   for(LECnt=0, LEFound=FALSE;LECnt<LEdgeNum;LECnt++)
   {
    if((LEdges[LECnt].Start==LActStartV)&&(LEdges[LECnt].End==LActEndV)) { LEFound=TRUE;break; }
   }
   if(!LEFound) { LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }

   LVNum=LPolygon->NumOfVertexNodes;
//------------------------------------------------
// Other contours of the Polygon (holes)
//------------------------------------------------
   LVCnt+=2;
   while(LVCnt<(LVNum-1))
   {
    for(LCVCnt=LVCnt;(LVCnt<(LVNum-1))&&(LPolyVertNodes[LVCnt+1].VertexID!=-1);LVCnt++)
    {
     LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LVCnt+1].VertexID;
     if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Make LActStartV always be the lower one
     for(LECnt=0, LEFound=FALSE;LECnt<LEdgeNum;LECnt++)
     {
      if((LEdges[LECnt].Start==LActStartV)&&(LEdges[LECnt].End==LActEndV)) { LEFound=TRUE;break; }
     }
     if(!LEFound) { LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
    }
    LActStartV=LPolyVertNodes[LVCnt].VertexID;LActEndV=LPolyVertNodes[LCVCnt].VertexID;
    if(LActStartV>LActEndV) { LTmpV=LActStartV;LActStartV=LActEndV;LActEndV=LTmpV; }	// Let LActStartV always be the lower one
    for(LECnt=0, LEFound=FALSE;LECnt<LEdgeNum;LECnt++)
    {
     if((LEdges[LECnt].Start==LActStartV)&&(LEdges[LECnt].End==LActEndV)) { LEFound=TRUE;break; }
    }
    if(!LEFound) { LNextEdge->Start=LActStartV;LNextEdge->End=LActEndV;LEdgeNum++;LNextEdge++; }
    if(LVCnt<(LVNum-1)) LVCnt+=2;
   }
  }
 }
 LPCStart=0;

 LPolyGroup->NumOfEdges=LEdgeNum;
 if(LEdgeNum<LNEdgeNum) LPolyGroup->Edges=(E3dEdge*)ERealloc(LPolyGroup->Edges, sizeof(E3dEdge)*LNEdgeNum);	// Free unnecessary edges
}


//========================================
// Add a Polygon to a Mesh
//========================================
int E3d_PolyGroupAppendPolygon(E3dPolyGroup* LPolyGroup, E3dPolygon* LPolygon)
{
 E3dPolygon*	LPolygons=NULL;


 if(LPolyGroup->Polygons==NULL)
 {
  if((LPolyGroup->Polygons=LPolygons=E3d_PolygonsAllocate(1))!=NULL) LPolyGroup->NumOfPolygons=1;
 }
 else
 {
  if((LPolygons=(E3dPolygon*)ERealloc(LPolyGroup->Polygons, sizeof(E3dPolygon)*(LPolyGroup->NumOfPolygons+1)))!=NULL)
  {
   LPolyGroup->Polygons=LPolygons;
   LPolygons+=LPolyGroup->NumOfPolygons;
   LPolyGroup->NumOfPolygons++;
  }
 }
 if(LPolygons)
 {
  memcpy(LPolygons, LPolygon, sizeof(E3dPolygon));
 }
 return(LPolyGroup->NumOfPolygons);
}


//========================================================================
// Create renderable polygons for a PolyGroup from the original data
// (only planar, convex and solid polygons (no holes))
//========================================================================
int E3d_PolyGroupCreateRenderablePolygons(E3dVertex* LVertices, E3dPolyGroup* LPolyGroup)
{
 E3dPolygon*	LPolygon;
 unsigned int	LC, LN;


 LN=LPolyGroup->NumOfPolygons;LPolygon=LPolyGroup->Polygons;
 for(LC=0;LC<LN;LC++, LPolygon++)
 {
  if(LPolygon->NumOfVertices>3)
  {
   if((E3d_TriangulatePolygon(LVertices, LPolygon, TRUE))==0) LPolygon->Flags|=E3dPolyFlagSELECTED;
  }
  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;
   }
  }
 }
 return(0);
}


//================================================================================================
// Re-map Vertex indices of Polygons and TriangleStrips in a PolyGroup according to a table
//================================================================================================
void E3d_PolyGroupRemapVertexIndices(E3dPolyGroup* LPolyGroup, int* LInToNewVertexIndexTable)
{
 E3dVertexNode*	LVertexNode;
 unsigned int	LPCnt, LPNum=LPolyGroup->NumOfPolygons,
		LVC, LVN;

 if(LPolyGroup->Polygons)
 {
  E3dPolygon*	LPolygon=LPolyGroup->Polygons;

  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LPolygon++)
  {
   LVertexNode=LPolygon->VertexNodes;
   LVN=LPolygon->NumOfVertexNodes;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++)
   {
    if(LVertexNode->VertexID>=0) LVertexNode->VertexID=LInToNewVertexIndexTable[LVertexNode->VertexID];
   }
  }
 }

 if(LPolyGroup->TriangleStrips)
 {
  E3dTriangleStrip*	LTriangleStrip=LPolyGroup->TriangleStrips;

  LPNum=LPolyGroup->NumOfTriangleStrips;
  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LTriangleStrip++)
  {
   LVertexNode=LTriangleStrip->VertexNodes;
   LVN=LTriangleStrip->NumOfVertices;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++)
   {
    if(LVertexNode->VertexID>=0) LVertexNode->VertexID=LInToNewVertexIndexTable[LVertexNode->VertexID];
   }
  }
 }

 if(LPolyGroup->Edges)
 {
  E3dEdge*	LEdge=LPolyGroup->Edges;

  LVN=LPolyGroup->NumOfEdges;
  for(LVC=0;LVC<LVN;LVC++, LEdge++)
  {
   LEdge->Start=LInToNewVertexIndexTable[LEdge->Start];
   LEdge->End=LInToNewVertexIndexTable[LEdge->End];
  }
 }
}


//================================================================================================
// Re-map Vertex indices of Polygons and TriangleStrips in a PolyGroup using an offset
//================================================================================================
void E3d_PolyGroupOffsetVertexIndices(E3dPolyGroup* LPolyGroup, int LOffset)
{
 E3dVertexNode*	LVertexNode;
 unsigned int	LPCnt, LPNum=LPolyGroup->NumOfPolygons,
		LVC, LVN;

 if(LPolyGroup->Polygons)
 {
  E3dPolygon*	LPolygon=LPolyGroup->Polygons;

  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LPolygon++)
  {
   LVertexNode=LPolygon->VertexNodes;
   LVN=LPolygon->NumOfVertexNodes;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++)
   {
    if(LVertexNode->VertexID>=0) LVertexNode->VertexID+=LOffset;
   }
  }
 }

 if(LPolyGroup->TriangleStrips)
 {
  E3dTriangleStrip*	LTriangleStrip=LPolyGroup->TriangleStrips;

  LPNum=LPolyGroup->NumOfTriangleStrips;
  for(LPCnt=0;LPCnt<LPNum;LPCnt++, LTriangleStrip++)
  {
   LVertexNode=LTriangleStrip->VertexNodes;
   LVN=LTriangleStrip->NumOfVertices;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++)
   {
    if(LVertexNode->VertexID>=0) LVertexNode->VertexID+=LOffset;
   }
  }
 }

 if(LPolyGroup->Edges)
 {
  E3dEdge*	LEdge=LPolyGroup->Edges;

  LVN=LPolyGroup->NumOfEdges;
  for(LVC=0;LVC<LVN;LVC++, LEdge++)
  {
   LEdge->Start+=LOffset;
   LEdge->End+=LOffset;
//printf("%d-%d\n", LEdge->Start, LEdge->End);fflush(stdout);
  }
 }
}
