/*======================================================================================================*/
/* 3DPanel												*/
/*													*/
/* - 3DWindow creation											*/
/* - Callbacks and event handlers									*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-16 23:44:33									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <stdio.h>
#include <assert.h>
#include <float.h>
#include <math.h>


#include <Xe/ToggleButton.h>

#include <EMalloc.h>
#include <EInput.h>

#include <EGUI/Dialog.h>
#include <EGUI/Menu.h>

#include <E3D/E3D.h>

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

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

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

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

#include <E3D/StatusPanel.h>


//------------------------------------------------
// Externs from the 'host' (main) program
//------------------------------------------------
extern Pixel		MenuBackgroundColor, MenuForegroundColor, MenuTroughColor, MenuTopShadowColor, MenuBottomShadowColor,
			PanelBackgroundColor, PanelForegroundColor, PanelTopShadowColor, PanelBottomShadowColor,
			InTextBackgroundColor, InTextForegroundColor,
			HighlightColor;


extern E3dDrawContext		E3dp_NormalPrefs, E3dp_LeftEyePrefs, E3dp_RightEyePrefs;

//------------------------------------------------
// From Panel/3DWindow.c
//------------------------------------------------
extern void	_Draw3DWindow(E3dWindow* L3DWindow);

//------------------------------------------------
// From E3DPMenu.c
//------------------------------------------------
extern Widget		E3dMenu_SelectCascadeBW;


//------------------------------------------------
// From editing.c
//------------------------------------------------
extern void E3dp_DeleteNode(E3dItem* LPickedItems, int LHits);
extern void E3dp_DeleteBranch(E3dItem* LPickedItems, int LHits);
extern void E3dp_DeleteTree(E3dItem* LPickedItems, int LHits);


//------------------------------------------------
// From Panel/3DPanel.c
//------------------------------------------------
extern XVisualInfo*	E3dp_GLVisualId;
extern Widget		E3dp_TopWidget;
extern Pixmap		E3d_SettingsPixmap, E3d_SettingsPixmapMask;


//------------------------------------------------
// From Panel/Display.c
//------------------------------------------------
extern void		E3d_WindowDraw2DRectangle(E3dWindow* L3DWindow, float LX1, float LY1, float LX2, float LY2, EcRGBAiColor LRGBAiColor);


//------------------------------------------------
// From Panel/Pick.c
//------------------------------------------------
extern int			E3dp_PickBufferSize;
extern E3dPickCallbackProc	E3d_PickCallback;
extern EBool			E3d_PickActivateOnMotion;
extern E3dEdge*			_LastActiveEdge;
extern E3dVertex*		_LastActiveVertex;
extern E3dPolyGroup*		_LastActivePolyGroup;


//------------------------------------------------
// From Panel/TransformPanel.c
//------------------------------------------------
extern int		E3dp_SelectionMode;
extern int		E3dp_TransformTarget;
extern int		E3dp_NumOfTransformedModels;
extern E3dModel**	E3dp_TransformedModels;
extern int		E3dp_NumOfTransformedModelRoots;
extern E3dModel**	E3dp_TransformedModelRoots;
extern Widget		_TTB_TagW;

#define E3dBEZIER_MINDISTANCESQ	0.00001	// If the length-square of a control-line on a Bezier spline is less than this, move the Previous or Next points...

extern int		E3dp_OldPointFlags;
extern int		E3dp_BezCVActiveSubCV;
extern E3dGeometry*	E3dp_ActiveGeometry;
extern E3dSpline*	E3dp_ActiveSpline;

extern E3dVertex* 	E3dp_ActiveVertex;
extern E3dBezierCV*	E3dp_ActiveBezierCV;
extern E3dSplineCV*	E3dp_ActiveSplineCV;

extern E3dMatrix	E3dp_WorldToLocalMatrix, E3dp_LocalToWorldMatrix;

extern EBool		E3dp_Transforming;

extern void		E3dp_MovePoint(E3dWindow* L3DWindow, int LPtrX, int LPtrY, unsigned int LState);
extern void		E3dp_InitInteractiveTransform(E3dWindow* L3DWindow);



unsigned int	E3dp_PolygonSelectFlag=0;


int		E3dp_RectAreaStartX=0, E3dp_RectAreaStartY=0;


//========================================
// Capture the image from a 3DWindow
//========================================
EpImage* E3d_WindowGetImage(E3dWindow* L3DWindow, EpImage* LDImage)
{
 EpImage*	LImage;


 if(LDImage) LImage=LDImage;
 else LImage=Ep_ImageAllocate(L3DWindow->XSize, L3DWindow->YSize, EpPixelRGBA8);

 if(LImage)
 {
  GLint	swapbytes, lsbfirst, rowlength;
  GLint	skiprows, skippixels, alignment;


  if((LImage->XSize!=L3DWindow->XSize)||(LImage->YSize!=L3DWindow->YSize)) Ep_ImageResize(LImage, L3DWindow->XSize, L3DWindow->YSize, LImage->PixelFormats, TRUE);


  glXMakeCurrent(E3dp_Display, L3DWindow->XWindow, L3DWindow->Panel->EGLXContext);

  E3dp_CurrentGLXWindow=L3DWindow->XWindow;

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


// 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);

  glPixelStorei(GL_UNPACK_ROW_LENGTH, LImage->XSize);
//     glRasterPos2i(0, L3DWindow->YSize-1);
//     glPixelZoom(1.0, -1.0);

  glReadPixels(0, 0, L3DWindow->XSize, L3DWindow->YSize, GL_RGBA, GL_UNSIGNED_BYTE, LImage->RGBA8Image);

// 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);


  Ep_ImageFlipV(LImage, LImage);
 }
 return(LImage);
}


//================================================
// Get 3DWindow's active Camera
//================================================
E3dCamera* E3dp_WindowGetActiveCamera(E3dWindow* L3DWindow)
{
 switch(L3DWindow->ViewMode)
 {
  case E3dVM_PERSPECTIVE:
   return(&(L3DWindow->PerspectiveCamera));

  case E3dVM_TOP:
   return(&(L3DWindow->TopCamera));

  case E3dVM_FRONT:
   return(&(L3DWindow->FrontCamera));

  case E3dVM_RIGHT:
   return(&(L3DWindow->RightCamera));

  case E3dVM_SCHEMATICS:
   return(&(L3DWindow->SchematicsCamera));

  default:	return(NULL);
 }
}


//========================================
// Print info on object under pointer
//========================================
void E3d_PrintInfoOnObjectUnderPointer(E3dWindow* L3DWindow, int LX, int LY, int LPushMessage)
{
 int		LPickHits;
 E3dItem*	LPickedItems;

 switch(E3d_SelectionTarget)
 {
  case E3dSELTG_MODEL:
   if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

   if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_MODEL, LPickedItems, LX, LY))>0)
   {
    E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;

    if(LGeoItem->Model->Name==NULL) E3dp_SetCurrentMessage(LPushMessage, 0, "Select Model [ <no name> ]   L: Node   M: Branch   R: Tree");
    else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Model [ %s ]   L: Node   M: Branch   R: Tree", LGeoItem->Model->Name);
   }
   else
   {
    E3dp_SetCurrentMessage(LPushMessage, 0, "Select Model  L: Node   M: Branch   R: Tree");
   }

   EFree(LPickedItems);
  break;

  case E3dSELTG_GEOMETRY:
   if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

   if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_GEOMETRY, LPickedItems, LX, LY))>0)
   {
    E3dGeoItem*		LGeoItem=(E3dGeoItem*)LPickedItems;
    E3dGeometry*	LGeometry=LGeoItem->Geometry;

    if(LGeometry)
    {
     switch(LGeometry->GeoType)
     {
      case E3dGEO_MESH:
       if(LGeometry->Name==NULL) E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry [ <no name> ]    L: Mesh   M: PolyGroup   R: Polygon");
       else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry  [ %s ]    L: Mesh   M: PolyGroup   R: Polygon", LGeometry->Name);
      break;

      case E3dGEO_SKINMESH:
       if(LGeometry->Name==NULL) E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry [ <no name> ]    L: SkinMesh   M: PolyGroup   R: Polygon");
       else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry  [ %s ]    L: SkinMesh   M: PolyGroup   R: Polygon", LGeometry->Name);
      break;

      case E3dGEO_SPLINE:
       if(LGeometry->Name==NULL) E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry [ <no name> ]   L: Spline  M: Spline segment");
       else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry  [ %s ]    L: Spline  M: Spline segment", LGeometry->Name);
      break;

      case E3dGEO_FACE:
       if(LGeometry->Name==NULL) E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry [ <no name> ]   L: Face  M: Contour (Spline)  R: Spline segment");
       else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry  [ %s ]    L: Face  M: Contour (Spline)  R: Spline segment", LGeometry->Name);
      break;
     }
    }
   } else E3dp_SetCurrentMessage(LPushMessage, 0, "Select Geometry  [ - ]");

   EFree(LPickedItems);
  break;
 }
}



//================================================================================
// Try to pick a single point moving and if successful, initialize moving it.
//================================================================================
static void _CheckMovePoint(E3dWindow* L3DWindow, int LPtrX, int LPtrY, int LButton)
{
 E3dItem*	LPickedItems;
 E3dCoordinate	LX, LY, LZ;
 int		LPickHits;

 switch(LButton)
 {
  case Button1:
   if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

   if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_POINT, LPickedItems, LPtrX, LPtrY))>0)
   {
    E3dAnyItem*		LItem=(E3dAnyItem*)LPickedItems;
    E3dCoordinate	mX, mY, mZ;

    switch(LItem->Type)
    {
     case E3dITEM_VERTEX:
      {
       E3dPolyItem*	LPolyItem=(E3dPolyItem*)LPickedItems;
       E3dVertex*	LVertex=LPolyItem->Vertex;

       E3d_MatrixInvert3x4(LPolyItem->Model->LocalToWorldMatrix, E3dp_WorldToLocalMatrix);
       E3d_MatrixCopy(LPolyItem->Model->LocalToWorldMatrix, E3dp_LocalToWorldMatrix);

//printf("Vertex: %f,%f,%f\n", LVertex->X, LVertex->Y, LVertex->Z);fflush(stdout);
       E3dp_OldPointFlags=LVertex->Flags;
       LVertex->Flags|=E3dVTXF_ACTIVE;
       E3dp_ActiveVertex=LVertex;
       E3dp_ActiveGeometry=LPolyItem->Geometry;

// Transform the position into world space
//
       mX=LVertex->X;
       mY=LVertex->Y;
       mZ=LVertex->Z;
       E3dM_MatrixTransform3x4(LPolyItem->Model->LocalToWorldMatrix, LX, LY, LZ);
       E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);

       E3dp_PushPointerMode(E3dpPTR_MOVE_POINT);E3dp_PushMessage(0, 0, "Moving vertex");
       E3dp_PrintMessage(0, 0, "Vertex:  #%d  %.4f, %.4f, %.4f (local: %.4f, %.4f, %.4f)", LPolyItem->PointIndex, LX, LY, LZ, LVertex->X, LVertex->Y, LVertex->Z);
       E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
      }
     break;

     case E3dITEM_SPLINE_KEY:
     case E3dITEM_BEZIER_PREVIOUS:
     case E3dITEM_BEZIER_NEXT:
      {
       E3dSplineItem*	LSplineItem=(E3dSplineItem*)LPickedItems;
       E3dSpline*	LSpline;

       E3d_MatrixInvert3x4(LSplineItem->Model->LocalToWorldMatrix, E3dp_WorldToLocalMatrix);
       E3d_MatrixCopy(LSplineItem->Model->LocalToWorldMatrix, E3dp_LocalToWorldMatrix);

       switch(LSplineItem->Geometry->GeoType)
       {
	case E3dGEO_SPLINE:
	 LSpline=(E3dSpline*)(LSplineItem->Geometry);
	break;

	case E3dGEO_FACE:
	 LSpline=LSplineItem->Spline;
	break;

	default:
	return;
       }

       E3dp_ActiveSpline=LSpline;

       E3dp_PushPointerMode(E3dpPTR_MOVE_POINT);E3dp_PushMessage(0, 0, "Moving Spline CV");

       switch(LSpline->SplineType)
       {
	case E3dSPL_BEZIER:
	 {
	  E3dBezierCV*	LBezierCV=(E3dBezierCV*)(LSplineItem->CV);
	  E3dCoordinate	LDX=LBezierCV->Position.X-LBezierCV->Previous.X,
			LDY=LBezierCV->Position.Y-LBezierCV->Previous.Y,
			LDZ=LBezierCV->Position.Z-LBezierCV->Previous.Z;


	  E3dp_ActiveBezierCV=LBezierCV;

	  switch(LItem->Type)
	  {
	   case E3dITEM_BEZIER_PREVIOUS:
	    mX=LBezierCV->Previous.X;
	    mY=LBezierCV->Previous.Y;
	    mZ=LBezierCV->Previous.Z;
	    E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);

	    E3dp_BezCVActiveSubCV=E3dBEZ_PREVIOUS;
	    E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	    E3dp_PrintMessage(0, 0, "Point #%d (previous): %.4f, %.4f, %.4f (local: %.4f, %.4f, %.4f)", LSplineItem->PointIndex, LX, LY, LZ, LBezierCV->Previous.X, LBezierCV->Previous.Y, LBezierCV->Previous.Z);
	   break;

	   case E3dITEM_SPLINE_KEY:
	    E3dp_BezCVActiveSubCV=E3dBEZ_POSITION;
	    if((LDX*LDX+LDY*LDY+LDZ*LDZ)<E3dBEZIER_MINDISTANCESQ)
	    {
	     E3dp_BezCVActiveSubCV=E3dBEZ_PREVIOUS;

	     mX=LBezierCV->Previous.X;
	     mY=LBezierCV->Previous.Y;
	     mZ=LBezierCV->Previous.Z;
	     E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);
	     E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	    }
	    else
	    {
	     LDX=LBezierCV->Position.X-LBezierCV->Next.X,
	     LDY=LBezierCV->Position.Y-LBezierCV->Next.Y,
	     LDZ=LBezierCV->Position.Z-LBezierCV->Next.Z;

	     if((LDX*LDX+LDY*LDY+LDZ*LDZ)<E3dBEZIER_MINDISTANCESQ)
	     {
	      E3dp_BezCVActiveSubCV=E3dBEZ_NEXT;

	      mX=LBezierCV->Next.X;
	      mY=LBezierCV->Next.Y;
	      mZ=LBezierCV->Next.Z;
	      E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);
	      E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	     }
	    }

	    if(E3dp_BezCVActiveSubCV==E3dBEZ_POSITION)
	    {
	     mX=LBezierCV->Position.X;
	     mY=LBezierCV->Position.Y;
	     mZ=LBezierCV->Position.Z;
	     E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);
	     E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	     E3dp_PrintMessage(0, 0, "Point #%d: %.4f, %.4f, %.4f (local: %.4f, %.4f, %.4f)", LSplineItem->PointIndex, LX, LY, LZ, LBezierCV->Position.X, LBezierCV->Position.Y, LBezierCV->Position.Z);
	    }
	   break;

	   case E3dITEM_BEZIER_NEXT:
	    E3dp_BezCVActiveSubCV=E3dBEZ_NEXT;

	    mX=LBezierCV->Next.X;
	    mY=LBezierCV->Next.Y;
	    mZ=LBezierCV->Next.Z;
	    E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);
	    E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	    E3dp_PrintMessage(0, 0, "Point #%d (next): %.4f, %.4f, %.4f (local: %.4f, %.4f, %.4f)", LSplineItem->PointIndex, LX, LY, LZ, LBezierCV->Next.X, LBezierCV->Next.Y, LBezierCV->Next.Z);
	   break;
	  }

	  E3dp_ActiveGeometry=LSplineItem->Geometry;
	  E3dp_OldPointFlags=LBezierCV->Flags;
	  LBezierCV->Flags|=E3dKEYF_ACTIVE;
	 }
	break;

	default:
	 {
	  E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSplineItem->CV);

	  mX=LSplineCV->Position.X;
	  mY=LSplineCV->Position.Y;
	  mZ=LSplineCV->Position.Z;
	  E3dM_MatrixTransform3x4(LSplineItem->Model->LocalToWorldMatrix, LX, LY, LZ);

	  E3dp_ActiveSplineCV=LSplineCV;

	  E3dp_InitPointMove(L3DWindow, LPtrX, LPtrY, LX, LY, LZ, TRUE);
	  E3dp_PrintMessage(0, 0, "Point #%d: %.4f, %.4f, %.4f (local: %.4f, %.4f, %.4f)", LSplineItem->PointIndex, LX, LY, LZ, LSplineCV->Position.X, LSplineCV->Position.Y, LSplineCV->Position.Z);

	  E3dp_ActiveGeometry=LSplineItem->Geometry;
	  E3dp_OldPointFlags=LSplineCV->Flags;
	  LSplineCV->Flags|=E3dKEYF_ACTIVE;
	 }
	break;
       }

       E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
      }
     break;
    }
   }
   EFree(LPickedItems);
  break;
 }
}


//========================================
// Process pick results
//========================================
static void _ProcessPick(E3dWindow* L3DWindow, int LPtrX, int LPtrY, int LButton, unsigned int LModifiers, EBool LMotion)
{
 E3dCamera*		LCamera;
 E3dItem*		LPickedItems;
 E3dPickCallbackStruct	LCBS;
 E3dRay			LRay;
 int			LPickHits;
 EBool			LRedraw=FALSE;


 LCBS.Reason=E3dCR_PICK;
 LCBS.Modifiers=LModifiers;
 LCBS.Button=LButton;
 LCBS.PtrX=LPtrX;LCBS.PtrY=LPtrY;
 LCBS.MWindow=L3DWindow;

 switch(E3d_PickMode)
 {
  case E3dITEM_WORLD_POSITION:
   if((LCamera=E3d_WindowCastPrimaryRay(L3DWindow, LPtrX, LPtrY, &LRay))!=NULL)
   {
    E3dWindowSettings*	L3DWindowSettings=&(L3DWindow->Settings);

    LCBS.Camera=LCamera;

    LCBS.SnapToX=L3DWindowSettings->GridSnapToX;
    LCBS.SnapToY=L3DWindowSettings->GridSnapToY;
    LCBS.SnapToZ=L3DWindowSettings->GridSnapToZ;
    LCBS.SnapToXV=L3DWindowSettings->GridSize.X;
    LCBS.SnapToYV=L3DWindowSettings->GridSize.Y;
    LCBS.SnapToZV=L3DWindowSettings->GridSize.Z;

    LCBS.WorldPosition.X=LRay.X;
    LCBS.WorldPosition.Y=LRay.Y;
    LCBS.WorldPosition.Z=0.0;

    LCBS.RayOrigin.X=LRay.X;
    LCBS.RayOrigin.Y=LRay.Y;
    LCBS.RayOrigin.Z=LRay.Z;
    LCBS.RayDirection.X=LRay.DirX;
    LCBS.RayDirection.Y=LRay.DirY;
    LCBS.RayDirection.Z=LRay.DirZ;

    E3d_PickCallback(&LCBS, 0, NULL);
   }
  break;

  case E3dITEM_MODEL:
  case E3dITEM_VERTEX:
  case E3dITEM_EDGE:
  case E3dITEM_VERTEX|E3dITEM_EDGE:
  case E3dITEM_SPLINE_KEY:
  case E3dITEM_POINT:
  case E3dITEM_GEOMETRY:
  case E3dITEM_SPLINE:
  case E3dITEM_MESH:
  case E3dITEM_POLYGROUP:
   if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

   if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3d_PickMode, LPickedItems, LPtrX, LPtrY))>0)
   {
    if(LMotion)
    {
// Edge and Vertex highlighting
//
     E3dPolyGroup*	LPolyGroup=NULL;
     E3dEdge*		LEdge=NULL;
     E3dVertex*		LVertex=NULL;

     if(LPickedItems)
     {
      if(LPickedItems->Type==E3dITEM_EDGE)
      {
       E3dEdgeItem*	LEdgeItem=(E3dEdgeItem*)LPickedItems;

       LPolyGroup=LEdgeItem->PolyGroup;
       LEdge=LEdgeItem->Edge;
      }

      if(LPickedItems->Type==E3dITEM_VERTEX)
      {
       E3dPolyItem*	LPolyItem=(E3dPolyItem*)LPickedItems;

       LVertex=LPolyItem->Vertex;
      }
     }


     if(LEdge)
     {
      if(_LastActiveEdge!=LEdge)
      {
       LEdge->Flags|=E3dEDGE_ACTIVE;

       if(_LastActiveEdge) _LastActiveEdge->Flags&=(E3dALL-E3dEDGE_ACTIVE);
       _LastActiveEdge=LEdge;

       if(_LastActivePolyGroup!=LPolyGroup)
       {
	LPolyGroup->Flags|=E3dACTIVE_EDGES;

	if(_LastActivePolyGroup) _LastActivePolyGroup->Flags&=(E3dALL-E3dACTIVE_EDGES);
	_LastActivePolyGroup=LPolyGroup;
       }

       LRedraw=TRUE;
      }
     }
     else
     {
      if(_LastActiveEdge)
      {
       _LastActiveEdge->Flags&=(E3dALL-E3dEDGE_ACTIVE);
       if(_LastActivePolyGroup) _LastActivePolyGroup->Flags&=(E3dALL-E3dACTIVE_EDGES);

       _LastActiveEdge=NULL;
       _LastActivePolyGroup=NULL;

       LRedraw=TRUE;
      }
     }


     if(LVertex)
     {
      if(_LastActiveVertex!=LVertex)
      {
       LVertex->Flags|=E3dVTXF_ACTIVE;

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

       LRedraw=TRUE;
      }
     }
     else
     {
      if(_LastActiveVertex)
      {
       _LastActiveVertex->Flags&=(E3dALL-E3dVTXF_ACTIVE);

       _LastActiveVertex=NULL;
       _LastActivePolyGroup=NULL;

       LRedraw=TRUE;
      }
     }

     if(LRedraw) E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);

     E3d_PickCallback(&LCBS, LPickHits, LPickedItems);
    }
    else E3d_PickCallback(&LCBS, LPickHits, LPickedItems);
   }
   else
   {
    if(_LastActiveEdge)
    {
     _LastActiveEdge->Flags&=(E3dALL-E3dEDGE_ACTIVE);
     if(_LastActivePolyGroup) _LastActivePolyGroup->Flags&=(E3dALL-E3dACTIVE_EDGES);

     _LastActiveEdge=NULL;
     _LastActivePolyGroup=NULL;

     LRedraw=TRUE;
    }

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

     _LastActiveVertex=NULL;
     _LastActivePolyGroup=NULL;

     LRedraw=TRUE;
    }

    if(LRedraw) E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);

    E3d_PickCallback(&LCBS, 0, NULL);
   }

   EFree(LPickedItems);
  break;

  case E3dITEM_MATERIAL:
   if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

   if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_MODEL|E3dITEM_POLYGROUP|E3dITEM_FACE, LPickedItems, LPtrX, LPtrY))>0)
   {
    E3dMaterial*	LMaterial=NULL;
    E3dAnyItem*		LItem=(E3dAnyItem*)LPickedItems;
    E3dGeometry*	LGeometry;


    switch(LItem->Type)
    {
     case E3dITEM_MODEL:
     case E3dITEM_MESH:
     case E3dITEM_FACE:
     case E3dITEM_POLYGROUP:
     case E3dITEM_POLYGON:
      {
       E3dGeoItem*	LGeoItem=(E3dGeoItem*)LItem;

       LGeometry=LGeoItem->Geometry;
       switch(LGeometry->GeoType)
       {
	caseE3dMESH():
	 {
	  E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;

	  if(LPolyItem->PolyGroup)
	  {
	   LMaterial=LPolyItem->PolyGroup->DrawingMaterial;
	   if(LMaterial==(&E3d_DefaultMaterial)) { E3dp_PrintMessage(0, 2500, "This object has no Material");return; }
	  }
	 }
	break;

	case E3dGEO_FACE:
	 {
	  LMaterial=((E3dFace*)(LGeoItem->Geometry))->DrawingMaterial;
	  if(LMaterial==(&E3d_DefaultMaterial)) { E3dp_PrintMessage(0, 2500, "This object has no Material");return; }
	 }
	break;
       }
      }
     break;
    }


    if(LMaterial)
    {
     E3dItem		LItems[1];
     E3dMaterialItem*	LItem=(E3dMaterialItem*)LItems;

     LItem->Type=E3dITEM_MATERIAL;
     LItem->Material=LMaterial;

     E3d_PickCallback(&LCBS, 1, LItems);
    }
   }
   else E3d_PickCallback(&LCBS, 0, NULL);		// So that the pick requestor can tell the user that nothing was picked...

   EFree(LPickedItems);
  break;
 }
}


//================================================
// Process button-press events
//================================================
void E3dp_WindowButtonPress(E3dWindow* L3DWindow, int LPtrX, int LPtrY, int LButton, unsigned int LModifiers)
{
 E3dPanel*		L3DPanel=L3DWindow->Panel;
 E3dCamera*		LCamera;
 E3dItem*		LPickedItems;
 int			LPickHits;


 E3dp_PrevPtrX=LPtrX;E3dp_PrevPtrY=LPtrY;
 E3dp_BasePtrX=LPtrX;E3dp_BasePtrY=LPtrY;

 switch(E3dp_PointerMode)
 {
  default:
   E3dp_InitInteractiveTransform(L3DWindow);
  break;

  case E3dpPTR_DELETING:
   {
    if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

    if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_MODEL, LPickedItems, LPtrX, LPtrY))>0)
    {
     switch(LButton)
     {
/*
      case Button1:	E3dp_DeleteNode(LPickedItems, LPickHits);break;
      case Button2:	printf("Delete branch\n");fflush(stdout);E3dp_DeleteBranch(LPickedItems, LPickHits);break;
      case Button3:	E3dp_DeleteTree(LPickedItems, LPickHits);break;
*/
     }
     E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
    }

    EFree(LPickedItems);
   }
  break;

  case E3dpPTR_PICK:
   if(E3d_PickCallback)
   {
    _ProcessPick(L3DWindow, LPtrX, LPtrY, LButton, LModifiers, FALSE);
   }
  break;

  case E3dpPTR_SELECTING:
   switch(E3d_SelectionTarget)
   {
    case E3dSELTG_MODEL:
     if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

     if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_MODEL, LPickedItems, LPtrX, LPtrY))>0)
     {
      E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;
      E3dModel*		LModel=LGeoItem->Model;

//      E3dp_ResetWorkModes();
      switch(LButton)
      {
       case Button1:
	{
	 E3dSceneModelsCallbackStruct	LCBS;
	 E3dModel*			LModels[1];


	 E3d_ModelNodeToggleSelection(LModel, (E3dp_SelectionMode==E3dpSELM_SINGLE));

	 if(LModel->Selection==E3dSEL_NONE) LCBS.Reason=E3dCR_NODE_UNSELECTED;
	 else LCBS.Reason=E3dCR_NODE_SELECTED;

	 LModels[0]=LModel;
	 LCBS.Models=LModels;
	 LCBS.NumOfModels=1;
//Printf("E3d_Scene->NumOfSelectCallbacks %d\n", E3d_Scene->NumOfSelectCallbacks);fflush(stdout);
	 E3d_CallCallbacks(E3d_Scene, E3d_Scene->SelectCallbacks, E3d_Scene->NumOfSelectCallbacks, &LCBS);
	}

	E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
       break;

       case Button2:
	E3d_ModelBranchToggleSelection(LModel, (E3dp_SelectionMode==E3dpSELM_SINGLE));
	E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
       break;

       case Button3:
	E3d_ModelTreeToggleSelection(LModel, (E3dp_SelectionMode==E3dpSELM_SINGLE));
	E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
       break;
      }
     }
     else
     {
      if(E3dp_SelectionMode==E3dpSELM_SINGLE) E3d_SceneUnSelectAll(E3d_Scene, E3dSF_MODELS|E3dSF_GEOMETRIES);		// 'Nothing' picked, unselect everything
     }

     EFree(LPickedItems);
    break;

    case E3dSELTG_GEOMETRY:
     if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

     switch(LButton)
     {
      case Button1:	// Geometry (Mesh, Spline, Face)
       if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_GEOMETRY, LPickedItems, LPtrX, LPtrY))>0)
       {
	E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;

	if(LGeoItem->Model->Type==E3dMDL_NORMAL)
	{
//	 E3dp_ResetWorkModes();
	 E3d_GeometryToggleSelection(LGeoItem->Geometry, (EBool)(E3dp_SelectionMode==E3dpSELM_SINGLE));
	 E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
	}
       }
       else
       {
	if(E3dp_SelectionMode==E3dpSELM_SINGLE) E3d_SceneUnSelectAll(E3d_Scene, E3dSF_GEOMETRIES);	// 'Nothing' picked, unselect everything
       }
      break;

      case Button2:	// PolyGroup/Spline segment/Face contour
       if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_POLYGROUP|E3dITEM_SPLINE_SEGMENT|E3dITEM_FACE_CONTOUR, LPickedItems, LPtrX, LPtrY))>0)
       {
	E3dGeoItem*	LGeoItem=(E3dGeoItem*)LPickedItems;

//	E3dp_ResetWorkModes();
	switch(LGeoItem->Geometry->GeoType)
	{
	 caseE3dMESH():
	  if(LGeoItem->Model->Type==E3dMDL_NORMAL)
	  {
	   E3dPolyItem*	LPolyItem=(E3dPolyItem*)LGeoItem;

	   E3d_SelectMeshPolyGroup((E3dMesh*)(LPolyItem->Geometry), LPolyItem->PolyGroup, (E3dp_SelectionMode==E3dpSELM_SINGLE));
	   E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
	  }
	 break;

	 case E3dGEO_SPLINE:
	  {
	   E3dSplineItem*			LSplineItem=(E3dSplineItem*)LGeoItem;
	   E3dSceneGeometryCallbackStruct	LCBS;
	   EBool				LState;


	   E3dp_PrintMessage(0, 5000, "Spline segment: %d", LSplineItem->PointIndex);
	   LState=E3d_SplineSegmentToggleSelection((E3dSpline*)(LSplineItem->Geometry), LSplineItem->PointIndex, (EBool)(E3dp_SelectionMode==E3dpSELM_SINGLE));


	   if(LState) LCBS.Reason=E3dCR_SPLINE_SEGMENT_SELECTED;
	   else LCBS.Reason=E3dCR_SPLINE_SEGMENT_SELECTED;

	   LCBS.Geometry=LSplineItem->Geometry;
	   LCBS.SubGeoIndex=LSplineItem->PointIndex;
	   E3d_CallCallbacks(E3d_Scene, E3d_Scene->SelectCallbacks, E3d_Scene->NumOfSelectCallbacks, &LCBS);
	  }
	 break;

	 case E3dGEO_FACE:
	  {
	   E3dSplineItem*	LSplineItem=(E3dSplineItem*)LGeoItem;
	   E3dFace*		LFace=(E3dFace*)(LGeoItem->Geometry);
	   E3dSpline*		LSpline=LSplineItem->Spline;

	   if(LSpline) E3d_FaceContourToggleSelection(LFace, LSplineItem->ContourIndex, LSpline, (EBool)(E3dp_SelectionMode==E3dpSELM_SINGLE));
	  }
	 break;
	}
       }
       else
       {
	if(E3dp_SelectionMode==E3dpSELM_SINGLE) E3d_SceneUnSelectAll(E3d_Scene, E3dSF_POLYGROUPS|E3dSF_SPLINE_SEGMENTS);	// 'Nothing' picked, unselect everything
       }
      break;

      case Button3:	// Polygon/Spline segment of a Face Contour
       if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_POLYGON|E3dITEM_SPLINE_SEGMENT|E3dITEM_FACE_CONTOUR, LPickedItems, LPtrX, LPtrY))>0)
       {
	E3dAnyItem*	LItem=(E3dAnyItem*)LPickedItems;

	switch(((E3dAnyItem*)LItem)->Type)
	{
	 case E3dITEM_POLYGON:
	  {
	   E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;

	   switch(LPolyItem->Geometry->GeoType)
	   {
	    caseE3dMESH():
	     if(LPolyItem->PolyGroup)
	     {
	      E3dPolyGroup*	LPolyGroup=LPolyItem->PolyGroup;
	      E3dPolygon*	LPolygon=LPolyGroup->Polygons+LPolyItem->PolygonIndex;

	      LPolygon=E3d_SelectTogglePolygonSelection(LPolyItem->Model, LPolyItem->Geometry, LPolyGroup, LPolygon, (E3dp_SelectionMode==E3dpSELM_SINGLE));

	      if(LPolygon)
	      {
	       E3dp_PushPointerMode(E3dpPTR_POLY_PAINT);
	       E3dp_PolygonSelectFlag=LPolygon->Flags&E3dPolyFlagSELECTED;

	       if(LPolygon->Flags&E3dPolyFlagSELECTED) E3dp_PrintMessage(0, 2500, "Selected Polygon %d", LPolyItem->PolygonIndex);
	       else  E3dp_PrintMessage(0, 2500, "Unselected Polygon %d", LPolyItem->PolygonIndex);
	      }
	     } else { printf("Geometry is not a Mesh\n");fflush(stdout); }
	    break;
	   }
	  }
	 break;

	 case E3dITEM_SPLINE_SEGMENT:
	  {
	   E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;

	   switch(LSplineItem->Geometry->GeoType)
	   {
	    case E3dGEO_FACE:
	     {
	      E3dSceneGeometryCallbackStruct	LCBS;
//	     E3dFace*				LFace=(E3dFace*)(LSplineItem->Geometry);
	      E3dSpline*			LSpline=LSplineItem->Spline;
	      EBool				LState;


	      E3dp_PrintMessage(0, 5000, "Spline segment: %d", LSplineItem->PointIndex);
	      LState=E3d_SplineSegmentToggleSelection(LSpline, LSplineItem->PointIndex, (EBool)(E3dp_SelectionMode==E3dpSELM_SINGLE));

	      if(LState) LCBS.Reason=E3dCR_SPLINE_SEGMENT_SELECTED;
	      else LCBS.Reason=E3dCR_SPLINE_SEGMENT_SELECTED;
	      LCBS.Geometry=LSplineItem->Geometry;
	      LCBS.SubGeoIndex=LSplineItem->PointIndex;
	      E3d_CallCallbacks(E3d_Scene, E3d_Scene->SelectCallbacks, E3d_Scene->NumOfSelectCallbacks, &LCBS);
	     }
	    break;
	   }
	  }
	 break;
	}
       }
       else
       {
// 'Nothing' picked, unselect all Polygons on selected objects
//
	if(E3dp_SelectionMode==E3dpSELM_SINGLE) E3d_SceneUnSelectAll(E3d_Scene, E3dSF_POLYGONS);	// 'Nothing' picked, unselect everything
       }
      break;
     }
     EFree(LPickedItems);
    break;
   }
   E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   E3dp_TransformPanelUpdate(E3dDO_ALL, E3dDO_ALL, E3dDO_ALL, TRUE);
  break;

  case E3dpPTR_ORBIT_STANDBY:
   if((L3DWindow->PerspectiveCamera.ViewDistance!=0.0)&&(L3DWindow->ViewMode==E3dVM_PERSPECTIVE))
   {
    E3dp_PushPointerMode(E3dpPTR_ORBIT);E3dp_PushMessage(0, 0, "Orbiting camera");
    E3d_CameraCalcOrbit(&(L3DWindow->PerspectiveCamera));
   }
  break;

  case E3dpPTR_DOLLY_STANDBY:
   if(L3DWindow->ViewMode==E3dVM_PERSPECTIVE)
   {
    E3dp_PushPointerMode(E3dpPTR_DOLLY);E3dp_PushMessage(0, 0, "Dollying camera");
   }
  break;

  case E3dpPTR_BANK_STANDBY:
   E3dp_PushPointerMode(E3dpPTR_BANK);E3dp_PushMessage(0, 0, "Banking camera");
  break;

  case E3dpPTR_TRACK_ZOOM_STANDBY:
   switch(LButton)
   {
    case Button1:	E3dp_PushPointerMode(E3dpPTR_TRACK);E3dp_PushMessage(0, 0, "Scrolling");break;

    case Button2:
     E3dp_PushPointerMode(E3dpPTR_ZOOM);

     LCamera=E3dp_WindowGetActiveCamera(L3DWindow);

     E3dp_PushMessage(0, 0, "Zooming camera. Field of view: %5.2f degrees", LCamera->FieldOfView);
//     E3dp_PushMessage(0, 0, "Zooming");
    break;
   }
  break;

  case E3dpPTR_MOVE_POINT_STANDBY:
   _CheckMovePoint(L3DWindow, LPtrX, LPtrY, LButton);
  break;

  case E3dpPTR_TAG_POINTS_STANDBY:
   E3dp_RectAreaStartX=LPtrX;E3dp_RectAreaStartY=LPtrY;
   switch(LButton)
   {
    case Button1:
     E3dp_PushPointerMode(E3dpPTR_TAG_POINTS);
     E3dp_PushMessage(0, 0, "Select region to tag Points");
    break;

    case Button2:
     E3dp_PushPointerMode(E3dpPTR_TAG_POINTS);
     E3dp_PushMessage(0, 0, "Select region to un-tag Points");
    break;

    case Button3:
     E3dp_PushPointerMode(E3dpPTR_TAG_POINTS);
     E3dp_PushMessage(0, 0, "Select region to invert tag");
    break;
   }
  break;

  case E3dpPTR_ORBIT_TRACK_DOLLY_STANDBY:	// Maya-like camera control with Alt
   switch(LButton)
   {
    case Button1:
     E3dp_PushPointerMode(E3dpPTR_ORBIT_OTD);
     E3dp_PushMessage(0, 0, "Orbiting camera");
     LCamera=&(L3DWindow->PerspectiveCamera);
     E3d_CameraCalcOrbit(LCamera);
     E3dp_PrintMessage(0, 0, "Orbiting camera: longitude: %3.2f  latitude: %3.2f", LCamera->Longitude, LCamera->Latitude);
    break;

    case Button2:	E3dp_PushPointerMode(E3dpPTR_TRACK_OTD);E3dp_PushMessage(0, 0, "Tracking camera");break;
    case Button3:	E3dp_PushPointerMode(E3dpPTR_DOLLY_OTD);E3dp_PushMessage(0, 0, "Dollying camera");break;
   }
  break;

  case E3dpPTR_ORBIT_OTD:			// Maya-like camera control with Alt
