/*======================================================================================*/
/* 3DPanel										*/
/*											*/
/* - Polygon Mesh Drawing								*/
/*											*/
/* AUTHOR:	Gabor Nagy								*/
/* DATE:	1996-Jan-10 23:29:45							*/
/*											*/
/* 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================*/
#include <stdio.h>
#include <float.h>
#include <math.h>

#include <Color/Color.h>
#include <Image/Panel.h>

#include <GL/glu.h>

#ifndef _E3DDrawing_h
#include <E3D/Drawing.h>
#endif

#ifndef _E3DDefaultDrawingMacros_h
#include <E3D/DefaultDrawingMacros.h>
#endif

#ifndef _E3DMath_h
#include <E3D/Math.h>
#endif

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

#ifndef _E3D3DWindow_h
#include <E3D/3DWindow.h>
#endif

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


#include "Display.h"


static unsigned int _FilledVertex_bits[] =
{
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
};



#if defined(GL_EXT_polygon_offset) || defined(GL_VERSION_1_1)

// To force stenciled solid+wire, comment this out:
//
#define E3dGL_POLYGON_OFFSET_SUPPORTED

#endif




#define E3dpM_SelectWireColor()\
  if(LDisplayModeIn&E3dDF_SELECTED) E3dM_glColorRGBAI(LHWireRGBAiColor);\
  else\
  {\
   if(LDisplayModeIn&E3dDF_LOCKED) E3dM_glColorRGBAI(LLockedItemRGBAiColor);\
   else E3dM_glColorRGBAI(LWireRGBAiColor);\
  }



/*----------------------------------------------*/


//========================================================
// Draw a PolygonGroup in Shaded-Solid, Textured mode
//========================================================
void E3d_DrawPolyGroupShadedSolidTextured(E3dMesh* LMesh, E3dPolyGroup* LPolyGroup, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;


 if(LPolyGroup->Visible)
 {
  LMaterial=LPolyGroup->DrawingMaterial;

  E3dM_DrawPolyGroupShadedSolid2DTextured();
 }
}


//================================================
// Draw Vertices of a Mesh
//================================================
static void E3d_MeshDrawVertices(E3dMesh* LMesh, E3dDrawContext* LDrawContext)
{
 E3dVertex*	LMeshVertices;
 E3dVertex*	LVertex;
 unsigned int	LC, LN, LPointSize, LHalfPointSize, LDrawnC=0;


 LMeshVertices=LMesh->Vertices;

// Draw vertices
//
 if((LN=LMesh->NumOfVertices)==0) return;


 E3dM_glColorRGBAi(LDrawContext->VertexRGBAiColor);

 LPointSize=E3dp_Prefs.PointSize;
 LHalfPointSize=LPointSize>>1;

// Draw non-tagged and inactive/unselected Vertices
//
 LVertex=LMeshVertices;
 LC=LN;
 do
 {
  if((LVertex->Flags&(E3dVTXF_TAGGED|E3dVTXF_ACTIVE|E3dVTXF_SELECTED))==0)
  {
   MglRasterPos3(LVertex->X, LVertex->Y, LVertex->Z);
   glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledVertex_bits);
   LDrawnC++;
  }
  LVertex++;
 } while(--LC);


 if(LDrawnC<LN)
 {
// Draw tagged vertices
//
  E3dM_glColorRGBAi(LDrawContext->TaggedPointRGBAiColor);

  LPointSize=E3dp_Prefs.TaggedPointSize;
  LHalfPointSize=LPointSize>>1;

  LVertex=LMeshVertices;
  LC=LN;
  do
  {
   if((LVertex->Flags&(E3dVTXF_TAGGED|E3dVTXF_ACTIVE))==E3dVTXF_TAGGED)
   {
    MglRasterPos3(LVertex->X, LVertex->Y, LVertex->Z);
    glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledVertex_bits);
    LDrawnC++;
   }
   LVertex++;
  } while(--LC);
 }

 if(LDrawnC<LN)
 {
// Draw active / selected Vertices
//
  E3dM_glColorRGBAi(LDrawContext->ActivePointRGBAiColor);

  LPointSize=E3dp_Prefs.TaggedPointSize;
  LHalfPointSize=LPointSize>>1;

  LVertex=LMeshVertices;
  LC=LN;
  do
  {
   if(LVertex->Flags&(E3dVTXF_ACTIVE|E3dVTXF_SELECTED))
   {
//printf("VF %08x\n", LVertex->Flags);fflush(stdout);
    MglRasterPos3(LVertex->X, LVertex->Y, LVertex->Z);
    glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledVertex_bits);
   }
   LVertex++;
  } while(--LC);
 }
}


