/*======================================================================================================*/
/* Create a sphere and change it interactively								*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Dec-17 23:05:50									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <math.h>
#include <stdio.h>


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


#include <Xe/Matrix.h>
#include <Xe/Scale.h>
#include <Xe/PushButton.h>
#include <Xe/Separator.h>
#include <Xe/ToggleButton.h>

#include <Xe/CascadeButtonP.h>
#include <Xe/MenuP.h>


#include <Color/XColor.h>
#include <EGUI/PushButton.h>
#include <EGUI/Dialog.h>
#include <EGUI/Menu.h>

#include <E3D/3DWindow.h>



extern EpCImage		Image_Sphere;
extern EpCImage		Image_SphereActive;
extern EpCImage		Image_SphereArm;
extern EpCImage		Image_SphereBig;

#include "../Icons/handmade/GeoSphere.c"
#include "../Icons/handmade/UVPolySphere.c"



static EPlugin*		_Plugin=NULL;

enum
{
 E3dpSphereUVPOLY=0,
 E3dpSphereGEOSPHERE_POLY,
 E3dpSphereUVNURBS
};


static EguiItem		_Dialog=NULL;
static Widget		_SmoothW, _TypesOptionMenuW=NULL, _RadiusScaleW, _USectionsScaleW, _VSectionsScaleW;

static E3dModel*	_Model=NULL;
static E3dGeometry*	_MainGeometry=NULL;
static E3dGeometry*	_Geometry=NULL;

static E3d3DPosition*	_UnitSpherePoints=NULL;
static int		_NumOfPoints=0;

static EguiItem		_MenuButton=NULL, _ToolPanelButton=NULL;

static Arg		Args[256];
static Cardinal		ArgCnt;




typedef struct E3dSTriangle
{
 unsigned int	Vertices[3];
 unsigned int	Edges[3];
} E3dSTriangle;

typedef struct E3dSEdge
{
 unsigned int	Start, End, Bisector, BisectorEdge0, BisectorEdge1;
} E3dSEdge;



#define E3dpICOSA_X 0.525731112119133606
#define E3dpICOSA_Z 0.850650808352039932

static double E3dp_IcosaVertices[12][3]=
{    
 { -E3dpICOSA_X, 0.0, E3dpICOSA_Z}, { E3dpICOSA_X, 0.0, E3dpICOSA_Z}, {-E3dpICOSA_X, 0.0, -E3dpICOSA_Z}, { E3dpICOSA_X, 0.0, -E3dpICOSA_Z},    
 { 0.0, E3dpICOSA_Z, E3dpICOSA_X }, {0.0, E3dpICOSA_Z, -E3dpICOSA_X}, { 0.0, -E3dpICOSA_Z, E3dpICOSA_X}, {0.0, -E3dpICOSA_Z, -E3dpICOSA_X},    
 { E3dpICOSA_Z, E3dpICOSA_X, 0.0 }, {-E3dpICOSA_Z, E3dpICOSA_X, 0.0}, { E3dpICOSA_Z, -E3dpICOSA_X, 0.0}, {-E3dpICOSA_Z, -E3dpICOSA_X, 0.0} 
}; 

int	E3dpSphere_IcosaPolyIndices[20][3]=
{
 {  1,4,0 },{  4,9,0 },{  4,5,9 },{  8,5,4 },{ 1,8,4 },{ 1,10,8 },{ 10,3,8 },{  8,3,5 },{ 3,2,5 },{ 3 ,7,2 },
 { 3,10,7 },{ 10,6,7 },{ 6,11,7 },{ 6,0,11 },{ 6,1,0 },{ 10,1,6 },{ 11,0,9 },{ 2,11,9 },{ 5,2,9 },{ 11,2,7 }
};

int	E3dpSphere_IcosaPolyEdgeIndices[20][3]=
{
 { 0,1,2 },{ 3,4,1 },{ 5,6,3 },{ 7,5,8 },{ 9,8,0 },{ 10,11,9 },{ 12,13,11 },{ 13,14,7 },{ 15,16,14 },{ 17,18,15 },
 { 12,19,17 },{ 20,21,19 },{ 22,23,21 },{ 24,25,22 },{ 26,2,24 },{ 10,26,20 },{ 25,4,27 },{ 28,27,29 },{ 16,29,6 },{ 28,18,23 }
};

int	E3dpSphere_IcosaEdgeIndices[30][2]=
{
 {  1, 4 },{  0, 4 },{  0, 1 },{  4, 9 },{  0, 9 },{  4, 5 },{  5, 9 },{  5, 8 },{  4, 8 },{  1, 8 },
 {  1,10 },{  8,10 },{  3,10 },{  3, 8 },{  3, 5 },{  2, 3 },{  2, 5 },{  3, 7 },{  2, 7 },{  7,10 },
 {  6,10 },{  6, 7 },{  6,11 },{  7,11 },{  0, 6 },{  0,11 },{  1, 6 },{  9,11 },{  2,11 },{  2, 9 }
};




enum
{
 _DCB_RADIUS=EguiCUSTOM0,
 _DCB_U_SECTIONS,
 _DCB_V_SECTIONS,
 E3dCR_SET_SMOOTH
};



typedef struct
{
// E3dGeometryInfo part
//
 E3dGeometryClass*	Class;

// E3dSphereInfo part
//
 E3dCoordinate	Radius;

 int		Iterations, USections, VSections;
 int		Type;

 EBool		Smooth;
} E3dSphereInfo;


#define ROffset(field) (void*)(EPtrOffset(E3dSphereInfo, field))

static EResource _Resources[]=
{
 { "Radius",		EResFLOAT32,	ROffset(Radius), NULL },
 { "Iterations",	EResINT,	ROffset(Iterations), NULL },
 { "USections",		EResINT,	ROffset(USections), NULL },
 { "VSections",		EResINT,	ROffset(VSections), NULL },

 { "Type",		EResINT,	ROffset(Type), NULL },
 { "Smooth",		EResBOOLEAN,	ROffset(Smooth), NULL },
 { "",			EResNULL,	NULL, NULL }
};


static E3dSphereInfo	_Info;
static E3dGeometryPanel	_GeometryPanel;
static EBool		_CreateMode=FALSE;







/*==============================================*/
/* Compute Vertex coordinates for the Sphere	*/
/*==============================================*/
static void _SetVertices(E3dMesh* LMesh, E3dSphereInfo* LInfo)
{
 E3dVertex*	LVertices=LMesh->Vertices;
 E3dVertex*	LVertex;
 E3d3DPosition*	LUnitCirclePoints;
 E3dUV*		LUV;
 E3dCoordinate	LRadius, LU, LV,
		LTheta, LHeight, LDisc, LPhi;
 unsigned int	LC, LH,
		LVNum, LPNum;


 LRadius=LInfo->Radius;

 switch(LInfo->Type)
 {
  case E3dpSphereUVPOLY:
   {
    int	LUSect, LVSect;

    if(LMesh->UVs) LMesh->UVs=(E3dUV*)ERealloc(LMesh->UVs, sizeof(E3dUV) * LMesh->NumOfVertices);
    else LMesh->UVs=(E3dUV*)EMalloc(sizeof(E3dUV) * LMesh->NumOfVertices);

    LUV=LMesh->UVs;

    LUSect=LInfo->USections;
    LVSect=LInfo->VSections;

    LVertex=LVertices;
    LVertex->X=0.0;LVertex->Y=LRadius;LVertex->Z=0.0;LVertex++;
    LVertex->X=0.0;LVertex->Y=-LRadius;LVertex->Z=0.0;LVertex++;
    for(LH=1;LH<LVSect;LH++)
    {
     LV=(E3dCoordinate)LH/(E3dCoordinate)LVSect;
     LTheta=3.14159265358979323846264 * LV;
     LHeight=cos(LTheta)*LRadius;
     LDisc=sin(LTheta)*LRadius;


     for(LC=0;LC<LUSect;LC++)
     {
      LU=(E3dCoordinate)LC/(E3dCoordinate)LUSect;
      LPhi=E3dPI*2.0*LU+E3dPI*0.5;		// +90 degrees, so the "seam" is in the back (-Z)
      LVertex->X=cos(LPhi)*LDisc;LVertex->Y=LHeight;LVertex->Z=-sin(LPhi)*LDisc;
      LVertex++;

      LUV->U=1.0-LU;LUV->V=LV;
      LUV++;
     }
    }
   }
  break;

  case E3dpSphereGEOSPHERE_POLY:
   {
    int	LIterations;

    if(LMesh->UVs) { EFree(LMesh->UVs);LMesh->UVs=NULL; }

    LIterations=LInfo->Iterations;

    for(LC=0, LVNum=12, LPNum=20;LC<LIterations;LC++) { LVNum+=(LVNum+LPNum-2);LPNum*=4; }
    LVertex=LVertices;LUnitCirclePoints=_UnitSpherePoints;
    if(LUnitCirclePoints)
    {
     for(LC=0;LC<LVNum;LC++, LUnitCirclePoints++, LVertex++)  { LVertex->X=LUnitCirclePoints->X*LRadius;LVertex->Y=LUnitCirclePoints->Y*LRadius;LVertex->Z=LUnitCirclePoints->Z*LRadius; }
    }
   }
  break;
 }
}


