/*======================================================================================================*/
/* 3DPanel												*/
/*													*/
/* - Picking and selecting										*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Jan-10 23:55:15									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/

#include <stdio.h>
#include <float.h>
#include <math.h>
#include <stdlib.h>		// For qsort

#include <EMalloc.h>

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

#include <GL/glu.h>

#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 _E3DPanel_h
#include <E3D/Panel.h>
#endif

#ifndef _E3DPick_h
#include <E3D/Pick.h>
#endif

#ifndef _E3DRay_h
#include <E3D/Ray.h>
#endif

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

#ifndef _E3DSelect_h
#include <E3D/Select.h>
#endif


#include <E3D/StatusPanel.h>

#include "Display.h"


extern E3dDrawContext	E3dp_NormalDrawContext, E3dp_LeftEyeDrawContext, E3dp_RightEyeDrawContext;

//----------------------------------------
// From Display.c
//----------------------------------------
extern void		E3d_DrawPoints(E3dPoints* LPointGeo, int LModelType, unsigned int LDisplayMode, E3dDrawContext* LDrawContext);
extern void		E3d_DrawSpline(E3dSpline* LSpline, int LModelType, unsigned int LDisplayMode, E3dDrawContext* LDrawContext);


//----------------------------------------
// From Schematics.c
//----------------------------------------
extern void		E3d_SchemPickDrawModel(E3dModel* LModel);



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



int	E3d_SelectionTarget=E3dSELTG_NONE;

E3dModel*	E3dp_Models[256];

int	E3d_PickMode=E3dNONE;

int	E3dp_PickBufferSize=50000;

E3dPickCallbackProc	E3d_PickCallback=NULL;
EBool			E3d_PickActivateOnMotion=FALSE;


// For highlighting Edges during Edge picking
//
E3dEdge*		_LastActiveEdge=NULL;
E3dVertex*		_LastActiveVertex=NULL;
E3dPolyGroup*		_LastActivePolyGroup=NULL;



/*======================================*/
/* Request picking			*/
/*======================================*/
int E3dp_PickRequest(int LWhat, E3dPickCallbackProc LPickCallback, int LTimeOut, EBool LActivateOnMotion)
{
 E3d_PickMode=LWhat;
 E3d_PickCallback=LPickCallback;
 E3d_PickActivateOnMotion=LActivateOnMotion;
 
 E3dp_PushPointerMode(E3dpPTR_PICK);

 return(0);
}


/*======================================*/
/* End picking				*/
/*======================================*/
void E3dp_PickEnd()
{
 if(_LastActiveEdge)
 {
  _LastActiveEdge->Flags&=(E3dALL-E3dEDGE_ACTIVE);
  if(_LastActivePolyGroup) _LastActivePolyGroup->Flags&=(E3dALL-E3dACTIVE_EDGES);

  _LastActiveEdge=NULL;
  _LastActivePolyGroup=NULL;
 }

 if(_LastActiveVertex)
 {
  _LastActiveVertex->Flags&=(E3dALL-E3dVTXF_ACTIVE);

  _LastActiveVertex=NULL;
  _LastActivePolyGroup=NULL;
 }

 E3d_PickMode=E3dNONE;
 E3d_PickCallback=NULL;
 E3dp_PopPointerMode(TRUE);
}


/*=====================================*/
/* Abort picking			*/
/*======================================*/
void E3dp_PickAbort()
{
 if(_LastActiveEdge)
 {
  _LastActiveEdge->Flags&=(E3dALL-E3dEDGE_ACTIVE);
  if(_LastActivePolyGroup) _LastActivePolyGroup->Flags&=(E3dALL-E3dACTIVE_EDGES);

  _LastActiveEdge=NULL;
  _LastActivePolyGroup=NULL;
 }

 if(_LastActiveVertex)
 {
  _LastActiveVertex->Flags&=(E3dALL-E3dVTXF_ACTIVE);

  _LastActiveVertex=NULL;
  _LastActivePolyGroup=NULL;
 }

 if(E3d_PickCallback)
 {
  E3dPickCallbackStruct	LCBS;

  LCBS.Reason=E3dCR_ABORT;
  LCBS.Modifiers=0;
  E3d_PickCallback(&LCBS, 0, NULL);
 }

 E3d_PickMode=E3dNONE;
 E3d_PickCallback=NULL;
 E3dp_PopPointerMode(TRUE);
}


/*======================================*/
/* Draw a Spline for picking		*/
/*======================================*/
static void E3d_PickDrawSpline(E3dSpline* LSpline, int LModelType, unsigned int LDisplayMode, E3dDrawContext* LDrawContext, int LPickTargets)
{
 E3dGLCoordinate*	LGLVertices;
 E3dSplineCV*		LCV;
 E3dSplineCV*		LCVs;
 E3dBezierCV*		LBezierCV;
 unsigned int		LVC, LVN=LSpline->NumOfCVs;


 LCVs=(E3dSplineCV*)(LSpline->CVs);

 if((LPickTargets&E3dITEM_SPLINE_KEY)==0)
 {
  unsigned int	LLN;

  if(LVN<2) return;

  if((LSpline->GLLinSegments)==NULL) E3d_SplineCreateLinearSegments(LSpline);

  if(((LGLVertices=LSpline->GLLinSegments)!=NULL)&&((LLN=LSpline->NumOfLinSegments)!=0))
  {
   unsigned int	LCVC=LSpline->NumOfCVs, LSegmentC=0;

    switch(LSpline->SplineType)
    {
     case E3dSPL_BEZIER:
      {
       E3dBezierCV*	LBezierCV=(E3dBezierCV*)(LSpline->CVs);

       if(LSpline->Closed)
       {
	if(LSpline->UniformSteps)
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LCVC>1)
	    {
	     if(LBezierCV->Segment==E3dBEZ_LINEAR) LVC=2;
	     else LVC=LSpline->NumOfDrawSteps+1;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     LGLVertices-=3;
	    }
	    else		// Last segment
	    {
	     if(LBezierCV->Segment==E3dBEZ_LINEAR) { MglVertex3v(LGLVertices); }
	     else
	     {
	      LVC=LSpline->NumOfDrawSteps;
	      do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     }
	     MglVertex3v(LSpline->GLLinSegments);
	    }
	   glEnd();

	  glPopName();

	  LSegmentC++;LBezierCV++;
	 } while(--LCVC);
	}
	else
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LCVC>1)
	    {
	     if(LBezierCV->Segment==E3dBEZ_LINEAR) LVC=2;
	     else LVC=LBezierCV->NumOfDrawSteps+1;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     LGLVertices-=3;
	    }
	    else		// Last segment
	    {
	     if(LBezierCV->Segment==E3dBEZ_LINEAR) { MglVertex3v(LGLVertices); }
	     else
	     {
	      LVC=LBezierCV->NumOfDrawSteps;
	      do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     }
	     MglVertex3v(LSpline->GLLinSegments);
	    }
	   glEnd();

	  glPopName();

	  LSegmentC++;LBezierCV++;
	 } while(--LCVC);
	}
       }
       else
       {
// Open Bezier Splines have NumOfCVs-1 segments
//
	LCVC--;
	if(LSpline->UniformSteps)
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LBezierCV->Segment==E3dBEZ_LINEAR) LVC=2;
	    else LVC=LSpline->NumOfDrawSteps+1;
	    do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	    LGLVertices-=3;
	   glEnd();

	  glPopName();

	  LSegmentC++;LBezierCV++;
	 } while(--LCVC);
	}
	else
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LBezierCV->Segment==E3dBEZ_LINEAR) LVC=2;
	    else LVC=LBezierCV->NumOfDrawSteps+1;
	    do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	    LGLVertices-=3;
	   glEnd();

	  glPopName();

	  LSegmentC++;LBezierCV++;
	 } while(--LCVC);
	}
       }
      }
     break;

     case E3dSPL_LINEAR:
      {
       E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSpline->CVs);

       if(LSpline->Closed)
       {
	do
	{
	 glPushName((GLuint)LSegmentC);	// Push Spline segment index

	  glBegin(GL_LINE_STRIP);
	   if(LCVC>1)
	   {
	    MglVertex3v(LGLVertices);LGLVertices+=3;
	    MglVertex3v(LGLVertices);
	   }
	   else		// Last segment
	   {
	    MglVertex3v(LGLVertices);
	    MglVertex3v(LSpline->GLLinSegments);
	   }
	  glEnd();

	 glPopName();

	 LSegmentC++;LSplineCV++;
	} while(--LCVC);
       }
       else
       {
// Open Linear Splines have NumOfCVs-1 segments
//
	LCVC--;

	do
	{
	 glPushName((GLuint)LSegmentC);	// Push Spline segment index

	  glBegin(GL_LINE_STRIP);
	   MglVertex3v(LGLVertices);LGLVertices+=3;
	   MglVertex3v(LGLVertices);
	  glEnd();

	 glPopName();

	 LSegmentC++;LSplineCV++;
	} while(--LCVC);
       }
      }
     break;

     default:
      {
       E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSpline->CVs)+1;

       if(LSpline->Closed)
       {
	if(LSpline->UniformSteps)
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LCVC>1)
	    {
	     LVC=LSpline->NumOfDrawSteps+1;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     LGLVertices-=3;
	    }
	    else				// Last segment
	    {
	     LVC=LSpline->NumOfDrawSteps;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     MglVertex3v(LSpline->GLLinSegments);
	    }
	   glEnd();

	  glPopName();

	  LSegmentC++;LSplineCV++;
	 } while(--LCVC);
	}
	else
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);	// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    if(LCVC>1)
	    {
	     LVC=LSplineCV->NumOfDrawSteps+1;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     LGLVertices-=3;
	    }
	    else				// Last segment
	    {
	     LSplineCV=(E3dSplineCV*)(LSpline->CVs);
	     LVC=LSplineCV->NumOfDrawSteps;
	     do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	     MglVertex3v(LSpline->GLLinSegments);
	    }
	   glEnd();

	  glPopName();

	  LSegmentC++;LSplineCV++;
	 } while(--LCVC);
	}
       }
       else
       {
// Open BSplines, and CardinalSplines have NumOfCVs-3 segments
//
	LCVC-=3;

	if(LSpline->UniformSteps)
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);		// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    LVC=LSpline->NumOfDrawSteps+1;
	    do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	    LGLVertices-=3;
	   glEnd();

	  glPopName();

	  LSegmentC++;LSplineCV++;
	 } while(--LCVC);
	}
	else
	{
	 do
	 {
	  glPushName((GLuint)LSegmentC);		// Push Spline segment index

	   glBegin(GL_LINE_STRIP);
	    LVC=LSplineCV->NumOfDrawSteps+1;
	    do { MglVertex3v(LGLVertices);LGLVertices+=3; } while(--LVC);
	    LGLVertices-=3;
	   glEnd();

	  glPopName();

	  LSegmentC++;LSplineCV++;
	 } while(--LCVC);
	}
       }
      }
     break;
    }
  }
 }


 if(LDisplayMode&E3dDF_SELECTED)
 {
/*--------------------------------------*/
/* Draw CVs				*/
/*--------------------------------------*/
  if(E3dp_Prefs.ShowControlPoints&&(LPickTargets&E3dITEM_SPLINE_KEY)!=0)
  {
   unsigned int	LPointSize, LHalfPointSize;

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

//   glPushName((GLuint)E3dITEM_SPLINE_KEY);

   switch(LSpline->SplineType)
   {
    case E3dSPL_BEZIER:
     LBezierCV=(E3dBezierCV*)LCVs;

     for(LVC=0;LVC<LVN;LVC++, LBezierCV++)
     {
      glPushName((GLuint)LVC);		// Push CV index

      glPushName((GLuint)0);		// 0: Previous
      MglRasterPos3(LBezierCV->Previous.X, LBezierCV->Previous.Y, LBezierCV->Previous.Z);
      glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledCV_bits);
      glPopName();


      glPushName((GLuint)1);		// 1: Position
      MglRasterPos3(LBezierCV->Position.X, LBezierCV->Position.Y, LBezierCV->Position.Z);
      glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledCV_bits);
      glPopName();


      glPushName((GLuint)2);		// 2: Next
      MglRasterPos3(LBezierCV->Next.X, LBezierCV->Next.Y, LBezierCV->Next.Z);
      glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledCV_bits);
      glPopName();

      glPopName();
     }
    break;

    default:
