/*======================================================================================*/
/* 3DPanel										*/
/*											*/
/* - Interactive rendering								*/
/*											*/
/* AUTHOR:	Gabor Nagy								*/
/* DATE:	1996-Jan-09 23:28:43							*/
/*											*/
/* 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 _E3DFace_h
#include <E3D/Face.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

#ifndef _E3DGeometry_h
#include <E3D/Geometry.h>
#endif


#include "Display.h"
#include "Bitmaps.h"


// To force stenciled solid+wire, comment this out:
//
#if defined(GL_EXT_polygon_offset) || defined(GL_VERSION_1_1)
#define E3dGL_POLYGON_OFFSET_SUPPORTED
#endif

#if defined(GL_EXT_polygon_offset) && !defined(GL_VERSION_1_1)
#define glPolygonOffset(A, B)     glPolygonOffsetEXT(A, B)

// OpenGL 1.1's polygon offset can be different for each
// polygon mode primitive type.
// The EXT extension has only one offset.
//
#define GL_POLYGON_OFFSET_FILL   GL_POLYGON_OFFSET_EXT
#endif


#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

int	E3d_Eye=1;

float	E3dp_LabelFontYSize=0.0;

EBool	E3dp_DrawMeshVerticesIfPolyGroupSelected=FALSE;


extern float		MIN_GRIDVS;
extern E3dDrawContext	E3dp_NormalDrawContext, E3dp_LeftEyeDrawContext, E3dp_RightEyeDrawContext;
extern E3dMatrix	E3d_3DCursorMatrix;

extern EBool		E3d_ZBufferOk;


//----------------------------------------
// From Schematics.c
//----------------------------------------
extern void		E3dp_DrawSchematics(E3dWindow* L3DWindow, GLenum LGLColorBuffer, int LDisplayMode, int LViewMode, E3dMatrix LWorldToViewerMatrix, E3dMatrix LProjCamMatrix, E3dDrawContext* LDrawContext);



// Shadow-mapping
//
E3dShadowMap*	_ShadowMap=NULL;

E3dShadowMap* 	(*E3d_ShadowMapAllocateProc)()=NULL;
int		(*E3d_ShadowMapUpdateProc)(E3dWindow*, E3dScene*, E3dLight*, E3dShadowMap*)=NULL;
int		(*E3d_DrawShadowPassProc)(E3dScene*, E3dWindow*, E3dShadowMap*, GLenum, int, int, E3dMatrix, E3dDrawContext*, EBool);



E3dMatrix	E3dp_ProjectionMatrix;

int		E3dp_SelectionMode=E3dpSELM_SINGLE;

int		E3dp_GLFontFirstID=-1;		// Display list ID for the ' ' character in the GL font


long		E3dp_StereoBlendS=1, E3dp_StereoBlendD=0;



E3dHardwareInfo	E3dp_RenderingHWInfo;



//================================================
// Check if an OpenGL extension is supported
//================================================
EBool E3dGL_ExtensionSupported(const char* LName)
{
 static const GLubyte*	LExtensions = NULL;
 const GLubyte*		LStart;
 GLubyte*		LWhere;
 GLubyte*		LTerminator;


// Extension names should not have spaces
//
 LWhere = (GLubyte*)strchr(LName, ' ');
 if(LWhere || *LName == '\0') return(FALSE);

 if(!LExtensions) LExtensions = glGetString(GL_EXTENSIONS);

// It takes a bit of care to be fool-proof about parsing the
// OpenGL LExtensions string.  Don't be fooled by sub-strings etc.
//
 LStart = LExtensions;
 for(;;)
 {
  LWhere = (GLubyte *) strstr((const char*) LStart, LName);
  if(!LWhere) break;
  LTerminator = LWhere + strlen(LName);
  if(LWhere == LStart || *(LWhere - 1) == ' ')
  {
   if(*LTerminator == ' ' || *LTerminator == '\0') return(TRUE);
  }
  LStart = LTerminator;
 }
 return(FALSE);
}


EBool	E3dGL_HasOpenGL12 = FALSE;

EBool	E3dGL_HasTextureBorderClamp = FALSE;
EBool	E3dGL_HasTextureEdgeClamp = FALSE;


// _SupportsGL12 - returns true if supports OpenGL 1.2 or higher
//
static int _SupportsGL12()
{
 const char*	version;
 int		major, minor;

 version = (char*)glGetString(GL_VERSION);
 if(sscanf(version, "%d.%d", &major, &minor) == 2) return(major > 1 || minor >= 2);

 return(0);	// OpenGL version string malformed!
}


//================================================
// Check for useful OpenGL extensions
//================================================
int E3dGL_InitExtensions()
{
 int	LNum=0;

 E3dGL_HasOpenGL12 = _SupportsGL12();

 if(E3dGL_ExtensionSupported("GL_ARB_texture_border_clamp")) { E3dGL_HasTextureBorderClamp = 1;LNum++; }
 if(E3dGL_HasOpenGL12 || E3dGL_ExtensionSupported("GL_EXT_texture_edge_clamp")) { E3dGL_HasTextureEdgeClamp = TRUE;LNum++; }

 return(LNum);
}





//========================================
// Draw a Points Geometry
//========================================
void E3d_DrawPoints(E3dPoints* LPointGeo, int LModelType, unsigned int LDisplayMode, E3dDrawContext* LDrawContext)
{
 E3dPoint*		LPoints;
 unsigned int		LVC, LVN;
 unsigned long		LColor;


 if((LPoints=LPointGeo->Points)!=NULL)
 {
  switch(LDisplayMode&(E3dDF_MODEMASK|E3dDF_TEXTURE))
  {
   case E3dDM_Wireframe:
   case E3dDM_WireAndZ:
   case E3dDM_WireDepthCue:
    LColor=LPoints[0].GLColor;E3dM_glColorRGBAp8(LColor);
    glBegin(GL_POINTS);
    for(LVC=0, LVN=LPointGeo->NumOfPoints;LVC<LVN;LVC++)
    {
     LColor=LPoints[LVC].GLColor;
     E3dM_glColorRGBAp8(LColor);
     ME3dVertex3dot(LPoints[LVC]);
    }
    glEnd();
   break;

   case E3dDM_Solid:
   case E3dDM_ShadedSolid:
    glBegin(GL_POINTS);
    for(LVC=0, LVN=LPointGeo->NumOfPoints;LVC<LVN;LVC++)
    {
     ME3dVertex3dot(LPoints[LVC]);
    }
    glEnd();
   break;
  }
 }
}



E3dGLCoordinate	E3dp_MCenter[][3]=
{
 {  0.00, 0.00, 0.00 }, { 1.00, 0.00, 0.00 }, { 0.00, 1.00, 0.00 }, { 0.00, 0.00, 1.00 },
 {  0.90, 0.00,-0.07 }, { 0.90, 0.00, 0.07 }, { 0.90,-0.07, 0.00 }, { 0.90, 0.07, 0.00 },
 { -0.07, 0.90, 0.00 }, { 0.07, 0.90, 0.00 }, { 0.00, 0.90,-0.07 }, { 0.00, 0.90, 0.07 },
 { -0.07, 0.00, 0.90 }, { 0.07, 0.00, 0.90 }, { 0.00,-0.07, 0.90 }, { 0.00, 0.07, 0.90 }
};

extern float	E_ActScaleX, E_ActScaleY, E_ActScaleZ;



//================================================
// Render a Model with OpenGL
//================================================
void E3d_DrawModel(E3dModel* LModel, GLenum LGLColorBuffer, unsigned int LDisplayMode, E3dDrawContext* LDrawContext, EBool LOpaqueOnly)
{
 E3dRenderInfo*	LRenderInfo=LDrawContext->RenderInfo;
 E3dCamera*	LCamera=LRenderInfo->Camera;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dCoordinate	LSizeFactor=LRenderInfo->SizeFactor/200.0;	// For LODs
 unsigned int	LGmCnt, LGmNum,
		LModelFlags, LGeometryFlags;
 int		LIndex;


 LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;

 switch(LModel->Selection)
 {
  case E3dSEL_NODE:
  case E3dSEL_BRANCH:
  case E3dSEL_BRANCH_ROOT:
   LModelFlags=E3dDF_SELECTED;
  break;

  default:
   LModelFlags=0;
  break;
 }


 if(LModel->LockCount>0) LModelFlags|=E3dDF_LOCKED;

 switch(LModel->Type)
 {
  case E3dMDL_JOINT:
  case E3dMDL_NORMAL:
  case E3dMDL_LIGHT:
  case E3dMDL_CAMERA:
  case E3dMDL_CAMERA_INTEREST:
   for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
   {
    LGeometry=LGeometries[LGmCnt];

    E3dM_GeometryGetLODForDrawing(LGeometry);

    if(LGeometry->Visible)
    {
     if(LGeometry->Selection==E3dGSEL_GEOMETRY) LGeometryFlags=E3dDF_SELECTED;
     else LGeometryFlags=0;


     switch(LGeometry->GeoType)
     {
      case E3dGEO_POINTS:
       E3d_DrawPoints((E3dPoints*)LGeometry, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext);
      break;

      case E3dGEO_SPLINE:
       if(LGeometry->Selection==E3dGSEL_SPLINE_SEGMENT) LGeometryFlags|=E3dDF_SELECTED;
       E3d_DrawSpline((E3dSpline*)LGeometry, LGeometry->Selection, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext);
      break;

      case E3dGEO_FACE:
       if(LGeometry->Selection==E3dGSEL_SPLINE_SEGMENT) LGeometryFlags|=E3dDF_SELECTED;
       if(LOpaqueOnly)
       {
	E3dFace*	LFace=(E3dFace*)LGeometry;

	if(LFace->DrawingMaterial->Transparency==0.0) E3d_DrawFace((E3dFace*)LGeometry, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext);
       }
       else E3d_DrawFace((E3dFace*)LGeometry, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext);
      break;

      caseE3dMESH():
       if(LOpaqueOnly) E3d_DrawMeshOpaque(LModel, (E3dMesh*)LGeometry, LModel->Type, LGLColorBuffer, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext, E3dp_Prefs.ShowSelectedPolygons);
       else E3d_DrawMesh(LModel, (E3dMesh*)LGeometry, LModel->Type, LGLColorBuffer, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext, E3dp_Prefs.ShowSelectedPolygons);


// Draw texture projectors
//
       if(E3dp_Prefs.ShowTextureProjectors)
       {
	 E3dMesh*		LMesh=(E3dMesh*)LGeometry;
	 E3dPolyGroup*		LPolyGroup;
	 E3dPolyGroup**		LPolyGroups;
	 E3d2DTextureMapper*	L2DTextureMapper;
	 unsigned int		LPGC, LPGN, LMC, LMN;
	 EBool			LPGSelected;


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

	   LPGSelected=FALSE;

	   if(LModelFlags&E3dDF_SELECTED) LPGSelected=TRUE;
	   else
	   {
	    switch(LGeometry->Selection)
	    {
	     case E3dSEL_GEOMETRY:	LPGSelected=TRUE;break;

	     case E3dGSEL_POLYGROUP:
	      if(LPolyGroup->Selected) LPGSelected=TRUE;
	     break;
	    }
	   }

	   if(LPGSelected)
	   {
	     LMN=LPolyGroup->NumOfTextureMappers;
	     L2DTextureMapper=LPolyGroup->TextureMappers;
	     for(LMC=0;LMC<LMN;LMC++, L2DTextureMapper++)
	     {
	       if(L2DTextureMapper->Geometry!=NULL)
	       {
		glPushMatrix();
		MglMultMatrix(L2DTextureMapper->ProjectorMatrix);
		switch(L2DTextureMapper->Geometry->GeoType)
		{
		 case E3dGEO_MESH:
		  glDisable(GL_LIGHTING);

		  E3d_DrawMeshTextureProjector((E3dMesh*)(L2DTextureMapper->Geometry), LGLColorBuffer, (LDisplayMode&E3dDF_Z_BUFFER)|E3dDF_WIREFRAME, LDrawContext);
		 break;
		}
		glPopMatrix();
	       }
	     }
	   }
	 }
       }

      break;
     }

    }
   }
  break;
 }

}


//================================================
// Draw transparent Geometries of a Model
//================================================
void E3d_DrawModelTransparent(E3dModel* LModel, GLenum LGLColorBuffer, unsigned int LDisplayMode, E3dDrawContext* LDrawContext)
{
 E3dRenderInfo*	LRenderInfo=LDrawContext->RenderInfo;
 E3dCamera*	LCamera=LRenderInfo->Camera;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dCoordinate	LSizeFactor=LRenderInfo->SizeFactor/200.0;	// For LODs
 unsigned int	LGmCnt, LGmNum;
 unsigned int	LModelFlags, LGeometryFlags;
 int		LIndex;


 LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;

 switch(LModel->Selection)
 {
  case E3dSEL_NODE:
  case E3dSEL_BRANCH:
  case E3dSEL_BRANCH_ROOT:
   LModelFlags=E3dDF_SELECTED;
  break;

  default:
   LModelFlags=0;
  break;
 }

 if(LModel->LockCount>0) LModelFlags|=E3dDF_LOCKED;

 switch(LModel->Type)
 {
  case E3dMDL_NORMAL:
   for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
   {
    LGeometry=LGeometries[LGmCnt];

    E3dM_GeometryGetLODForDrawing(LGeometry);

    if(LGeometry->Visible)
    {
     if(LGeometry->Selection==E3dSEL_GEOMETRY) LGeometryFlags=E3dDF_SELECTED;
     else LGeometryFlags=0;

     switch(LGeometry->GeoType)
     {
      caseE3dMESH():
       E3d_DrawMeshTransparent(LModel, (E3dMesh*)LGeometry, LModel->Type, LGLColorBuffer, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext, E3dp_Prefs.ShowSelectedPolygons);


// Draw texture projectors
//
//      if(E3dp_Prefs.ShowTextureProjectors)
       if(0)
       {
	 E3dMesh*		LMesh=(E3dMesh*)LGeometry;
	 E3dPolyGroup*		LPolyGroup;
	 E3dPolyGroup**		LPolyGroups;
	 E3d2DTextureMapper*	L2DTextureMapper;
	 unsigned int		LPGC, LPGN, LMC, LMN;
	 EBool			LPGSelected;


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

	   LPGSelected=FALSE;

	   if(LModelFlags&E3dDF_SELECTED) LPGSelected=TRUE;
	   else
	   {
	    switch(LGeometry->Selection)
	    {
	     case E3dSEL_GEOMETRY:	LPGSelected=TRUE;break;

	     case E3dGSEL_POLYGROUP:
	      if(LPolyGroup->Selected) LPGSelected=TRUE;
	     break;
	    }
	   }

	   if(LPGSelected)
	   {
	     LMN=LPolyGroup->NumOfTextureMappers;
	     L2DTextureMapper=LPolyGroup->TextureMappers;
	     for(LMC=0;LMC<LMN;LMC++, L2DTextureMapper++)
	     {
	       if(L2DTextureMapper->Geometry!=NULL)
	       {
		glPushMatrix();
		MglMultMatrix(L2DTextureMapper->ProjectorMatrix);
		switch(L2DTextureMapper->Geometry->GeoType)
		{
		 case E3dGEO_MESH:
		  glDisable(GL_LIGHTING);

		  E3d_DrawMeshTextureProjector((E3dMesh*)(L2DTextureMapper->Geometry), LGLColorBuffer, (LDisplayMode&E3dDF_Z_BUFFER)|E3dDF_WIREFRAME, LDrawContext);
		 break;
		}
		glPopMatrix();
	       }
	     }
	   }
	 }
       }

      break;

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

	if(LFace->DrawingMaterial->Transparency>0.0)
	{
	 glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	 E3d_DrawFace((E3dFace*)LGeometry, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, LDrawContext);
	}
       }
      break;
     }
    }
   }
  break;
 }
}


//================================================================================
// Draw a Model hierarchy using the pre-computed transformation Matrices
//================================================================================
void E3dp_DrawHierarchy(E3dModel* LRootModel, E3dWindowSettings* LWindowSettings, GLenum LGLColorBuffer, int LDisplayMode, E3dMatrix LWorldToViewerMatrix, E3dDrawContext* LDrawContext, EBool LOpaqueOnly, EBool LDrawCamera)
{
 E3dModel*		LModel;
 EcRGBAiColor*		LWireRGBAiColor=&(LDrawContext->WireRGBAiColor);
 EcRGBAiColor*		LHWireRGBAiColor=&(LDrawContext->HWireRGBAiColor);
 EcRGBAiColor*		LLockedItemRGBAiColor=&(LDrawContext->LockedItemRGBAiColor);
 E3dGLCoordinate	LStart[3], LEnd[3], LP0[3], LP1[3], LP2[3], LP3[3], LPTop[3];
 EBool			LDrawCenter, LDrawGeometries;


 for(LModel=LRootModel;LModel;LModel=LModel->Next)
 {
  if((LModel->Visible)&&(LDrawCamera||(LModel->Type!=E3dMDL_CAMERA)))
  {
   LDrawGeometries=FALSE;

   glPushMatrix();
    MglLoadMatrix(LWorldToViewerMatrix);
   
    if(!E3d_GLDataTransformed)
    {
     MglMultMatrix(LModel->LocalToWorldMatrix);
//    if(LDisplayMode&E3dDF_LIGHTING) E3d_LightsDefineGL();
    }

    if(E3dp_Prefs.ShowCenters&&(LModel->Selection!=E3dSEL_NONE)) LDrawCenter=TRUE;
    else LDrawCenter=FALSE;
    LDrawGeometries=TRUE;

    switch(LModel->Type)
    {
     case E3dMDL_LIGHT:
      if(LDisplayMode&E3dDF_SOLID)
      {
       if(!E3dp_Prefs.ShowLampsInSolidMode) { LDrawCenter=FALSE;LDrawGeometries=FALSE;break; }
      }
      else
      {
       if(!E3dp_Prefs.ShowLampsInWireMode) { LDrawCenter=FALSE;LDrawGeometries=FALSE;break; }
      }
     break;

     case E3dMDL_JOINT:
      if(LModel->Selection!=E3dSEL_NONE) E3dM_glColorRGBAiPTR(LHWireRGBAiColor);
      else
      {
       if(LModel->LockCount>0) E3dM_glColorRGBAiPTR(LLockedItemRGBAiColor);
       else E3dM_glColorRGBAiPTR(LWireRGBAiColor);
      }

      E3dM_DrawBone();
     break;
    }

    if(LDrawCenter)
    {
// Turn off lighting for drawing Model centers
//
     glDisable(GL_LIGHTING);

     if(LWindowSettings->StereoMode==E3dStereoANAGLYPH)
     {
      E3dM_glColorRGBAi(LDrawContext->AxisRGBAiColor);
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[1]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[4]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[5]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[6]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[7]);glEnd();

      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[2]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[8]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[9]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[10]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[11]);glEnd();

      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[3]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[12]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[13]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[14]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[15]);glEnd();
     }
     else
     {
      E3dM_glColorRGBAp8(0xFF0000FF);

      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[1]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[4]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[5]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[6]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[7]);glEnd();

      E3dM_glColorRGBAp8(0x00FF00FF);
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[2]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[8]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[9]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[10]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[11]);glEnd();

// Last color change in this sequence, set E3dp_CurrentRGBAColorPI
//
      E3dM_glColorRGBAp8(0x0000FFFF);
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[3]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[12]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[13]);glEnd();
      glBegin(GL_LINE_STRIP);MglVertex3v(E3dp_MCenter[14]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[15]);glEnd();
     }
    }

    if(LDrawGeometries)
    {
     E3d_DrawModel(LModel, LGLColorBuffer, LDisplayMode, LDrawContext, LOpaqueOnly);
    }
   glPopMatrix();
  }
 }
}


//================================================================================
// Draw a Model hierarchy using the pre-computed transformation Matrices
// - Draw transparent Geometries
//================================================================================
void E3dp_DrawHierarchyTransparent(E3dModel* LModel, E3dWindowSettings* LWindowSettings, GLenum LGLColorBuffer, int LDisplayMode, E3dMatrix LWorldToViewerMatrix, E3dDrawContext* LDrawContext)
{
 EBool		LDrawGeometries;

 for(;LModel!=NULL;LModel=LModel->Next)
 {
  LDrawGeometries=FALSE;
  if(LModel->Visible) LDrawGeometries=TRUE;
  else LDrawGeometries=FALSE;
  switch(LModel->Type)
  {
   case E3dMDL_LIGHT:
    if(LDisplayMode&E3dDF_SOLID)
    {
     if(!E3dp_Prefs.ShowLampsInSolidMode) { LDrawGeometries=FALSE;break; }
    }
    else
    {
     if(!E3dp_Prefs.ShowLampsInWireMode) { LDrawGeometries=FALSE;break; }
    }
   break;
  }

  if(LDrawGeometries)
  {
   glPushMatrix();
    MglLoadMatrix(LWorldToViewerMatrix);
   
    if(!E3d_GLDataTransformed)
    {
     MglMultMatrix(LModel->LocalToWorldMatrix);
//    if(LDisplayMode&E3dDF_LIGHTING) E3d_LightsDefineGL();
    }

    E3d_DrawModelTransparent(LModel, LGLColorBuffer, LDisplayMode, LDrawContext);
   glPopMatrix();
  }
 }
}


//========================================================
// Draw the grid, coordinate system etc. on a 3DWindow
//========================================================
void E3dp_3DWindowDrawExtras(E3dWindow* L3DWindow, int LDisplayMode, int LViewMode, E3dDrawContext* LDrawContext)
{
 E3dCamera*		LCamera;
 E3dWindowSettings*	LWindowSettings=&(L3DWindow->Settings);
 E3dMatrix		LTmpMatrix;
 E3dGLCoordinate	LGridLine[2][3];
 E3dGLCoordinate	LGridStarts[E3dpGRID_MAX][3];
 E3dGLCoordinate	LGridEnds[E3dpGRID_MAX][3];
 float			LXF, LYF, LZF, LFS, LFE, LFStep;
 float			LWXSize, LWYSize;
 double			LWGridX, LWGridY, LWCamX, LWCamY, LZoom, LSizeFact, LSizeF;
 double			LCamModX, LCamModY, LGridX, LGridY, LGridZ;
 unsigned short		LGXNum, LGYNum, LGZNum, LGXMid, LGYMid, LGZMid;
 unsigned short		LWGXID, LWGYID, LWGZID;
 unsigned int		LGCnt;
 EBool			LGXYOn, LGXZOn, LGYZOn, LGOOn;


 glDisable(GL_LIGHTING);		// No lighting on grid and axes
// glDisable(GL_BLEND);
 glDisable(GL_TEXTURE_2D);

 if(E3dp_Prefs.LineWidth>2.0) glLineWidth(E3dp_Prefs.LineWidth*0.75);

 LGXYOn=LWindowSettings->GridXYOn;
 LGXZOn=LWindowSettings->GridXZOn;
 LGYZOn=LWindowSettings->GridYZOn;
 LGOOn=LWindowSettings->GridOn;
 if(((LDisplayMode&E3dDF_LIGHTING)!=0)&&(!E3dp_Prefs.ShowGridsInShadedMode))
 {
  LGXYOn=LGXZOn=LGYZOn=LGOOn=FALSE;
 }
// Grid display in perspective mode
//
 switch(LViewMode)
 {
  case E3dVM_PERSPECTIVE:
   if(LGXYOn||LGXZOn||LGYZOn)
   {
    LGXNum=LWindowSettings->GridXCount+1;LGXMid=(LWindowSettings->GridXCount)>>1;
    LGYNum=LWindowSettings->GridYCount+1;LGYMid=(LWindowSettings->GridYCount)>>1;
    LGZNum=LWindowSettings->GridZCount+1;LGZMid=(LWindowSettings->GridZCount)>>1;
    LGridX=LWindowSettings->GridDisplay.X;
    LGridY=LWindowSettings->GridDisplay.Y;
    LGridZ=LWindowSettings->GridDisplay.Z;


// Sub-grids
//
    if(((LDisplayMode&E3dDF_FINAL)!=0)||(LWindowSettings->StereoMode==E3dStereoANAGLYPH)) E3dM_glColorRGBAi(LDrawContext->ShadedSubGridRGBAiColor);
    else E3dM_glColorRGBAi(LDrawContext->SubGridRGBAiColor);

// X-Y sub-grid
//
    if(LGXYOn)
    {
     LFS=-LGXMid*LGridX;LFE=LGXMid*LGridX;LYF=-LGYMid*LGridY;		// Horizontal lines
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGYNum;LGCnt++,LYF+=LGridY)
     {
      if((LGCnt!=LGYMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=LFS;LGridStarts[LGCnt][E3dY]=LYF;LGridStarts[LGCnt][E3dZ]=0.0;
       LGridEnds[LGCnt][E3dX]=LFE;LGridEnds[LGCnt][E3dY]=LYF;LGridEnds[LGCnt][E3dZ]=0.0;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LXF=-LGXMid*LGridX;LFS=-LGYMid*LGridY;LFE=LGYMid*LGridY;		// Vertical lines
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGXNum;LGCnt++,LXF+=LGridX)
     {
      if((LGCnt!=LGXMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=LXF;LGridStarts[LGCnt][E3dY]=LFS;LGridStarts[LGCnt][E3dZ]=0.0;
       LGridEnds[LGCnt][E3dX]=LXF;LGridEnds[LGCnt][E3dY]=LFE;LGridEnds[LGCnt][E3dZ]=0.0;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }


// X-Z sub-grid
//
    if(LGXZOn)
    {
     LXF=-LGXMid*LGridX;LFS=-LGZMid*LGridZ;LFE=LGZMid*LGridZ;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGXNum;LGCnt++,LXF+=LGridX)
     {
      if((LGCnt!=LGXMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=LXF;LGridStarts[LGCnt][E3dY]=0.0;LGridStarts[LGCnt][E3dZ]=LFS;
       LGridEnds[LGCnt][E3dX]=LXF;LGridEnds[LGCnt][E3dY]=0.0;LGridEnds[LGCnt][E3dZ]=LFE;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LFS=-LGXMid*LGridX;LFE=LGXMid*LGridX;LZF=-LGZMid*LGridZ;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGZNum;LGCnt++,LZF+=LGridZ)
     {
      if((LGCnt!=LGZMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=LFS;LGridStarts[LGCnt][E3dY]=0.0;LGridStarts[LGCnt][E3dZ]=LZF;
       LGridEnds[LGCnt][E3dX]=LFE;LGridEnds[LGCnt][E3dY]=0.0;LGridEnds[LGCnt][E3dZ]=LZF;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }

// Y-Z sub-grid
//
    if(LGYZOn)
    {
     LYF=-LGYMid*LGridY;LFS=-LGZMid*LGridZ;LFE=LGZMid*LGridZ;		// Horizontal lines
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGYNum;LGCnt++,LYF+=LGridY)
     {
      if((LGCnt!=LGYMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=0.0;LGridStarts[LGCnt][E3dY]=LYF;LGridStarts[LGCnt][E3dZ]=LFS;
       LGridEnds[LGCnt][E3dX]=0.0;LGridEnds[LGCnt][E3dY]=LYF;LGridEnds[LGCnt][E3dZ]=LFE;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LFS=-LGYMid*LGridY;LFE=LGYMid*LGridY;LZF=-LGZMid*LGridZ;		// Vertical lines
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGZNum;LGCnt++,LZF+=LGridZ)
     {
      if((LGCnt!=LGZMid)&&((LGCnt%10)!=0))
      {
       LGridStarts[LGCnt][E3dX]=0.0;LGridStarts[LGCnt][E3dY]=LFS;LGridStarts[LGCnt][E3dZ]=LZF;
       LGridEnds[LGCnt][E3dX]=0.0;LGridEnds[LGCnt][E3dY]=LFE;LGridEnds[LGCnt][E3dZ]=LZF;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }


// Main grids
//
    if(((LDisplayMode&E3dDF_FINAL)!=0)||(LWindowSettings->StereoMode==E3dStereoANAGLYPH)) E3dM_glColorRGBAi(LDrawContext->ShadedGridRGBAiColor);
    else E3dM_glColorRGBAi(LDrawContext->GridRGBAiColor);

// Draw main X-Y grid
//
    if(LGXYOn)
    {
     LFS=-LGXMid*LGridX;LFE=LGXMid*LGridX;LYF=-LGYMid*LGridY;		// Horizontal lines
     LFStep=LGridY*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGYNum;LGCnt+=10,LYF+=LFStep)
     {
      if(LGCnt!=LGYMid)
      {
       LGridStarts[LGCnt][E3dX]=LFS;LGridStarts[LGCnt][E3dY]=LYF;LGridStarts[LGCnt][E3dZ]=0.0;
       LGridEnds[LGCnt][E3dX]=LFE;LGridEnds[LGCnt][E3dY]=LYF;LGridEnds[LGCnt][E3dZ]=0.0;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LXF=-LGXMid*LGridX;LFS=-LGYMid*LGridY;LFE=LGYMid*LGridY;		// Vertical lines
     LFStep=LGridX*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGXNum;LGCnt+=10,LXF+=LFStep)
     {
      if(LGCnt!=LGXMid)
      {
       LGridStarts[LGCnt][E3dX]=LXF;LGridStarts[LGCnt][E3dY]=LFS;LGridStarts[LGCnt][E3dZ]=0.0;
       LGridEnds[LGCnt][E3dX]=LXF;LGridEnds[LGCnt][E3dY]=LFE;LGridEnds[LGCnt][E3dZ]=0.0;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }

// Draw main X-Z grid
//
    if(LGXZOn)
    {
     LXF=-LGXMid*LGridX;LFS=-LGZMid*LGridZ;LFE=LGZMid*LGridZ;
     LFStep=LGridX*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGXNum;LGCnt+=10,LXF+=LFStep)
     {
      if(LGCnt!=LGXMid)
      {
       LGridStarts[LGCnt][E3dX]=LXF;LGridStarts[LGCnt][E3dY]=0.0;LGridStarts[LGCnt][E3dZ]=LFS;
       LGridEnds[LGCnt][E3dX]=LXF;LGridEnds[LGCnt][E3dY]=0.0;LGridEnds[LGCnt][E3dZ]=LFE;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LFS=-LGXMid*LGridX;LFE=LGXMid*LGridX;LZF=-LGZMid*LGridZ;
     LFStep=LGridZ*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGZNum;LGCnt+=10,LZF+=LFStep)
     {
      if(LGCnt!=LGZMid)
      {
       LGridStarts[LGCnt][E3dX]=LFS;LGridStarts[LGCnt][E3dY]=0.0;LGridStarts[LGCnt][E3dZ]=LZF;
       LGridEnds[LGCnt][E3dX]=LFE;LGridEnds[LGCnt][E3dY]=0.0;LGridEnds[LGCnt][E3dZ]=LZF;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }


// Y-Z main grid
//
    if(LGYZOn)
    {
     LYF=-LGYMid*LGridY;LFS=-LGZMid*LGridZ;LFE=LGZMid*LGridZ;		// Horizontal lines
     LFStep=LGridY*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGYNum;LGCnt+=10,LYF+=LFStep)
     {
      if(LGCnt!=LGYMid)
      {
       LGridStarts[LGCnt][E3dX]=0.0;LGridStarts[LGCnt][E3dY]=LYF;LGridStarts[LGCnt][E3dZ]=LFS;
       LGridEnds[LGCnt][E3dX]=0.0;LGridEnds[LGCnt][E3dY]=LYF;LGridEnds[LGCnt][E3dZ]=LFE;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();

     LFS=-LGYMid*LGridY;LFE=LGYMid*LGridY;LZF=-LGZMid*LGridZ;		// Vertical lines
     LFStep=LGridZ*10.0;
     glBegin(GL_LINES);
     for(LGCnt=0;LGCnt<LGZNum;LGCnt+=10,LZF+=LFStep)
     {
      if(LGCnt!=LGZMid)
      {
       LGridStarts[LGCnt][E3dX]=0.0;LGridStarts[LGCnt][E3dY]=LFS;LGridStarts[LGCnt][E3dZ]=LZF;
       LGridEnds[LGCnt][E3dX]=0.0;LGridEnds[LGCnt][E3dY]=LFE;LGridEnds[LGCnt][E3dZ]=LZF;
       MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
      }
     }
     glEnd();
    }

// Draw axes
//
    if(((LDisplayMode&E3dDF_FINAL)!=0)||(LWindowSettings->StereoMode==E3dStereoANAGLYPH)) E3dM_glColorRGBAi(LDrawContext->ShadedAxisRGBAiColor);
    else E3dM_glColorRGBAi(LDrawContext->AxisRGBAiColor);

    glBegin(GL_LINES);
     if(LGXYOn||LGXZOn)
     {
      LGridLine[0][E3dX]=-LGXMid*LGridX;LGridLine[0][E3dY]=0.0;LGridLine[0][E3dZ]=0.0;
      LGridLine[1][E3dX]=LGXMid*LGridX;LGridLine[1][E3dY]=0.0;LGridLine[1][E3dZ]=0.0;
      MglVertex3v(LGridLine[0]);MglVertex3v(LGridLine[1]);
     }
     if(LGXYOn||LGYZOn)
     {
      LGridLine[0][E3dX]=0.0;LGridLine[0][E3dY]=-LGYMid*LGridY;LGridLine[0][E3dZ]=0.0;
      LGridLine[1][E3dX]=0.0;LGridLine[1][E3dY]=LGYMid*LGridY;LGridLine[1][E3dZ]=0.0;
      MglVertex3v(LGridLine[0]);MglVertex3v(LGridLine[1]);
     }
     if(LGXZOn||LGYZOn)
     {
      LGridLine[0][E3dX]=0.0;LGridLine[0][E3dY]=0.0;LGridLine[0][E3dZ]=-LGZMid*LGridZ;
      LGridLine[1][E3dX]=0.0;LGridLine[1][E3dY]=0.0;LGridLine[1][E3dZ]=LGZMid*LGridZ;
      MglVertex3v(LGridLine[0]);MglVertex3v(LGridLine[1]);
     }
    glEnd();
   }
  break;

  case E3dVM_TOP:
  case E3dVM_FRONT:
  case E3dVM_RIGHT:
   if(LGOOn)
   {
    if(((LDisplayMode&E3dDF_FINAL)!=0)||(LWindowSettings->StereoMode==E3dStereoANAGLYPH)) E3dM_glColorRGBAi(LDrawContext->ShadedGridRGBAiColor);
    else E3dM_glColorRGBAi(LDrawContext->GridRGBAiColor);

    switch(LViewMode)
    {
     case E3dVM_TOP:
      LCamera=&(L3DWindow->TopCamera);
      LWCamX=LCamera->Position.X;LWCamY=LCamera->Position.Z;
      LWGridX=LWindowSettings->GridDisplay.X;LWGridY=LWindowSettings->GridDisplay.Z;
      LWGXID=E3dX;LWGYID=E3dZ;LWGZID=E3dY;
     break;

     case E3dVM_FRONT:
      LCamera=&(L3DWindow->FrontCamera);
      LWCamX=LCamera->Position.X;LWCamY=LCamera->Position.Y;
      LWGridX=LWindowSettings->GridDisplay.X;LWGridY=LWindowSettings->GridDisplay.Y;
      LWGXID=E3dX;LWGYID=E3dY;LWGZID=E3dZ;
     break;

//     case E3dVM_RIGHT:
     default:
      LCamera=&(L3DWindow->RightCamera);
      LWCamX=LCamera->Position.Z;LWCamY=LCamera->Position.Y;
      LWGridX=LWindowSettings->GridDisplay.Z;LWGridY=LWindowSettings->GridDisplay.Y;
      LWGXID=E3dZ;LWGYID=E3dY;LWGZID=E3dX;
     break;
    }

    LWXSize=LCamera->XZoom;
    LWYSize=LCamera->YZoom/L3DWindow->AspectRatio;

    LCamModX=((int)((LWCamX-LWXSize)/LWGridX))*LWGridX;
    LGXNum=(int)(LWXSize*2.0/LWGridX);
    LGYNum=(int)(LWYSize*2.0/LWGridY);

    if(LCamModX<(LWCamX-LWXSize)) LCamModX+=LWGridX;

    if((LGXNum<E3dpGRID_MAX)&&(LGYNum<E3dpGRID_MAX))
    {
     if((LCamModX-LWCamX+LWXSize)<(LWXSize*2.0-LGXNum*LWGridX)) LGXNum++;

// Main grids
//
     LXF=LCamModX;LFS=-LWYSize+LWCamY;LFE=LWYSize+LWCamY;
     glBegin(GL_LINES);
      for(LGCnt=0;LGCnt<LGXNum;LGCnt++,LXF+=LWGridX)
      {
       if(LXF!=0.0)
       {
	LGridStarts[LGCnt][LWGXID]=LXF;LGridStarts[LGCnt][LWGYID]=LFS;LGridStarts[LGCnt][LWGZID]=0.0;
	LGridEnds[LGCnt][LWGXID]=LXF;LGridEnds[LGCnt][LWGYID]=LFE;LGridEnds[LGCnt][LWGZID]=0.0;
	MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
       }
      }
     glEnd();

     LCamModY=((int)((LWCamY-LWYSize)/LWGridY))*LWGridY;

     if(LCamModY<(LWCamY-LWYSize)) LCamModY+=LWGridY;
//    if((LGYNum==0)||((L3DWindow->YSize/LGYNum)>=MIN_GRIDVS))

     if(LGYNum<E3dpGRID_MAX)
     {
      if((LCamModY-LWCamY+LWYSize)<(LWYSize*2.0-LGYNum*LWGridY)) LGYNum++;

      LFS=-LWXSize+LWCamX;LFE=LWXSize+LWCamX;LYF=LCamModY;
      glBegin(GL_LINES);
       for(LGCnt=0;LGCnt<LGYNum;LGCnt++,LYF+=LWGridY)
       {
	if(LYF!=0.0)
	{
	 LGridStarts[LGCnt][LWGXID]=LFS;LGridStarts[LGCnt][LWGYID]=LYF;LGridStarts[LGCnt][LWGZID]=0.0;
	 LGridEnds[LGCnt][LWGXID]=LFE;LGridEnds[LGCnt][LWGYID]=LYF;LGridEnds[LGCnt][LWGZID]=0.0;
	 MglVertex3v(LGridStarts[LGCnt]);MglVertex3v(LGridEnds[LGCnt]);
	}
       }
      glEnd();
     }

// Draw axes
//
     if(((LDisplayMode&E3dDF_FINAL)!=0)||(LWindowSettings->StereoMode==E3dStereoANAGLYPH)) E3dM_glColorRGBAi(LDrawContext->ShadedAxisRGBAiColor);
     else E3dM_glColorRGBAi(LDrawContext->AxisRGBAiColor);

     if((LCamModX<=0.0)&&((LCamModX+LGXNum*LWGridX)>=0.0))
     {
      LGridLine[0][LWGXID]=0.0;LGridLine[0][LWGYID]=-LWYSize+LWCamY;LGridLine[0][LWGZID]=0.0;
      LGridLine[1][LWGXID]=0.0;LGridLine[1][LWGYID]=LWYSize+LWCamY;LGridLine[1][LWGZID]=0.0;
      glBegin(GL_LINE_STRIP);MglVertex3v(LGridLine[0]);MglVertex3v(LGridLine[1]);glEnd();
     }
     if((LCamModY<=0.0)&&((LCamModY+LGYNum*LWGridY)>=0.0))
     {
      LGridLine[0][LWGXID]=-LWXSize+LWCamX;LGridLine[0][LWGYID]=0.0;LGridLine[0][LWGZID]=0.0;
      LGridLine[1][LWGXID]=LWXSize+LWCamX;LGridLine[1][LWGYID]=0.0;LGridLine[1][LWGZID]=0.0;
      glBegin(GL_LINE_STRIP);MglVertex3v(LGridLine[0]);MglVertex3v(LGridLine[1]);glEnd();
     }
    }
   }
  break;
 }

// Coordinate system display
//
 if(LWindowSettings->AxisOn)
 {
  E3dCamera	LTmpCamera;

  glMatrixMode(GL_PROJECTION);

// glPushMatrix() won't work on the PROJECTION matrix, so we have to save it 'manually'
//
  MglGetFloatV(GL_PROJECTION_MATRIX, LTmpMatrix);
  glLoadIdentity();
  LZoom=1000.0;LSizeFact=64000.0/(float)(L3DWindow->XSize);
  glOrtho(-LZoom,LZoom,-LZoom/L3DWindow->AspectRatio,LZoom/L3DWindow->AspectRatio, 0.01, 8192.0);
  switch(L3DWindow->ViewMode)
  {
   case E3dVM_PERSPECTIVE:
    LCamera=&(L3DWindow->PerspectiveCamera);
    MglTranslate(-(LZoom-LSizeFact*1.0), -(LZoom/L3DWindow->AspectRatio-LSizeFact*1.0), 0.0);
    LTmpCamera.Position.X=LCamera->Direction.X*200.0;
    LTmpCamera.Position.Y=LCamera->Direction.Y*200.0;
    LTmpCamera.Position.Z=LCamera->Direction.Z*200.0;
    LTmpCamera.InterestPoint.X=0.0;
    LTmpCamera.InterestPoint.Y=0.0;
    LTmpCamera.InterestPoint.Z=0.0;
    LTmpCamera.UpVector.X=LCamera->UpVector.X;
    LTmpCamera.UpVector.Y=LCamera->UpVector.Y;
    LTmpCamera.UpVector.Z=LCamera->UpVector.Z;

    E3dGL_CameraLookAt(&LTmpCamera);
   break;

   case E3dVM_TOP:
    MglTranslate(-(LZoom-LSizeFact*0.5),-(LZoom/L3DWindow->AspectRatio-LSizeFact*1.2),0.0);

    LTmpCamera.Position.X=0.0;
    LTmpCamera.Position.Y=200.0;
    LTmpCamera.Position.Z=0.0;
    LTmpCamera.InterestPoint.X=0.0;
    LTmpCamera.InterestPoint.Y=0.0;
    LTmpCamera.InterestPoint.Z=0.0;
    LTmpCamera.UpVector.X=0.0;
    LTmpCamera.UpVector.Y=0.0;
    LTmpCamera.UpVector.Z=-1.0;
    E3dGL_CameraLookAt(&LTmpCamera);
   break;

   case E3dVM_FRONT:
    MglTranslate(-(LZoom-LSizeFact*0.5),-(LZoom/L3DWindow->AspectRatio-LSizeFact*0.5),0.0);

    LTmpCamera.Position.X=0.0;
    LTmpCamera.Position.Y=0.0;
    LTmpCamera.Position.Z=200.0;
    LTmpCamera.InterestPoint.X=0.0;
    LTmpCamera.InterestPoint.Y=0.0;
    LTmpCamera.InterestPoint.Z=0.0;
    LTmpCamera.UpVector.X=0.0;
    LTmpCamera.UpVector.Y=1.0;
    LTmpCamera.UpVector.Z=0.0;
    E3dGL_CameraLookAt(&LTmpCamera);
   break;

   case E3dVM_RIGHT:
    MglTranslate(-(LZoom-LSizeFact*1.2), -(LZoom/L3DWindow->AspectRatio-LSizeFact*0.5), 0.0);
    LTmpCamera.Position.X=200.0;
    LTmpCamera.Position.Y=0.0;
    LTmpCamera.Position.Z=0.0;
    LTmpCamera.InterestPoint.X=0.0;
    LTmpCamera.InterestPoint.Y=0.0;
    LTmpCamera.InterestPoint.Z=0.0;
    LTmpCamera.UpVector.X=0.0;
    LTmpCamera.UpVector.Y=1.0;
    LTmpCamera.UpVector.Z=0.0;
    E3dGL_CameraLookAt(&LTmpCamera);
   break;
  }

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  LSizeF=LSizeFact*0.9;
  MglScale(LSizeF, LSizeF, LSizeF);

  if(0)
  {
   if(LDisplayMode&E3dDF_LIGHTING) E3dM_glColorRGBAi(LDrawContext->ShadedAxisRGBAiColor);
   else E3dM_glColorRGBAi(LDrawContext->AxisRGBAiColor);
  }

  {
   GLint swapbytes, lsbfirst, rowlength;
   GLint skiprows, skippixels, alignment;

// Save current modes
//
   glGetIntegerv(GL_UNPACK_SWAP_BYTES, &swapbytes);
   glGetIntegerv(GL_UNPACK_LSB_FIRST, &lsbfirst);
   glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rowlength);
   glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skiprows);
   glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skippixels);
   glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);

// Set up our settings
//
   glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
   glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);


// X axis
//
   E3dM_glColorRGBAp8(0xC00000FF);
   glBegin(GL_LINES);
    MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[1]);
    MglVertex3v(E3dp_MCenter[4]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[5]);
    MglVertex3v(E3dp_MCenter[6]);MglVertex3v(E3dp_MCenter[1]);MglVertex3v(E3dp_MCenter[7]);
   glEnd();
   MglRasterPos3(E3dp_MCenter[1][0],E3dp_MCenter[1][1],E3dp_MCenter[1][2]);
   glBitmap(8, 10, -4, 0, 0, 0, (const GLubyte*)X_bits);

// Y axis
//
   E3dM_glColorRGBAp8(0x00FF00FF);
   glBegin(GL_LINES);
    MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[2]);
    MglVertex3v(E3dp_MCenter[8]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[9]);
    MglVertex3v(E3dp_MCenter[10]);MglVertex3v(E3dp_MCenter[2]);MglVertex3v(E3dp_MCenter[11]);
   glEnd();
   MglRasterPos3(E3dp_MCenter[2][0],E3dp_MCenter[2][1],E3dp_MCenter[2][2]);
   glBitmap(8, 10, -4, 0, 0, 0, (const GLubyte*)Y_bits);

// Z axis
//
// Last color change in this sequence, set E3dp_CurrentRGBAColorPI
//
   E3dM_glColorRGBAp8(0x0000FFFF);
   glBegin(GL_LINES);
    MglVertex3v(E3dp_MCenter[0]);MglVertex3v(E3dp_MCenter[3]);
    MglVertex3v(E3dp_MCenter[12]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[13]);
    MglVertex3v(E3dp_MCenter[14]);MglVertex3v(E3dp_MCenter[3]);MglVertex3v(E3dp_MCenter[15]);
   glEnd();
   MglRasterPos3(E3dp_MCenter[3][0],E3dp_MCenter[3][1],E3dp_MCenter[3][2]);
   glBitmap(8, 10, -4, -2, 0, 0, (const GLubyte*)Z_bits);

/*
   MglRasterPos3(E3dp_MCenter[3][0],E3dp_MCenter[3][1],E3dp_MCenter[3][2]);charstr("Z");
*/