/*======================================*/
/* Get a Sphere				*/
/*======================================*/
static E3dModel* _Get(E3dModel* LModel, E3dGeometry* LGeometry, E3dSphereInfo* LInfoV, unsigned int LFlags)
{
 E3dMesh*			LMesh;
 E3dPolyGroup*			LPolyGroup;
 E3dVertex*			LVertices;
 E3dPolygon*			LPolygon;
 E3dPolygon*			LPolygons;
 E3d3DPosition*			LPoint0;
 E3d3DPosition*			LPoint1;
 E3dSTriangle*			LTrianglesA;
 E3dSTriangle*			LTrianglesB;
 E3dSTriangle*			LTrianglesS;
 E3dSTriangle*			LTrianglesD;
 E3dSTriangle*			LTriangleS;
 E3dSTriangle*			LTriangleD;
 E3dSEdge*			LEdgesA;
 E3dSEdge*			LEdgesB;
 E3dSEdge*			LEdgesS;
 E3dSEdge*			LEdgesD;
 unsigned int*			LEdgesTS;
 unsigned int*			LVerticesTS;
 E3dSphereInfo*			LInfo;
 E3dCoordinate			LRadius;
 double				LLenRec, LX, LY, LZ;
 unsigned int			LNEdg, LNTri, LCED, LCIter;
 unsigned int			LVNum=0, LPNum=0, LENum=0, LC, LH;
 int				LUSect=0, LVSect=0, LIterations=0;
 EBool				LSmooth;


 if(LGeometry==NULL)
 {
  if((LModel=E3d_ModelAllocate("Sphere"))==NULL) return(NULL);
  if((LMesh=(E3dMesh*)E3d_ModelAddGeometry(LModel, E3dGEO_MESH, "sphere"))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }
  LMesh->LockCount=1;

  LInfo=(E3dSphereInfo*)E3d_GeometryInfoAdd((E3dGeometry*)LMesh, LInfoV->Class);
  if(LInfo)
  {
   memcpy(LInfo, LInfoV, sizeof(E3dSphereInfo));
  }

  if((LPolyGroup=E3d_MeshAddPolyGroup(LMesh))==NULL) { E3d_ModelHrcFree(LModel, TRUE);return(NULL); }
  LPolyGroup->VertexNormalType=E3dNormalPROCESS;
 }
 else
 {
  LMesh=(E3dMesh*)(LModel->Geometries[0]);
  LInfo=LInfoV;
  LPolyGroup=LMesh->PolyGroups[0];
 }

 LSmooth=LInfo->Smooth;
 LRadius=LInfo->Radius;

 switch(LInfo->Type)
 {
  case E3dpSphereUVPOLY:
   LUSect=LInfo->USections;
   LVSect=LInfo->VSections;
   LVNum=(LVSect-1)*LUSect+2;
   if(LUSect<3) LUSect=3;if(LVSect<2) LVSect=2;
   LPNum=LUSect*2+(LVSect-2)*LUSect;
  break;

  case E3dpSphereGEOSPHERE_POLY:
   LIterations=LInfo->Iterations;
   for(LC=0, LVNum=12, LENum=30, LPNum=20;LC<LIterations;LC++) { LVNum+=(LVNum+LPNum-2);LENum=LENum*2+LPNum*3;LPNum*=4; }

// Allocate vertices for the unit-sphere
//
   if(LVNum!=_NumOfPoints)
   {
    if(_UnitSpherePoints) { EFree(_UnitSpherePoints);_NumOfPoints=0; }
    if((_UnitSpherePoints=(E3d3DPosition*)EMalloc(sizeof(E3d3DPosition)*LVNum))!=NULL) _NumOfPoints=LVNum;
    else _NumOfPoints=0;
   }
  break;
 }

// Allocate Vertices for the Mesh
//
 if(LVNum==LMesh->NumOfVertices) LVertices=LMesh->Vertices;
 else
 {
  if(LMesh->Vertices) E3d_MeshFreeVertices(LMesh);
  if((LVertices=E3d_VerticesAllocate(LVNum, TRUE))!=NULL)
  {
   LMesh->Vertices=LVertices;LMesh->NumOfVertices=LVNum;
  }
  else LMesh->NumOfVertices=0;
 }

// Allocate Polygons for the Mesh
//
 if(LPNum==LPolyGroup->NumOfPolygons) LPolygons=LPolyGroup->Polygons;
 else
 {
  if(LPolyGroup->Polygons) E3d_PolyGroupFreePolygons(LPolyGroup);
  if((LPolygons=E3d_PolygonsAllocate(LPNum))!=NULL)
  {
   LPolyGroup->Polygons=LPolygons;LPolyGroup->NumOfPolygons=LPNum;
  }
 }

 if((LVertices!=NULL)&&(LPolygons!=NULL))
 {
  E3dVertexNode*	LVertexNode;
  E3dCoordinate		LS, LT, LSStep=1.0/(E3dCoordinate)(LInfo->USections), LTStep=1.0/(E3dCoordinate)(LInfo->VSections);


printf("LTStep %f\n",LTStep);fflush(stdout);

  switch(LInfo->Type)
  {
   case E3dpSphereUVPOLY:
    _SetVertices(LMesh, LInfo);
#define west(row, col)	(2+(row)*LUSect+(col))
#define east(row, col)	(2+(row)*LUSect+((col)+1)%LUSect)
    LPolygon=LPolygons;

// Triangles around North pole
//
    LT=LTStep;
    LS=0.0;
    for(LC=0;LC<LUSect;LC++, LS+=LSStep)
    {
     E3d_PolygonSetAsTriangle(LPolygon, east(0, LC), 0, west(0, LC));

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->S=LS+LSStep;LVertexNode->T=LT;LVertexNode++;
     LVertexNode->S=LS;LVertexNode->T=0.0;LVertexNode++;
     LVertexNode->S=LS;LVertexNode->T=LT;

     LPolygon++;
    }

    for(LH=0;LH<LVSect-2;LH++, LT+=LTStep)
    {
     LS=0.0;
     for(LC=0;LC<LUSect;LC++, LS+=LSStep)
     {
      E3d_PolygonSet(LPolygon, 4, west(LH+1, LC), east(LH+1, LC), east(LH, LC), west(LH, LC));
      LVertexNode=LPolygon->VertexNodes;

      LVertexNode->S=LS;LVertexNode->T=LT+LTStep;LVertexNode++;
      LVertexNode->S=LS+LSStep;LVertexNode->T=LT+LTStep;LVertexNode++;
      LVertexNode->S=LS+LSStep;LVertexNode->T=LT;LVertexNode++;
      LVertexNode->S=LS;LVertexNode->T=LT;

      LPolygon++;
     }
    }

    LS=0.0;
    for(LC=0;LC<LUSect;LC++, LS+=LSStep)
    {
     E3d_PolygonSetAsTriangle(LPolygon, 1, east(LVSect-2, LC), west(LVSect-2, LC));

     LVertexNode=LPolygon->VertexNodes;
     LVertexNode->S=LS;LVertexNode->T=LT+LTStep;LVertexNode++;
     LVertexNode->S=LS+LSStep;LVertexNode->T=LT;LVertexNode++;
     LVertexNode->S=LS;LVertexNode->T=LT;

     LPolygon++;
    }
   break;

   case E3dpSphereGEOSPHERE_POLY:
    LTrianglesA=(E3dSTriangle*)EMalloc(sizeof(E3dSTriangle)*LPNum);
    LTrianglesB=(E3dSTriangle*)EMalloc(sizeof(E3dSTriangle)*LPNum);
    LEdgesA=(E3dSEdge*)EMalloc(sizeof(E3dSEdge)*LENum);
    LEdgesB=(E3dSEdge*)EMalloc(sizeof(E3dSEdge)*LENum);

// Store original icosahedron vertices
//
    if((LTrianglesA!=NULL)&&(LTrianglesB!=NULL)&&(LEdgesA!=NULL)&&(LEdgesB!=NULL))
    {
     E3d3DPosition*	LUnitCirclePoints=_UnitSpherePoints;

     for(LH=0;LH<12;LH++)
     {
      LUnitCirclePoints[LH].X=E3dp_IcosaVertices[LH][0];
      LUnitCirclePoints[LH].Y=E3dp_IcosaVertices[LH][1];
      LUnitCirclePoints[LH].Z=E3dp_IcosaVertices[LH][2];
     }

     LTrianglesS=LTrianglesA;LTrianglesD=LTrianglesB;LEdgesS=LEdgesA;LEdgesD=LEdgesB;

// Copy icosa triangles to the current triangle list
//
     for(LC=0;LC<20;LC++)
     {
      LTrianglesS[LC].Vertices[0]=E3dpSphere_IcosaPolyIndices[LC][0];
      LTrianglesS[LC].Vertices[1]=E3dpSphere_IcosaPolyIndices[LC][1];
      LTrianglesS[LC].Vertices[2]=E3dpSphere_IcosaPolyIndices[LC][2];
      LTrianglesS[LC].Edges[0]=E3dpSphere_IcosaPolyEdgeIndices[LC][0];
      LTrianglesS[LC].Edges[1]=E3dpSphere_IcosaPolyEdgeIndices[LC][1];
      LTrianglesS[LC].Edges[2]=E3dpSphere_IcosaPolyEdgeIndices[LC][2];
     }

// Copy icosa edges to the current edge list
//
     for(LC=0;LC<30;LC++)
     {
      LEdgesS[LC].Start=E3dpSphere_IcosaEdgeIndices[LC][0];
      LEdgesS[LC].End=E3dpSphere_IcosaEdgeIndices[LC][1];
     }

     for(LCIter=0, LNEdg=30, LNTri=20;LCIter<LIterations;LCIter++, LNTri*=4)
     {
// Compute and normalize bi-sector points of the edges and add bi-sector edges to the new edge list
//
      LCED=0;
      for(LC=0;LC<LNEdg;LC++)
      {
       LPoint0=LUnitCirclePoints+LEdgesS[LC].Start;
       LPoint1=LUnitCirclePoints+LEdgesS[LC].End;
       LEdgesS[LC].Bisector=LH;

       LX=LPoint0->X+LPoint1->X;LY=LPoint0->Y+LPoint1->Y;LZ=LPoint0->Z+LPoint1->Z;

       LLenRec=sqrt(LX*LX+LY*LY+LZ*LZ);if(LLenRec>0.0) LLenRec=1.0/LLenRec;

       LUnitCirclePoints[LH].X=LX*LLenRec;LUnitCirclePoints[LH].Y=LY*LLenRec;LUnitCirclePoints[LH].Z=LZ*LLenRec;

       LEdgesS[LC].BisectorEdge0=LCED;
       LEdgesD[LCED].Start=LEdgesS[LC].Start;LEdgesD[LCED].End=LH;LCED++;
       LEdgesS[LC].BisectorEdge1=LCED;
       LEdgesD[LCED].Start=LEdgesS[LC].End;LEdgesD[LCED].End=LH;LCED++;
       LH++;
      }

// Subdivide triangles
//
      LTriangleD=LTrianglesD;
      for(LC=0, LTriangleS=LTrianglesS;LC<LNTri;LC++, LTriangleS++)
      {
       LVerticesTS=LTriangleS->Vertices;
       LEdgesTS=LTriangleS->Edges;
       LTriangleD->Vertices[0]=LVerticesTS[0];
       LTriangleD->Vertices[1]=LEdgesS[LEdgesTS[0]].Bisector;
       LTriangleD->Vertices[2]=LEdgesS[LEdgesTS[2]].Bisector;
       if(LEdgesD[LTriangleD->Edges[0]=LEdgesS[LEdgesTS[0]].BisectorEdge0].Start!=LVerticesTS[0]) LTriangleD->Edges[0]+=1;
       LTriangleD->Edges[1]=LCED;

       if(LEdgesD[LTriangleD->Edges[2]=LEdgesS[LEdgesTS[2]].BisectorEdge0].Start!=LVerticesTS[0]) LTriangleD->Edges[2]+=1;
       LEdgesD[LCED].Start=LEdgesS[LEdgesTS[0]].Bisector;LEdgesD[LCED].End=LEdgesS[LEdgesTS[2]].Bisector;LCED++;
       LTriangleD++;

       LTriangleD->Vertices[0]=LVerticesTS[1];
       LTriangleD->Vertices[1]=LEdgesS[LEdgesTS[1]].Bisector;
       LTriangleD->Vertices[2]=LEdgesS[LEdgesTS[0]].Bisector;

       if(LEdgesD[LTriangleD->Edges[0]=LEdgesS[LEdgesTS[1]].BisectorEdge0].Start!=LVerticesTS[1]) LTriangleD->Edges[0]+=1;
       LTriangleD->Edges[1]=LCED;

       if(LEdgesD[LTriangleD->Edges[2]=LEdgesS[LEdgesTS[0]].BisectorEdge0].Start!=LVerticesTS[1]) LTriangleD->Edges[2]+=1;
       LEdgesD[LCED].Start=LEdgesS[LEdgesTS[1]].Bisector;LEdgesD[LCED].End=LEdgesS[LEdgesTS[0]].Bisector;LCED++;
       LTriangleD++;

       LTriangleD->Vertices[0]=LVerticesTS[2];
       LTriangleD->Vertices[1]=LEdgesS[LEdgesTS[2]].Bisector;
       LTriangleD->Vertices[2]=LEdgesS[LEdgesTS[1]].Bisector;
       if(LEdgesD[LTriangleD->Edges[0]=LEdgesS[LEdgesTS[2]].BisectorEdge0].Start!=LVerticesTS[2]) LTriangleD->Edges[0]+=1;
       LTriangleD->Edges[1]=LCED;
       if(LEdgesD[LTriangleD->Edges[2]=LEdgesS[LEdgesTS[1]].BisectorEdge0].Start!=LVerticesTS[2]) LTriangleD->Edges[2]+=1;
       LEdgesD[LCED].Start=LEdgesS[LEdgesTS[2]].Bisector;LEdgesD[LCED].End=LEdgesS[LEdgesTS[1]].Bisector;LCED++;
       LTriangleD++;

       LTriangleD->Vertices[1]=LEdgesS[LEdgesTS[1]].Bisector;
       LTriangleD->Vertices[2]=LEdgesS[LEdgesTS[2]].Bisector;
       LTriangleD->Vertices[0]=LEdgesS[LEdgesTS[0]].Bisector;
       LTriangleD->Edges[2]=LCED-3;
       LTriangleD->Edges[0]=LCED-2;
       LTriangleD->Edges[1]=LCED-1;
       LTriangleD++;
      }
      LNEdg=LCED;

// Switch source and destination buffers
//
      if(LTrianglesS==LTrianglesA) { LTrianglesS=LTrianglesB;LTrianglesD=LTrianglesA;LEdgesS=LEdgesB;LEdgesD=LEdgesA; }
      else { LTrianglesS=LTrianglesA;LTrianglesD=LTrianglesB;LEdgesS=LEdgesA;LEdgesD=LEdgesB; }
     }
     if(LEdgesA) { EFree(LEdgesA);LEdgesA=NULL; }
     if(LEdgesB) { EFree(LEdgesB);LEdgesB=NULL; }

     LPolygon=LPolygons;
     for(LC=0;LC<LNTri;LC++)
     {
      E3d_PolygonSetAsTriangle(LPolygon+LC, LTrianglesS[LC].Vertices[0], LTrianglesS[LC].Vertices[1], LTrianglesS[LC].Vertices[2]);
     }
    }
    if(LTrianglesA) { EFree(LTrianglesA);LTrianglesA=NULL; }
    if(LTrianglesB) { EFree(LTrianglesB);LTrianglesB=NULL; }
    if(LEdgesA) { EFree(LEdgesA);LEdgesA=NULL; }
    if(LEdgesB) { EFree(LEdgesB);LEdgesB=NULL; }
    
    _SetVertices(LMesh, LInfo);
   break;
  }
  if(LInfo->Smooth)
  {
   LPolyGroup->VertexNormalType=E3dNormalAVERAGED;
   LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
  }
  else
  {
   LPolyGroup->VertexNormalType=E3dNormalNONE;
   LPolyGroup->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;
  }

  E3d_MeshRefreshNormals(LMesh, TRUE);
  E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, LFlags&(E3dGF_ALL-E3dGF_CALL_DESTROYPROCS));

  return(LModel);
 }
 return(NULL);
}