// Draw CVs
//
     LCV=LCVs;
     for(LVC=0;LVC<LVN;LVC++)
     {
      glPushName((GLuint)LVC);		// Push CV index

      MglRasterPos3(LCV->Position.X, LCV->Position.Y, LCV->Position.Z);
      glBitmap(LPointSize, LPointSize, LHalfPointSize, LHalfPointSize, 0, 0, (const GLubyte*)_FilledCV_bits);

      glPopName();

      LCV++;
     }
    break;
   }

//   glPopName();
  }
 }
}


/*======================================================*/
/* Draw edges of a Mesh for Edge picking		*/
/*======================================================*/
static void  E3d_MeshDrawPickEdges(E3dMesh* LMesh)
{
 E3dEdge*	LEdge;
 unsigned int	LGC, LGN=LMesh->NumOfPolyGroups,
		LC, LN;


 glPushName((GLuint)E3dITEM_EDGE);

// If the Mesh has only 1 PolyGroup, draw the Mesh edges
//
 if(LGN==1)
 {
  LN=LMesh->NumOfEdges;
  if((LN>0)&&(LMesh->PolyGroups[0]->Selected))
  {
   glPushName((GLuint)0);			// Push PolyGroup index
   LEdge=LMesh->Edges;
   for(LC=0;LC<LN;LC++, LEdge++)
   {
    glPushName(LC);				// Push Edge index

    glBegin(GL_LINES);
    MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
    glEnd();

    glPopName();				// Pop Edge index
   }

   glPopName();					// Pop PolyGroup index
  }
 }
 else
 {
  E3dPolyGroup**	LPolyGroups=LMesh->PolyGroups;
  E3dPolyGroup*		LPolyGroup;

// Draw PolyGroup edges
//
  for(LGC=0;LGC<LGN;LGC++)
  {
   LPolyGroup=LPolyGroups[LGC];

   LN=LPolyGroup->NumOfEdges;
   if((LN>0)&&(LPolyGroup->Selected))
   {
    glPushName((GLuint)LGC);		// Push PolyGroup index
    LEdge=LPolyGroup->Edges;

    for(LC=0;LC<LN;LC++, LEdge++)
    {
     glPushName(LC);			// Push Edge index

     glBegin(GL_LINES);
     MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
     glEnd();

     glPopName();				// Pop Edge index
    }
    glPopName();				// Pop PolyGroup index
   }

  }
 }

 glPopName();
}



/*----------------------------------------------*/
/* Polygon drawing macros			*/
/*----------------------------------------------*/
#define E3dM_DrawPolygon()\
 if((LITriangle=LPolygon->ITriangles)==NULL)\
 {\
  LVertexNode=LPolygon->VertexNodes;\
  LVN=LPolygon->NumOfExteriorVertices;\
  glBegin(GL_POLYGON);\
   for(LVC=0;LVC<LVN;LVC++, LVertexNode++) MglVertex3v(LVertexNode->GLVertex);\
  glEnd();\
 }\
 else\
 {\
  LPC1=LPolygon->NumOfTriangles;\
  glBegin(GL_TRIANGLES);\
  do\
  {\
   LVertexNodeP=LITriangle->VertexNodes;\
   MglVertex3v((LVertexNodeP[0])->GLVertex);\
   MglVertex3v((LVertexNodeP[1])->GLVertex);\
   MglVertex3v((LVertexNodeP[2])->GLVertex);\
   LITriangle++;\
  } while(--LPC1);\
  glEnd();\
 }


/*======================================*/
/* Draw a Polygon Mesh for pick		*/
/*======================================*/
void E3dp_PickDrawMesh(E3dMesh* LMesh, unsigned int LDisplayMode, E3dDrawContext* LDrawContext, int LPickTargets)
{
 E3dEdge*		LEdge;
 E3dPolyGroup*		LPolyGroup;
 E3dPolyGroup**		LPolyGroups=LMesh->PolyGroups;
 unsigned int		LGC, LGN=LMesh->NumOfPolyGroups,
			LEC, LEN;


// Draw Polygons
//
 {
  E3dPolygon*		LPolygon;
  E3dITriangle*		LITriangle;
  E3dVertexNode*	LVertexNode;
  E3dVertexNode**	LVertexNodeP;
  unsigned int		LPC, LPN,
			LPC1, LVC, LVN;


  glPushName((GLuint)E3dITEM_POLYGON);

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

   if(LPolyGroup->Visible)
   {

    glPushName((GLuint)LGC);		// Push PolyGroup index

    LPN=LPolyGroup->NumOfPolygons;
    if(LPN)
    {
     LPolygon=LPolyGroup->Polygons;
     for(LPC=0;LPC<LPN;LPC++, LPolygon++)
     {
      glPushName((GLuint)LPC);		// Push Polygon index

      E3dM_DrawPolygon();

      glPopName();
     }
    }

    glPopName();
   }
  }

  glPopName();
 }


 if(LPickTargets&E3dITEM_VERTEX)
 {
  if(((LDisplayMode&E3dDF_SELECTED)!=0)||((LMesh->Selection==E3dGSEL_POLYGROUP)&&E3dp_DrawMeshVerticesIfPolyGroupSelected))
  {
   E3dVertex*	LMeshVertices;
   E3dVertex*	LVertex;
   unsigned int	LC, LN=LMesh->NumOfVertices,
		LVtxSize, LMidVtxSize;


   LMeshVertices=LMesh->Vertices;

   glPushName((GLuint)E3dITEM_VERTEX);


// Draw vertices
//
   LVtxSize=E3dp_Prefs.PointSize+2;
   LMidVtxSize=LVtxSize>>1;

   LVertex=LMeshVertices;
   for(LC=0;LC<LN;LC++)
   {
    glPushName((GLuint)LC);			// Push Vertex index

    MglRasterPos3(LVertex->X, LVertex->Y, LVertex->Z);
    glBitmap(LVtxSize, LVtxSize, LMidVtxSize, LMidVtxSize, 0, 0, (const GLubyte*)_FilledCV_bits);

    glPopName();				// Pop Vertex index

    LVertex++;
   }

   glPopName();
  }

  if(LPickTargets&E3dITEM_EDGE) E3d_MeshDrawPickEdges(LMesh);
 }
 else
 {
  if(LPickTargets&E3dITEM_EDGE) E3d_MeshDrawPickEdges(LMesh);
  else if(LDisplayMode&E3dDF_WIREFRAME)
  {
    glPushName((GLuint)E3dITEM_POLYGROUP);

// If the Mesh has only 1 PolyGroup, draw the Mesh Edges
//
    if(LGN==1)
    {
     if((LEN=LMesh->NumOfEdges)>0)
     {
      glPushName((GLuint)0);			// Push PolyGroup index
      LEC=LEN;LEdge=LMesh->Edges;
      glBegin(GL_LINES);
      do
      {
       MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
       LEdge++;
      }
      while(--LEC);
      glEnd();
      glPopName();
     }
    }
    else
    {
// Draw Edges of the PolyGroups
//
     for(LGC=0;LGC<LGN;LGC++)
     {
      LPolyGroup=LPolyGroups[LGC];

      if((LEN=LPolyGroup->NumOfEdges)>0)
      {
       glPushName((GLuint)LGC);		// Push PolyGroup index
       LEC=LEN;LEdge=LPolyGroup->Edges;
       glBegin(GL_LINES);
       do
       {
	MglVertex3v(LEdge->GLStart);MglVertex3v(LEdge->GLEnd);
	LEdge++;
       } while(--LEC);
       glEnd();

       glPopName();
      }

     }
    }

    glPopName();
  }
 }

} 