// Restore saved modes
//
   glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
   glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
   glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
   glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
   glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
   glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
  }


  glPopMatrix();

  glMatrixMode(GL_PROJECTION);
  MglLoadMatrix(LTmpMatrix);

  glMatrixMode(GL_MODELVIEW);
 }
 if(E3dp_Prefs.LineWidth>2.0) glLineWidth(E3dp_Prefs.LineWidth);
}


//========================================
// Draw a Scene on a 3DWindow
//========================================
void E3dp_DrawScene(E3dScene* LScene, E3dWindow* L3DWindow, GLenum LGLColorBuffer, int LDisplayMode, int LViewMode, E3dMatrix LWorldToViewerMatrix, E3dDrawContext* LDrawContext, EBool LDrawCamera)
{
 E3dWindowSettings*	LSettings=&(L3DWindow->Settings);
 unsigned int		LLightC, LLightN,
			LMCnt, LRN;
 EBool			LGridLast=FALSE;


 if(E3d_ZBufferOk&&((LDisplayMode&E3dDF_LINEANTIALIAS)!=0)&&((LDisplayMode&E3dDF_SOLID)!=0)) LGridLast=TRUE;
 else LGridLast=FALSE;

// Z1: If there is ZBuffer and the displaymode is not wireframe,
// draw grid after the Models to make antialiasing nicer
//
 if(LGridLast)
 {
  if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD)
  {
// Set up lighting
//
   if(LDisplayMode&E3dDF_LIGHTING)
   {
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);

// If the GL vertex and normal data are fully transformed,
// we don't want GL to re-normalize the normals
//
/*
    if(E3d_GLDataTransformed) glDisable(GL_NORMALIZE);
    else glEnable(GL_NORMALIZE);
*/

glDisable(GL_NORMALIZE);


// Turn on/off GL lights
//
    LLightN=LScene->NumOfLights;if(LLightN>GL_MAX_LIGHTS) LLightN=GL_MAX_LIGHTS;
    for(LLightC=0;LLightC<LLightN;LLightC++) glEnable(GL_LIGHT0+LLightC);
    for(LLightC=LLightN;LLightC<GL_MAX_LIGHTS;LLightC++) glDisable(GL_LIGHT0+LLightC);

    E3d_SceneLightsDefineGL(LScene);
   }
   else
   {
    glShadeModel(GL_FLAT);
    glDisable(GL_LIGHTING);
   }


   LRN=LScene->NumOfRootModels;

   if(LSettings->TransparencyOn)
   {
// Draw opaque objects first
//
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchy(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext, TRUE, LDrawCamera);

// Turn off Z update
//
    if(LDisplayMode&E3dDF_Z_BUFFER) glDepthMask(GL_FALSE);

// Draw transparent objects
//
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchyTransparent(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext);


// Turn on Z update
//
    if(LDisplayMode&E3dDF_Z_BUFFER) glDepthMask(GL_TRUE);
   }
   else
   {
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchy(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext, FALSE, LDrawCamera);
   }

  }
  if((L3DWindow->DisplayFunctionFlags&E3dDFUNCT_CUSTOM)&&(L3DWindow->CustomDisplayFunction!=NULL)) L3DWindow->CustomDisplayFunction(LDisplayMode);
 }