//================================================
// Draw normals of a Polygon Mesh
//================================================
static void E3d_MeshDrawNormals(E3dMesh* LMesh, EBool LAllPolyGroups)
{
 E3dVertex*		LMeshVertices;
 E3dPolyGroup**		LPolyGroups;
 E3dPolyGroup*		LPolyGroup;
 E3dPolygon*		LPolygon;
 E3dVertexNode*		LVertexNode;
 E3dGLCoordinate	LNormE[3];
 unsigned int		LGC, LGN,
			LPCnt,
			LVC, LVN;


// Draw normals
//
 if((LGN=LMesh->NumOfPolyGroups)!=0)
 {
  LMeshVertices=LMesh->Vertices;
  LPolyGroups=LMesh->PolyGroups;

  if(E3dp_Prefs.ShowPolygonNormals)
  {
   glBegin(GL_LINES);
   for(LGC=0;LGC<LGN;LGC++)
   {
    LPolyGroup=LPolyGroups[LGC];

    if(LPolyGroup->Visible)
    {
     LPCnt=LPolyGroup->NumOfPolygons;

     if((LAllPolyGroups||(LPolyGroup->Selected))&&(LPCnt>0))
     {
      LPolygon=LPolyGroup->Polygons;
      do
      {
       LNormE[E3dX]=LPolygon->GLCenter[E3dX]+LPolygon->GLNormal[E3dX];
       LNormE[E3dY]=LPolygon->GLCenter[E3dY]+LPolygon->GLNormal[E3dY];
       LNormE[E3dZ]=LPolygon->GLCenter[E3dZ]+LPolygon->GLNormal[E3dZ];
       MglVertex3v(LPolygon->GLCenter);MglVertex3v(LNormE);
       LPolygon++;
      } while(--LPCnt);
     }
    }
   }
   glEnd();
  }

  if(E3dp_Prefs.ShowVertexNormals)
  {
   for(LGC=0;LGC<LGN;LGC++)
   {
    LPolyGroup=LPolyGroups[LGC];

    if(LPolyGroup->Visible)
    {
     LPCnt=LPolyGroup->NumOfPolygons;
     if((LAllPolyGroups||(LPolyGroup->Selected))&&(LPCnt>0))
     {
      LPolygon=LPolyGroup->Polygons;

      glBegin(GL_LINES);
      do
      {
       LVN=LPolygon->NumOfVertexNodes;
       LVertexNode=LPolygon->VertexNodes;

       for(LVC=0;LVC<LVN;LVC++,LVertexNode++)
       {
	if(LVertexNode->VertexID!=-1)
	{
	 LNormE[E3dX]=LVertexNode->GLVertex[E3dX]+LVertexNode->GLNormal[E3dX];
	 LNormE[E3dY]=LVertexNode->GLVertex[E3dY]+LVertexNode->GLNormal[E3dY];
	 LNormE[E3dZ]=LVertexNode->GLVertex[E3dZ]+LVertexNode->GLNormal[E3dZ];
	 MglVertex3v(LVertexNode->GLVertex);MglVertex3v(LNormE);
	}
       }
       LPolygon++;
      } while(--LPCnt);
      glEnd();
     }
    }
   }
  }
 }
}



//========================================================
// Draw the edges of the a Mesh
//========================================================
static void E3d_MeshDrawEdges(E3dMesh* LMesh)
{
 unsigned int	LECnt=LMesh->NumOfEdges;

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

  glBegin(GL_LINES);
  do
  {
   MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
   LEdge++;
  } while(--LECnt);
  glEnd();
 }
}



/*======================================================*/
/* Draw the edges of the a Mesh				*/
/*======================================================*/
static void E3d_MeshDrawEdgesHighlight(E3dMesh* LMesh, E3dDrawContext* LDrawContext, EcRGBAiColor* LDefaultEdgeRGBAiColor)
{
 EcRGBAiColor*	LActiveEdgeRGBAiColor=&(LDrawContext->ActiveEdgeRGBAiColor);
 EcRGBAiColor*	LSelectedEdgeRGBAiColor=&(LDrawContext->SelectedEdgeRGBAiColor);
 EcRGBAiColor*	LCurrentRGBAiColor=LDefaultEdgeRGBAiColor;
 unsigned int	LECnt=LMesh->NumOfEdges;



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


  do
  {
   switch(LEdge->Flags&(E3dEDGE_ACTIVE|E3dEDGE_SELECTED))
   {
    case E3dEDGE_ACTIVE:
    case E3dEDGE_ACTIVE|E3dEDGE_SELECTED:
     if(LCurrentRGBAiColor!=LActiveEdgeRGBAiColor)
     {
      LCurrentRGBAiColor=LActiveEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
      glLineWidth(E3dp_Prefs.LineWidth*2.0);
     }
    break;

    case E3dEDGE_SELECTED:
     if(LCurrentRGBAiColor!=LSelectedEdgeRGBAiColor)
     {
      LCurrentRGBAiColor=LSelectedEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
      glLineWidth(E3dp_Prefs.LineWidth*2.0);
     }
    break;

    default:
     if(LCurrentRGBAiColor!=LDefaultEdgeRGBAiColor)
     {
      LCurrentRGBAiColor=LDefaultEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
      glLineWidth(E3dp_Prefs.LineWidth);
     }
    break;
   }
   glBegin(GL_LINES);
   MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
   glEnd();
   LEdge++;
  } while(--LECnt);
 }

 glLineWidth(E3dp_Prefs.LineWidth);
}


//========================================================
// Draw the edges of the selected PolyGroups of a Mesh
//========================================================
void E3d_MeshDrawPolyGroupEdges(E3dMesh* LMesh, EBool LPolyGroupSelected)
{
 E3dPolyGroup*		LPolyGroup;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 unsigned int		LGN=LMesh->NumOfPolyGroups;


// If the Mesh has only 1 PolyGroup, the Mesh edges are the same as the PolyGroup Edges,
// so they were only stored on the Mesh (LPolyGroup->Edges is NULL)
//
 if(LGN==1)
 {
  LPolyGroup=LPolyGroups[0];
  if((LPolyGroup->Visible)&&(LPolyGroup->Selected==LPolyGroupSelected))
  {
   E3d_MeshDrawEdges(LMesh);
  }
 }
 else
 {
  E3dEdge*	LEdge;
  unsigned int	LGC,
		LEC;

// Draw edges of selected or unselected PolyGroups
//
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if((LPolyGroup->Visible)&&(LPolyGroup->Selected==LPolyGroupSelected))
   {
    LEC=LPolyGroup->NumOfEdges;
    if(LEC)
    {
     LEdge=LPolyGroup->Edges;
     glBegin(GL_LINES);
     do
     {
      MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
      LEdge++;
     } while(--LEC);
     glEnd();
    }
   }
  }
 }
}