//printf("ORB\n");fflush(stdout);
   switch(LButton)
   {
    case Button2:
     E3dp_SetPointerMode(E3dpPTR_DOLLY_OTD);E3dp_SetCurrentMessage(0, 0, "Dollying camera");
    break;
   }
  break;

  case E3dpPTR_TRACK_OTD:			// Maya-like camera control with Alt
//printf("TRC\n");fflush(stdout);
   switch(LButton)
   {
    case Button1:
     E3dp_SetPointerMode(E3dpPTR_DOLLY_OTD);E3dp_SetCurrentMessage(0, 0, "Dollying camera");
    break;
   }
  break;

  case E3dpPTR_ORBIT_DOLLY_TRACK_STANDBY:	// Ctrl
   LCamera=&(L3DWindow->PerspectiveCamera);
   switch(LButton)
   {
    case Button1:
     E3dp_PushPointerMode(E3dpPTR_ORBIT_ODT);
     E3dp_PushMessage(0, 0, "Orbiting camera");
     E3d_CameraCalcOrbit(LCamera);
     E3dp_PrintMessage(0, 0, "Orbiting camera: longitude: %3.2f  latitude: %3.2f", LCamera->Longitude, LCamera->Latitude);
    break;

    case Button2:
     E3dp_PushPointerMode(E3dpPTR_DOLLY_ODT);
     E3dp_PushMessage(0, 0, "Dollying camera: view distance: %.4f Position: %f,%f,%f", LCamera->ViewDistance, LCamera->Position.X, LCamera->Position.Y, LCamera->Position.Z);
    break;

    case Button3:
     E3dp_PushPointerMode(E3dpPTR_TRACK_ODT);
     E3dp_PushMessage(0, 0, "Tracking camera");
    break;
   }
  break;

  case E3dpPTR_BANK_ZOOM_STANDBY:		// Ctrl+Shift
   LCamera=&(L3DWindow->PerspectiveCamera);
   switch(LButton)
   {
    case Button1:
     E3dp_PushPointerMode(E3dpPTR_BANK_BZ);
     E3dp_PushMessage(0, 0, "Banking camera: bank angle: %.4f", LCamera->BankAngle);
    break;

    case Button2:
     E3dp_PushPointerMode(E3dpPTR_ZOOM_BZ);

     switch(L3DWindow->ViewMode)
     {
      case E3dVM_PERSPECTIVE:
       E3dp_PushMessage(0, 0, "Zooming camera. Field of view: %5.2f degrees", LCamera->FieldOfView);
      break;

      case E3dVM_TOP:
      case E3dVM_FRONT:
      case E3dVM_RIGHT:
      break;
     }
    break;
   }
  break;

  case E3dpPTR_UNDO_REDO:
   switch(LButton)
   {
    case Button1:	// Undo
     E3dp_Undo(L3DPanel);
    break;

    case Button2:	// Redo
     E3dp_Redo(L3DPanel);
    break;
   }
  break;

  case E3dpPTR_SELECT_ZOOM_REGION_STANDBY:
   E3dp_RectAreaStartX=LPtrX;E3dp_RectAreaStartY=LPtrY;
   switch(LButton)
   {
    case Button1:
     E3dp_PushPointerMode(E3dpPTR_SELECT_ZOOM_REGION);
     E3dp_PushMessage(0, 0, "Select region to zoom into");
    break;
   }
  break;
 }
}