// Draw grid and coordinate axes
//
 E3dp_3DWindowDrawExtras(L3DWindow, LDisplayMode, LViewMode, LDrawContext);


 if((E3dp_Prefs.Show3DCursor)&&(E3d_3DCursorModel!=NULL))
 {
  MglLoadMatrix(E3d_3DCursorMatrix);
  E3d_DrawModel(E3d_3DCursorModel, LGLColorBuffer, LDisplayMode, LDrawContext, FALSE);
 }

 if(!LGridLast)
 {
  if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD)
  {
// Set up lighting
//
   if(LDisplayMode&E3dDF_LIGHTING)
   {
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);

// If the GL vertex and normal data are fully transformed,
// we don't want GL to re-normalize the normals
//
/*
    if(E3d_GLDataTransformed) glDisable(GL_NORMALIZE);
    else glEnable(GL_NORMALIZE);
*/

glDisable(GL_NORMALIZE);


// Turn on/off GL lights
//
    LLightN=LScene->NumOfLights;if(LLightN>GL_MAX_LIGHTS) LLightN=GL_MAX_LIGHTS;
    for(LLightC=0;LLightC<LLightN;LLightC++) glEnable(GL_LIGHT0+LLightC);
    for(LLightC=LLightN;LLightC<GL_MAX_LIGHTS;LLightC++) glDisable(GL_LIGHT0+LLightC);

    E3d_SceneLightsDefineGL(LScene);
   }
   else
   {
    glShadeModel(GL_FLAT);
    glDisable(GL_LIGHTING);
   }


   LRN=LScene->NumOfRootModels;