//========================================================
// Draw the edges of the a Mesh
//========================================================
void E3d_MeshDrawPolyGroupEdgesHighlight(E3dMesh* LMesh, E3dDrawContext* LDrawContext, EcRGBAiColor* LDefaultEdgeRGBAiColor, EBool LPolyGroupSelected)
{
 E3dPolyGroup**	LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*	LPolyGroup;
 unsigned int	LGN=LMesh->NumOfPolyGroups;


// If the Mesh has only 1 PolyGroup, the Mesh edges are the same as the PolyGroup Edges,
// so they were only stored on the Mesh (LPolyGroup->Edges is NULL)
//
 if(LGN==1)
 {
  LPolyGroup=LPolyGroups[0];
  if((LPolyGroup->Visible)&&(LPolyGroup->Selected==LPolyGroupSelected))
  {
   if(LPolyGroup->Flags&(E3dACTIVE_EDGES|E3dSELECTED_EDGES)) E3d_MeshDrawEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor);
   else E3d_MeshDrawEdges(LMesh);
  }
 }
 else
 {
  E3dEdge*		LEdge;
  EcRGBAiColor*		LActiveEdgeRGBAiColor=&(LDrawContext->ActiveEdgeRGBAiColor);
  EcRGBAiColor*		LSelectedEdgeRGBAiColor=&(LDrawContext->SelectedEdgeRGBAiColor);
  EcRGBAiColor*		LCurrentRGBAiColor=LDefaultEdgeRGBAiColor;
  unsigned int		LGC, LEC;

// Draw Edges of selected or unselected PolyGroups
//
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if((LPolyGroup->Visible)&&(LPolyGroup->Selected==LPolyGroupSelected))
   {
    LEC=LPolyGroup->NumOfEdges;
    if(LEC)
    {
     LEdge=LPolyGroup->Edges;

     if(LPolyGroup->Flags&(E3dACTIVE_EDGES|E3dSELECTED_EDGES))
     {
      do
      {
       switch(LEdge->Flags&(E3dEDGE_ACTIVE|E3dEDGE_SELECTED))
       {
	case E3dEDGE_ACTIVE:
	case E3dEDGE_ACTIVE|E3dEDGE_SELECTED:
	 if(LCurrentRGBAiColor!=LActiveEdgeRGBAiColor)
	 {
	  LCurrentRGBAiColor=LActiveEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
	  glLineWidth(E3dp_Prefs.LineWidth*2.0);
	 }
	break;

	case E3dEDGE_SELECTED:
	 if(LCurrentRGBAiColor!=LSelectedEdgeRGBAiColor)
	 {
	  LCurrentRGBAiColor=LSelectedEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
	  glLineWidth(E3dp_Prefs.LineWidth*2.0);
	 }
	break;

	default:
	 if(LCurrentRGBAiColor!=LDefaultEdgeRGBAiColor)
	 {
	  LCurrentRGBAiColor=LDefaultEdgeRGBAiColor;E3dM_glColorRGBAiPTR(LCurrentRGBAiColor);
	  glLineWidth(E3dp_Prefs.LineWidth);
	 }
	break;
       }
       glBegin(GL_LINES);
       MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
       glEnd();
       LEdge++;
      } while(--LEC);

     }
     else
     {
      glBegin(GL_LINES);
      do
      {
       MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
       LEdge++;
      } while(--LEC);
      glEnd();
     }
    }
   }
  }

  glLineWidth(E3dp_Prefs.LineWidth);
 }
}





//================================================
// Draw a Polygon Mesh in Hidden-line mode
//================================================
EBool E3d_DrawMeshHiddenLine(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext)
{
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 E3dPolygon*		mPolygon;
 E3dITriangle*		mITriangle;
 E3dVertexNode*		mVertexNode;
 E3dVertexNode**	mVertexNodeP;
 EcRGBAiColor		LBackgroundRGBAiColor, LSelectedPolygonRGBAiColor;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups,
			mPolyCnt, mPC1, mVC, mVN;


 if(LMesh->NumOfPolyGroups==0) return(FALSE);
 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
 glEnable(GL_DEPTH_TEST);

#ifdef E3dGL_POLYGON_OFFSET_SUPPORTED
 if(LMesh->LockCount) LDisplayModeIn|=E3dDF_LOCKED;

 LBackgroundRGBAiColor=LDrawContext->BackgroundRGBAiColor;

 LSelectedPolygonRGBAiColor=LDrawContext->SelectedPolygonRGBAiColor;

 E3dM_glColorRGBAi(LBackgroundRGBAiColor);

// Should we highlight selected Polygons?
//
 if(((LDisplayModeIn&E3dDF_SELECTED)!=0)||(LMesh->Selection==E3dGSEL_POLYGROUP))
 {
// Draw unselected Polygons first
//
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if(LPolyGroup->Visible)
   {
    if((LPolyGroup->Selected)||((LDisplayModeIn&E3dDF_SELECTED)!=0))
    {
     if((mPolyCnt=LPolyGroup->NumOfPolygons)>0)
     {
      mPolygon=LPolyGroup->Polygons;
      do
      {
       if((mPolygon->Flags&E3dPolyFlagSELECTED)==0) { E3dM_DrawPolygon(); }
       mPolygon++;
      } while(--mPolyCnt);
     }
    }
    else { E3dM_DrawPolyGroupConstSolid(); }
   }
  }


// Now draw the selected Polygons
//
  E3dM_glColorRGBAi(LSelectedPolygonRGBAiColor);

  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if(LPolyGroup->Visible)
   {

    if(((LPolyGroup->Selected)||((LDisplayModeIn&E3dDF_SELECTED)!=0))&&(LPolyGroup->Visible))
    {
     if((mPolyCnt=LPolyGroup->NumOfPolygons)>0)
     {
      mPolygon=LPolyGroup->Polygons;
      do
      {
       if(mPolygon->Flags&E3dPolyFlagSELECTED) { E3dM_DrawPolygon(); }

       mPolygon++;
      } while(--mPolyCnt);
     }
    }
   }
  }
 }
 else
 {
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if(LPolyGroup->Visible) { E3dM_DrawPolyGroupConstSolid(); }
  }
 }
 return(TRUE);