/*==============================================================*/
/* "Draw" hierarchies of Models for picking/selecting		*/
/*==============================================================*/
static void E3dp_SceneDrawModelsForPick(E3dScene* LScene, int LDisplayMode, E3dMatrix LWorldToViewerMatrix, int LPickTargets, EBool LDrawCamera)
{
 E3dModel*		LRootModel;
 E3dModel*		LModel;
 unsigned int		LGmCnt, LGmNum;
 E3dGeometry**		LGeometries;
 E3dGeometry*		LGeometry;
 register unsigned int	LRMCnt, LMCnt, LChildCnt;
 unsigned int		LRN;
 unsigned int		LModelFlags, LGeometryFlags;

 E3dModel*	LChildModel;
 E3dGLType	LStart[3], LEnd[3], LP0[3], LP1[3], LP2[3], LP3[3], LPTop[3];
 EBool		LDrawGeometries;


 LRN=LScene->NumOfRootModels;
 for(LRMCnt=0, LMCnt=0;LRMCnt<LRN;LRMCnt++)
 {
  LModel=LRootModel=LScene->RootModels[LRMCnt];

  LModel=LRootModel;
  for(LChildCnt=0;LModel;LModel=LModel->Next, LChildCnt++)
  {
   if(LModel->Visible)
   {
    LDrawGeometries=TRUE;

    glPushMatrix();
    MglLoadMatrix(LWorldToViewerMatrix);

    if(!E3d_GLDataTransformed)
    {
     MglMultMatrix(LModel->LocalToWorldMatrix);
    }

    glLoadName((GLuint)LRMCnt);	// Store Hierarchy number (RootModel index)
    glPushName((GLuint)LMCnt);	// And push Model index in E3dp_Models array

    switch(LModel->Type)
    {
     case E3dMDL_JOINT:
      if((LChildModel=LModel->Child)!=NULL)
      {
       LStart[0]=0.0;
       LStart[1]=0.0;
       LStart[2]=0.0;

       LEnd[0]=LChildModel->Translation.X;
       LEnd[1]=LChildModel->Translation.Y;
       LEnd[2]=LChildModel->Translation.Z;

#define BWd	0.5
       LP0[0]=0.0;LP0[1]=BWd;LP0[2]=0.0;
       LP1[0]=0.0;LP1[1]=0.0;LP1[2]=-BWd;
       LP2[0]=0.0;LP2[1]=-BWd;LP2[2]=0.0;
       LP3[0]=0.0;LP3[1]=0.0;LP3[2]=BWd;
       LPTop[0]=-BWd;LPTop[1]=0.0;LPTop[2]=0.0;

       glBegin(GL_LINES);
	MglVertex3v(LP0);MglVertex3v(LEnd);
	MglVertex3v(LP1);MglVertex3v(LEnd);
	MglVertex3v(LP2);MglVertex3v(LEnd);
	MglVertex3v(LP3);MglVertex3v(LEnd);

	MglVertex3v(LP0);MglVertex3v(LPTop);
	MglVertex3v(LP1);MglVertex3v(LPTop);
	MglVertex3v(LP2);MglVertex3v(LPTop);
	MglVertex3v(LP3);MglVertex3v(LPTop);

	MglVertex3v(LP0);MglVertex3v(LP1);
	MglVertex3v(LP1);MglVertex3v(LP2);
	MglVertex3v(LP2);MglVertex3v(LP3);
	MglVertex3v(LP3);MglVertex3v(LP0);
       glEnd();
      }
     break;
    }

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

     default:
      LModelFlags=0;
     break;
    }

    if(LDrawGeometries)
    {
     LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
     for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
     {
      if((LGeometry=LGeometries[LGmCnt])!=NULL)
      {
       if(LGeometry->Visible)
       {
	switch(LGeometry->Selection)
	{
	 case E3dGSEL_GEOMETRY:
	 case E3dGSEL_POLYGROUP:
	 case E3dGSEL_SPLINE_SEGMENT:
	 case E3dGSEL_FACE_CONTOUR:
	  LGeometryFlags=E3dDF_SELECTED;
	 break;

	 default:
	  LGeometryFlags=0;
	 break;
	}

	glPushName((GLuint)LGmCnt);		// And push Geometry index
	switch(LGeometry->GeoType)
	{
	 case E3dGEO_POINTS:
	  E3d_DrawPoints((E3dPoints*)LGeometry, LModel->Type, LDisplayMode, &E3dp_NormalDrawContext);
	 break;

	 case E3dGEO_SPLINE:
	  if(((LDisplayMode&E3dDF_SOLID)==0)||E3dp_Prefs.ShowSplinesInSolidMode)
	  {
	   E3d_PickDrawSpline((E3dSpline*)LGeometry, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, &E3dp_NormalDrawContext, LPickTargets);
	  }
	 break;

	 case E3dGEO_FACE:
	  if(((LDisplayMode&E3dDF_SOLID)==0)||E3dp_Prefs.ShowSplinesInSolidMode)
	  {
	   E3dFace*	LFace=(E3dFace*)LGeometry;
	   unsigned int	LHC, LHN=LFace->NumOfHoles;

	   glPushName((GLuint)0);		// Push Contour index
	   E3d_PickDrawSpline(LFace->Exterior, LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, &E3dp_NormalDrawContext, LPickTargets);
	   glPopName();

	   for(LHC=0;LHC<LHN;LHC++)
	   {
	    glPushName((GLuint)(LHC+1));	// Push Contour index
	    E3d_PickDrawSpline(LFace->Holes[LHC], LModel->Type, LDisplayMode|LModelFlags|LGeometryFlags, &E3dp_NormalDrawContext, LPickTargets);
	    glPopName();
	   }
	  }
	 break;

	 caseE3dMESH():
	  E3dp_PickDrawMesh((E3dMesh*)LGeometry, LDisplayMode|LModelFlags|LGeometryFlags, &E3dp_NormalDrawContext, LPickTargets);
	 break;
	}
	glPopName();
       }
      }
     }
    }

    E3dp_Models[LMCnt]=LModel;LMCnt++;
    glPopName();

    glPopMatrix();
   }
  }
 }
}


/*==============================================================*/
/* "Draw" hierarchies of Models for picking/selecting		*/
/*==============================================================*/
static void E3dp_SceneSchemPutModelsForPick(E3dScene* LScene, int LDisplayMode, E3dMatrix LWorldToViewerMatrix)
{
 register E3dModel*	LModel;
/*
 unsigned int		LGmCnt, LGmNum;
 E3dGeometry**		LGeometries;
 E3dGeometry*		LGeometry;
*/
 register unsigned int	LRMCnt, LMCnt, LChildCnt;
 unsigned int		LRN;
 E3dMatrix		LMatrix;

 E3d_MatrixLoadIdentity(LMatrix);

 LRN=LScene->NumOfRootModels;
 for(LRMCnt=0, LMCnt=0;LRMCnt<LRN;LRMCnt++)
 {
  LModel=LScene->RootModels[LRMCnt];

  for(LChildCnt=0;LModel;LModel=LModel->Next, LChildCnt++)
  {
   glPushMatrix();
   MglLoadMatrix(LWorldToViewerMatrix);

   LMatrix[M30]=LModel->SchemPosition.X;
   LMatrix[M31]=LModel->SchemPosition.Y;
   LMatrix[M32]=LModel->SchemPosition.Z;
   MglMultMatrix(LMatrix);

   glLoadName((GLuint)LRMCnt);	// Store Hierarchy number (RootModel index)
   glPushName((GLuint)LMCnt);	// And push model index in E3dp_Models array

   E3d_SchemPickDrawModel(LModel);


/*
   LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
   for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
   {
    if((LGeometry=LGeometries[LGmCnt])!=NULL)
    {
     if(LGeometry->Visible)
     {
      glPushName((GLuint)LGmCnt);	// And push Geometry index
      switch(LGeometry->GeoType)
      {
       case E3dGEO_POINTS:
	E3d_DrawPoints((E3dPoints*)LGeometry, LModel->Type, LDisplayMode, &E3dp_NormalDrawContext);
       break;

       case E3dGEO_SPLINE:
	E3d_DrawSpline((E3dSpline*)LGeometry, LModel->Type, LDisplayMode, &E3dp_NormalDrawContext);
       break;

       case E3dGEO_MESH:
       case E3dGEO_SKINMESH:
	E3dp_PickDrawMesh((E3dMesh*)LGeometry, LDisplayMode, &E3dp_NormalDrawContext, 0);
       break;
      }
      glPopName();
     }
    }
   }
*/

   E3dp_Models[LMCnt]=LModel;LMCnt++;
   glPopName();

   glPopMatrix();
  }
 }
}