//printf("LScene->NumOfRootModels %d\n", LScene->NumOfRootModels);fflush(stdout);

   if(LSettings->TransparencyOn)
   {
// Draw opaque objects first
//
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchy(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext, TRUE, LDrawCamera);

// Turn off Z update
//
    glDepthMask(GL_FALSE);

// Draw transparent objects
//
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchyTransparent(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext);


// Turn on Z update
//
    glDepthMask(GL_TRUE);
   }
   else
   {
    for(LMCnt=0;LMCnt<LRN;LMCnt++) E3dp_DrawHierarchy(LScene->RootModels[LMCnt], &(L3DWindow->Settings), LGLColorBuffer, LDisplayMode, LWorldToViewerMatrix, LDrawContext, FALSE, LDrawCamera);
   }
  }
  if((L3DWindow->DisplayFunctionFlags&E3dDFUNCT_CUSTOM)&&(L3DWindow->CustomDisplayFunction!=NULL)) L3DWindow->CustomDisplayFunction(LDisplayMode);
 }
}



//========================================
// Redraw contents of a 3DWindow
//========================================
void _Draw3DWindow(E3dWindow* L3DWindow)
{
 E3dDrawContext*	LDrawContext;
 E3dScene*		LScene=E3d_Scene;
 E3dRenderInfo		LRenderInfo;
 E3dCamera*		LCamera;
 E3dWindowSettings*	LWindowSettings=&(L3DWindow->Settings);
 E3dMatrix		LProjectionMatrix, LWorldToViewerMatrix, LProjCamMatrix;
 double			LZoom;
 double			LPX, LPY, LPZ, LVX, LVY, LVZ, LSinTh, LCosTh, LSinFi, LCosFi;
 float			LFogCol[4];
 int			LDisplayMode, LViewMode;
 EBool			LFog, LDrawCamera=TRUE;


 LDisplayMode=L3DWindow->DisplayMode;
 LViewMode=L3DWindow->ViewMode;

 if(E3dp_CurrentGLXWindow!=L3DWindow->XWindow)
 {
  glXMakeCurrent(E3dp_Display, L3DWindow->XWindow, L3DWindow->Panel->EGLXContext);

//printf("Size: %dx%d\n", L3DWindow->XSize, L3DWindow->YSize);fflush(stdout);
  E3dp_CurrentGLXWindow=L3DWindow->XWindow;

  glViewport(0, 0, L3DWindow->XSize, L3DWindow->YSize);

// The hollow CVs of Bezier Splines need these
//
  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 }

 if(E3dp_GLFontFirstID==-1)
 {
  if(EGUI_LabelXFontStruct)
  {
   E3dp_LabelFontYSize=(float)(EGUI_LabelXFontStruct->ascent+EGUI_LabelXFontStruct->descent-2);

   glXUseXFont(EGUI_LabelXFontStruct->fid, 32, 96, 1);E3dp_GLFontFirstID=1;
  }
 }


 LFog=FALSE;
 if(LScene->RenderInfo.Fog)
 {
  LFog=TRUE;
  glEnable(GL_FOG);
  glFogf(GL_FOG_START, LScene->RenderInfo.FogNear);
  glFogf(GL_FOG_END, LScene->RenderInfo.FogFar);
  glFogi(GL_FOG_MODE, GL_LINEAR);
  LFogCol[0]=LScene->RenderInfo.FogColor.R;
  LFogCol[1]=LScene->RenderInfo.FogColor.G;
  LFogCol[2]=LScene->RenderInfo.FogColor.B;
  LFogCol[3]=1.0;
  glFogfv(GL_FOG_COLOR, LFogCol);
 }
 else glDisable(GL_FOG);

 if(LDisplayMode&E3dDF_LIGHTING)
 {
  if(LFog) glClearColor(LScene->RenderInfo.FogColor.R, LScene->RenderInfo.FogColor.G, LScene->RenderInfo.FogColor.B, 1.0);
  else
  {
   if(LDisplayMode&E3dDF_FINAL) glClearColor(0.0, 0.0, 0.0, 1.0);
   else E3dM_glClearColorRGBAi(E3dp_Prefs.BackgroundRGBAiColor);
  }

  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
 }
 else
 {
  if(LFog) glClearColor(LScene->RenderInfo.FogColor.R, LScene->RenderInfo.FogColor.G, LScene->RenderInfo.FogColor.B, 1.0);
  else
  {
   if((LWindowSettings->StereoMode==E3dStereoANAGLYPH)&&(LViewMode==E3dVM_PERSPECTIVE)) glClearColor(0.0, 0.0, 0.0, 1.0);
   else E3dM_glClearColorRGBAi(E3dp_Prefs.BackgroundRGBAiColor);
  }

  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0);
  L3DWindow->LightOn=FALSE;
 }