//================================
// Tag/untag/invert points
//================================
static void _TagPoints(E3dWindow* L3DWindow, int LPtrX, int LPtrY, int LButton)
{
 E3dItem*	LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize);
 int		LPickHits;

 if(LPickedItems==NULL) return;

 LPickHits=E3d_WindowRectanglePickItems(L3DWindow, E3dITEM_POINT, LPickedItems, E3dp_RectAreaStartX, E3dp_RectAreaStartY, LPtrX-E3dp_RectAreaStartX, LPtrY-E3dp_RectAreaStartY);

 if(LPickHits)
 {
  E3dItem*	LItem=LPickedItems;
  unsigned int	LC;

  switch(LButton)
  {
   case Button1:
    for(LC=0;LC<LPickHits;LC++, LItem++)
    {
     switch(((E3dAnyItem*)LItem)->Type)
     {
      case E3dITEM_VERTEX:
       {
	E3dVertex*	LVertex=((E3dPolyItem*)LItem)->Vertex;

	LVertex->Flags|=E3dVTXF_TAGGED;
       }
      break;

      case E3dITEM_SPLINE_KEY:
       {
        E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;
	E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSplineItem->CV);

	LSplineCV->Flags|=E3dKEYF_TAGGED;
       }
      break;
     }
    }
   break;

   case Button2:
    for(LC=0;LC<LPickHits;LC++, LItem++)
    {
     switch(((E3dAnyItem*)LItem)->Type)
     {
      case E3dITEM_VERTEX:
       {
	E3dVertex*	LVertex=((E3dPolyItem*)LItem)->Vertex;

	LVertex->Flags&=(E3dVTXF_ALL-E3dVTXF_TAGGED);
       }
      break;

      case E3dITEM_SPLINE_KEY:
       {
        E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;
	E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSplineItem->CV);

	LSplineCV->Flags&=(E3dKEYF_ALL-E3dKEYF_TAGGED);
       }
      break;
     }
    }
   break;

   case Button3:
    for(LC=0;LC<LPickHits;LC++, LItem++)
    {
     switch(((E3dAnyItem*)LItem)->Type)
     {
      case E3dITEM_VERTEX:
       {
	E3dVertex*	LVertex=((E3dPolyItem*)LItem)->Vertex;

	LVertex->Flags^=E3dVTXF_TAGGED;
       }
      break;

      case E3dITEM_SPLINE_KEY:
       {
        E3dSplineItem*	LSplineItem=(E3dSplineItem*)LItem;
	E3dSplineCV*	LSplineCV=(E3dSplineCV*)(LSplineItem->CV);

	LSplineCV->Flags^=E3dKEYF_TAGGED;
       }
      break;
     }
    }
   break;
  }
 }

 EFree(LPickedItems);

 E3dp_PopPointerMode();
 E3dp_PopMessage();

 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
}