/*==============================================================*/
/* Refresh a 3DWindow for pick (no actual drawing takes place)	*/
/*==============================================================*/
static void E3d_WindowRedrawForPick(E3dWindow* L3DWindow, int LX, int LY, int LXSize, int LYSize, int LPickTargets)
{
 E3dDrawContext*	LDrawContext;
 E3dRenderInfo		LRenderInfo;
 E3dCamera*		LCamera=NULL;
 E3dMatrix		LWorldToViewerMatrix;
 int			LWindowFlags, LDisplayMode, LViewMode;
 float			LZoom;
 GLdouble		LFX=(GLdouble)LX, LFXSize=(GLdouble)LXSize, LFYSize=(GLdouble)LYSize;
 GLint			LViewPort[4];
 EBool			LDrawCamera=TRUE;

 if((LXSize==0)||(LYSize==0)) return;

 LWindowFlags=L3DWindow->WindowFlags;
 if((LWindowFlags&E3dWF_CREATED)&&(LWindowFlags&E3dWF_MANAGED)&&(LWindowFlags&E3dWF_ALIVE))
 {
  glXMakeCurrent(E3dp_Display, L3DWindow->XWindow, L3DWindow->Panel->EGLXContext);
  glViewport(0, 0, L3DWindow->XSize, L3DWindow->YSize);
  E3dp_CurrentGLXWindow=(Window)NULL;

  glGetIntegerv(GL_VIEWPORT, LViewPort);
  LDisplayMode=L3DWindow->DisplayMode;
  LViewMode=L3DWindow->ViewMode;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

//printf("PW: %d,%d %dx%d   VP: %d,%d,%d,%d\n", LX, LY, LXSize, LYSize, LViewPort[0], LViewPort[1], LViewPort[2], LViewPort[3]);fflush(stdout);

//  E3dGL_CameraPickMatrix((GLdouble)LX, (GLdouble)(LViewPort[3]-LY), (GLdouble)(LX+LXSize), (GLdouble)LYSize, LViewPort);
  E3dGL_CameraPickMatrix(LFX+LFXSize*0.5, (GLdouble)(LViewPort[3]-LY)-LFYSize*0.5, LFXSize, LFYSize, LViewPort);

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

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

  switch(LViewMode)
  {
   case E3dVM_PERSPECTIVE:
    LCamera=&(L3DWindow->PerspectiveCamera);
    LCamera->AspectRatio=L3DWindow->AspectRatio;

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

    E3dGL_CameraPerspective(LCamera);

    if(L3DWindow==E3dp_Main3DWindow) LDrawCamera=FALSE;

// In stereo mode, use right-eye view for picking
//
    if(L3DWindow->Settings.StereoMode!=E3dStereoNONE)
    {
     glMatrixMode(GL_MODELVIEW);
     MglLoadMatrix(LCamera->WorldToRightViewerMatrix);

     if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD) E3dp_SceneDrawModelsForPick(E3d_Scene, LDisplayMode, LCamera->WorldToRightViewerMatrix, LPickTargets, LDrawCamera);
    }
    else
    {
     glMatrixMode(GL_MODELVIEW);
     MglLoadMatrix(LCamera->WorldToViewerMatrix);

     if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD) E3dp_SceneDrawModelsForPick(E3d_Scene, LDisplayMode, LCamera->WorldToViewerMatrix, LPickTargets, LDrawCamera);
    }
   break;

   case E3dVM_TOP:
   case E3dVM_FRONT:
   case E3dVM_RIGHT:
    switch(LViewMode)
    {
     case E3dVM_TOP:
      LCamera=&(L3DWindow->TopCamera);
     break;

     case E3dVM_FRONT:
      LCamera=&(L3DWindow->FrontCamera);
     break;

     case E3dVM_RIGHT:
      LCamera=&(L3DWindow->RightCamera);
     break;
    }

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

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

    MglGetFloatV(GL_MODELVIEW_MATRIX, LWorldToViewerMatrix);

    if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD) E3dp_SceneDrawModelsForPick(E3d_Scene, LDisplayMode, LWorldToViewerMatrix, LPickTargets, LDrawCamera);
   break;

   case E3dVM_SCHEMATICS:
    LCamera=&(L3DWindow->SchematicsCamera);

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

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

    MglGetFloatV(GL_MODELVIEW_MATRIX, LWorldToViewerMatrix);

    if(L3DWindow->DisplayFunctionFlags&E3dDFUNCT_STANDARD) E3dp_SceneSchemPutModelsForPick(E3d_Scene, LDisplayMode, LWorldToViewerMatrix);
   break;
  }
 }
}