// Create shadow-maps if necessary
//
 if(LWindowSettings->ShadowsOn)
 {
  if((_ShadowMap==NULL)&&(E3d_ShadowMapAllocateProc!=NULL))
  {
   _ShadowMap=E3d_ShadowMapAllocateProc();
  }

  if(_ShadowMap)
  {
   if(!_ShadowMap->ShadowMapValid)
   {
    if((LScene->NumOfLights>0)&&(E3d_ShadowMapUpdateProc!=NULL))
    {
     E3d_ShadowMapUpdateProc(L3DWindow, LScene, LScene->Lights[0], _ShadowMap);
    }
   }
  }
 }


// Clear buffers
//
 if(LViewMode==E3dVM_SCHEMATICS) LDisplayMode|=E3dDF_Z_BUFFER;

 if(E3d_ZBufferOk)
 {
  if(LDisplayMode&E3dDF_Z_BUFFER)
  {
   glDepthMask(GL_TRUE);
   glClearDepth(1);
   if(LDisplayMode&E3dDF_Z_BUFFER) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
   else glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   glEnable(GL_DEPTH_TEST);
  }
  else
  {
   glDepthMask(GL_FALSE);
   glDepthFunc(GL_LEQUAL);
   glClear(GL_COLOR_BUFFER_BIT);
   glDisable(GL_DEPTH_TEST);
  }
 }
 else
 {
  glClear(GL_COLOR_BUFFER_BIT);
 }



 glLineWidth(E3dp_Prefs.LineWidth);

