/*======================================================================================================*/
/* Tesselate												*/
/*													*/
/* - Tesselate Mesh: create HW renderable Polygons (convex, no holes)					*/
/* - Triangulate: convert mesh to triangles								*/
/* - Refresh Polygon normals										*/
/* - Refresh normals											*/
/* - Smooth normals											*/
/* - Invert PolyGroups and Splines									*/
/* - Add inverse PolyGroups (create two-faced polys)							*/
/* - Invert Polygons											*/
/*													*/
/* Plugin for EQUINOX-3D										*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-24 22:28:55									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <stdio.h>

#include <EMalloc.h>
#include <EPlugins.h>

#include <EGUI/Dialog.h>

#include <E3D/3DWindow.h>
#include <E3D/StatusPanel.h>	// For E3dp_PrintMessage

extern EpCImage		Image_Triangulate;
extern EpCImage		Image_TriangulateActive;
extern EpCImage		Image_TriangulateArm;


static EPlugin*	_PluginRec=NULL;


static EguiItem		_MenuButtons[16];

static unsigned int	_NumOfMenuButtons=0;


static EguiItem	E3d_TriangulateToolPanelButton=NULL;

static EBool	E3d_RefreshNormalsGoOn=FALSE;

//========================================
// Abort callback
//========================================
static void _CB_Abort(EPointer LClientData)
{
 E3d_RefreshNormalsGoOn=FALSE;
}


//========================================
// Menu callback
//========================================
static void _MCB_Tesselate(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dScene*	LScene=E3d_Scene;
 E3dModel**	LRootModels;
 E3dModel*	LModel;
 unsigned int	LC, LDid=0;


 switch((int)LClientData)
 {
  case 0:	// Tesselate
   LRootModels=LScene->RootModels;
   for(LC=0;LC<LScene->NumOfRootModels;LC++)
   {
    LModel=LRootModels[LC];
    if(LModel->Selection!=E3dSEL_NONE)
    {
     for(;LModel;LModel=LModel->Next)
     {
      if(LModel->LockCount>0) E3dp_PrintMessage(0, 2500, "Object geometry %s is not editable!", LModel->Name);
      else
      {
       E3d_ModelTesselate(LModel, FALSE);
       LDid++;
      }
     }
    }
   }
   if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh or PolyGroup to triangulate");
  break;

  case 1:	// Triangulate
   {
    E3dGeometry**	LGeometries;
    E3dGeometry*	LGeometry;
    E3dMesh*		LMesh;
    E3dPolyGroup*	LPolyGroup;
    E3dPolyGroup**	LPolyGroups;
    E3dPolygon*		LPolygon;
    unsigned int	LGmCnt, LGmNum, LGC, LPolyCnt, LNonTris;
    EBool		LModelSelected;


    LRootModels=LScene->RootModels;
    for(LC=0;LC<LScene->NumOfRootModels;LC++)
    {
     LModel=LRootModels[LC];
     for(;LModel;LModel=LModel->Next)
     {
      E3dM_IsModelSelected(LModel, LModelSelected);

      if(LModel->Selection != E3dSEL_NONE)
      {
       if(LModel->LockCount>0) E3dp_PrintMessage(0, 2500, "Model %s is locked (not editable)!", LModel->Name);
       else
       {
	if(E3d_ModelTesselate(LModel, TRUE))
	{
	 LNonTris=0;
	 LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
	 for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
	 {
	  LGeometry=LGeometries[LGmCnt];

	  if(LModelSelected||(LGeometry->Selection!=E3dGSEL_NONE))
	  {
	   switch(LGeometry->GeoType)
	   {
	    caseE3dMESH():
	     LMesh=(E3dMesh*)LGeometry;
	     if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'
	     LPolyGroups=LMesh->PolyGroups;

	     for(LGC=0;LGC<LMesh->NumOfPolyGroups;LGC++)
	     {
	      LPolyGroup=LPolyGroups[LGC];
	      LNonTris=0;
	      if((LPolyCnt=LPolyGroup->NumOfPolygons)>0)
	      {
	       LPolygon=LPolyGroup->Polygons;
	       do
	       {
		if(LPolygon->VertexNodes)
		{
		 if(LPolygon->NumOfVertices==3)
		 {
		 }
		 else LNonTris++;
		}
		LPolygon++;
	       } while(--LPolyCnt);
//printf("%s NonTris: %d total: %d\n", LModel->Name, (int)LNonTris, (int)(LPolyGroup->NumOfPolygons));fflush(stdout);
	      }
	     }
	     E3d_GeometryUpdateForDisplay(LGeometry, E3dGF_ALL-E3dGF_REMOVE_STRIPS);
	     LDid++;
	    break;
	   }
	  }
	 }
	}
       }
      }
     }
    }
   }
   if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh or PolyGroup to triangulate");
  break;
 }
 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
}


//========================================
// Menu callback
//========================================
static void _MCB_SmoothNormals(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dModel**	LRootModels;
 E3dModel*	LModel;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMesh*	LMesh;
 E3dPolyGroup*	LPolyGroup;
 E3dPolyGroup**	LPolyGroups;
 unsigned int	LC, LN;
 unsigned int	LGmCnt, LGmNum, LGC;
 int		LDid=0;


 LRootModels=E3d_Scene->RootModels;
 for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
 {
  LModel=LRootModels[LC];

/*
  LMaterial=E3d_Materials[0];
  LMaterial->Ambient.R=0.1;LMaterial->Ambient.G=0.1;LMaterial->Ambient.B=0.0;
  LMaterial->Diffuse.R=0.3;LMaterial->Diffuse.G=0.21;LMaterial->Diffuse.B=0.0;
  LMaterial->Specular.R=1.0;LMaterial->Specular.G=0.82;LMaterial->Specular.B=0.3;
  LMaterial->Specularity=16.0;
  E3d_MaterialConvertToGL(LMaterial);
*/
  for(;LModel;LModel=LModel->Next)
  {
   if(LModel->Selection!=E3dSEL_NONE)
   {
    LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
    for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
    {
     if((LGeometry=LGeometries[LGmCnt])!=NULL)
     {
      switch(LGeometry->GeoType)
      {
       case E3dGEO_MESH:
       case E3dGEO_SKINMESH:
	LMesh=(E3dMesh*)LGeometry;

	if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'
	LPolyGroups=LMesh->PolyGroups;
	for(LGC=0;LGC<LMesh->NumOfPolyGroups;LGC++)
	{
	 LPolyGroup=LPolyGroups[LGC];
	 LPolyGroup->VertexNormalType=E3dNormalAVERAGED;
	 E3d_MeshPolyGroupRefreshNormals(LMesh, LPolyGroup, LPolyGroup->VertexNormalType, TRUE);
	 LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
	 LDid++;
	}
	E3d_MeshRefreshGLNormals(LMesh, LModel->LocalToWorldMatrix);
       break;
      }
     }
    }
   }
  }
  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
 }
 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh or PolyGroup");
}