/*======================================================*/
/* Intersect triangles in scene with a ray		*/
/*======================================================*/
int E3d_RayIntersectScene(E3dModel** LRootModels, unsigned int LNumOfRootModels, E3dItem* LItem, E3dRay* LRay, EBool LSelectedOnly)
{
 unsigned int	LRootModelC;
 int		LHits=0;
 E3dModel*	LModel;
 E3dCoordinate	LTMin=0.0;
 EBool		LTestModel, LTestGeometries;

 for(LRootModelC=0;LRootModelC<LNumOfRootModels;LRootModelC++)
 {
  LModel=LRootModels[LRootModelC];

  {
   E3dGeometry**	LGeometries;
   E3dGeometry*		LGeometry;
   E3dMatrix		LLocalToWorldMatrix;
   unsigned int		LGmCnt, LGmNum, LGC, LPolyCnt;


   for(;LModel;LModel=LModel->Next)
   {
    LTestGeometries=FALSE;

    if(LSelectedOnly)
    {
     switch(LModel->Selection)
     {
      default:	LTestModel=FALSE;break;

      case E3dSEL_NODE:
      case E3dSEL_BRANCH:
      case E3dSEL_BRANCH_ROOT:
       LTestModel=LModel->Visible;
      break;

      case E3dSEL_GEOMETRY:
       LTestGeometries=TRUE;
       LTestModel=LModel->Visible;
      break;
     }
    }
    else LTestModel=LModel->Visible;

    if(LTestModel)
    {
     E3d_MatrixCopy(LModel->LocalToWorldMatrix, LLocalToWorldMatrix);

     LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
     for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
     {
      if((LGeometry=LGeometries[LGmCnt])!=NULL)
      {
       if(LGeometry->Visible)
       {
	switch(LGeometry->GeoType)
	{
	 caseE3dMESH():
	  {
	   E3dMesh*		LMesh=(E3dMesh*)LGeometry;
	   E3dPolyGroup*	LPolyGroup;
	   E3dPolyGroup**	LPolyGroups;
	   E3dPolygon*		LPolygon;
	   E3dTriangleStrip*	LTriangleStrip;
	   E3dITriangle*	LITriangle;
	   E3dVertexNode*	LVertexNode;
	   E3dVertexNode**	LVertexNodes;
	   E3dVertex*		LVertices;
	   E3dVertex*		LVieverVertices;
	   E3dVertex*		LVertex0;
	   E3dVertex*		LVertex1;
	   E3dVertex*		LVertex2;
	   E3dVertex*		LNewVertex;
	   E3dCoordinate	mX, mY, mZ;
	   unsigned int		LC, LN,
				LNumOfVertices;
	   EBool		LDoPolyGroup;


	   if((LNumOfVertices=LMesh->NumOfVertices)>0)
	   {
	    E3dCoordinate	LVert0[3], LEdge1[3], LEdge2[3];
	    E3dCoordinate	LT, LU, LV;

	    if((LVieverVertices=E3d_VerticesAllocate(LNumOfVertices, FALSE))!=NULL)
	    {
	     LVertices=LMesh->Vertices;

// Transform Mesh Vertices into the world coordinate system
//
	     LVertex0=LMesh->Vertices;
	     LNewVertex=LVieverVertices;
	     for(LC=0;LC<LNumOfVertices;LC++,LVertex0++, LNewVertex++)
	     {
	       mX=LVertex0->X;mY=LVertex0->Y;mZ=LVertex0->Z;
	       E3dM_MatrixTransform3x4(LLocalToWorldMatrix, LNewVertex->X, LNewVertex->Y, LNewVertex->Z);
	     }

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

	       if(LTestGeometries)
	       {
		switch(LGeometry->Selection)
		{
		 case E3dGSEL_GEOMETRY:	LDoPolyGroup=LPolyGroup->Visible;break;

		 case E3dGSEL_POLYGROUP:
		  LDoPolyGroup=(LPolyGroup->Visible) & (LPolyGroup->Selected);
		 break;

		 default:	LDoPolyGroup=FALSE;break;
		}
	       }
	       else LDoPolyGroup=LPolyGroup->Visible;

	       if(((LPolygon=LPolyGroup->Polygons)!=NULL)&&LDoPolyGroup)
	       {
		LPolyCnt=LPolyGroup->NumOfPolygons;
		do
		{
		  if(LPolygon->ITriangles==NULL) E3d_PolygonCreateTriangles(LMesh->Vertices, LPolygon, TRUE);

		  if((LITriangle=LPolygon->ITriangles)!=NULL)
		  {
		    LN=LPolygon->NumOfTriangles;
// Go through the triangles of this Polygon
//
		    for(LC=0;LC<LN;LC++, LITriangle++)
		    {
		      LVertexNodes=LITriangle->VertexNodes;
		      LVertexNode=LVertexNodes[0];LVertex0=LVieverVertices+LVertexNode->VertexID;
		      LVertexNode=LVertexNodes[1];LVertex1=LVieverVertices+LVertexNode->VertexID;
		      LVertexNode=LVertexNodes[2];LVertex2=LVieverVertices+LVertexNode->VertexID;

		      LVert0[E3dX]=LVertex0->X;
		      LVert0[E3dY]=LVertex0->Y;
		      LVert0[E3dZ]=LVertex0->Z;
		      LEdge1[E3dX]=LVertex1->X - LVertex0->X;
		      LEdge1[E3dY]=LVertex1->Y - LVertex0->Y;
		      LEdge1[E3dZ]=LVertex1->Z - LVertex0->Z;
		      LEdge2[E3dX]=LVertex2->X - LVertex0->X;
		      LEdge2[E3dY]=LVertex2->Y - LVertex0->Y;
		      LEdge2[E3dZ]=LVertex2->Z - LVertex0->Z;

		      if(E3d_RayIntersectWithTriangleBackCullE(LRay, LVert0, LEdge1, LEdge2, &LU, &LV))
		      {
		       LT=LRay->LastT;
		       if(LHits==0)
		       {
		        E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;

			LHits=1;		// We only care about the closest hit
			LTMin=LT;
			LPolyItem->Type=E3dITEM_POLYGON;
			LPolyItem->Model=LModel;
			LPolyItem->Geometry=LGeometry;
			LPolyItem->PolyGroup=LPolyGroup;
			LPolyItem->PolygonIndex=LPolyGroup->NumOfPolygons-LPolyCnt;
			LPolyItem->Vertex=NULL;
			LPolyItem->PointIndex=-1;
		       }
		       else
		       {
			if(LT<LTMin)
			{
		         E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;

			 LPolyItem->Type=E3dITEM_POLYGON;
			 LPolyItem->Model=LModel;
			 LPolyItem->Geometry=LGeometry;
			 LPolyItem->PolyGroup=LPolyGroup;
			 LPolyItem->PolygonIndex=LPolyGroup->NumOfPolygons-LPolyCnt;
			 LPolyItem->Vertex=NULL;
			 LPolyItem->PointIndex=-1;
			 LTMin=LT;
			}
		       }
		      }
		    }
		  }

		 LPolygon++;
		} while(--LPolyCnt);
	       }
	       else
	       {
		if((LTriangleStrip=LPolyGroup->TriangleStrips)!=NULL) E3dp_PrintMessage(0, 2000, "Model: %s: PolyGroup has only Triangle strips", LModel->Name);
	       }
	     }
	     EFree(LVieverVertices);
	    }
	   }
	  }
	 break;

	 case E3dGEO_FACE:
	  {
	   unsigned int		LC, LN;
	   E3dFace*		LFace=(E3dFace*)LGeometry;
	   unsigned int		LNumOfVertices;
	   E3dCoordinate	mX, mY, mZ;
	   E3dTriangle*		LTriangle;
	   E3dVertexNode*	LVertexNode;
	   E3dVertex*		LVertices;
	   E3dVertex*		LVieverVertices;
	   E3dVertex*		LVertex0;
	   E3dVertex*		LVertex1;
	   E3dVertex*		LVertex2;
	   E3dVertex*		LNewVertex;

	   if((LNumOfVertices=LFace->NumOfVertices)>0)
	   {
	    E3dCoordinate	LVert0[3], LEdge1[3], LEdge2[3];
	    E3dCoordinate	LT, LU, LV;

	    if((LVieverVertices=E3d_VerticesAllocate(LNumOfVertices, FALSE))!=NULL)
	    {
	     LVertices=LFace->Vertices;

// Transform Face Vertices into the world coordinate system
//
	     LVertex0=LFace->Vertices;
	     LNewVertex=LVieverVertices;
	     for(LC=0;LC<LNumOfVertices;LC++,LVertex0++, LNewVertex++)
	     {
	       mX=LVertex0->X;mY=LVertex0->Y;mZ=LVertex0->Z;
	       E3dM_MatrixTransform3x4(LLocalToWorldMatrix, LNewVertex->X, LNewVertex->Y, LNewVertex->Z);
	     }


	     if((LTriangle=LFace->Triangles)!=NULL)
	     {
	      LN=LFace->NumOfTriangles;
// Go through the triangles of this Face
//
	      for(LC=0;LC<LN;LC++, LTriangle++)
	      {
	       LVertexNode=LTriangle->VertexNodes;
	       LVertex0=LVieverVertices+LVertexNode->VertexID;LVertexNode++;
	       LVertex1=LVieverVertices+LVertexNode->VertexID;LVertexNode++;
	       LVertex2=LVieverVertices+LVertexNode->VertexID;

	       LVert0[E3dX]=LVertex0->X;
	       LVert0[E3dY]=LVertex0->Y;
	       LVert0[E3dZ]=LVertex0->Z;
	       LEdge1[E3dX]=LVertex1->X - LVertex0->X;
	       LEdge1[E3dY]=LVertex1->Y - LVertex0->Y;
	       LEdge1[E3dZ]=LVertex1->Z - LVertex0->Z;
	       LEdge2[E3dX]=LVertex2->X - LVertex0->X;
	       LEdge2[E3dY]=LVertex2->Y - LVertex0->Y;
	       LEdge2[E3dZ]=LVertex2->Z - LVertex0->Z;

	       if(E3d_RayIntersectWithTriangleBackCullE(LRay, LVert0, LEdge1, LEdge2, &LU, &LV))
	       {
	        LT=LRay->LastT;
		if(LHits==0)
		{
		 E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;

		 LHits=1;		// We only care about the closest hit
		 LTMin=LT;
		 LSplineItem->Type=E3dITEM_FACE;
		 LSplineItem->Model=LModel;
		 LSplineItem->Geometry=LGeometry;
		 LSplineItem->Spline=NULL;
		 LSplineItem->ContourIndex=-1;
		 LSplineItem->CV=NULL;
		 LSplineItem->PointIndex=-1;
		}
		else
		{
		 if(LT<LTMin)
		 {
		  E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;

		  LSplineItem->Type=E3dITEM_FACE;
		  LSplineItem->Model=LModel;
		  LSplineItem->Geometry=LGeometry;
		  LSplineItem->Spline=NULL;
		  LSplineItem->ContourIndex=-1;
		  LSplineItem->CV=NULL;
		  LSplineItem->PointIndex=-1;
		  LTMin=LT;
		 }
		}
	       }
	      }
	     }

	    }
	    EFree(LVieverVertices);
	   }
	  }
	 break;
	}
       }
      }
     }
    }
   }
  }
 }
 return(LHits);
}