#ifdef E3dGL_POLYGON_OFFSET_SUPPORTED
 glEnable(GL_POLYGON_OFFSET_FILL);
 glPolygonOffset(1.0, 1.0);
#endif // E3dGL_POLYGON_OFFSET_SUPPORTED


 LRenderInfo.WindowXSize=L3DWindow->XSize;LRenderInfo.WindowYSize=L3DWindow->YSize;

 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();

// Initialize this for non-perspective views
//
 LRenderInfo.SizeFactor=0.0;
 switch(LViewMode)
 {
  case E3dVM_PERSPECTIVE:
   {
    double	LCamDX, LCamDY, LCamDZ, LCamDXZ, LViewDistance;


    LCamera=&(L3DWindow->PerspectiveCamera);
    LCamera->AspectRatio=L3DWindow->AspectRatio;

    LRenderInfo.SizeFactor=tan(LCamera->FieldOfView*E3dDEGREES_TO_RADIANS*0.5)*(L3DWindow->YSize);
    LRenderInfo.Camera=LCamera;


    E3dGL_CameraPerspective(LCamera);
    MglGetFloatV(GL_PROJECTION_MATRIX, LProjectionMatrix);

    if(L3DWindow==E3dp_Main3DWindow) LDrawCamera=FALSE;

// Stereoscopic view for colored glasses
//
    switch(LWindowSettings->StereoMode)
    {
     case E3dStereoANAGLYPH:
      {
       E3dMatrix	LMatrix, LPPCMatrixR;
       double		LA;

       switch(L3DWindow->CSStereoBlendingFunct)
       {
	case CSSTEREO_ONE_ZERO:
	 glDisable(GL_BLEND);
	break;

	case CSSTEREO_ONE_ONE:
	 glEnable(GL_BLEND);glBlendFunc(GL_ONE, GL_ONE);
	break;
       }

       LA=E3d_GetAngle2D(LCamera->ViewDistance, LCamera->EyeDistance*0.5);

       LVX=LCamera->Position.X;
       LVY=LCamera->Position.Y;
       LVZ=LCamera->Position.Z;
       LPX=LCamera->InterestPoint.X;
       LPY=LCamera->InterestPoint.Y;
       LPZ=LCamera->InterestPoint.Z;
       LCamDX=LVX-LPX;
       LCamDY=LVY-LPY;
       LCamDZ=LVZ-LPZ;

       LCamDXZ=sqrt(LCamDX*LCamDX+LCamDZ*LCamDZ);
       LViewDistance=LCamera->ViewDistance;

       LSinTh=-LCamDX/LCamDXZ;
       LCosTh=LCamDZ/LCamDXZ;
       LSinFi=LCamDY/LViewDistance;
       LCosFi=LCamDXZ/LViewDistance;

// Left eye transformation
//
       E3d_MatrixLoadIdentity(LWorldToViewerMatrix);
       E3d_MatrixRotate(LWorldToViewerMatrix, 'y', LA);
       E3d_MatrixTranslate(LWorldToViewerMatrix, LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LWorldToViewerMatrix, 'z', -LCamera->BankAngle);
       E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'x', LSinFi, LCosFi);
       E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'y', LSinTh, LCosTh);
       E3d_MatrixTranslate(LWorldToViewerMatrix, -LVX, -LVY, -LVZ);
       E3d_MatrixCopy(LWorldToViewerMatrix, LCamera->WorldToLeftViewerMatrix);

       E3d_MatrixCopy(LProjectionMatrix, LMatrix);
       E3d_MatrixMult(LWorldToViewerMatrix, LMatrix);
       E3d_MatrixCopy(LMatrix, E3dp_LeftEyeDrawContext.WorldToScreenMatrix);

// For my own transformations like the display of vertices
//
       E3d_MatrixLoadIdentity(LProjCamMatrix);
       LProjCamMatrix[M32]=1.0/(LCamera->ViewDistance);
       E3d_MatrixMult(LProjectionMatrix, LProjCamMatrix);
       E3d_MatrixMult(LWorldToViewerMatrix, LProjCamMatrix);


// Right eye transformation
//
       if(LDisplayMode&E3dDF_Z_BUFFER) { glClearDepth(1.0);glClear(GL_DEPTH_BUFFER_BIT); }

       E3d_MatrixLoadIdentity(LWorldToViewerMatrix);
       E3d_MatrixRotate(LWorldToViewerMatrix, 'y', -LA);
       E3d_MatrixTranslate(LWorldToViewerMatrix, -LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LWorldToViewerMatrix, 'z', -LCamera->BankAngle);
       E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'x', LSinFi, LCosFi);
       E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'y', LSinTh, LCosTh);
       E3d_MatrixTranslate(LWorldToViewerMatrix, -LVX, -LVY, -LVZ);
       E3d_MatrixCopy(LWorldToViewerMatrix, LCamera->WorldToRightViewerMatrix);

       E3d_MatrixCopy(LProjectionMatrix, LMatrix);
       E3d_MatrixMult(LWorldToViewerMatrix, LMatrix);
       E3d_MatrixCopy(LMatrix, E3dp_RightEyeDrawContext.WorldToScreenMatrix);


// For my own transformations, like the display of vertices
//
       E3d_MatrixLoadIdentity(LPPCMatrixR);
       LProjCamMatrix[M32]=1.0/(LCamera->ViewDistance);
       E3d_MatrixMult(LProjectionMatrix, LPPCMatrixR);
       E3d_MatrixMult(LWorldToViewerMatrix, LPPCMatrixR);