//========================================
// Menu callback
//========================================
static void _MCB_RefreshPolygonNormals(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dModel**	LRootModels;
 E3dModel*	LModel;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMesh*	LMesh;
 E3dPolyGroup*	LPolyGroup;
 E3dPolyGroup**	LPolyGroups;
 E3dPolygon*	LPolygon;
 E3dVertexNode*	LVertexNode;
 E3dCoordinate	LFX, LFY, LFZ;
 unsigned int	LC, LN, LPCnt, LVCnt;
 unsigned int	LGmCnt, LGmNum, LGC;
 int		LDid=0;


 LRootModels=E3d_Scene->RootModels;
 for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
 {
  LModel=LRootModels[LC];

  for(;LModel;LModel=LModel->Next)
  {
   if(LModel->Selection!=E3dSEL_NONE)
   {
    LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
    for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
    {
     if((LGeometry=LGeometries[LGmCnt])!=NULL)
     {
      switch(LGeometry->GeoType)
      {
       case E3dGEO_MESH:
       case E3dGEO_SKINMESH:
	LMesh=(E3dMesh*)LGeometry;

	if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'
	LPolyGroups=LMesh->PolyGroups;
	for(LGC=0;LGC<LMesh->NumOfPolyGroups;LGC++)
	{
	 LPolyGroup=LPolyGroups[LGC];

	 E3d_MeshPolyGroupRefreshPolygonNormals(LMesh, LPolyGroup);

// Set the Polygon normal on each Vertex of the Polygons if the PolyGroup is faceted
//
	 if((LPolyGroup->VertexNormalType==E3dNormalNONE)&&(LPolygon=LPolyGroup->Polygons)!=NULL)
	 {
	  LPCnt=LPolyGroup->NumOfPolygons;
	  do
	  {
	   if((LVertexNode=LPolygon->VertexNodes)!=NULL)
	   {
	    LVCnt=LPolygon->NumOfVertexNodes;
	    LFX=LPolygon->Normal.X;
	    LFY=LPolygon->Normal.Y;
	    LFZ=LPolygon->Normal.Z;
	    do
	    {
	     if(LVertexNode->VertexID>-1)
	     {
	      LVertexNode->Normal.X=LFX;
	      LVertexNode->Normal.Y=LFY;
	      LVertexNode->Normal.Z=LFZ;
	     }
	     LVertexNode++;
	    } while(--LVCnt);
	    LPolygon++;
	   }
	  } while(--LPCnt);

	  LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
	 }

	}
	E3d_MeshRefreshGLNormals(LMesh, LModel->LocalToWorldMatrix);
	LDid++;
       break;
      }
     }
    }
   }
  }
  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
 }

 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh or PolyGroup");
}