//================================================
// Process button-release events
//================================================
void E3dp_WindowButtonRelease(E3dWindow* L3DWindow, int LPtrX, int LPtrY, int LButton, unsigned int LModifiers)
{
 if(E3dp_NumOfTransformedModels>0)
 {
  EFree(E3dp_TransformedModels);
  E3dp_NumOfTransformedModels=0;
  E3dp_TransformedModels=NULL;
 }

 if(E3dp_NumOfTransformedModelRoots>0)
 {
  EFree(E3dp_TransformedModelRoots);
  E3dp_NumOfTransformedModelRoots=0;
  E3dp_TransformedModelRoots=NULL;
 }

 switch(E3dp_PointerMode)
 {
  case E3dpPTR_TRACK:
  case E3dpPTR_ZOOM:
  case E3dpPTR_ORBIT:
  case E3dpPTR_DOLLY:
  case E3dpPTR_BANK:
  case E3dpPTR_BANK_BZ:		// Ctrl+Shift
  case E3dpPTR_ZOOM_BZ:		// Ctrl+Shift
   E3dp_PopPointerMode();E3dp_PopMessage();
  break;

  case E3dpPTR_MOVE_POINT:
   {
    EBool	LChanged=FALSE;

    if(E3dp_ActiveVertex) { E3dp_ActiveVertex->Flags=E3dp_OldPointFlags;E3dp_ActiveVertex=NULL;LChanged=TRUE; }
    if(E3dp_ActiveBezierCV) { E3dp_ActiveBezierCV->Flags=E3dp_OldPointFlags;E3dp_ActiveBezierCV=NULL;LChanged=TRUE; }
    if(E3dp_ActiveSplineCV) { E3dp_ActiveSplineCV->Flags=E3dp_OldPointFlags;E3dp_ActiveSplineCV=NULL;LChanged=TRUE; }
    E3dp_ActiveGeometry=NULL;

    E3dp_PopPointerMode();E3dp_PopMessage();
    if(LChanged) E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
   }
  break;

  case E3dpPTR_ORBIT_OTD:	// Alt
  case E3dpPTR_ORBIT_ODT:	// Ctrl
   switch(LButton)
   {
    case Button1:	E3dp_PopPointerMode();E3dp_PopMessage();break;
   }
  break;

  case E3dpPTR_TRACK_OTD:	// Alt
  case E3dpPTR_DOLLY_ODT:	// Ctrl
   switch(LButton)
   {
    case Button2:	E3dp_PopPointerMode();E3dp_PopMessage();break;
   }
  break;

  case E3dpPTR_DOLLY_OTD:	// Alt
   switch(LButton)
   {
    case Button2:
    case Button3:
     E3dp_PopPointerMode();E3dp_PopMessage();
    break;
   }
  break;

  case E3dpPTR_TRACK_ODT:	// Ctrl
   switch(LButton)
   {
    case Button3:	E3dp_PopPointerMode();E3dp_PopMessage();break;
   }
  break;

  case E3dpPTR_POLY_PAINT:
   switch(LButton)
   {
    case Button3:
     E3dp_PopPointerMode();
//     E3dp_PopMessage();
    break;
   }
  break;

  case E3dpPTR_TAG_POINTS:		_TagPoints(L3DWindow, LPtrX, LPtrY, LButton);break;

  case E3dpPTR_SELECT_ZOOM_REGION:
   {
    E3dCamera*	LCamera;
    E3dRay	LRay;


//    E3dp_RectAreaStartX, E3dp_RectAreaStartY, LPtrX-E3dp_RectAreaStartX, LPtrY-E3dp_RectAreaStartY;

    E3dp_PopPointerMode();
    E3dp_PopMessage();


// Cast a ray at the start X1,Y1 and another ray at X2,Y2 of the relected region
//
    if((LCamera=E3d_WindowCastPrimaryRay(L3DWindow, E3dp_RectAreaStartX, E3dp_RectAreaStartY, &LRay))!=NULL)
    {
     E3dRay		LRay2;
     E3dCoordinate	LFV, LZoom;
     E3d3DPosition	LBBMiddle;


//printf("Zoom into: %d,%d - %d,%d\n", E3dp_RectAreaStartX, E3dp_RectAreaStartY, LPtrX, LPtrY);fflush(stdout);

     E3d_WindowCastPrimaryRay(L3DWindow, LPtrX, LPtrY, &LRay2);

// Make a bounding 'box' from these two rays
//
     if(LRay.X>LRay2.X) { LFV=LRay.X;LRay.X=LRay2.X;LRay2.X=LFV; }
     if(LRay.Y>LRay2.Y) { LFV=LRay.Y;LRay.Y=LRay2.Y;LRay2.Y=LFV; }
     if(LRay.Z>LRay2.Z) { LFV=LRay.Z;LRay.Z=LRay2.Z;LRay2.Z=LFV; }

     LBBMiddle.X=(LRay.X+LRay2.X)*0.5;
     LBBMiddle.Y=(LRay.Y+LRay2.Y)*0.5;
     LBBMiddle.Z=(LRay.Z+LRay2.Z)*0.5;

     switch(L3DWindow->ViewMode)
     {
      case E3dVM_PERSPECTIVE:
      break;

      case E3dVM_TOP:
       LCamera->Position.X=LBBMiddle.X;
       LCamera->Position.Z=LBBMiddle.Z;
       LCamera->InterestPoint.X=LBBMiddle.X;
       LCamera->InterestPoint.Z=LBBMiddle.Z;

       LZoom=(LRay2.Z-LRay.Z)*L3DWindow->AspectRatio;
       if((LRay2.X-LRay.X)>LZoom) LZoom=LRay2.X-LRay.X;

// 0.5 with a little "margin"
//
       LCamera->XZoom=LCamera->YZoom=LZoom*0.64;

       E3d_CameraRefreshMatrices(LCamera);
      break;

      case E3dVM_FRONT:
       LCamera->Position.X=LBBMiddle.X;
       LCamera->Position.Y=LBBMiddle.Y;
       LCamera->InterestPoint.X=LBBMiddle.X;
       LCamera->InterestPoint.Y=LBBMiddle.Y;

       LZoom=(LRay2.Y-LRay.Y)*L3DWindow->AspectRatio;
       if((LRay2.X-LRay.X)>LZoom) LZoom=LRay2.X-LRay.X;

// 0.5 with a little "margin"
//
       LCamera->XZoom=LCamera->YZoom=LZoom*0.64;

       E3d_CameraRefreshMatrices(LCamera);
      break;

      case E3dVM_RIGHT:
       LCamera->Position.Z=LBBMiddle.Z;
       LCamera->Position.Y=LBBMiddle.Y;
       LCamera->InterestPoint.Z=LBBMiddle.Z;
       LCamera->InterestPoint.Y=LBBMiddle.Y;

       LZoom=(LRay2.Y-LRay.Y)*L3DWindow->AspectRatio;
       if((LRay2.Z-LRay.Z)>LZoom) LZoom=LRay2.Z-LRay.Z;

// 0.5 with a little "margin"
//
       LCamera->XZoom=LCamera->YZoom=LZoom*0.64;

       E3d_CameraRefreshMatrices(LCamera);
      break;

      case E3dVM_SCHEMATICS:
       LCamera->Position.X=LBBMiddle.X;
       LCamera->Position.Y=LBBMiddle.Y;
       LCamera->InterestPoint.X=LBBMiddle.X;
       LCamera->InterestPoint.Y=LBBMiddle.Y;

       LZoom=(LRay2.Y-LRay.Y)*L3DWindow->AspectRatio;
       if((LRay2.X-LRay.X)>LZoom) LZoom=LRay2.X-LRay.X;

// 0.5 with a little "margin"
//
       LCamera->XZoom=LCamera->YZoom=LZoom*0.56;

       E3d_CameraRefreshMatrices(LCamera);
      break;
     }

    }

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
   }
  break;
 }


// In case we were transforming tagged points
//
 if(E3dp_ShapeSnapshots)
 {
  E3d_ShapeSnapshotsFree(E3dp_ShapeSnapshots, E3dp_NumOfShapeSnapshots);
  E3dp_ShapeSnapshots=NULL;E3dp_NumOfShapeSnapshots=0;
 }

 switch(E3dp_TransformTarget)
 {
  case E3dpTT_OBJECT_TMP_FROM_TAG:
   XeToggleButtonSetState(_TTB_TagW, True, True);
  break;
 }
}