#else								// Hidden line with stencil buffer

 glEnable(GL_STENCIL_TEST);
 glStencilFunc(GL_ALWAYS, 0, 1);
 glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
 glStencilMask(1);

 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if((LPolyCnt=LPolyGroup->NumOfPolygons)>0)
  {
   LPolygon=LPolyGroup->Polygons;
   do
   {
    glDisable(GL_DEPTH_TEST);

    glDrawBuffer(GL_NONE);				// Draw polygon contours in stencil buffer
    E3dpM_DrawPolygonContours();

    glEnable(GL_DEPTH_TEST);				// Fill in Polygon
    glStencilFunc(GL_EQUAL, 0, 1);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    E3dM_glColorRGBAI(LBackgroundRGBAiColor);
    glDrawBuffer(LGLColorBuffer);
    E3dM_DrawPolygon();

    glStencilFunc(GL_ALWAYS, 0, 1);			// Undraw Polygon outline in stencil buffer and draw the outline in the color buffer
    glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
    E3dpM_SelectWireColor();
    E3dpM_DrawPolygonContours();

    LPolygon++;
   } while(--LPolyCnt);
  }
 }
 glDisable(GL_STENCIL_TEST);

 return(FALSE);
#endif // E3dGL_POLYGON_OFFSET_SUPPORTED

}




//================================================
// Draw edges of a Mesh or its PolyGroups
//================================================
void E3d_MeshDrawEdgesAndNormals(E3dMesh* LMesh, EBool LNormalModel, E3dDrawContext* LDrawContext, EBool LDrawMeshVertices, EBool LDrawEdges, unsigned int LDisplayModeIn)
{
 EcRGBAiColor*		LWireRGBAiColor=&(LDrawContext->WireRGBAiColor);
 EcRGBAiColor*		LHWireRGBAiColor=&(LDrawContext->HWireRGBAiColor);
 EcRGBAiColor*		LSelGeoWireRGBAiColor=&(LDrawContext->SelGeoWireRGBAiColor);
 EcRGBAiColor*		LNormalRGBAiColor=&(LDrawContext->NormalRGBAiColor);
 EcRGBAiColor*		LLockedItemRGBAiColor=&(LDrawContext->LockedItemRGBAiColor);
 EcRGBAiColor*		LDefaultEdgeRGBAiColor=NULL;



 if(LMesh->LockCount) { LDisplayModeIn|=E3dDF_LOCKED; }

// Make sure lighting and texturing are turned OFF
//
 glDisable(GL_LIGHTING);
 glDisable(GL_TEXTURE_2D);

 if(LDrawEdges)
 {
  E3dPolyGroup**	LPolyGroups=LMesh->PolyGroups;
  unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;
  EBool			LPGVisibleDiffers=FALSE,
			LPGSelectedDiffers=FALSE,
			LSelected=FALSE;


  if(LGN>1)
  {
   EBool	LPGVisible=LPolyGroups[0]->Visible,
		LPGSelected=LPolyGroups[0]->Selected;


   for(LGC=1;LGC<LGN;LGC++) { if(LPolyGroups[LGC]->Visible!=LPGVisible) { LPGVisibleDiffers=TRUE;break; } }

   if(LMesh->Selection==E3dGSEL_POLYGROUP)
   {
    for(LGC=1;LGC<LGN;LGC++) { if(LPolyGroups[LGC]->Selected!=LPGSelected) { LPGSelectedDiffers=TRUE;break; } }
   }




// If some PolyGroups are selected, some aren't or some are visible,
// some aren't, we have to draw the PolyGroup Edges...
//
   if(LPGVisibleDiffers||LPGSelectedDiffers)
   {
    if(LDisplayModeIn&E3dDF_SELECTED)
    {
     if(LMesh->Selection==E3dGSEL_NONE) LDefaultEdgeRGBAiColor=LHWireRGBAiColor;
     else LDefaultEdgeRGBAiColor=LSelGeoWireRGBAiColor;
     LSelected=TRUE;
     E3dM_glColorRGBAiPTR(LDefaultEdgeRGBAiColor);

// Draw Edges of unselected PolyGroups
//
     E3d_MeshDrawPolyGroupEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor, FALSE);

// Draw Edges of selected PolyGroups
//
     E3d_MeshDrawPolyGroupEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor, TRUE);
    }
    else
    {
     if(LDisplayModeIn&E3dDF_LOCKED) LDefaultEdgeRGBAiColor=LLockedItemRGBAiColor;
     else LDefaultEdgeRGBAiColor=LWireRGBAiColor;
     E3dM_glColorRGBAiPTR(LDefaultEdgeRGBAiColor);

     E3d_MeshDrawPolyGroupEdges(LMesh, FALSE);		// Draw Edges of unselected PolyGroups

     LDefaultEdgeRGBAiColor=LSelGeoWireRGBAiColor;
     E3dM_glColorRGBAiPTR(LDefaultEdgeRGBAiColor);

// Draw Edges of selected PolyGroups
//
     E3d_MeshDrawPolyGroupEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor, TRUE);
    }

   }
   else
   {
    E3dPolyGroup*	LPolyGroup=LPolyGroups[0];

// ...otherwise draw the Mesh edges
//
    unsigned int	LECnt=LMesh->NumOfEdges;

    if(LDisplayModeIn&E3dDF_SELECTED)
    {
     LSelected=TRUE;
     if(LMesh->Selection==E3dGSEL_NONE) LDefaultEdgeRGBAiColor=LHWireRGBAiColor;	// Model is selected, use HWireRGBAiColor
     else LDefaultEdgeRGBAiColor=LSelGeoWireRGBAiColor;					// Mesh or PolyGroup is selected, use SelGeoWireRGBAiColor
    }
    else
    {
     if(LDisplayModeIn&E3dDF_LOCKED) LDefaultEdgeRGBAiColor=LLockedItemRGBAiColor;
     else LDefaultEdgeRGBAiColor=LWireRGBAiColor;
    }


    E3dM_glColorRGBAiPTR(LDefaultEdgeRGBAiColor);

    if(LECnt)
    {
// Are there active or selected Edges?
//
//printf("PG %p  Sel %d  %08x %08x\n", LPolyGroup, LSelected, LPolyGroup->Flags, E3dACTIVE_EDGES|E3dSELECTED_EDGES);fflush(stdout);

     if(LSelected&&((LPolyGroup->Flags&(E3dACTIVE_EDGES|E3dSELECTED_EDGES))!=0)) E3d_MeshDrawEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor);
     else E3d_MeshDrawEdges(LMesh);
    }
   }
  }
  else
  {
// There's only 1 PolyGroup in the Mesh
//
   if(LGN)
   {
    E3dPolyGroup*	LPolyGroup=LPolyGroups[0];

    if(LPolyGroup->Visible)
    {
     if(LDisplayModeIn&E3dDF_SELECTED)
     {
      LSelected=TRUE;
      if(LMesh->Selection==E3dGSEL_NONE) LDefaultEdgeRGBAiColor=LHWireRGBAiColor;	// Model is selected, use HWireRGBAiColor
      else LDefaultEdgeRGBAiColor=LSelGeoWireRGBAiColor;				// Mesh or PolyGroup is selected, use SelGeoWireRGBAiColor
     }
     else
     {
      if((LMesh->Selection==E3dGSEL_POLYGROUP)&&(LPolyGroup->Selected)) { LDefaultEdgeRGBAiColor=LSelGeoWireRGBAiColor;LSelected=TRUE; }
      else if(LDisplayModeIn&E3dDF_LOCKED) LDefaultEdgeRGBAiColor=LLockedItemRGBAiColor;
      else LDefaultEdgeRGBAiColor=LWireRGBAiColor;
     }

     E3dM_glColorRGBAiPTR(LDefaultEdgeRGBAiColor);

// Draw Mesh edges
//
     if(LSelected&&((LPolyGroup->Flags&(E3dACTIVE_EDGES|E3dSELECTED_EDGES))!=0)) E3d_MeshDrawEdgesHighlight(LMesh, LDrawContext, LDefaultEdgeRGBAiColor);
     else E3d_MeshDrawEdges(LMesh);
    }
   }
  }
 }