/*==============================================================================*/
/* Cast a primary ray in the given 3DWindow based on the pointer position	*/
/*==============================================================================*/
E3dCamera* E3d_WindowCastPrimaryRay(E3dWindow* L3DWindow, int LMX, int LMY, E3dRay* LRay)
{
 E3dCamera*	LCamera;
 E3dCoordinate	LFOVMul, LRayX, LRayY, LRayZ,
		LWindowFXSize, LWindowFYSize,
		LAspectRatio,
		LRF;

 switch(L3DWindow->ViewMode)
 {
  case E3dVM_PERSPECTIVE:
   LCamera=&(L3DWindow->PerspectiveCamera);

// FieldOfView/2 * (PI/180)
//
   LFOVMul=tan((LCamera->FieldOfView)*0.5*0.017453293);

   LWindowFXSize=(E3dCoordinate)(L3DWindow->XSize);
   LWindowFYSize=(E3dCoordinate)(L3DWindow->YSize);
   LAspectRatio=LWindowFXSize/LWindowFYSize;

   LRayX=(E3dCoordinate)LMX-LWindowFXSize*0.5;
   LRayY=(E3dCoordinate)LMY-LWindowFYSize*0.5;
   LRayX=LRayX*LFOVMul/LWindowFXSize*2.0*LAspectRatio;
   LRayY=LRayY*LFOVMul/LWindowFYSize*-2.0;
   LRayZ=-1.0;

// Normalize ray and transform it into world-space
//
   LRF=sqrt(LRayX*LRayX+LRayY*LRayY+LRayZ*LRayZ);
   LRF=1.0/LRF;

   {
    E3dMatrix		LMatrix;
    E3dCoordinate	mX=LRayX*LRF, mY=LRayY*LRF, mZ=LRayZ*LRF;

    E3d_MatrixInvert3x3(LCamera->WorldToViewerMatrix, LMatrix);

    E3dM_MatrixTransform3x3(LMatrix, LRay->DirX, LRay->DirY, LRay->DirZ);

    LRay->X=LCamera->Position.X;
    LRay->Y=LCamera->Position.Y;
    LRay->Z=LCamera->Position.Z;

//printf("Ray %f,%f,%f -> %f,%f,%f\n", LRay->X, LRay->Y, LRay->Z, LRay->DirX, LRay->DirY, LRay->DirZ);fflush(stdout);
   }

  return(LCamera);

  case E3dVM_TOP:
   LCamera=&(L3DWindow->TopCamera);

   LWindowFXSize=(E3dCoordinate)(L3DWindow->XSize);
   LWindowFYSize=(E3dCoordinate)(L3DWindow->YSize);
   LAspectRatio=LWindowFXSize/LWindowFYSize;

   LRayX=(E3dCoordinate)LMX-LWindowFXSize*0.5;
   LRayZ=(E3dCoordinate)LMY-LWindowFYSize*0.5;

   LRF=1.0/(LWindowFXSize*0.5);

   LRay->X=(LRayX*LCamera->XZoom)*LRF+LCamera->Position.X;
   LRay->Y=LCamera->Position.Y;
   LRay->Z=(LRayZ*LCamera->XZoom)*LRF+LCamera->Position.Z;

   LRay->DirX=0.0;
   LRay->DirY=-1.0;
   LRay->DirZ=0.0;

  return(LCamera);

  case E3dVM_FRONT:
   LCamera=&(L3DWindow->FrontCamera);

   LWindowFXSize=(E3dCoordinate)(L3DWindow->XSize);
   LWindowFYSize=(E3dCoordinate)(L3DWindow->YSize);
   LAspectRatio=LWindowFXSize/LWindowFYSize;

   LRayX=(E3dCoordinate)LMX-LWindowFXSize*0.5;
   LRayY=(E3dCoordinate)LMY-LWindowFYSize*0.5;

   LRF=1.0/(LWindowFXSize*0.5);

   LRay->X=(LRayX*LCamera->XZoom)*LRF+LCamera->Position.X;
   LRay->Y=-(LRayY*LCamera->XZoom)*LRF+LCamera->Position.Y;
   LRay->Z=LCamera->Position.Z;

   LRay->DirX=0.0;
   LRay->DirY=0.0;
   LRay->DirZ=-1.0;
  return(LCamera);

  case E3dVM_RIGHT:
   LCamera=&(L3DWindow->RightCamera);

   LWindowFXSize=(E3dCoordinate)(L3DWindow->XSize);
   LWindowFYSize=(E3dCoordinate)(L3DWindow->YSize);
   LAspectRatio=LWindowFXSize/LWindowFYSize;

   LRayY=(E3dCoordinate)LMY-LWindowFYSize*0.5;
   LRayZ=(E3dCoordinate)LMX-LWindowFXSize*0.5;

   LRF=1.0/(LWindowFXSize*0.5);

   LRay->X=LCamera->Position.X;
   LRay->Y=-(LRayY*LCamera->XZoom)*LRF+LCamera->Position.Y;
   LRay->Z=-(LRayZ*LCamera->XZoom)*LRF+LCamera->Position.Z;

   LRay->DirX=-1.0;
   LRay->DirY=0.0;
   LRay->DirZ=0.0;
  return(LCamera);

  case E3dVM_SCHEMATICS:
   LCamera=&(L3DWindow->SchematicsCamera);

   LWindowFXSize=(E3dCoordinate)(L3DWindow->XSize);
   LWindowFYSize=(E3dCoordinate)(L3DWindow->YSize);
   LAspectRatio=LWindowFXSize/LWindowFYSize;

   LRayX=(E3dCoordinate)LMX-LWindowFXSize*0.5;
   LRayY=(E3dCoordinate)LMY-LWindowFYSize*0.5;

   LRF=1.0/(LWindowFXSize*0.5);

   LRay->X=(LRayX*LCamera->XZoom)*LRF+LCamera->Position.X;
   LRay->Y=-(LRayY*LCamera->XZoom)*LRF+LCamera->Position.Y;
   LRay->Z=LCamera->Position.Z;

   LRay->DirX=0.0;
   LRay->DirY=0.0;
   LRay->DirZ=-1.0;

  return(LCamera);
 }
 return(NULL);
}


/*==============================================================*/
/* Evaluate Mesh pick (Mesh, PolyGroup, Polygon, Vertex etc.)	*/
/*==============================================================*/
static int _EvalMeshPick(E3dMesh* LMesh, E3dItem** LItemPtr, int LPickTargets, GLuint* LGLPickBufferPtr, int* LPIndexPtr)
{
 E3dItem*	LItem=*LItemPtr;
 E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;
 int		LIndex, LPIndex=*LPIndexPtr, LHitCnt=0;


 switch(LGLPickBufferPtr[LPIndex++])
 {
  case E3dITEM_NONE:
   if(LPickTargets&(E3dITEM_MODEL|E3dITEM_GEOMETRY|E3dITEM_MESH))
   {
    if(LPickTargets&E3dITEM_MODEL) LPolyItem->Type=E3dITEM_MODEL;
    else
    {
     if(LPickTargets&E3dITEM_MESH) LPolyItem->Type=E3dITEM_MESH;
     else LPolyItem->Type=E3dITEM_GEOMETRY;
    }

    LPolyItem->Type=E3dITEM_MODEL;

    LPolyItem->PolyGroup=NULL;
    LPolyItem->Vertex=NULL;
    LPolyItem->PointIndex=-1;
    LPolyItem->PolygonIndex=-1;
    LHitCnt++;
    LItem++;
   }
  break;

  case E3dITEM_VERTEX:
   LIndex=LGLPickBufferPtr[LPIndex++];
   if(LPickTargets&E3dITEM_VERTEX)
   {
    LPolyItem->Type=E3dITEM_VERTEX;
    LPolyItem->PolyGroup=NULL;
    LPolyItem->Vertex=LMesh->Vertices+LIndex;
    LPolyItem->PointIndex=LIndex;
    LPolyItem->PolygonIndex=-1;
    LHitCnt++;
    LItem++;
   }
  break;

  case E3dITEM_POLYGON:
   LPolyItem->PolyGroup=LMesh->PolyGroups[LGLPickBufferPtr[LPIndex++]];
   LIndex=LGLPickBufferPtr[LPIndex++];

   if(LPickTargets&E3dITEM_POLYGON)
   {
    LPolyItem->Type=E3dITEM_POLYGON;
    LPolyItem->PolygonIndex=LIndex;
    LPolyItem->Vertex=NULL;
    LPolyItem->PointIndex=-1;
    LHitCnt++;
    LItem++;
   }
  break;

  case E3dITEM_POLYGROUP:
   LPolyItem->PolyGroup=LMesh->PolyGroups[LGLPickBufferPtr[LPIndex++]];
   if(LPickTargets&E3dITEM_POLYGROUP)
   {
    LPolyItem->Type=E3dITEM_POLYGROUP;
    LPolyItem->PolygonIndex=-1;
    LHitCnt++;
    LItem++;
   }
   else if(LPickTargets&(E3dITEM_MODEL|E3dITEM_GEOMETRY))
   {
    LPolyItem->Type=E3dITEM_MODEL;
    LPolyItem->PolygonIndex=-1;
    LHitCnt++;
    LItem++;
   }
   else LPIndex++;
  break;

  case E3dITEM_EDGE:
   {
    E3dEdgeItem*	LEdgeItem=(E3dEdgeItem*)LItem;

    LEdgeItem->PolyGroup=LMesh->PolyGroups[LGLPickBufferPtr[LPIndex++]];
    if(LPickTargets&E3dITEM_EDGE)
    {
     LEdgeItem->Type=E3dITEM_EDGE;
     LEdgeItem->EdgeIndex=LGLPickBufferPtr[LPIndex++];
     if(LMesh->NumOfPolyGroups==1) LEdgeItem->Edge=LMesh->Edges+LEdgeItem->EdgeIndex;
     else LEdgeItem->Edge=LEdgeItem->PolyGroup->Edges+LEdgeItem->EdgeIndex;
     LHitCnt++;
     LItem++;
    }
   }
  break;
 }

 *LItemPtr=LItem;
 *LPIndexPtr=LPIndex;
 return(LHitCnt);
}