//================================================
// Process pointer-move events
//================================================
void E3dp_WindowPointerMove(E3dWindow* L3DWindow, int LPtrX, int LPtrY, unsigned int LState)
{
 switch(E3dp_PointerMode)
 {
  case E3dpPTR_PICK:
   if(E3d_PickCallback)
   {
    if(E3d_PickActivateOnMotion||((E3d_PickMode&E3dITEM_EDGE)!=0)) _ProcessPick(L3DWindow, LPtrX, LPtrY, 0, LState, TRUE);
   }
  break;

  case E3dpPTR_POLY_PAINT:
   {
    E3dItem*	LPickedItems;
    int		LPickHits;

    if((LPickedItems=(E3dItem*)EMalloc(sizeof(E3dItem)*E3dp_PickBufferSize))==NULL) return;

    if((LPickHits=E3dM_WindowPointPickItems(L3DWindow, E3dITEM_POLYGON, LPickedItems, LPtrX, LPtrY))>0)
    {
     E3dItem*		LItem=LPickedItems;

     switch(((E3dAnyItem*)LItem)->Type)
     {
      case E3dITEM_POLYGON:
       {
	E3dPolyItem*	LPolyItem=(E3dPolyItem*)LItem;
	E3dModel*	LModel=LPolyItem->Model;
	E3dPolygon*	LPolygon=LPolyItem->PolyGroup->Polygons+LPolyItem->PolygonIndex;

	LPolygon=E3d_SelectTogglePolygonSelection(LModel, LPolyItem->Geometry, LPolyItem->PolyGroup, LPolygon, (E3dp_SelectionMode==E3dpSELM_SINGLE));

	if(LPolygon)
	{
	 if((LPolygon->Flags&E3dPolyFlagSELECTED)!=E3dp_PolygonSelectFlag)
	 {
	  LPolygon->Flags^=E3dPolyFlagSELECTED;

	  if(LPolygon->Flags&E3dPolyFlagSELECTED) E3dp_PrintMessage(0, 2500, "Selected Polygon %d", LPolyItem->PolygonIndex);
	  else E3dp_PrintMessage(0, 2500, "Unselected Polygon %d", LPolyItem->PolygonIndex);

	  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
	 }
	}
       break;
      }
     }
    }
    EFree(LPickedItems);
   }
  return;

  case E3dpPTR_MOVE_POINT:
   E3dp_MovePoint(L3DWindow, LPtrX, LPtrY, LState);
  break;

  case E3dpPTR_SELECTING:
   E3d_PrintInfoOnObjectUnderPointer(L3DWindow, LPtrX, LPtrY, FALSE);
  break;

  case E3dpPTR_TAG_POINTS:
   if(LState)
   {
    EcRGBAiColor	LRGBAiColor={ 0xFFFF, 0xFFFF, 0x5000, 0xFFFF };

    E3d_WindowDraw2DRectangle(L3DWindow, (float)E3dp_RectAreaStartX, (float)E3dp_RectAreaStartY, (float)LPtrX, (float)LPtrY, LRGBAiColor);
//    E3d_WindowDraw2DRectangle(L3DWindow, (float)E3dp_RectAreaStartX, (float)E3dp_RectAreaStartY, (float)LPtrX, (float)LPtrY, E3dp_NormalPrefs.AxisRGBAiColor);
   }
  break;

  case E3dpPTR_SELECT_ZOOM_REGION:
   if(LState)
   {
    EcRGBAiColor	LRGBAiColor={ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };

    E3d_WindowDraw2DRectangle(L3DWindow, (float)E3dp_RectAreaStartX, (float)E3dp_RectAreaStartY, (float)LPtrX, (float)LPtrY, LRGBAiColor);
//    E3d_WindowDraw2DRectangle(L3DWindow, (float)E3dp_RectAreaStartX, (float)E3dp_RectAreaStartY, (float)LPtrX, (float)LPtrY, E3dp_NormalPrefs.AxisRGBAiColor);
   }
  break;
 }

// Is there a transformation mode active ?
//
 if(E3dp_PointerMode==E3dpPTR_NONE) E3dp_InteractiveTransform(L3DWindow, LPtrX, LPtrY, LState);
 else E3dp_CameraControl(L3DWindow, LPtrX, LPtrY, LState);

 E3dp_PrevPtrX=LPtrX;
 E3dp_PrevPtrY=LPtrY;
}