// Draw Vertices and normals
//
 if(LNormalModel)
 {
  if((((LDisplayModeIn&E3dDF_SELECTED)!=0)&&E3dp_Prefs.ShowVertices&&LDrawMeshVertices)||((LMesh->Selection==E3dGSEL_POLYGROUP)&&E3dp_DrawMeshVerticesIfPolyGroupSelected)) E3d_MeshDrawVertices(LMesh, LDrawContext);
  if((E3dp_Prefs.ShowPolygonNormals)||(E3dp_Prefs.ShowVertexNormals))
  {
   E3dM_glColorRGBAiPTR(LNormalRGBAiColor);
   if((LDisplayModeIn&E3dDF_SELECTED)!=0) E3d_MeshDrawNormals(LMesh, TRUE);
   else E3d_MeshDrawNormals(LMesh, FALSE);
  }
 }
}



//========================================
// Draw a Polygon Mesh in Wire+Z mode
//========================================
void E3d_DrawMeshZWire(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 E3dPolygon*		mPolygon;
 E3dITriangle*		mITriangle;
 E3dVertexNode*		mVertexNode;
 E3dVertexNode**	mVertexNodeP;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups,
			mVC, mVN,
			mPolyCnt, mPC1;


 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
 glEnable(GL_DEPTH_TEST);

// Draw selected Polygons
//	
 if(((LDisplayModeIn&E3dDF_SELECTED)!=0)||(LMesh->Selection==E3dGSEL_POLYGROUP))
 {
  E3dM_glColorRGBAi(LDrawContext->SelectedPolygonRGBAiColor);

  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];
   if((LPolyGroup->Selected)||((LDisplayModeIn&E3dDF_SELECTED)!=0))
   {
    if((mPolyCnt=LPolyGroup->NumOfPolygons)>0)
    {
     mPolygon=LPolyGroup->Polygons;
     do
     {
      if(mPolygon->Flags&E3dPolyFlagSELECTED) { E3dM_DrawPolygon(); }
      mPolygon++;
     } while(--mPolyCnt);
    }
   }
  }
 }
}