/*======================================*/
/* Set GUI for sphere type		*/
/*======================================*/
static void _SetGUIToType(E3dSphereInfo* LInfo)
{
 XeString	LXeStr;
 EWidthAlignRec	LWAs[3];
 int		LXSize, LC, LN;

 switch(LInfo->Type)
 {
  case E3dpSphereUVNURBS:
  case E3dpSphereUVPOLY:
   XeScaleSetRangeAndValue(_USectionsScaleW, 3, 720, LInfo->USections, False);
   XeScaleSetRangeAndValue(_VSectionsScaleW, 3, 360, LInfo->VSections, False);
   if((LXeStr=XeStringCreateSimple("U sections:"))!=NULL)
   {
    XtVaSetValues(_USectionsScaleW, XeNlabelString, LXeStr, NULL);
    XeStringFree(LXeStr);
   }
   XtVaSetValues(_USectionsScaleW, XeNbottomAttachment, XeATTACH_NONE, NULL);
   XtVaSetValues(_VSectionsScaleW, XeNbottomAttachment, XeATTACH_FORM, XeNbottomOffset, 4, NULL);
   XtManageChild(_VSectionsScaleW);
   LN=0;
   XtVaGetValues(LWAs[LN].W=_RadiusScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_USectionsScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_VSectionsScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   for(LC=1, LXSize=LWAs[0].XSize;LC<LN;LC++) { if(LWAs[LC].XSize>LXSize) LXSize=LWAs[LC].XSize; }
   for(LC=0;LC<LN;LC++) { XtVaSetValues(LWAs[LC].W, XeNleftOffset, LXSize-LWAs[LC].XSize+8, NULL); }
  break;

  case E3dpSphereGEOSPHERE_POLY:
   XeScaleSetRangeAndValue(_USectionsScaleW, 0, 7, LInfo->Iterations, False);
   if((LXeStr=XeStringCreateSimple("# of iterations:"))!=NULL)
   {
    XtVaSetValues(_USectionsScaleW, XeNlabelString, LXeStr, NULL);
    XeStringFree(LXeStr);
   }
   XeScaleSetValue(_USectionsScaleW, LInfo->Iterations, False);
   XtUnmanageChild(_VSectionsScaleW);
   XtVaSetValues(_VSectionsScaleW, XeNbottomAttachment, XeATTACH_NONE, NULL);
   XtVaSetValues(_USectionsScaleW, XeNbottomAttachment, XeATTACH_FORM, XeNbottomOffset, 4, NULL);

   LN=0;
   XtVaGetValues(LWAs[LN].W=_RadiusScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   XtVaGetValues(LWAs[LN].W=_USectionsScaleW, XeNlabelWidth, &(LWAs[LN].XSize), NULL);LN++;
   for(LC=1, LXSize=LWAs[0].XSize;LC<LN;LC++) { if(LWAs[LC].XSize>LXSize) LXSize=LWAs[LC].XSize; }
   for(LC=0;LC<LN;LC++) { XtVaSetValues(LWAs[LC].W, XeNleftOffset, LXSize-LWAs[LC].XSize+8, NULL); }
  break;
 }
}


/*======================================*/
/* Set sphere type			*/
/*======================================*/
static void _CB_SetType(Widget LW, XtPointer LClientData, XtPointer LCallData)
{
 E3dSphereInfo*	LInfo=(E3dSphereInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);


 if(LInfo->Type!=(int)LClientData)
 {
  if(_UnitSpherePoints) { EFree(_UnitSpherePoints);_UnitSpherePoints=NULL;_NumOfPoints=0; }
  LInfo->Type=(int)LClientData;
  _SetGUIToType(LInfo);

  _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

  E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
 }
}


/*======================================*/
/* Update dialog			*/
/*======================================*/
static void _UpdateDialog(E3dSphereInfo* LInfo)
{
 E3d_GeometryPanelUpdate(&_GeometryPanel, _MainGeometry);

 XeToggleButtonSetState(_SmoothW, LInfo->Smooth, False);

 EGUI_OptionMenuSetOptionByNumber(_TypesOptionMenuW, LInfo->Type);

 XeScaleSetValueD(_RadiusScaleW, LInfo->Radius, False);

 _SetGUIToType(LInfo);
}


/*======================================*/
/* Dialog callback			*/
/*======================================*/
static void _DCB(EguiItem LW, EPointer LClientData, EPointer LCallData)
{
 switch((int)LClientData)
 {
  case EguiOK:
   E3dTpl_GeoOK(_);
   if(_UnitSpherePoints) { EFree(_UnitSpherePoints);_UnitSpherePoints=NULL;_NumOfPoints=0; }
  break;

  case EguiADD:
   if(_UnitSpherePoints) { EFree(_UnitSpherePoints);_UnitSpherePoints=NULL;_NumOfPoints=0; }
   E3dTpl_GeoADD(_);
  break;

  case EguiCANCEL:
   E3dTpl_GeoCANCEL(_);
   if(_UnitSpherePoints) { EFree(_UnitSpherePoints);_UnitSpherePoints=NULL;_NumOfPoints=0; }
  break;
 }
}


/*======================================*/
/* Dialog Xt callback			*/
/*======================================*/
static void _XtDCB(Widget LW, XtPointer LClientData, XtPointer LCallData)
{
 E3dMesh*	LMesh;
 E3dPolyGroup*	LPolyGroup;
 E3dSphereInfo*	LInfo=(E3dSphereInfo*)E3d_GeometryInfoByClass(_Geometry, _Info.Class);


 switch((int)LClientData)
 {
  case _DCB_RADIUS:
    switch(LInfo->Type)
    {
     case E3dpSphereUVPOLY:
     case E3dpSphereGEOSPHERE_POLY:
      LMesh=(E3dMesh*)_Geometry;
      LInfo->Radius=((XeScaleCallbackStruct*)LCallData)->FloatValue;
      _SetVertices(LMesh, LInfo);
      E3d_GeometryUpdateForDisplay((E3dGeometry*)LMesh, E3dGF_SHAPE);
     break;
    }

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case _DCB_U_SECTIONS:
  case _DCB_V_SECTIONS:
    switch(LInfo->Type)
    {
     case E3dpSphereUVNURBS:
     case E3dpSphereUVPOLY:
      LInfo->USections=XeScaleGetValue(_USectionsScaleW);
      LInfo->VSections=XeScaleGetValue(_VSectionsScaleW);
     break;

     case E3dpSphereGEOSPHERE_POLY:
      LInfo->Iterations=XeScaleGetValue(_USectionsScaleW);
     break;
    }

    _Get(_Model, _Geometry, LInfo, E3dGF_ALL);

    E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
  break;

  case E3dCR_SET_SMOOTH:
    switch(LInfo->Type)
    {
     case E3dpSphereUVPOLY:
     case E3dpSphereGEOSPHERE_POLY:
      LMesh=(E3dMesh*)_Geometry;
      LPolyGroup=LMesh->PolyGroups[0];

      LInfo->Smooth=(EBool)(((XeToggleButtonCallbackStruct*)LCallData)->set);
      if(LInfo->Smooth)
      {
       LPolyGroup->VertexNormalType=E3dNormalAVERAGED;
       E3d_MeshRefreshNormals(LMesh, TRUE);
       LPolyGroup->Flags|=E3dUSE_VERTEX_NORMALS;
      }
      else
      {
       LPolyGroup->VertexNormalType=E3dNormalNONE;
       E3d_MeshRefreshNormals(LMesh, TRUE);
       LPolyGroup->Flags&=E3dALL-E3dUSE_VERTEX_NORMALS;
      }

      E3d_GeometryUpdateForDisplay(_Geometry, E3dGF_SHAPE);

      E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
     break;
    }
  break;
 }
}


/*======================================*/
/* Pop up the GetSphere dialog		*/
/*======================================*/
static void _PopupDialog(E3dSphereInfo* LInfo)
{
 if(_Dialog==NULL)
 {
  Widget	LTopMatrixW, LMatrixW, LTW;
  Widget	LIndentWs[7];			// For indenting the ScaleWidgets
  Widget	LTraverseWs[7];			// For TextField traversal
  EguiItem	LDialog=E3d_GeometryDialogCreate("Sphere v0.1.1", _MainGeometry, &_GeometryPanel, _DCB, &Image_SphereBig);
  int		LIndentN, LTraverseN;
  EguiVisual*	LVisual=EGUI_TruecolorVisual;
  EcRGBAiColor	LRGBAiColor;


  _Dialog=LDialog;

  LTopMatrixW=EGUI_DialogGetChild(LDialog, EguiDIALOG_WORK_AREA);
  EXtStart;
  EXtSetArg(XeNySpacing, 4);
  XtSetValues(LTopMatrixW, Args, ArgCnt);


  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  EXtSetArg(XeNxSpacing, 16);
  EXtSetArg(XeNwidthAdjustable, False);EXtSetArg(XeNxAlignment, XeALIGN_BEGINNING);
  LMatrixW=XtCreateManagedWidget("LMatrixW", xeMatrixWidgetClass, LTopMatrixW, Args, ArgCnt);

  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNselectColor, EGUI_HighlightColor);
  EXtSetArg(XeNshadowThickness, 0);
  EXtSetArg(XeNhighlightThickness, 0);
  EXtSetArg(XeNmarginLeft, 0);EXtSetArg(XeNmarginRight, 0);EXtSetArg(XeNmarginTop, 0);EXtSetArg(XeNmarginBottom, 0);
  EXtSetArg(XeNrecomputeSize, False);
  EXtSetArg(XeNfontList, EGUI_LabelFontList);
  EXtSetArg(XeNset, LInfo->Smooth);
  _SmoothW=XtCreateManagedWidget("Smooth", xeToggleButtonWidgetClass, LMatrixW, Args, ArgCnt);
  XtAddCallback(_SmoothW, XeNvalueChangedCallback, _XtDCB, (XtPointer)E3dCR_SET_SMOOTH);

  EXtStart;
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  EXtSetArg(XeNbuttonColor, EGUI_ButtonColor);
  EXtSetArg(XeNforeground, EGUI_ForegroundColor);
  EXtSetArg(XeNfontList, EGUI_MenuFontList);
  EXtSetArg(XeNlabelFontList, EGUI_LabelFontList);
  EXtSetArg(XeNcastShadowWidth, 4);
  EXtSetArg(XeNcascadeMarginHeight, 0);
  EXtSetArg(XeNmenuPaneBackground, EGUI_OptionMenuColor);
  if(LVisual)
  {
   EXtSetArg(XeNvisualInfo, LVisual->X_VisualInfo);EXtSetArg(XeNcolormap, LVisual->X_Colormap);
  }
  _TypesOptionMenuW=EGUI_OptionMenuCreate(LMatrixW, NULL, NULL, 0, LInfo->Type, "Type:", NULL, NULL, Args, ArgCnt);
  XtManageChild(_TypesOptionMenuW);

  Ec_GetRGBAiShade(&(E3dp_Prefs.MenuRGBAiColor), &LRGBAiColor, XeActiveColorFactor);

  if(LVisual)
  {
   Widget	LMenuPaneW=XeOptionMenuGetPane(_TypesOptionMenuW);
   Colormap	LColormap=LVisual->X_Colormap;

   EXtStart;
   EXtSetArg(XeNvisualInfo, LVisual->X_VisualInfo);
   EXtSetArg(XeNcolormap, LColormap);
   EXtSetArg(XeNfontList, EGUI_MenuFontList);
   EXtSetArg(XeNbackground, EGUI_OptionMenuColor);
   EXtSetArg(XeNforeground, EGUI_ForegroundColor);
//   EXtSetArg(XeNarmColor, Ec_XGetPRGBAColor(EGUI_Display, LColormap, 0x0050B000));
   EXtSetArg(XeNarmColor, Ec_XGetRGBAiColor(EGUI_Display, LColormap, &EGUI_MenuButtonArmRGBAiColor));
   EXtSetArg(XeNlabelType, XePIXMAP_STRING_HORIZONTAL);
   EXtSetArg(XeNlabelPixmap, EGUI_VaRGBAToPixmap(RGBA_UVPolySphere, UVPolySphere_XSize, UVPolySphere_YSize, EguiNbackground, &EGUI_BackgroundRGBAiColor, NULL));
   EXtSetArg(XeNarmPixmap, EGUI_VaRGBAToPixmap(RGBA_UVPolySphere, UVPolySphere_XSize, UVPolySphere_YSize, EguiNbackground, &EGUI_MenuButtonArmRGBAiColor, NULL));
   LTW=XtCreateManagedWidget("UV-Polygons", xePushButtonWidgetClass, LMenuPaneW, Args, ArgCnt);
   XtAddCallback(LTW, XeNactivateCallback, _CB_SetType, (XtPointer)E3dpSphereUVPOLY);

   EXtStart;
   EXtSetArg(XeNvisualInfo, LVisual->X_VisualInfo);
   EXtSetArg(XeNcolormap, LColormap);
   EXtSetArg(XeNfontList, EGUI_MenuFontList);
   EXtSetArg(XeNbackground, EGUI_OptionMenuColor);
   EXtSetArg(XeNforeground, EGUI_ForegroundColor);
   EXtSetArg(XeNarmColor, Ec_XGetRGBAiColor(EGUI_Display, LColormap, &EGUI_MenuButtonArmRGBAiColor));
   EXtSetArg(XeNlabelType, XePIXMAP_STRING_HORIZONTAL);
   EXtSetArg(XeNlabelPixmap, EGUI_VaRGBAToPixmap(RGBA_GeoSphere, GeoSphere_XSize, GeoSphere_YSize, EguiNbackground, &EGUI_BackgroundRGBAiColor, NULL));
   EXtSetArg(XeNarmPixmap, EGUI_VaRGBAToPixmap(RGBA_GeoSphere, GeoSphere_XSize, GeoSphere_YSize, EguiNbackground, &EGUI_MenuButtonArmRGBAiColor, NULL));
   LTW=XtCreateManagedWidget("Geo-sphere", xePushButtonWidgetClass, LMenuPaneW, Args, ArgCnt);
   XtAddCallback(LTW, XeNactivateCallback, _CB_SetType, (XtPointer)E3dpSphereGEOSPHERE_POLY);


   EGUI_OptionMenuSetOptionByNumber(_TypesOptionMenuW, LInfo->Type);
  }


  EXtStart;
  EXtSetArg(XeNshadowThickness, 2);
  EXtSetArg(XeNshadowType, XeSHADOW_ETCHED_IN);
  EXtSetArg(XeNbackground, EGUI_BackgroundColor);
  XtCreateManagedWidget("", xeSeparatorWidgetClass, LTopMatrixW, Args, ArgCnt);

  LTraverseN=0;
  LIndentN=0;
  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);EXtSetArg(XeNdecimalPoints, 4);
  EXtSetArg(XeNminimum, 1);EXtSetArg(XeNmaximum, 100000000);EXtSetArg(XeNvalue, (int)(LInfo->Radius*10000.0));
  EXtSetArg(XeNscaleMinimum, 100);EXtSetArg(XeNscaleMaximum, 1000000);
  EXtSetArg(XeNincrement, 10);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_RadiusScaleW=XtCreateManagedWidget("Radius:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_RadiusScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)_DCB_RADIUS);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);
  EXtSetArg(XeNminimum, 3);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, 12);
  EXtSetArg(XeNincrement, 1);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_USectionsScaleW=XtCreateManagedWidget("U sections:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_USectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)_DCB_U_SECTIONS);

  EXtStart;
  EXtSetArg(XeNtextFieldDigits, 8);
  EXtSetArg(XeNminimum, 2);EXtSetArg(XeNmaximum, 100);EXtSetArg(XeNvalue, 6);
  EXtSetArg(XeNincrement, 1);
  EXtSetArg(XeNorientation, XeHORIZONTAL);
  E3dp_SetScaleArgs(Args, &ArgCnt);
  LIndentWs[LIndentN++]=LTraverseWs[LTraverseN++]=_VSectionsScaleW=XtCreateManagedWidget("V sections:", xeScaleWidgetClass, LTopMatrixW, Args, ArgCnt);
  XtAddCallback(_VSectionsScaleW, XeNvalueChangedCallback, _XtDCB, (XtPointer)_DCB_V_SECTIONS);

  XeIndentWidgetsFromLeft(LIndentWs, LIndentN, 4);


// Set up TextField traversal
//
  XeRingTraversal(LTraverseWs, LTraverseN);

  _SetGUIToType(LInfo);
 }
 else _UpdateDialog(LInfo);

 EGUI_RaiseShell(_Dialog);_Plugin->LockCount+=1;
}


/*======================================*/
/* Menu callback (create new sphere)	*/
/*======================================*/
static void _MCB(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 E3dTpl_ACB_CHist(E3dSphere, _);
}


/*==============================================*/
/* EditProc for the "Sphere" Geometry type	*/
/*==============================================*/
static int _EditProc(E3dModel* LModel, E3dGeometry* LGeometry, E3dGeometryInfo* LInfo, int LLODIndex)
{
 E3dTplEditProc_LODCHist(E3dSphere, _);
}


/*======================================*/
/* Entry point of the plugin		*/
/*======================================*/
int Plugin_Init(EPlugin* LPlugin)
{
 E3dGeometryClass	LGeoClass=
   {
    "Sphere",			// Name
    sizeof(E3dSphereInfo),	// StructSize
    _EditProc,			// EditProc
    NULL,			// DestroyProc
    _Resources			// Resources
   };
 _Plugin=LPlugin;


 if((_Info.Class=E3d_GeometryClassRegister(&LGeoClass))!=NULL)
 {
  EpImage*	LImage;
  EpImage*	LActiveImage;
  EpImage*	LArmImage;

  EpM_RGBA8ImageFromCStruct(LImage, Image_Sphere);
  EpM_RGBA8ImageFromCStruct(LActiveImage, Image_SphereActive);
  EpM_RGBA8ImageFromCStruct(LArmImage, Image_SphereArm);
  _ToolPanelButton=EGUI_AddPushButtonImg("Tool->Create", "Sphere", '\0', NULL, NULL, TRUE, "Create sphere\nRight button:  dialog box for this tool", _MCB, (EPointer)0, LImage, LActiveImage, LArmImage);

  _Info.Smooth=TRUE;
  _Info.Radius=2.0;
  _Info.Type=E3dpSphereGEOSPHERE_POLY;
  _Info.Iterations=2;
  _Info.USections=16;
  _Info.VSections=12;

  _MenuButton=EGUI_AddPushButton("Menu->Create", "Sphere", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
 }

 return(0);
}


/*======================================*/
/* Exit method of the plugin		*/
/*======================================*/
int Plugin_Exit()
{
 if(_ToolPanelButton) EGUI_DestroyItem(_ToolPanelButton);
 if(_MenuButton) EGUI_DestroyItem(_MenuButton);

 if(_Info.Class) E3d_GeometryClassDeactivate(_Info.Class);

 return(0);
}