//========================================================================================
// Do a full refresh on all 3DWindows with the given ViewModes and DisplayModes
//
// Arguments
//  unsigned long LDisplayMode            An ORed list of flags for Display modes
//  unsigned long LViewModes              An ORed list of flags for View modes
//
// Description
//  Performs a full redraw of all 3DWindows and re-generates the OpenGL display list of
//  all the objects.
//
// See also
//  E3dp_Redraw3DWindows
//========================================================================================
void E3dp_Refresh3DWindows(unsigned long LDisplayMode, unsigned long LViewModes)
{
 E3dWindow*	L3DWindow;
 unsigned int	LC, LN;


 E3d_SceneRemoveGLDisplayLists();

// E3dp_CurrentGLXWindow=(Window)NULL;		// Force glXMakeCurrent() and glViewport()
 LN=E3dp_NumOf3DWindows;
 for(LC=0;LC<LN;LC++)
 {
  L3DWindow=E3dp_3DWindows[LC];
  if((((L3DWindow->DisplayMode)&LDisplayMode)!=0)&&((L3DWindow->ViewMode&LViewModes)!=0))
  {
   E3dp_Redraw3DWindow(L3DWindow);
  }
 }
// E3dp_CurrentGLXWindow=(Window)NULL;		// Force glXMakeCurrent() and glViewport()
}