//========================================
// Menu callback
//========================================
static void _MCB_RefreshNormals(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dModel**	LRootModels;
 E3dModel*	LModel;
 E3dGeometry**	LGeometries;
 E3dGeometry*	LGeometry;
 E3dMesh*	LMesh;
 E3dPolyGroup*	LPolyGroup;
 E3dPolyGroup**	LPolyGroups;
 E3dPolygon*	LPolygon;
 E3dVertexNode*	LVertexNode;
 E3dCoordinate	LFX, LFY, LFZ;
 unsigned int	LC, LN, LPCnt, LVCnt;
 unsigned int	LGmCnt, LGmNum, LGC;
 unsigned int	LPolyGroupNum, LPolyGroupCnt;
 int		LDid=0;



// Count PolyGroups
//
 LPolyGroupNum=0;
 LRootModels=E3d_Scene->RootModels;
 for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
 {
  LModel=LRootModels[LC];

  for(;LModel;LModel=LModel->Next)
  {
   if(LModel->Selection!=E3dSEL_NONE)
   {
    LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
    for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
    {
     if((LGeometry=LGeometries[LGmCnt])!=NULL)
     {
      switch(LGeometry->GeoType)
      {
       case E3dGEO_MESH:
       case E3dGEO_SKINMESH:
	LMesh=(E3dMesh*)LGeometry;

	LPolyGroupNum+=LMesh->NumOfPolyGroups;
       break;
      }
     }
    }
   }
  }
 }

 if(LPolyGroupNum)
 {
  E3dp_SetProgressMessage("Recomputing normals");
  E3dp_SetProgressIndicator(0, _CB_Abort, NULL);
 }

 LPolyGroupCnt=0;
 E3d_RefreshNormalsGoOn=TRUE;
 for(LC=0, LN=E3d_Scene->NumOfRootModels;LC<LN;LC++)
 {
  if(!E3d_RefreshNormalsGoOn) break;

  LModel=LRootModels[LC];
  for(;LModel;LModel=LModel->Next)
  {
   if(!E3d_RefreshNormalsGoOn) break;

   if(LModel->Selection!=E3dSEL_NONE)
   {
    LGmNum=LModel->NumOfGeometries;LGeometries=LModel->Geometries;
    for(LGmCnt=0;LGmCnt<LGmNum;LGmCnt++)
    {
     if(!E3d_RefreshNormalsGoOn) break;

     if((LGeometry=LGeometries[LGmCnt])!=NULL)
     {
      switch(LGeometry->GeoType)
      {
       case E3dGEO_MESH:
       case E3dGEO_SKINMESH:
	LMesh=(E3dMesh*)LGeometry;

	if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'
	LPolyGroups=LMesh->PolyGroups;

	if(!E3d_RefreshNormalsGoOn) break;

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

	 if(!E3d_RefreshNormalsGoOn) break;

	 E3d_MeshPolyGroupRefreshNormals(LMesh, LPolyGroup, LPolyGroup->VertexNormalType, TRUE);

// Set the Polygon normal on each Vertex of the Polygons if the PolyGroup is faceted
//
	 if((LPolyGroup->VertexNormalType==E3dNormalNONE)&&(LPolygon=LPolyGroup->Polygons)!=NULL)
	 {
	  LPCnt=LPolyGroup->NumOfPolygons;
	  do
	  {
	   if((LVertexNode=LPolygon->VertexNodes)!=NULL)
	   {
	    LVCnt=LPolygon->NumOfVertexNodes;
	    LFX=LPolygon->Normal.X;
	    LFY=LPolygon->Normal.Y;
	    LFZ=LPolygon->Normal.Z;
	    do
	    {
	     if(LVertexNode->VertexID>-1)
	     {
	      LVertexNode->Normal.X=LFX;
	      LVertexNode->Normal.Y=LFY;
	      LVertexNode->Normal.Z=LFZ;
	     }
	     LVertexNode++;
	    } while(--LVCnt);
	    LPolygon++;
	   }
	  } while(--LPCnt);

	  LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
	 }
	 LPolyGroupCnt++;
         LDid++;
	 E3dp_SetProgressIndicator((LPolyGroupCnt*1000)/LPolyGroupNum, _CB_Abort, NULL);
	}
	E3d_MeshRefreshGLNormals(LMesh, LModel->LocalToWorldMatrix);
       break;
      }
     }
    }
   }
  }
  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
 }

 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh or PolyGroup");
 else
 {
  E3dp_SetProgressIndicator(-1, NULL, NULL);
 }
}