// Calculate camera and interest matrices
//
       E3d_MatrixResetTrans(LMatrix, LVX, LVY, LVZ);
       E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
       E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
       E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
       E3d_MatrixTranslate(LMatrix, -LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LMatrix, 'y', -LA);
       E3d_MatrixCopy(LMatrix, LCamera->LeftCameraMatrix);

       E3d_MatrixResetTrans(LMatrix, LVX, LVY, LVZ);
       E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
       E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
       E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
       E3d_MatrixTranslate(LMatrix, LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LMatrix, 'y', LA);
       E3d_MatrixCopy(LMatrix, LCamera->RightCameraMatrix);
       LCamera->InterestMatrix[M30]=LPX;LCamera->InterestMatrix[M31]=LPY;LCamera->InterestMatrix[M32]=LPZ;

       glMatrixMode(GL_MODELVIEW);

       LDrawContext=&E3dp_LeftEyeDrawContext;
       LDrawContext->RenderInfo=&LRenderInfo;

       MglLoadMatrix(LCamera->WorldToLeftViewerMatrix);
       E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToLeftViewerMatrix, LDrawContext, LDrawCamera);

       LDrawContext=&E3dp_RightEyeDrawContext;
       LDrawContext->RenderInfo=&LRenderInfo;
       MglLoadMatrix(LCamera->WorldToRightViewerMatrix);
       E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToRightViewerMatrix, LDrawContext, LDrawCamera);
       glDisable(GL_BLEND);
      }
     break;

     case E3dStereoSHUTTER:
      {
       E3dMatrix	LMatrix, LPPCMatrixR;
       double		LA;

       LA=E3d_GetAngle2D(LCamera->ViewDistance, LCamera->EyeDistance*0.5);

       LVX=LCamera->Position.X;
       LVY=LCamera->Position.Y;
       LVZ=LCamera->Position.Z;
       LPX=LCamera->InterestPoint.X;
       LPY=LCamera->InterestPoint.Y;
       LPZ=LCamera->InterestPoint.Z;
       LCamDX=LVX-LPX;
       LCamDY=LVY-LPY;
       LCamDZ=LVZ-LPZ;

       LCamDXZ=sqrt(LCamDX*LCamDX+LCamDZ*LCamDZ);
       LViewDistance=LCamera->ViewDistance;

       LSinTh=-LCamDX/LCamDXZ;
       LCosTh=LCamDZ/LCamDXZ;
       LSinFi=LCamDY/LViewDistance;
       LCosFi=LCamDXZ/LViewDistance;


       if(E3d_Eye)
       {
// Left eye transformation
//
	E3d_MatrixLoadIdentity(LWorldToViewerMatrix);
	E3d_MatrixRotate(LWorldToViewerMatrix, 'y', LA);
	E3d_MatrixTranslate(LWorldToViewerMatrix, LCamera->EyeDistance*0.5, 0.0, 0.0);
	E3d_MatrixRotate(LWorldToViewerMatrix, 'z', -LCamera->BankAngle);
	E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'x', LSinFi, LCosFi);
	E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'y', LSinTh, LCosTh);
	E3d_MatrixTranslate(LWorldToViewerMatrix, -LVX, -LVY, -LVZ);
	E3d_MatrixCopy(LWorldToViewerMatrix, LCamera->WorldToLeftViewerMatrix);

	E3d_MatrixCopy(LProjectionMatrix, LMatrix);
	E3d_MatrixMult(LWorldToViewerMatrix, LMatrix);
//	  E3d_MatrixCopy(LMatrix, E3dp_LeftEyeDrawContext.WorldToScreenMatrix);
	E3d_MatrixCopy(LMatrix, E3dp_NormalDrawContext.WorldToScreenMatrix);

// For my own transformations like the display of vertices
//
	E3d_MatrixLoadIdentity(LProjCamMatrix);
	LProjCamMatrix[M32]=1.0/(LCamera->ViewDistance);
	E3d_MatrixMult(LProjectionMatrix, LProjCamMatrix);
	E3d_MatrixMult(LWorldToViewerMatrix, LProjCamMatrix);
       }
       else
       {
// Right eye transformation
//
	if(LDisplayMode&E3dDF_Z_BUFFER) { glClearDepth(1.0);glClear(GL_DEPTH_BUFFER_BIT); }

	E3d_MatrixLoadIdentity(LWorldToViewerMatrix);
	E3d_MatrixRotate(LWorldToViewerMatrix, 'y', -LA);
	E3d_MatrixTranslate(LWorldToViewerMatrix, -LCamera->EyeDistance*0.5, 0.0, 0.0);
	E3d_MatrixRotate(LWorldToViewerMatrix, 'z', -LCamera->BankAngle);
	E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'x', LSinFi, LCosFi);
	E3d_MatrixRotateSinCos(LWorldToViewerMatrix, 'y', LSinTh, LCosTh);
	E3d_MatrixTranslate(LWorldToViewerMatrix, -LVX, -LVY, -LVZ);
	E3d_MatrixCopy(LWorldToViewerMatrix, LCamera->WorldToRightViewerMatrix);

	E3d_MatrixCopy(LProjectionMatrix, LMatrix);
	E3d_MatrixMult(LWorldToViewerMatrix, LMatrix);
//	  E3d_MatrixCopy(LMatrix, E3dp_RightEyeDrawContext.WorldToScreenMatrix);
	E3d_MatrixCopy(LMatrix, E3dp_NormalDrawContext.WorldToScreenMatrix);


// For my own transformations, like the display of vertices
//
	E3d_MatrixLoadIdentity(LPPCMatrixR);
	LProjCamMatrix[M32]=1.0/(LCamera->ViewDistance);
	E3d_MatrixMult(LProjectionMatrix, LPPCMatrixR);
	E3d_MatrixMult(LWorldToViewerMatrix, LPPCMatrixR);
       }

// Calculate Camera and interest matrices
//
       E3d_MatrixResetTrans(LMatrix, LVX, LVY, LVZ);
       E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
       E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
       E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
       E3d_MatrixTranslate(LMatrix, -LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LMatrix, 'y', -LA);
       E3d_MatrixCopy(LMatrix, LCamera->LeftCameraMatrix);

       E3d_MatrixResetTrans(LMatrix, LVX, LVY, LVZ);
       E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
       E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
       E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
       E3d_MatrixTranslate(LMatrix, LCamera->EyeDistance*0.5, 0.0, 0.0);
       E3d_MatrixRotate(LMatrix, 'y', LA);
       E3d_MatrixCopy(LMatrix, LCamera->RightCameraMatrix);
       LCamera->InterestMatrix[M30]=LPX;LCamera->InterestMatrix[M31]=LPY;LCamera->InterestMatrix[M32]=LPZ;

       glMatrixMode(GL_MODELVIEW);

       LDrawContext=&E3dp_NormalDrawContext;
       LDrawContext->RenderInfo=&LRenderInfo;

       if(E3d_Eye)
       {
	MglLoadMatrix(LCamera->WorldToLeftViewerMatrix);

	if((LWindowSettings->ShadowsOn)&&(E3d_DrawShadowPassProc!=NULL))
	{
	 E3d_DrawShadowPassProc(LScene, L3DWindow, _ShadowMap, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToLeftViewerMatrix, LDrawContext, LDrawCamera);
	}
	else E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToLeftViewerMatrix, LDrawContext, LDrawCamera);
       }
       else
       {
	MglLoadMatrix(LCamera->WorldToRightViewerMatrix);
	if((LWindowSettings->ShadowsOn)&&(E3d_DrawShadowPassProc!=NULL))
	{
	 E3d_DrawShadowPassProc(LScene, L3DWindow, _ShadowMap, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToRightViewerMatrix, LDrawContext, LDrawCamera);
	}
	else E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToRightViewerMatrix, LDrawContext, LDrawCamera);
       }
      }
     break;

     case E3dStereoNONE:
      {
       E3dModel*	LConstraintModel;
       E3dMatrix	LMatrix, LTMatrix;
       float		LCX, LCY, LCZ;

       if((L3DWindow==E3dp_Main3DWindow) && (E3dp_Prefs.MoveCameraWith3DCursor||(LCamera->PositionConstraint!=NULL)))
       {
// Constrain Camera to this Model
//
	if((LConstraintModel=LCamera->PositionConstraint)!=NULL)
	{
	 LCX=LConstraintModel->LocalToWorldMatrix[M30];
	 LCY=LConstraintModel->LocalToWorldMatrix[M31];
	 LCZ=LConstraintModel->LocalToWorldMatrix[M32];

	 E3d_MatrixInvert3x3(LConstraintModel->LocalToWorldMatrix, LTMatrix);

	 E3d_MatrixSetLookAlong(LTMatrix, LTMatrix[M02], LTMatrix[M12], LTMatrix[M22], LTMatrix[M01], LTMatrix[M11], LTMatrix[M21], LCX, LCY, LCZ);

	 E3d_MatrixCopy(LTMatrix, LCamera->WorldToViewerMatrix);
	}


// For moving the camera with Polhemus
//
//	  E3d_MatrixCopy(E3d_CameraTransMatrix, LCamera->WorldToViewerMatrix);
       }
       else
       {
	LVX=LCamera->Position.X;
	LVY=LCamera->Position.Y;
	LVZ=LCamera->Position.Z;
	LPX=LCamera->InterestPoint.X;
	LPY=LCamera->InterestPoint.Y;
	LPZ=LCamera->InterestPoint.Z;
	LCamDX=LVX-LPX;
	LCamDY=LVY-LPY;
	LCamDZ=LVZ-LPZ;

	LCamDXZ=sqrt(LCamDX*LCamDX+LCamDZ*LCamDZ);
	LViewDistance=LCamera->ViewDistance;

	LSinTh=-LCamDX/LCamDXZ;
	LCosTh=LCamDZ/LCamDXZ;
	LSinFi=LCamDY/LViewDistance;
	LCosFi=LCamDXZ/LViewDistance;

// E3d_CameraRefreshMatrices() should keep LCamera->WorldToViewerMatrix up-to-date for the
// non-stereo Camera, so we don't have to recompute it here
//

// World->Screen Matrix
//
	E3d_MatrixCopy(LProjectionMatrix, LMatrix);
	E3d_MatrixMult(LCamera->WorldToViewerMatrix, LMatrix);
	E3d_MatrixCopy(LMatrix, E3dp_NormalDrawContext.WorldToScreenMatrix);

	glMatrixMode(GL_MODELVIEW);
	MglLoadMatrix(LCamera->WorldToViewerMatrix);

// Calculate Camera and interest-point matrices
//
	E3d_MatrixResetTrans(LMatrix, LVX, LVY, LVZ);
	E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
	E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
	E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
	E3d_MatrixCopy(LMatrix, LCamera->CameraMatrix);
	LCamera->InterestMatrix[M30]=LPX;LCamera->InterestMatrix[M31]=LPY;LCamera->InterestMatrix[M32]=LPZ;
       }

       LDrawContext=&E3dp_NormalDrawContext;
       LDrawContext->RenderInfo=&LRenderInfo;

       if((LWindowSettings->ShadowsOn)&&(E3d_DrawShadowPassProc!=NULL))
       {
	E3d_DrawShadowPassProc(LScene, L3DWindow, _ShadowMap, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToViewerMatrix, LDrawContext, LDrawCamera);
       }
       else E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LCamera->WorldToViewerMatrix, LDrawContext, LDrawCamera);
      }
     break;
    }
   }
  break;

  case E3dVM_TOP:
  case E3dVM_FRONT:
  case E3dVM_RIGHT:
   switch(LViewMode)
   {
    case E3dVM_TOP:
     LCamera=&(L3DWindow->TopCamera);
     LZoom=LCamera->XZoom;
     glOrtho(-LZoom, LZoom, -LZoom/L3DWindow->AspectRatio, LZoom/L3DWindow->AspectRatio, LCamera->ZNear, LCamera->ZFar);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     E3dGL_CameraLookAt(LCamera);
    break;

    case E3dVM_FRONT:
     LCamera=&(L3DWindow->FrontCamera);
     LZoom=LCamera->XZoom;
     glOrtho(-LZoom, LZoom, -LZoom/L3DWindow->AspectRatio, LZoom/L3DWindow->AspectRatio, LCamera->ZNear, LCamera->ZFar);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     E3dGL_CameraLookAt(LCamera);
    break;

    case E3dVM_RIGHT:
     LCamera=&(L3DWindow->RightCamera);
     LZoom=LCamera->XZoom;
     glOrtho(-LZoom, LZoom, -LZoom/L3DWindow->AspectRatio, LZoom/L3DWindow->AspectRatio, LCamera->ZNear, LCamera->ZFar);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     E3dGL_CameraLookAt(LCamera);
    break;

    default:
     LCamera=NULL;
    break;
   }