//========================================================================================
// Redraw all 3DWindows with the given ViewModes and DisplayModes
//
// Arguments
//  unsigned long LDisplayMode           ORed list of flags for Display modes
//  unsigned long LViewModes             ORed list of flags for View modes
//
// Description
//  Performs a full redraw of all 3DWindows and re-generates the OpenGL display list of
//  all the objects.
//
// See also
//  E3dp_Refresh3DWindows
//========================================================================================
void E3dp_Redraw3DWindows(unsigned long LDisplayMode, unsigned long LViewModes)
{
 E3dWindow*	L3DWindow;
 unsigned int	LC, LN;


 LN=E3dp_NumOf3DWindows;
// E3dp_CurrentGLXWindow=(Window)NULL;		// Force glXMakeCurrent() and glViewport()
 for(LC=0;LC<LN;LC++)
 {
  L3DWindow=E3dp_3DWindows[LC];
  if((((L3DWindow->DisplayMode)&LDisplayMode)!=0)&&((L3DWindow->ViewMode&LViewModes)!=0))
  {
   if(L3DWindow->Visibility!=VisibilityFullyObscured)
   {
    if((((L3DWindow->WindowFlags)&(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE))==(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE)))
    {
     _Draw3DWindow(L3DWindow);
    }
   }
  }
 }

// Do the buffer swaps all at once
//
 for(LC=0;LC<LN;LC++)
 {
  L3DWindow=E3dp_3DWindows[LC];
  if((((L3DWindow->DisplayMode)&LDisplayMode)!=0)&&((L3DWindow->ViewMode&LViewModes)!=0))
  {
   if(L3DWindow->Visibility!=VisibilityFullyObscured)
   {
    if((((L3DWindow->WindowFlags)&(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE))==(E3dWF_CREATED|E3dWF_MANAGED|E3dWF_ALIVE)))
    {
     glXSwapBuffers(EGUI_Display, L3DWindow->XWindow);
    }
   }
  }
 }
}