//========================================
// Menu callback
//========================================
static void _MCB_Invert(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dGeometry**	LSelectedGeometries;
 E3dGeometry*	LGeometry;
 unsigned int	LC, LNumOfSelectedGeometries;
 int		LDid=0;


 LNumOfSelectedGeometries=E3d_SceneGetSelectedGeometries(E3d_Scene, &LSelectedGeometries, E3dGEO_ANY);

 if(LNumOfSelectedGeometries)
 {
  for(LC=0;LC<LNumOfSelectedGeometries; LC++)
  {
   LGeometry=LSelectedGeometries[LC];

   switch(LGeometry->GeoType)
   {
    caseE3dMESH():
     {
      E3dMesh*		LMesh=(E3dMesh*)LGeometry;
      E3dPolyGroup*	LPolyGroup;
      E3dPolyGroup**	LPolyGroups=LMesh->PolyGroups;
      E3dPolygon*	LPolygon;
      unsigned int	LGC, LGN=LMesh->NumOfPolyGroups;
      unsigned int	LPC, LPN, LPGsDid=0;


      if(LGN==0) break;		// Break for the 'case'

      for(LGC=0;LGC<LGN;LGC++)
      {
       LPolyGroup=LPolyGroups[LGC];
       if((LPolyGroup->Selected)||(LMesh->Selection!=E3dGSEL_POLYGROUP))
       {
	if((LPolygon=LPolyGroup->Polygons)!=NULL)
	{
	 LPN=LPolyGroup->NumOfPolygons;
	 for(LPC=0;LPC<LPN;LPC++, LPolygon++) E3d_PolygonInvert(LPolygon);

	 LPGsDid++;
         E3d_MeshPolyGroupCreateRenderablePolygons(LMesh, LPolyGroup);
	}
       }
      }
      if(LPGsDid) LDid++;
     }
    break;

    case E3dGEO_SPLINE:
     E3d_SplineInvert((E3dSpline*)LGeometry);
     E3d_GeometryUpdateForDisplay(LGeometry, E3dGF_ALL);
     LDid++;
    break;
   }
  }
  EFree(LSelectedGeometries);
 }

 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh, PolyGroup or Spline to invert");

 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
}