// For custom transformations
//
   MglGetFloatV(GL_PROJECTION_MATRIX, LProjCamMatrix);
   MglGetFloatV(GL_MODELVIEW_MATRIX, LWorldToViewerMatrix);
   E3d_MatrixMult(LWorldToViewerMatrix, LProjCamMatrix);
   E3d_MatrixCopy(LProjCamMatrix, E3dp_NormalDrawContext.WorldToScreenMatrix);

   LRenderInfo.Camera=LCamera;
   LDrawContext=&E3dp_NormalDrawContext;
   LDrawContext->RenderInfo=&LRenderInfo;

   E3dp_DrawScene(LScene, L3DWindow, GL_BACK, LDisplayMode, LViewMode, LWorldToViewerMatrix, LDrawContext, LDrawCamera);
  break;

  case E3dVM_SCHEMATICS:
   LCamera=&(L3DWindow->SchematicsCamera);
   LZoom=LCamera->XZoom;
   glOrtho(-LZoom, LZoom, -LZoom/L3DWindow->AspectRatio, LZoom/L3DWindow->AspectRatio, LCamera->ZNear, LCamera->ZFar);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   E3dGL_CameraLookAt(LCamera);

   LDrawContext=&E3dp_NormalDrawContext;
   LDrawContext->RenderInfo=&LRenderInfo;

// For custom transformations, like the display of text
//
   MglGetFloatV(GL_PROJECTION_MATRIX, LProjCamMatrix);
   MglGetFloatV(GL_MODELVIEW_MATRIX, LWorldToViewerMatrix);
   E3d_MatrixMult(LWorldToViewerMatrix, LProjCamMatrix);
   E3d_MatrixCopy(LProjCamMatrix, E3dp_NormalDrawContext.WorldToScreenMatrix);
   E3dp_DrawSchematics(L3DWindow, GL_BACK, LDisplayMode, LViewMode, LWorldToViewerMatrix, LProjCamMatrix, LDrawContext);
  break;
 }
}


//========================================
// Redraw a 3DWindow
//========================================
void E3dp_Redraw3DWindow(E3dWindow* L3DWindow)
{
 if(L3DWindow->Visibility!=VisibilityFullyObscured)
 {
  if((((L3DWindow->WindowFlags)&(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE))==(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE)))
  {
   _Draw3DWindow(L3DWindow);

   glXSwapBuffers(E3dp_Display, L3DWindow->XWindow);
  }
 }
}


//========================================
// RenderProc for the OpenGL Renderer
//========================================
int _RenderOpenGL(E3dWindow* L3DWindow, E3dScene* LScene, E3dRenderInfo* LRenderInfo, EBool LStartFromFirstIteration, EBool LFirstPassAbortable)
{
 E3d_CameraCopy(LRenderInfo->Camera, &(L3DWindow->PerspectiveCamera));

// L3DWindow->DisplayMode=E3dDM_ShadedSolid;
 L3DWindow->DisplayMode=E3dp_Main3DWindow->DisplayMode;

 if(LRenderInfo->Textures) L3DWindow->DisplayMode|=E3dDF_TEXTURE;

 if(LRenderInfo->Shadows) L3DWindow->Settings.ShadowsOn=TRUE;
 else L3DWindow->Settings.ShadowsOn=FALSE;

 if(LRenderInfo->Transparency) L3DWindow->Settings.TransparencyOn=TRUE;
 else L3DWindow->Settings.TransparencyOn=FALSE;

 _Draw3DWindow(L3DWindow);


 glReadBuffer(GL_BACK);

 E3d_WindowGetImage(L3DWindow, LRenderInfo->CanvasImage);

 glXSwapBuffers(EGUI_Display, L3DWindow->XWindow);

 LRenderInfo->CurrentPixelSize=1;

 return(0);
}



//========================================================================
// Draw an outline rectangle on a 3DWindow in Window coordinates
//========================================================================
void E3d_WindowDraw2DRectangle(E3dWindow* L3DWindow, float LX1, float LY1, float LX2, float LY2, EcRGBAiColor LRGBAiColor)
{
 if(L3DWindow->Visibility!=VisibilityFullyObscured)
 {
  if((((L3DWindow->WindowFlags)&(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE))==(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE)))
  {
   E3dGLCoordinate	LVertices[4][3];
   E3dMatrix		LTmpMatrix;


   _Draw3DWindow(L3DWindow);

// glPushMatrix() won't work on the PROJECTION matrix, so we have to save it 'manually'
//
   glMatrixMode(GL_PROJECTION);
   MglGetFloatV(GL_PROJECTION_MATRIX, LTmpMatrix);
   glLoadIdentity();
   glOrtho(0.0, (GLdouble)(L3DWindow->XSize), 0.0, (GLdouble)(L3DWindow->YSize), -1.0, 1.0);

   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();

   if(E3dp_Prefs.LineWidth>2.0) glLineWidth(E3dp_Prefs.LineWidth*0.75);

   LVertices[0][E3dX]=LX1;
   LVertices[0][E3dY]=(GLdouble)(L3DWindow->YSize)-LY1;
   LVertices[0][E3dZ]=0.0;

   LVertices[1][E3dX]=LX2;
   LVertices[1][E3dY]=(GLdouble)(L3DWindow->YSize)-LY1;
   LVertices[1][E3dZ]=0.0;

   LVertices[2][E3dX]=LX2;
   LVertices[2][E3dY]=(GLdouble)(L3DWindow->YSize)-LY2;
   LVertices[2][E3dZ]=0.0;

   LVertices[3][E3dX]=LX1;
   LVertices[3][E3dY]=(GLdouble)(L3DWindow->YSize)-LY2;
   LVertices[3][E3dZ]=0.0;

   glDisable(GL_LIGHTING);		// No lighting
   glDisable(GL_BLEND);

   E3dM_glColorRGBAi(LRGBAiColor);


   glBegin(GL_LINE_LOOP);
    MglVertex3v(LVertices[0]);
    MglVertex3v(LVertices[1]);
    MglVertex3v(LVertices[2]);
    MglVertex3v(LVertices[3]);
   glEnd();

   if(E3dp_Prefs.LineWidth>2.0) glLineWidth(E3dp_Prefs.LineWidth);

   glPopMatrix();

   glMatrixMode(GL_PROJECTION);
   MglLoadMatrix(LTmpMatrix);

   glMatrixMode(GL_MODELVIEW);


   glXSwapBuffers(E3dp_Display, L3DWindow->XWindow);
  }
 }
}


//========================================================================
// Redraw the appropriate 3DWindows, according to "change" flags
//========================================================================
void E3dp_WindowsRedrawByChangeFlags(unsigned int LChanged)
{
 E3dScene*	LScene=E3d_Scene;
 E3dWindow*	L3DWindow;
 unsigned int	LC, LWN=E3dp_NumOf3DWindows,
		LMainWViewModes=0, LMainWDisplayMode=0, LViewModes=0, LDisplayMode=0;


 if(LChanged&E3dCHG_LIGHT) { E3d_SceneLightsRefreshGL(LScene);LViewModes|=E3dVM_NORMAL3D;LDisplayMode|=E3dDF_LIGHTING; }
 if(LChanged&(E3dCHG_TRANSFORM|E3dCHG_SHAPE)) { LViewModes|=E3dVM_NORMAL3D;LDisplayMode|=E3dDF_ALL; }
 if(LChanged&E3dCHG_HIDEUNHIDE) { LViewModes|=E3dVM_ALL;LDisplayMode|=E3dDF_ALL; }
 if(LChanged&E3dCHG_MATERIAL) { LViewModes|=E3dVM_NORMAL3D;LDisplayMode|=E3dDF_SOLID; }

 LMainWViewModes=LViewModes;LMainWDisplayMode=LDisplayMode;

 if(LChanged&E3dCHG_CAMERA)
 {
  LMainWViewModes|=E3dVM_PERSPECTIVE;LMainWDisplayMode|=E3dDF_ALL;
  if(E3dp_Prefs.ShowCamera) { LViewModes|=E3dVM_NORMAL3D;LDisplayMode|=E3dDF_ALL; }
 }

 if(_ShadowMap) _ShadowMap->ShadowMapValid=FALSE;

 L3DWindow=E3dp_Main3DWindow;
 if(((L3DWindow->ViewMode&LMainWViewModes)!=0)&&((L3DWindow->DisplayMode&LMainWDisplayMode)!=0)) E3dp_Redraw3DWindow(L3DWindow);

 if((LViewModes!=0)&&(LDisplayMode!=0))
 {
  for(LC=0;LC<LWN;LC++)
  {
   L3DWindow=E3dp_3DWindows[LC];
   if(L3DWindow!=E3dp_Main3DWindow)
   {
    if(((L3DWindow->ViewMode&LViewModes)!=0)&&((L3DWindow->DisplayMode&LDisplayMode)!=0)) E3dp_Redraw3DWindow(L3DWindow);
   }
  }
 }
}