//========================================
// Draw a Polygon Mesh in Solid mode
//========================================
void E3d_DrawMeshSolid(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dGLMaterial*		LGLMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if(LPolyGroup->Visible)
  {
   LGLMaterial=&(LPolyGroup->DrawingMaterial->GLMaterial);
   E3dM_glColorRGBAp8(LGLMaterial->FlatColor);

   E3dM_DrawPolyGroupConstSolid();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Solid, Textured mode
//================================================
static void E3d_DrawMeshSolidTextured(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;;


 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);glEnable(GL_CULL_FACE);

 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if(LPolyGroup->Visible)
  {
   E3dM_DrawPolyGroupConstSolid2DTextured();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Shaded-Solid mode
//================================================
void E3d_DrawMeshShadedSolid(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];

  if(LPolyGroup->Visible)
  {
   LMaterial=LPolyGroup->DrawingMaterial;

   E3dM_DrawPolyGroupShadedSolid();
  }
 }
 E3dM_MesDrawSelectedPolygonsOnTop();
}


//========================================================
// Draw a Polygon Mesh in Shaded-Solid, Textured mode
//========================================================
static void E3d_DrawMeshShadedSolidTextured(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];

  if(LPolyGroup->Visible)
  {
   LMaterial=LPolyGroup->DrawingMaterial;

   E3dM_DrawPolyGroupShadedSolid2DTextured();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}



//========================================
// Draw a Polygon Mesh
//========================================
void E3d_DrawMesh(E3dModel* LModel, E3dMesh* LMesh, int LModelType, GLenum LGLColorBuffer, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dVertex*		LMeshVertices;
 E3dPolyGroup**		LPolyGroups;
 E3dGLMaterial*		LPlasterGLMaterial;
 int*			LGLDisplayListPtr=NULL;

 unsigned int		LDisplayMode=LDisplayModeIn&(E3dDF_MODEMASK|E3dDF_TEXTURE);

 EBool			LSetBlendingFunction=TRUE,
			LDrawMeshVertices,
			LDrawEdges=TRUE,
			LDefiningDisplayList=FALSE;


 LPlasterGLMaterial=&(E3d_PlasterMaterial.GLMaterial);

 if(LDisplayModeIn&E3dDF_WIREFRAME) LDrawEdges=TRUE;
 else LDrawEdges=FALSE;

 if(LDisplayModeIn&E3dDF_SELECTED) { LDrawMeshVertices=TRUE;LDrawEdges=TRUE; }
 else LDrawMeshVertices=FALSE;


 LMeshVertices=LMesh->Vertices;


// If the Mesh is selected, highlight it by showing its edges
//
 switch(LDisplayMode)
 {
  case E3dDM_Wireframe:
  case E3dDM_Wireframe|E3dDF_TEXTURE:
// In this mode, anaglyph stereo might set the blending function...
   LSetBlendingFunction=FALSE;
  break;

  case E3dDM_ShadedSolid:
  case E3dDM_ShadedSolidAndWire:
   LGLDisplayListPtr=&(LMesh->GLDisplayListShaded);
  break;

  case E3dDM_ShadedSolid|E3dDF_TEXTURE:
  case E3dDM_ShadedSolidAndWire|E3dDF_TEXTURE:
   if(LDisplayModeIn&E3dDF_SELECTED) LDisplayMode=E3dDM_ShadedSolidAndWire|E3dDF_TEXTURE;
   LGLDisplayListPtr=&(LMesh->GLDisplayListShaded2DTextured);
  break;
 }


 if(LDisplayMode&E3dDF_LIGHTING) glEnable(GL_LIGHTING);


 glDisable(GL_BLEND);

 if(E3dp_Prefs.UseGLDisplayLists&&(LGLDisplayListPtr!=NULL))
 {
  if(*LGLDisplayListPtr)
  {
   glCallList(*LGLDisplayListPtr);
   return;
  }

  *LGLDisplayListPtr=glGenLists(1);

  if(*LGLDisplayListPtr==0) return;

  LDefiningDisplayList=TRUE;
  glNewList(*LGLDisplayListPtr, GL_COMPILE_AND_EXECUTE);
 }





 LPolyGroups=LMesh->PolyGroups;


 switch(LDisplayMode)
 {
  case E3dDM_WireAndZ:
  case E3dDM_WireAndZ|E3dDF_TEXTURE:
   E3d_DrawMeshZWire(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ColorWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolid(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ColorWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolidTextured(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolid(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTextured(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_HiddenLine:
  case E3dDM_HiddenLine|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   LDrawEdges=E3d_DrawMeshHiddenLine(LMesh, LDisplayModeIn, LDrawContext);
  break;
 
  case E3dDM_Solid:
  case E3dDM_SolidAndWire:				// Solid+wire
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolid(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_Solid|E3dDF_TEXTURE:
  case E3dDM_SolidAndWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTextured(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedSolid:
  case E3dDM_ShadedSolidAndWire:
  case E3dDM_ShadedSolidFinal:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolid(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedSolid|E3dDF_TEXTURE:
  case E3dDM_ShadedSolidAndWire|E3dDF_TEXTURE:			// Shaded solid+wire
  case E3dDM_ShadedSolidFinal|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTextured(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;
 }


 if(LDefiningDisplayList) glEndList();


 glDisable(GL_BLEND);


// Disable all Edge drawing in "final draw" mode
//
 switch(LDisplayMode)
 {
  case E3dDM_ShadedSolidFinal:
  case E3dDM_ShadedSolidFinal|E3dDF_TEXTURE:
   LDrawEdges=FALSE;
  break;

  default:
// Draw Edges of selected PolyGroups if there's no Edge drawing otherwise (SOLID mode)
//
   if((!LDrawEdges)&&(LMesh->Selection==E3dGSEL_POLYGROUP))
   {
    E3dM_glColorRGBAi(LDrawContext->SelGeoWireRGBAiColor);
    E3d_MeshDrawPolyGroupEdgesHighlight(LMesh, LDrawContext, &(LDrawContext->SelGeoWireRGBAiColor), TRUE);
   }
  break;
 }


 switch(LModelType)
 {
  case E3dMDL_NORMAL:
  case E3dMDL_INSTANCE:
   E3d_MeshDrawEdgesAndNormals(LMesh, TRUE, LDrawContext, LDrawMeshVertices, LDrawEdges, LDisplayModeIn);
  break;

  default:
   E3d_MeshDrawEdgesAndNormals(LMesh, FALSE, LDrawContext, LDrawMeshVertices, LDrawEdges, LDisplayModeIn);
  break;
 }
}


//================================================
// Draw a Polygon Mesh in Solid mode
//================================================
void E3d_DrawMeshSolidOpaque(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dGLMaterial*		LGLMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if(LPolyGroup->Visible&&(LPolyGroup->DrawingMaterial->Transparency==0.0))
  {
   LGLMaterial=&(LPolyGroup->DrawingMaterial->GLMaterial);
   E3dM_glColorRGBAp8(LGLMaterial->FlatColor);

   E3dM_DrawPolyGroupConstSolid();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Solid, Textured mode
//================================================
static void E3d_DrawMeshSolidTexturedOpaque(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);glEnable(GL_CULL_FACE);

 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];LMaterial=LPolyGroup->DrawingMaterial;
  if(LPolyGroup->Visible&&(LMaterial->Transparency==0.0))
  {
   E3dM_DrawPolyGroupConstSolid2DTextured();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Shaded-Solid mode
//================================================
void E3d_DrawMeshShadedSolidOpaque(E3dModel* LModel, E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 if(LGN)
 {
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if(LPolyGroup->Visible)
   {
    LMaterial=LPolyGroup->DrawingMaterial;

    if(LMaterial->Transparency==0.0)
    {
     if((LMaterial->Type>=E3dMAT_CUSTOM)&&(LMaterial->Shader!=NULL))
     {
      E3dShader*	LShader=LMaterial->Shader;
      E3dShaderClass*	LShaderClass=LShader->Class;
      E3dMatrix		LMatrix;

      E3d_MatrixLoadIdentity(LMatrix);
      if(LShaderClass->GLDrawProc) LShaderClass->GLDrawProc(E3d_Scene, LModel, (E3dGeometry*)LMesh, LGC, LMatrix, LDisplayModeIn);
     }
     else
     {
      E3dM_DrawPolyGroupShadedSolid();
     }
    }
   }
  }

  E3dM_MesDrawSelectedPolygonsOnTop();
 }
}


//========================================================
// Draw a Polygon Mesh in Shaded-Solid, Textured mode
//========================================================
void E3d_DrawMeshShadedSolidTexturedOpaque(E3dModel* LModel, E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 if(LGN)
 {
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   if(LPolyGroup->Visible)
   {
    LMaterial=LPolyGroup->DrawingMaterial;

    if(LMaterial->Transparency==0.0)
    {
     if((LMaterial->Type>=E3dMAT_CUSTOM)&&(LMaterial->Shader!=NULL))
     {
      E3dShader*	LShader=LMaterial->Shader;
      E3dShaderClass*	LShaderClass=LShader->Class;
      E3dMatrix		LMatrix;

      E3d_MatrixLoadIdentity(LMatrix);
      if(LShaderClass->GLDrawProc) LShaderClass->GLDrawProc(E3d_Scene, LModel, (E3dGeometry*)LMesh, LGC, LMatrix, LDisplayModeIn);
     }
     else
     {
      E3dM_DrawPolyGroupShadedSolid2DTextured();
     }
    }
   }
  }

  E3dM_MesDrawSelectedPolygonsOnTop();
 }
}


//================================================================
// Draw opaque Polygon Mesh
// This function tests the Material of each PolyGroup to see if
// it is transparent. If it is, it won't draw that PolyGroup
//================================================================
void E3d_DrawMeshOpaque(E3dModel* LModel, E3dMesh* LMesh, int LModelType, GLenum LGLColorBuffer, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dVertex*		LMeshVertices;
 E3dPolyGroup**		LPolyGroups;
 E3dGLMaterial*		LPlasterGLMaterial;
 unsigned int		LGN;
 unsigned int		LDisplayMode=LDisplayModeIn&(E3dDF_MODEMASK|E3dDF_TEXTURE);

 EBool			LSetBlendingFunction=TRUE,
			LDrawMeshVertices,
			LDrawEdges;


 LPlasterGLMaterial=&(E3d_PlasterMaterial.GLMaterial);

// if(LDisplayModeIn&E3dDF_LIGHTING) glEnable(GL_LIGHTING);

 if(LDisplayModeIn&E3dDF_WIREFRAME) LDrawEdges=TRUE;
 else LDrawEdges=FALSE;

 if(LDisplayModeIn&E3dDF_SELECTED) { LDrawMeshVertices=TRUE;LDrawEdges=TRUE; }
 else LDrawMeshVertices=FALSE;


 LMeshVertices=LMesh->Vertices;


// If the Mesh is selected, highlight it by showing its edges
//
 switch(LDisplayMode)
 {
  case E3dDM_Wireframe:
  case E3dDM_Wireframe|E3dDF_TEXTURE:
// In this mode, anaglyph stereo might set the blending function...
   LSetBlendingFunction=FALSE;
  break;
 }


 glDisable(GL_BLEND);

 LGN=LMesh->NumOfPolyGroups;
 LPolyGroups=LMesh->PolyGroups;


 switch(LDisplayMode)
 {
  case E3dDM_WireAndZ:
  case E3dDM_WireAndZ|E3dDF_TEXTURE:
   E3d_DrawMeshZWire(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ColorWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolidOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ColorWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_HiddenLine:
  case E3dDM_HiddenLine|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   LDrawEdges=E3d_DrawMeshHiddenLine(LMesh, LDisplayModeIn, LDrawContext);
  break;
 
  case E3dDM_Solid:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_Solid|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_SolidAndWire:				// Solid+wire
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_SolidAndWire|E3dDF_TEXTURE:		// Solid+wire+texture
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedOpaque(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_ShadedSolid:
  case E3dDM_ShadedSolidFinal:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_ShadedSolid|E3dDF_TEXTURE:
  case E3dDM_ShadedSolidFinal|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_ShadedSolidAndWire:			// Shaded solid+wire
   glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedSolidAndWire|E3dDF_TEXTURE:		// Shaded solid+wire
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedOpaque(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;
 }


 glDisable(GL_BLEND);


// Disable all Edge drawing in "final draw" mode
//
 switch(LDisplayMode)
 {
  case E3dDM_ShadedSolidFinal:
  case E3dDM_ShadedSolidFinal|E3dDF_TEXTURE:
   LDrawEdges=FALSE;
  break;

  default:
// Draw Edges of selected PolyGroups if there's no Edge drawing otherwise (SOLID mode)
//
   if((!LDrawEdges)&&(LMesh->Selection==E3dGSEL_POLYGROUP))
   {
    E3dM_glColorRGBAi(LDrawContext->SelGeoWireRGBAiColor);
    E3d_MeshDrawPolyGroupEdgesHighlight(LMesh, LDrawContext, &(LDrawContext->SelGeoWireRGBAiColor), TRUE);
   }
  break;
 }


 switch(LModelType)
 {
  case E3dMDL_NORMAL:
  case E3dMDL_INSTANCE:
   E3d_MeshDrawEdgesAndNormals(LMesh, TRUE, LDrawContext, LDrawMeshVertices, LDrawEdges, LDisplayModeIn);
  break;

  default:
   E3d_MeshDrawEdgesAndNormals(LMesh, FALSE, LDrawContext, LDrawMeshVertices, LDrawEdges, LDisplayModeIn);
  break;
 }
}





//================================================
// Draw a Polygon Mesh in Solid mode
//================================================
void E3d_DrawMeshSolidTransparent(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dGLMaterial*		LGLMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if(LPolyGroup->Visible&&(LPolyGroup->DrawingMaterial->Transparency>0.0))
  {
   LGLMaterial=&(LPolyGroup->DrawingMaterial->GLMaterial);
   E3dM_glColorRGBAp8(LGLMaterial->FlatColor);

   E3dM_DrawPolyGroupConstSolid();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Solid, Textured mode
//================================================
static void E3d_DrawMeshSolidTexturedTransparent(E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);glEnable(GL_CULL_FACE);

 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];
  if(LPolyGroup->Visible&&(LPolyGroup->DrawingMaterial->Transparency>0.0))
  {
   E3dM_DrawPolyGroupConstSolid2DTextured();
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//================================================
// Draw a Polygon Mesh in Shaded-Solid mode
//================================================
void E3d_DrawMeshShadedSolidTransparent(E3dModel* LModel, E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];

  if(LPolyGroup->Visible)
  {
   LMaterial=LPolyGroup->DrawingMaterial;

   if(LMaterial->Transparency>0.0)
   {
    if((LMaterial->Type>=E3dMAT_CUSTOM)&&(LMaterial->Shader!=NULL))
    {
     E3dShader*		LShader=LMaterial->Shader;
     E3dShaderClass*	LShaderClass=LShader->Class;
     E3dMatrix		LMatrix;

     E3d_MatrixLoadIdentity(LMatrix);
     if(LShaderClass->GLDrawProc) LShaderClass->GLDrawProc(E3d_Scene, LModel, (E3dGeometry*)LMesh, LGC, LMatrix, LDisplayModeIn);
    }
    else
    {
     E3dM_DrawPolyGroupShadedSolid();
    }
   }
  }
 }
 E3dM_MesDrawSelectedPolygonsOnTop();
}


//========================================================
// Draw a Polygon Mesh in Shaded-Solid, Textured mode
//========================================================
void E3d_DrawMeshShadedSolidTexturedTransparent(E3dModel* LModel, E3dMesh* LMesh, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 E3dMaterial*		LMaterial;
 E3d2DTexture*		L2DTexture;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 E3dPolyGroup*		LPolyGroup;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups;


 for(LGC=0;LGC<LGN;LGC++)
 {
  LPolyGroup=LPolyGroups[LGC];

  if(LPolyGroup->Visible)
  {
   LMaterial=LPolyGroup->DrawingMaterial;

   if(LMaterial->Transparency>0.0)
   {
    if((LMaterial->Type>=E3dMAT_CUSTOM)&&(LMaterial->Shader!=NULL))
    {
     E3dShader*		LShader=LMaterial->Shader;
     E3dShaderClass*	LShaderClass=LShader->Class;
     E3dMatrix		LMatrix;

     E3d_MatrixLoadIdentity(LMatrix);
     if(LShaderClass->GLDrawProc) LShaderClass->GLDrawProc(E3d_Scene, LModel, (E3dGeometry*)LMesh, LGC, LMatrix, LDisplayModeIn);
    }
    else
    {
     E3dM_DrawPolyGroupShadedSolid2DTextured();
    }
   }
  }
 }

 E3dM_MesDrawSelectedPolygonsOnTop();
}


//========================================
// Draw transparent Mesh
//========================================
void E3d_DrawMeshTransparent(E3dModel* LModel, E3dMesh* LMesh, int LModelType, GLenum LGLColorBuffer, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext, EBool LDrawSelectedPolygonsInSolidMode)
{
 unsigned int	LDisplayMode=LDisplayModeIn&(E3dDF_MODEMASK|E3dDF_TEXTURE);
 EBool		LSetBlendingFunction=TRUE;


// If the Mesh is selected, highlight it by showing its edges
//
 switch(LDisplayMode)
 {
  case E3dDM_Wireframe:
  case E3dDM_Wireframe|E3dDF_TEXTURE:
// In this mode, anaglyph stereo might set the blending function...
   LSetBlendingFunction=FALSE;
  break;
 }

 glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


 switch(LDisplayMode)
 {
  case E3dDM_ColorWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolidTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ColorWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedWire|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glDisable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_Solid:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_Solid|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_SolidAndWire:				// Solid+wire
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_SolidAndWire|E3dDF_TEXTURE:		// Solid+wire+texture
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshSolidTexturedTransparent(LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedSolid:
  case E3dDM_ShadedSolidFinal:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_ShadedSolid|E3dDF_TEXTURE:
  case E3dDM_ShadedSolidFinal|E3dDF_TEXTURE:
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;


  case E3dDM_ShadedSolidAndWire:			// Shaded solid+wire
   glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;

  case E3dDM_ShadedSolidAndWire|E3dDF_TEXTURE:		// Shaded solid+wire
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glEnable(GL_CULL_FACE);
   E3d_DrawMeshShadedSolidTexturedTransparent(LModel, LMesh, LDisplayModeIn, LDrawContext, LDrawSelectedPolygonsInSolidMode);
  break;
 }


 glDisable(GL_BLEND);
}


//========================================================
// Draw a polygon Mesh for a TextureProjector
//========================================================
void E3d_DrawMeshTextureProjector(E3dMesh* LMesh, GLenum LGLColorBuffer, unsigned int LDisplayModeIn, E3dDrawContext* LDrawContext)
{
 EcRGBAiColor		LTextureProjectorRGBAiColor;
 EcRGBAp8Color		LTextureProjectorRGBAp8Color;
 E3dVertex*		LMeshVertices;
 E3dGLMaterial*		LPlasterGLMaterial;


 LPlasterGLMaterial=&(E3d_PlasterMaterial.GLMaterial);

 LMeshVertices=LMesh->Vertices;

 LTextureProjectorRGBAiColor=LDrawContext->TextureProjectorRGBAiColor;
 LTextureProjectorRGBAp8Color=EcM_RGBAi_to_RGBAp8(LTextureProjectorRGBAiColor);


 glDisable(GL_LIGHTING);
 glDisable(GL_TEXTURE_2D);

 E3dM_glColorRGBAi(LTextureProjectorRGBAiColor);

 E3d_MeshDrawEdges(LMesh);
}