//========================================
// Menu callback
//========================================
static void _MCB_AddInverse(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dGeometry**	LSelectedGeometries;
 E3dGeometry*	LGeometry;
 unsigned int	LC, LNumOfSelectedGeometries;
 int		LDid=0;


 LNumOfSelectedGeometries=E3d_SceneGetSelectedGeometries(E3d_Scene, &LSelectedGeometries, E3dGEO_ANY);

 if(LNumOfSelectedGeometries)
 {
  for(LC=0;LC<LNumOfSelectedGeometries; LC++)
  {
   LGeometry=LSelectedGeometries[LC];

   switch(LGeometry->GeoType)
   {
    caseE3dMESH():
     {
      E3dMesh*		LMesh=(E3dMesh*)LGeometry;
      E3dPolyGroup*	LPolyGroup;
      E3dPolyGroup*	LNewPolyGroup;
      E3dPolyGroup**	LPolyGroups=LMesh->PolyGroups;
      E3dPolygon*	LPolygon;
      unsigned int	LGC, LGN=LMesh->NumOfPolyGroups;
      unsigned int	LPC, LPN, LPGsDid=0;
      E3dMatrix		LMatrix;


      if(LGN==0) break;		// Break for the 'case'

      E3d_MatrixLoadIdentity(LMatrix);

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

       if((LPolyGroup->Selected)||(LMesh->Selection!=E3dGSEL_POLYGROUP))
       {
	if(LPolyGroup->Polygons)
	{
	 LNewPolyGroup=E3d_PolyGroupClone(LPolyGroup, E3dCLONE_MATERIALS);

	 E3d_MeshAppendPolyGroup(LMesh, LNewPolyGroup);
	 LPGsDid++;

         LPolygon=LNewPolyGroup->Polygons;
	 LPN=LNewPolyGroup->NumOfPolygons;
	 for(LPC=0;LPC<LPN;LPC++, LPolygon++) E3d_PolygonInvert(LPolygon);

	 E3d_UpdateDrawingMaterial((E3dGeometry*)LMesh, LNewPolyGroup->Material, &(LNewPolyGroup->DrawingMaterial));

	 E3d_MeshPolyGroupUpdateForDisplay(LMesh, LNewPolyGroup, E3dGF_TOPOLOGY|E3dGF_REMAP_TEXTURES);
	}
       }
      }
      if(LPGsDid)
      {
       E3d_MeshCreateEdgesFromPolyData(LMesh, NULL);
       E3d_MeshRefreshGLEdges(LMesh, LMatrix);

       LDid++;
      }
     }
    break;

    case E3dGEO_SPLINE:
     E3d_SplineInvert((E3dSpline*)LGeometry);
     E3d_GeometryUpdateForDisplay(LGeometry, E3dGF_ALL);
     LDid++;
    break;
   }
  }
  EFree(LSelectedGeometries);
 }

 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Model, Mesh, PolyGroup or Spline to invert");

 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_NORMAL3D);
}