/*==============================================*/
/* Evaluate Spline pick				*/
/*==============================================*/
static int _EvalSplinePick(E3dGeometry* LGeometry, E3dItem** LItemPtr, int LPickTargets, GLuint* LGLPickBufferPtr, int* LPIndexPtr)
{
 E3dItem*	LItem=*LItemPtr;
 E3dSplineItem*	LSplineItem;
 E3dSpline*	LSpline=(E3dSpline*)LGeometry;
 int		LIndex, LPIndex=*LPIndexPtr, LHitCnt=0;

 LSplineItem=(E3dSplineItem*)LItem;

 if(LPickTargets&(E3dITEM_MODEL|E3dITEM_GEOMETRY|E3dITEM_SPLINE|E3dITEM_SPLINE_SEGMENT))
 {
  if(LPickTargets&E3dITEM_MODEL) LSplineItem->Type=E3dITEM_MODEL;
  else
  {
   if(LPickTargets&E3dITEM_SPLINE) LSplineItem->Type=E3dITEM_SPLINE;
   else LSplineItem->Type=E3dITEM_GEOMETRY;
  }
  LSplineItem->CV=NULL;

  LIndex=LGLPickBufferPtr[LPIndex++];		// Spline segment index

  switch(LSpline->SplineType)
  {
   case E3dSPL_BSPLINE:
   case E3dSPL_CARDINAL:
    if(LIndex>=(LSpline->NumOfCVs-1)) LIndex=0;
    else LIndex++;
   break;
  }

  LSplineItem->Spline=LSpline;

  LSplineItem->PointIndex=LIndex;

  LHitCnt++;
  LItem++;
 }
 else if(LPickTargets&E3dITEM_SPLINE_KEY)
//	 switch(LGLPickBufferPtr[LPIndex++])
 {
//	  case E3dITEM_SPLINE_KEY:

   LIndex=LGLPickBufferPtr[LPIndex++];

   switch(LSpline->SplineType)
   {
    case E3dSPL_BEZIER:
     switch(LGLPickBufferPtr[LPIndex++])		// Sub-key (Previous, Position or Next)
     {
      case 0:	LSplineItem->Type=E3dITEM_BEZIER_PREVIOUS;break;
      case 2:	LSplineItem->Type=E3dITEM_BEZIER_NEXT;break;
      default:	LSplineItem->Type=E3dITEM_SPLINE_KEY;break;
     }

     LSplineItem->Spline=LSpline;
     LSplineItem->CV=((E3dBezierCV*)(LSpline->CVs))+LIndex;
     LSplineItem->PointIndex=LIndex;

     LSplineItem->ContourIndex=0;
     LHitCnt++;
     LItem++;
    break;

    default:
     LSplineItem->Type=E3dITEM_SPLINE_KEY;

     LSplineItem->Spline=LSpline;
     LSplineItem->CV=((E3dSplineCV*)(LSpline->CVs))+LIndex;
     LSplineItem->PointIndex=LIndex;
     LSplineItem->ContourIndex=0;
     LHitCnt++;
     LItem++;
    break;
   }
//	  break;
 }

 *LItemPtr=LItem;
 *LPIndexPtr=LPIndex;
 return(LHitCnt);
}


/*==============================================*/
/* Evaluate Spline pick				*/
/*==============================================*/
static int _EvalFacePick(E3dGeometry* LGeometry, E3dItem** LItemPtr, int LPickTargets, GLuint* LGLPickBufferPtr, int* LPIndexPtr)
{
 E3dItem*	LItem=*LItemPtr;
 E3dSplineItem*	LSplineItem;
 E3dFace*	LFace=(E3dFace*)LGeometry;
 E3dSpline*	LSpline;
 int		LIndex, LPIndex=*LPIndexPtr, LHitCnt=0, LContourIndex;

 LSplineItem=(E3dSplineItem*)LItem;

 LContourIndex=LGLPickBufferPtr[LPIndex++];		// Face Contour index

 if(LContourIndex==0) LSpline=LFace->Exterior;
 else LSpline=LFace->Holes[LContourIndex-1];

 if(LPickTargets&(E3dITEM_MODEL|E3dITEM_GEOMETRY|E3dITEM_FACE|E3dITEM_FACE_CONTOUR|E3dITEM_SPLINE|E3dITEM_SPLINE_SEGMENT))
 {
  if(LPickTargets&E3dITEM_MODEL) LSplineItem->Type=E3dITEM_MODEL;
  else
  {
   if(LPickTargets&E3dITEM_FACE) LSplineItem->Type=E3dITEM_FACE;
   else if(LPickTargets&E3dITEM_FACE_CONTOUR) LSplineItem->Type=E3dITEM_SPLINE;
   else if(LPickTargets&E3dITEM_SPLINE) LSplineItem->Type=E3dITEM_SPLINE;
   else LSplineItem->Type=E3dITEM_GEOMETRY;
  }
  LSplineItem->CV=NULL;

  LIndex=LGLPickBufferPtr[LPIndex++];		// Spline segment index

  switch(LSpline->SplineType)
  {
   case E3dSPL_BSPLINE:
   case E3dSPL_CARDINAL:
    if(LIndex>=(LSpline->NumOfCVs-1)) LIndex=0;
    else LIndex++;
   break;
  }

  LSplineItem->PointIndex=LIndex;


  LSplineItem->Spline=LSpline;
  LSplineItem->ContourIndex=LContourIndex;
  LHitCnt++;
  LItem++;
 }
 else if(LPickTargets&E3dITEM_SPLINE_KEY)
//	 switch(LGLPickBufferPtr[LPIndex++])
 {
//	  case E3dITEM_SPLINE_KEY:

   LIndex=LGLPickBufferPtr[LPIndex++];

   switch(LSpline->SplineType)
   {
    case E3dSPL_BEZIER:
     switch(LGLPickBufferPtr[LPIndex++])		// Sub-key (Previous, Position or Next)
     {
      case 0:	LSplineItem->Type=E3dITEM_BEZIER_PREVIOUS;break;
      case 2:	LSplineItem->Type=E3dITEM_BEZIER_NEXT;break;
      default:	LSplineItem->Type=E3dITEM_SPLINE_KEY;break;
     }

     LSplineItem->Spline=LSpline;
     LSplineItem->ContourIndex=LContourIndex;

     LSplineItem->CV=((E3dBezierCV*)(LSpline->CVs))+LIndex;
     LSplineItem->PointIndex=LIndex;

     LHitCnt++;
     LItem++;
    break;

    default:
     LSplineItem->Type=E3dITEM_SPLINE_KEY;
     LSplineItem->Spline=LSpline;
     LSplineItem->ContourIndex=LContourIndex;

     LSplineItem->CV=((E3dSplineCV*)(LSpline->CVs))+LIndex;
     LSplineItem->PointIndex=LIndex;

     LHitCnt++;
     LItem++;
    break;
   }
//	  break;
 }

 *LItemPtr=LItem;
 *LPIndexPtr=LPIndex;
 return(LHitCnt);
}


/*==============================================================================*/
/* Comparison function for sorting pick hits by distance			*/
/*										*/
/* We use the connectivity information to resolve some ambiguities:		*/
/*  - If an Edge is on a Polygon, the Edge is always considered closer		*/
/*  - If an Vertex is on a Polygon, the Vertex is always considered closer	*/
/*  - If an Vertex is on a Edge, the Vertex is always considered closer		*/
/*==============================================================================*/
static int _ItemZCompare(const void* LItem0, const void* LItem1)
{
 E3dGeoItem*	LGeoItem0=(E3dGeoItem*)LItem0;
 E3dGeoItem*	LGeoItem1=(E3dGeoItem*)LItem1;


 if(LGeoItem0->Geometry==LGeoItem1->Geometry)
 {
  E3dGeoItem*	LGeoItemOther=NULL;
  int		LRetVal=0;

  {
   E3dPolyItem*	LPolygonItem=NULL;

// If an Edge or a Vertex is on a Polygon, it's always considered closer
//
   if(LGeoItem0->Type==E3dITEM_POLYGON) { LPolygonItem=(E3dPolyItem*)LGeoItem0;LGeoItemOther=LGeoItem1;LRetVal=1; }
   else if(LGeoItem1->Type==E3dITEM_POLYGON) { LPolygonItem=(E3dPolyItem*)LGeoItem1;LGeoItemOther=LGeoItem0;LRetVal=-1; }

// Is one of the LGeoItems a Polygon?
//
   if(LPolygonItem)
   {
    E3dPolygon*		LPolygon=LPolygonItem->PolyGroup->Polygons+LPolygonItem->PolygonIndex;

    switch(LGeoItemOther->Type)
    {
     case E3dITEM_VERTEX:
      {
       E3dPolyItem*	LPolyItem=(E3dPolyItem*)LGeoItemOther;

       if(E3d_PolygonHasVertexIndex(LPolygon, LPolyItem->PointIndex)) return(LRetVal);
      }
     break;

     case E3dITEM_EDGE:
      {
       E3dEdgeItem*	LEdgeItem=(E3dEdgeItem*)LGeoItemOther;

       if(E3d_PolygonHasEdge(LPolygon, LEdgeItem->Edge)) return(LRetVal);
      }
     break;
    }
   }
  }

  {
   E3dEdgeItem*	LEdgeItem=NULL;

// If an Vertex is on an Edge, the Vertex always considered closer
//
  if(LGeoItem0->Type==E3dITEM_EDGE) { LEdgeItem=(E3dEdgeItem*)LGeoItem0;LGeoItemOther=LGeoItem1;LRetVal=1; }
  else if(LGeoItem1->Type==E3dITEM_EDGE) { LEdgeItem=(E3dEdgeItem*)LGeoItem1;LGeoItemOther=LGeoItem0;LRetVal=-1; }

// Is one of the LGeoItems an Edge?
//
   if(LEdgeItem)
   {
    switch(LGeoItemOther->Type)
    {
     case E3dITEM_VERTEX:
      {
       E3dPolyItem*	LPolyItem=(E3dPolyItem*)LGeoItemOther;
       E3dEdge*		LEdge=LEdgeItem->Edge;
       int		LVertexIndex=LPolyItem->PointIndex;

       if((LVertexIndex==LEdge->Start)||(LVertexIndex==LEdge->End)) return(LRetVal);
      }
     break;
    }
   }
  }

 }

 if(LGeoItem0->Z1<LGeoItem1->Z1) return(-1);
 else return(1);
}