//========================================
// Menu callback
//========================================
static void _MCB_InvertPolygons(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dGeometry**	LSelectedGeometries;
 E3dGeometry*	LGeometry;
 E3dMesh*	LMesh;
 E3dPolyGroup*	LPolyGroup;
 E3dPolyGroup**	LPolyGroups;
 E3dPolygon*	LPolygon;
 unsigned int	LGC, LGN, LC, LN, LNumOfSelectedGeometries;
 int		LDid=0;


 LNumOfSelectedGeometries=E3d_SceneGetSelectedGeometries(E3d_Scene, &LSelectedGeometries, E3dGEO_ANY);

 if(LNumOfSelectedGeometries)
 {
  for(LC=0;LC<LNumOfSelectedGeometries; LC++)
  {
   LGeometry=LSelectedGeometries[LC];

   switch(LGeometry->GeoType)
   {
    caseE3dMESH():
     {
      EBool		LChanged;

      LMesh=(E3dMesh*)LGeometry;

      if(LMesh->NumOfPolyGroups==0) break;		// Break for the 'case'

      LPolyGroups=LMesh->PolyGroups;
      LGN=LMesh->NumOfPolyGroups;
      LChanged=FALSE;
      for(LGC=0;LGC<LGN;LGC++)
      {
       LPolyGroup=LPolyGroups[LGC];
       if((LPolyGroup->Selected)||(LMesh->Selection!=E3dGSEL_POLYGROUP))
       {
	if((LPolygon=LPolyGroup->Polygons)!=NULL)
	{
	 LN=LPolyGroup->NumOfPolygons;
	 for(LC=0;LC<LN;LC++, LPolygon++) if(LPolygon->Flags&E3dPolyFlagSELECTED) { E3d_PolygonInvert(LPolygon);LChanged=TRUE; }
	}
       }
      }
      if(LChanged)
      {
       LDid++;
       E3d_MeshCreateRenderablePolygons(LMesh);
       E3d_GeometryUpdateForDisplay(LGeometry, E3dGF_ALL-E3dGF_TOPOLOGY);
      }
     }
    break;
   }
  }
  EFree(LSelectedGeometries);
 }

 if(LDid==0) E3dp_PrintMessage(0, 5000, "Select Polygons to invert");

 E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
}


//========================================
// Entry point of the plugin
//========================================
int Plugin_Init(EPlugin* LPlugin)
{
 _PluginRec=LPlugin;

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Tesselate", '\0', NULL, NULL, FALSE, NULL, _MCB_Tesselate, (EPointer)0);
 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Triangulate", '\0', NULL, NULL, FALSE, NULL, _MCB_Tesselate, (EPointer)1);
 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Refresh Polygon normals", '\0', NULL, NULL, FALSE, NULL, _MCB_RefreshPolygonNormals, (EPointer)0);
 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Refresh normals", '\0', NULL, NULL, FALSE, NULL, _MCB_RefreshNormals, (EPointer)0);
 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "'Smooth' normals", '\0', NULL, NULL, FALSE, NULL, _MCB_SmoothNormals, (EPointer)0);

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Invert", '\0', NULL, NULL, FALSE, NULL, _MCB_Invert, (EPointer)0);
 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Add inverse", '\0', NULL, NULL, FALSE, NULL, _MCB_AddInverse, (EPointer)0);

 _MenuButtons[_NumOfMenuButtons++]=EGUI_AddPushButton("Menu->Geometry", "Invert polygons", '\0', NULL, NULL, FALSE, NULL, _MCB_InvertPolygons, (EPointer)0);

 {
  EpImage*	LImage;
  EpImage*	LActiveImage;
  EpImage*	LArmImage;

  EpM_RGBA8ImageFromCStruct(LImage, Image_Triangulate);
  EpM_RGBA8ImageFromCStruct(LActiveImage, Image_TriangulateActive);
  EpM_RGBA8ImageFromCStruct(LArmImage, Image_TriangulateArm);
  E3d_TriangulateToolPanelButton=EGUI_AddPushButtonImg("Tool->Geometry", "Triangulate", '\0', NULL, NULL, FALSE, "Triangulate: Convert polygons of selected objects to triangles", _MCB_Tesselate, (EPointer)1, LImage, LActiveImage, LArmImage);
 }


 return(0);
}


//========================================
// Exit method of the plugin
//========================================
int Plugin_Exit()
{
 unsigned int	LC;

 for(LC=0;LC<_NumOfMenuButtons;LC++) if(_MenuButtons[LC]) EGUI_DestroyItem(_MenuButtons[LC]);

 if(E3d_TriangulateToolPanelButton) EGUI_DestroyItem(E3d_TriangulateToolPanelButton);

 return(0);
}