/*==============================================*/
/* Pick items in the given rectangular region	*/
/*==============================================*/
int E3d_WindowRectanglePickItems(E3dWindow* L3DWindow, int LPickTargets, E3dItem* LItems, int LX, int LY, int LXSize, int LYSize)
{
 E3dItem*	LItem=LItems;
 int		LPickHits;
 int		LHitCnt=0,
		LPCnt, LPIndex, LHierarchyNum, LModelNum, LGeometryNum;
 EBool		LDoGLPick=FALSE;


//Printf("E3d_WindowRectanglePickItems()  %08x\n", LPickTargets);fflush(stdout);

 switch(L3DWindow->ViewMode)
 {
  case E3dVM_PERSPECTIVE:
  case E3dVM_TOP:
  case E3dVM_FRONT:
  case E3dVM_RIGHT:
   if(L3DWindow->DisplayMode&E3dDF_SOLID)
   {
// In SOLID mode, we must pick Polygons, so when getting a closest hit (e.g. for an Edge),
// a Polyon closer than that will properly obscure it
//
    LPickTargets|=E3dITEM_POLYGON;

    if(LPickTargets&(E3dITEM_POLYGROUP|E3dITEM_POLYGON|E3dITEM_EDGE|E3dITEM_VERTEX|E3dITEM_SPLINE_KEY|E3dITEM_SPLINE|E3dITEM_SPLINE_SEGMENT|E3dITEM_FACE_CONTOUR)) LDoGLPick=TRUE;
   }
   else LDoGLPick=TRUE;
  break;

  default:	LDoGLPick=TRUE;break;
 }

 if(LDoGLPick)
 {
  GLuint*	LGLPickBuffer=(GLuint*)EMalloc(sizeof(GLuint)*E3dp_PickBufferSize);


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

   glSelectBuffer(E3dp_PickBufferSize, LGLPickBuffer);
   glRenderMode(GL_SELECT);
   glInitNames();glPushName(-1);

   E3d_WindowRedrawForPick(L3DWindow, LX, LY, LXSize, LYSize, LPickTargets);

   LPickHits=glRenderMode(GL_RENDER);

//printf("LPickHits %d\n", LPickHits);fflush(stdout);

// Analyze pick-list
//
   if(LPickHits)
   {
    GLuint*	LGLPickBufferPtr=LGLPickBuffer;
    GLuint*	LGLPickBufferNext;
    int		LNumOfFieldsLeft;

    for(LPCnt=0, LPIndex=0;LPCnt<LPickHits;LPCnt++)
    {
     LPIndex=0;
     LNumOfFieldsLeft=LGLPickBufferPtr[LPIndex++];
     LGLPickBufferNext=LGLPickBufferPtr+2+LNumOfFieldsLeft+1;	// Z1 and Z2

//printf("Hit %d  LNumOfFieldsLeft %d      %d %d %d\n", LPCnt, LNumOfFieldsLeft, LGLPickBufferNext[0], LGLPickBufferNext[1], LGLPickBufferNext[2]);fflush(stdout);

// There should be 2, 3, 4 or 5 items :
//  Hierarchy-number
//   Model index in the E3dp_Models array
//   Geometry index,
//    PolyGroup index or Vertex/SplineCV index
//     Polygon index
//     Edge index
//
     if(LNumOfFieldsLeft>=2)
     {
      E3dGeoItem*	LGeoItem=(E3dGeoItem*)LItem;
      E3dModel*		LModel;
      E3dGeometry*	LGeometry;
      unsigned int	LZ1, LZ2;


      LZ1=LGLPickBufferPtr[LPIndex++];
      LZ2=LGLPickBufferPtr[LPIndex++];
      LGeoItem->Z1=LZ1;


//printf("Hit %d  Z1 %d  Z2 %d ", LPCnt, LZ1, LZ2);fflush(stdout);


      LNumOfFieldsLeft+=2;

      LHierarchyNum=LGLPickBufferPtr[LPIndex++];
      LModelNum=LGLPickBufferPtr[LPIndex++];

      if(LNumOfFieldsLeft>=LPIndex) LGeometryNum=LGLPickBufferPtr[LPIndex++];
      else LGeometryNum=-1;

      LGeoItem->Model=LModel=E3dp_Models[LModelNum];

      if(LGeometryNum>=0)
      {
       LGeoItem->Geometry=LGeometry=LModel->Geometries[LGeometryNum];

       switch(LGeometry->GeoType)
       {
	caseE3dMESH():
	 LHitCnt+=_EvalMeshPick((E3dMesh*)LGeometry, &LItem, LPickTargets, LGLPickBufferPtr, &LPIndex);
	break;

	case E3dGEO_SPLINE:
	 LHitCnt+=_EvalSplinePick(LGeometry, &LItem, LPickTargets, LGLPickBufferPtr, &LPIndex);
	break;

	case E3dGEO_FACE:
	 LHitCnt+=_EvalFacePick(LGeometry, &LItem, LPickTargets, LGLPickBufferPtr, &LPIndex);
	break;
       }
      }
      else
      {
// This is primarily for picking on the Schematics view...
//
       if(LPickTargets&(E3dITEM_MODEL|E3dITEM_GEOMETRY|E3dITEM_MESH|E3dITEM_SPLINE|E3dITEM_FACE))
       {
// If the target was GEOMETRY, but no Geometry was picked, take the 0th Geometry of the picked Model
//
	if(LPickTargets&E3dITEM_GEOMETRY)
	{
	 if(LModel->NumOfGeometries>0)
	 {
	  LGeoItem->Geometry=LModel->Geometries[0];
	  LGeoItem->Type=E3dITEM_GEOMETRY;
	  LHitCnt++;
	  LItem++;
	 }
	}
	else
	{
	 if(LPickTargets&E3dITEM_MESH)
	 {
	  LGeometry=E3d_ModelGetGeometryByType(LModel, E3dGEO_MESH);
	  if(LGeometry) LGeoItem->Type=E3dITEM_MESH;
	 }
	 else if(LPickTargets&E3dITEM_SPLINE)
	 {
	  LGeometry=E3d_ModelGetGeometryByType(LModel, E3dGEO_SPLINE);
	  if(LGeometry)
	  {
	   E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;

	   LSplineItem->Type=E3dITEM_SPLINE;
	   LSplineItem->Spline=(E3dSpline*)LGeometry;
	  }
	 }
	 else if(LPickTargets&E3dITEM_FACE)
	 {
	  LGeometry=E3d_ModelGetGeometryByType(LModel, E3dGEO_FACE);
	  if(LGeometry) LGeoItem->Type=E3dITEM_FACE;
	 }
	 else LGeometry=NULL;

	 if(LGeometry) LGeoItem->Geometry=LGeometry;
	 else LGeoItem->Type=E3dITEM_MODEL;
	 LHitCnt++;
	 LItem++;
	}
       }
      }

      LGLPickBufferPtr=LGLPickBufferNext;

     }
     else break;
    }

// Sort hits by distance. Hit-0 will be the closest
//
    qsort(LItems, LHitCnt, sizeof(E3dItem), _ItemZCompare);

/*
    {
     E3dGeoItem*	LGeoItem;

     LItem=LItems;

     for(LPCnt=0;LPCnt<LHitCnt;LPCnt++, LItem++)
     {
      LGeoItem=(E3dGeoItem*)LItem;

      switch(LItem->Type)
      {
       case E3dITEM_POLYGON:	printf("Hit%d (POLYGON) Z1 %d   ", LPCnt, LGeoItem->Z1);break;
       case E3dITEM_VERTEX:	printf("Hit%d (VERTEX)  Z1 %d   ", LPCnt, LGeoItem->Z1);break;
       case E3dITEM_EDGE:	printf("Hit%d (EDGE)    Z1 %d   ", LPCnt, LGeoItem->Z1);break;
       default:			printf("Hit%d           Z1 %d   ", LPCnt, LGeoItem->Z1);break;
      }

     }
Printf("\n");fflush(stdout);
    }
*/


   }
   EFree(LGLPickBuffer);
  }
 }

// In SOLID mode, cast a ray to where the mouse pointer is
//
/*
 if(((L3DWindow->DisplayMode&E3dDF_SOLID)!=0)||((LPickTargets&E3dITEM_POLYGON)!=0))
 {
  E3dScene*	LScene=E3d_Scene;
  E3dCamera*	LCamera=NULL;
  E3dRay	LRay;
  
// Test ray against surfaces first, regardless of the PickTarget,
// so we can tell if an Item is behind a surface
//
  if((LCamera=E3d_WindowCastPrimaryRay(L3DWindow, LX, LY, &LRay))!=NULL)
  {
   if(E3dp_Prefs.PolyPaintOnSelectionOnly&&((LPickTargets&E3dITEM_POLYGON)!=0))
   {
    LHitCnt+=E3d_RayIntersectScene(LScene->RootModels, LScene->NumOfRootModels, LItem, &LRay, LPickTargets==E3dITEM_POLYGON);
   }
   else LHitCnt+=E3d_RayIntersectScene(LScene->RootModels, LScene->NumOfRootModels, LItem, &LRay, FALSE);
  }

@*
  switch(LPickTargets&(E3dITEM_POLYGON|E3dITEM_SPLINE))
  {
   case E3dITEM_POLYGON:
    return(LHitCnt);
   break;
  }
@/
 }
*/

 return(LHitCnt);
}
