//================================================================
// Interactive revolve tool
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Aug-12 23:59:39
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//================================================================
#include <assert.h>
#include <math.h>
#include <stdio.h>

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

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

#include <E3D/Matrix.h>
#include <E3D/Select.h>

#include <E3D/GL/Draw.h>

#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>
#include <E3D/UI/StatusPanel.h>
#include <E3D/UI/WidgetCombos.h>


char*  E_PluginInfo = "Create a Mesh surface by revolving a Spline.";

static EPlugin*  _Plugin = NULL;


static const char*  E3dpLng_Revolve_Pick_a_Spline_or_a_Face_as_the_new_profile = "Pick a Spline or a Face as the new profile";


#define E3dCREATE_EDGES  1

typedef struct
{
	XeWidget          TrianglesW, TangentsW, UVsW,
	                  SmoothingAngleW,
	                  StartAngleW, EndAngleW,
	                  SidesW;
	E3dpAxisSelector  AxisSelector;
} E3dResourcesGUI;


static XeDWidget  _MenuButton = NULL, _ToolBarButton = NULL;

EpIconDef3I(static, Revolve);


static const char  _NewObjectAddingMode = E3dNEW_INSTANCE;
//static const char  _NewObjectAddingMode = E3dNEW_MODEL;

static void _Copy(const E3dObject* PSObject, const E3dObjectComponent* PSInfo,  E3dObject* PDObject, E3dObjectComponent* PDInfo);

enum
{
	CR_TRIANGLES = XeCR_CUSTOM0,
	CR_TANGENTS,
	CR_UVS,
	CR_CHANGE_SMOOTHING_ANGLE,
	CR_SET_START_ANGLE,
	CR_SET_END_ANGLE,
	CR_SET_SIDES,
	CR_SET_CLOSE
};


#define RevCLOSE              0x01
#define RevAROUND_WORLD_AXIS  0x02
#define RevTRIANGLES          0x04
#define RevCREATE_TANGENTS    0x08


typedef struct
{
	E3dObjectComponentCore;

// Resources
//
	E3dGeometry*   InputGeometry;
	E3dCoordinate  StartAngle, EndAngle;
	float          SmoothingAngle;
	E3dCoord3      Axis;
	int            Sides;
	EBool          Triangles, CreateTangents, CreateUVs, UseProfileSegmentVisibility;
	EBool          Full, AroundWorldAxis, Close;

// Internal variables
//
	EInterface       InputInterface;     // This is added to the profile Geometry as an output node
	E3dGLCoordinate  GLAxis[2][3];
	EBool            GLAxisUptodate;
} E3dInfo;



#define ROffset(field)  (void*)(EPtrOffset(E3dInfo, field))
static EResource  _Resources[]=
{
	{ "Sides",            EResINT32,           ROffset(Sides) },
	{ "Axis",             E3dCOORDINATE_TYPE,  ROffset(Axis),      .Ext.Count= 3 },
//{ "AxX",              E3dCOORDINATE_TYPE,  ROffset(Axis.X) },
//{ "AxY",              E3dCOORDINATE_TYPE,  ROffset(Axis.Y) },
//{ "AxZ",              E3dCOORDINATE_TYPE,  ROffset(Axis.Z) },

	{ "ProfileGeometry",  EResOBJECT,          ROffset(InputGeometry) },

	{ "StartAngle",       E3dCOORDINATE_TYPE,  ROffset(StartAngle) },
	{ "EndAngle",         E3dCOORDINATE_TYPE,  ROffset(EndAngle) },
	{ "Tris",             EResBOOLEAN,         ROffset(Triangles) },
	{ "Tangents",         EResBOOLEAN,         ROffset(CreateTangents) },
	{ "UVs",              EResBOOLEAN,         ROffset(CreateUVs) },
	{ "UseProfileSegmentVisibility", EResBOOLEAN,  ROffset(UseProfileSegmentVisibility) },
	{ "SmoothingAngle",   EResFLOAT32,         ROffset(SmoothingAngle) },
};
#undef ROffset



static E3dObjectComponentClass*  _ObjectComponent_class = NULL;

//========================================
// Set vertex coordinates
//========================================
static int* _SetVertices(E3dMesh* PMesh, E3dInfo* PInfo, E3dCoord3** PVertexTangentsRet, const EBool PSetVertices)
{
	E3dMatrix   LSplineToRotMatrix;
	E3dMatrix   LVertexMatrix;
	E3dCoord3*  LVertexTangents = NULL;
	int*        LVertexReindexingTable = NULL;
	EBool       LFull = PInfo->Full, LUsingDefaultAxis;

	const E3dCoordinate  LStartAngle = PInfo->StartAngle, LEndAngle = PInfo->EndAngle;

	E3dSpline*  LProfileSpline = E_DynamicCast(PInfo->InputGeometry, E3dSpline);if(LProfileSpline == NULL) return(NULL);
	if(LProfileSpline == NULL || LProfileSpline->SLinearSegments == NULL) return(NULL);

	const int  LVPoints = E3dSpline_NumOfLinearSegmentPoints(LProfileSpline);
	const int  LSides = PInfo->Sides;

	int  LUPoints;
	if(LFull) LUPoints = LSides;
	else LUPoints = LSides + 1;

	E3dCoord3		LAxis = PInfo->Axis;
	if(LAxis.X == 0.0 && LAxis.Y == 0.0 && LAxis.Z == 0.0) LAxis.Y = 1.0;
	E3d_Normalize3_v(LAxis);


	E3dMatrix_LoadIdentity(LVertexMatrix);
	E3dMatrix_LoadIdentity(LSplineToRotMatrix);

	const int		LDimensions = LProfileSpline->Dimensions;

	E3dCoordinate*  LLinSegments;

// If the revolution is not around the Y axis, transform the contour coordinates accordingly
//
	if(LAxis.X == 0.0 && LAxis.Y == 1.0 && LAxis.Z == 0.0)
	{
		LLinSegments = LProfileSpline->SLinearSegments;

		LUsingDefaultAxis = TRUE;
	}
	else
	{
		E3dCoord3  LBiAxis, LTrAxis;

		switch(E3d_PrincipalAxis_v(LAxis))
		{
			case E3dX:          LBiAxis = E3dCoord3_s(0.0, 1.0, 0.0);break;
			case E3dY:          LBiAxis = E3dCoord3_s(1.0, 0.0, 0.0);break;
			case E3dZ:default:  LBiAxis = E3dCoord3_s(0.0, 1.0, 0.0);break;
		}

		E_Cross3_vvv(LBiAxis,  LAxis,  LTrAxis);E3d_Normalize3_v(LTrAxis);

		E_Cross3_vvs(LAxis,  LTrAxis,  LSplineToRotMatrix[M00], LSplineToRotMatrix[M10], LSplineToRotMatrix[M20]);
		LSplineToRotMatrix[M01] = LAxis.X;  LSplineToRotMatrix[M11] = LAxis.Y;  LSplineToRotMatrix[M21] = LAxis.Z;
		LSplineToRotMatrix[M02] = LTrAxis.X;LSplineToRotMatrix[M12] = LTrAxis.Y;LSplineToRotMatrix[M22] = LTrAxis.Z;

		E3dMatrix_Invert3x3(LSplineToRotMatrix, LVertexMatrix);

		ENewNClr(LDimensions * LVPoints, LLinSegments);

		EIndex  LC = 0;const E3dCoordinate*  LLinSegmentIn = LProfileSpline->SLinearSegments;E3dCoordinate*  LLinSegment = LLinSegments;for(;LC < LVPoints;LC++, LLinSegmentIn += LDimensions, LLinSegment += LDimensions)
		{
			EMatrix_Transform3x3_aa(LSplineToRotMatrix,  LLinSegmentIn,  LLinSegment);
		}

		LUsingDefaultAxis = FALSE;
	}

	if(PSetVertices)
	{
		{
			ENewN(PMesh->Vertices.Count, LVertexReindexingTable);
			int*  LIndexPtr = LVertexReindexingTable;

// See if we need to merge Vertices too close to the Y axis
//
			{
				EIndex  LIndex = 0;
				EIndex  LUC = 0;for(;LUC < LUPoints;LUC++)
				{
					EIndex  LVI = 0;for(LVI = 0;LVI < LVPoints;LVI++, LIndex++, LIndexPtr++)
					{
						*LIndexPtr = LIndex;
					}
				}
			}


#define EPSILON	0.0001
			EIndex  LVI = 0;E3dCoordinate*  LLinSegment = LLinSegments;for(;LVI < LVPoints;LVI++, LLinSegment += LDimensions)
			{
				const EIndex  LIndex = LVI;
				LIndexPtr = LVertexReindexingTable + LIndex;

				E3dCoordinate  LX = LLinSegment[E3dX];
				if(LX < 0.0) LX = 0.0;

				if(LX < EPSILON)
				{
					EIndex  LUC = 0;for(;LUC < LUPoints;LUC++, LIndexPtr += LVPoints) *LIndexPtr = LIndex;
				}
			}
		}

		const E3dCoordinate  LAngleStep = (LEndAngle - LStartAngle) / ((E3dCoordinate)LSides);

		{
			E3dCoordinate   LAngle = LStartAngle;

			EIndex  LUC = 0;E3dVertex*  LVertex = PMesh->Vertices.Items;const E3dVertexSize  LVertexSize = PMesh->VertexSize;for(;LUC < LUPoints;LUC++, LAngle += LAngleStep)
			{
				EIndex  LVI = 0;E3dCoordinate*  LLinSegment = LLinSegments;for(;LVI < LVPoints;LVI++, LLinSegment += LDimensions)
				{
					E3dCoordinate  LX = LLinSegment[E3dX];
					if(LX < 0.0) LX = 0.0;

					const E3dCoordinate  LA = LAngle * E3dDEGREES_TO_RADIANS;
					LVertex->Position = E3dCoord3_s(E3d_Cos(LA) * LX, LLinSegment[E3dY], -E3d_Sin(LA) * LX);
					EStruct_Inc(LVertex, LVertexSize);
				}
			}
		}

		if(PInfo->CreateTangents)
		{
			ENewN(PMesh->Vertices.Count, LVertexTangents);

			E3dCoord3*  LVertexTangent = LVertexTangents;
			E3dCoordinate  LAngle = LStartAngle + 90.0;
			EIndex  LUC = 0;for(;LUC < LUPoints;LUC++, LAngle += LAngleStep)
			{
				EIndex  LVI = 0;for(;LVI < LVPoints;LVI++)
				{
					const E3dCoordinate  LA = LAngle*E3dDEGREES_TO_RADIANS;
					*LVertexTangent = E3dCoord3_s(E3d_Cos(LA), 0.0, -E3d_Sin(LA));
					LVertexTangent++;
				}
			}
		}

// If the revolution is not around the Y axis, transform the Vertex coordinates accordingly
//
		if(!LUsingDefaultAxis)
		{
			const EIndex  LVN = PMesh->Vertices.Count;
			E3dVertex*    LVertex = PMesh->Vertices.Items;const E3dVertexSize  LVertexSize = PMesh->VertexSize;

			{
				EIndex  LVI = 0;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LVertexSize))
				{
					EMatrix_Transform3x3_v(LVertexMatrix, LVertex->Position);
				}
			}

// Transform tangents
//
			if(LVertexTangents)
			{
				E3dCoord3*	LVertexTangent = LVertexTangents;

				EIndex  LVI = 0;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LVertexSize), LVertexTangent++)
				{
					EMatrix_Transform3x3_p(LVertexMatrix, LVertexTangent);
				}
			}
		}
	}


// Update GLAxis
	{
		E3dCoordinate   LYMin, LYMax;
		E3dCoordinate*  LLinSegment = LLinSegments;

		LYMin = LYMax = LLinSegment[E3dY];

		EIndex  LC = 1;for(;LC < LVPoints;LC++, LLinSegment += LDimensions)
		{
			const E3dCoordinate  LY = LLinSegment[E3dY];
			if(LYMin > LY) LYMin = LY;
			if(LYMax < LY) LYMax = LY;
		}

		{
			const E3dCoordinate  LAxisLength = LYMax - LYMin;

			EMatrix_Transform3x3_sa(LVertexMatrix, 0.0, LYMin - LAxisLength * 0.16, 0.0,  PInfo->GLAxis[0]);
			EMatrix_Transform3x3_sa(LVertexMatrix, 0.0, LYMax + LAxisLength * 0.16, 0.0,  PInfo->GLAxis[1]);
		}

		PInfo->GLAxisUptodate = TRUE;
	}

// If we're not using the default axis, this array was allocated here, so we must free it
//
	if(!LUsingDefaultAxis) EFree(LLinSegments);

	if(PVertexTangentsRet) *PVertexTangentsRet = LVertexTangents;

	return(LVertexReindexingTable);
}


#define _M_Add2Tris(pV0, pV1, pV2, pV3,  pUI, pVI) do\
{\
	const E3dVertexIndex  l0 = LVertexReindexingTable[pV0];\
	const E3dVertexIndex  l1 = LVertexReindexingTable[pV1];\
	const E3dVertexIndex  l2 = LVertexReindexingTable[pV2];\
	const E3dVertexIndex  l3 = LVertexReindexingTable[pV3];\
	if(l0 == l1)\
	{\
		if(l2 != l3)\
		{\
			E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l2, l3);\
			LPolygon++;\
		}\
	}\
	else if(l2 == l3)\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l1, l2);\
		LPolygon++;\
	}\
	else\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVertexReindexingTable[pV0], LVertexReindexingTable[pV1], LVertexReindexingTable[pV2]);\
		LPolygon++;\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVertexReindexingTable[pV2], LVertexReindexingTable[pV3], LVertexReindexingTable[pV0]);\
		LPolygon++;\
	}\
} while(0)


// Edges around the profile Spline's starting point, when the Spline's starting point touches the revolve axis and the quads degenerate to triangles
//
#define _EdgesT1(pUI, pVI) do\
{\
	if(l3 > l0) { LEdge->Start = l0;LEdge->End = l3; }\
	else { LEdge->Start = l3;LEdge->End = l0; }\
/*printf("%s() %s:%d  T1  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
	if((pUI) % LUEdgeVisibilityStep != 0) LEdge->Flags &= ~E3dEdgeVISIBLE;\
	LEdge++;\
\
	if((!lPathClosed) && pUI == lPathLastSegment)\
	{\
		const EIndex  l3b = l3 + lProfilePointCount;\
		if(l3b > l0) { LEdge->Start = l0;LEdge->End = l3b; }\
		else { LEdge->Start = l3b;LEdge->End = l0; }\
/*printf("%s() %s:%d  T1b %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
		LEdge++;\
	}\
\
	if(l3 > l2) { LEdge->Start = l2;LEdge->End = l3; }\
	else { LEdge->Start = l3;LEdge->End = l2; }\
	if(!LVEdgesVisibility[pVI]) LEdge->Flags &= ~E3dEdgeVISIBLE;\
/*printf("%s() %s:%d  t1  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
	LEdge++;\
} while(0)


// Edges around the profile Spline's end point, when the Spline's end point touches the revolve axis and the quads degenerate to triangles
//
#define _EdgesT2(pUI, pVI) do\
{\
	if(l2 > l0) { LEdge->Start = l0;LEdge->End = l2; }\
	else { LEdge->Start = l2;LEdge->End = l0; }\
/*printf("%s() %s:%d  T2  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
	if((pUI) % LUEdgeVisibilityStep != 0) LEdge->Flags &= ~E3dEdgeVISIBLE;\
	LEdge++;\
\
	if((!lPathClosed) && pUI == lPathLastSegment)\
	{\
		const EIndex  l0b = l0 + lProfilePointCount;\
		if(l2 > l0b) { LEdge->Start = l0b;LEdge->End = l2; }\
		else { LEdge->Start = l2;LEdge->End = l0b; }\
/*printf("%s() %s:%d  T2b %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
		LEdge++;\
	}\
\
	if(l1 > l0) { LEdge->Start = l0;LEdge->End = l1; }\
	else { LEdge->Start = l1;LEdge->End = l0; }\
	if(!LVEdgesVisibility[pVI]) LEdge->Flags &= ~E3dEdgeVISIBLE;\
/*printf("%s() %s:%d  t2  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);*/\
	LEdge++;\
} while(0)


// Edges for quads
//
static inline E3dEdge* _EdgesQ(E3dEdge* PEdge,  const EIndex PUC, const EBool PUClosed, const EIndex PULastSegment, const EIndex PUEdgeVisibilityStep,  const EIndex PVC, const EBool PVClosed, const EIndex PVLastSegment, const EIndex PVPointCount, const EBool* PVEdgesVisibility,  const E3dVertexIndex PV0, const E3dVertexIndex PV1, const E3dVertexIndex PV2, const E3dVertexIndex PV3)
{
// Along V Edge
//
	PEdge->Start = PV0;PEdge->End = PV3;
//printf("%s() %s:%d  Q   %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, PV0, PV3);fflush(stdout);
	if(PUC % PUEdgeVisibilityStep != 0) PEdge->Flags &= ~E3dEdgeVISIBLE;
	PEdge++;

	if((!PUClosed) && PUC == PULastSegment)
	{
		if(PV3 > PV0) { PEdge->Start = PV0 + PVPointCount;PEdge->End = PV3 + PVPointCount; }
		else { PEdge->Start = PV3 + PVPointCount;PEdge->End = PV0 + PVPointCount; }
//printf("%s() %s:%d  Qb  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, PEdge->Start, PEdge->End);fflush(stdout);
		PEdge++;
	}

// Along U Edge
//
	if(PV1 > PV0) { PEdge->Start = PV0;PEdge->End = PV1; }
	else { PEdge->Start = PV1;PEdge->End = PV0; }
	if(!PVEdgesVisibility[PVC]) PEdge->Flags &= ~E3dEdgeVISIBLE;
//printf("%s() %s:%d  q   %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, PEdge->Start, PEdge->End);fflush(stdout);
	PEdge++;

	if((!PVClosed) && PVC == PVLastSegment)
	{
		if(PV1 > PV0) { PEdge->Start = PV0 + 1;PEdge->End = PV1 + 1; }
		else { PEdge->Start = PV1 + 1;PEdge->End = PV0 + 1; }
		PEdge++;
	}

	return(PEdge);
}


/*
#define _EdgesQ(pUI, pVI)\
do {\
	LEdge->Start = l0;LEdge->End = l3;\
@*printf("%s() %s:%d  Q   %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, l0, l3);fflush(stdout);@/\
	if((pUI) % LUEdgeVisibilityStep != 0) LEdge->Flags &= ~E3dEdgeVISIBLE;\
	LEdge++;\
\
	if((!lPathClosed) && pUI == lPathLastSegment)\
	{\
		if(l3 > l0) { LEdge->Start = l0 + lProfilePointCount;LEdge->End = l3 + lProfilePointCount; }\
		else { LEdge->Start = l3 + lProfilePointCount;LEdge->End = l0 + lProfilePointCount; }\
@*printf("%s() %s:%d  Qb  %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);@/\
		LEdge++;\
	}\
\
	if(l1 > l0) { LEdge->Start = l0;LEdge->End = l1; }\
	else { LEdge->Start = l1;LEdge->End = l0; }\
@*printf("%s() %s:%d  q   %2d-%2d\n", __FUNCTION__, __FILE__, __LINE__, LEdge->Start, LEdge->End);fflush(stdout);@/\
	LEdge++;\
} while(0)
*/


#define _M_AddQuad(pV0, pV1, pV2, pV3,  pUI, pVI) do\
{\
	const EIndex  llUI = pUI, llVI = pVI;\
	const E3dVertexIndex  l0 = LVertexReindexingTable[pV0];\
	const E3dVertexIndex  l1 = LVertexReindexingTable[pV1];\
	const E3dVertexIndex  l2 = LVertexReindexingTable[pV2];\
	const E3dVertexIndex  l3 = LVertexReindexingTable[pV3];\
	if(l0 == l1)\
	{\
		if(l2 != l3)\
		{\
			E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l2, l3);\
			LPolygon++;\
			_EdgesT1(llUI, llVI + 1);\
		}\
	}\
	else if(l2 == l3)\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l1, l2);\
		LPolygon++;\
		_EdgesT2(llUI, llVI);\
	}\
	else\
	{\
		E3dITriangle*  lITriangle;\
\
		E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, l0, l1, l2, l3);\
		ENewN(2, LPolygon->ITriangles);lITriangle = LPolygon->ITriangles;\
		LPolygon->ITriangles_Count = 2;\
		lITriangle->VertexNodes[0] = 0;lITriangle->VertexNodes[1] = 1;lITriangle->VertexNodes[2] = 2;lITriangle++;\
		lITriangle->VertexNodes[0] = 2;lITriangle->VertexNodes[1] = 3;lITriangle->VertexNodes[2] = 0;\
		LPolygon++;\
		LEdge = _EdgesQ(LEdge,  llUI, lPathClosed, lPathLastSegment, LUEdgeVisibilityStep,  llVI, lProfileClosed, lProfileLastSegment, lProfilePointCount, LVEdgesVisibility,  l0, l1, l2, l3);\
	}\
} while(0)


#define _M_Add2TrisUV(pV0, pV1, pV2, pV3,  pUI, pVI) do\
{\
	const EIndex  llVI = pVI;\
	const E3dVertexIndex  l0 = LVertexReindexingTable[pV0];\
	const E3dVertexIndex  l1 = LVertexReindexingTable[pV1];\
	const E3dVertexIndex  l2 = LVertexReindexingTable[pV2];\
	const E3dVertexIndex  l3 = LVertexReindexingTable[pV3];\
	if(l0 == l1)\
	{\
		if(l2 != l3)\
		{\
			E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l2, l3);\
			E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
			E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
			lUV->X = lU;lUV->Y = LVTable[llVI];             EStruct_Inc(lUV, LVertexNodeSize);\
			lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];EStruct_Inc(lUV, LVertexNodeSize);\
			lUV->X = lU;lUV->Y = LVTable[llVI + 1];\
			LPolygon++;\
		}\
	}\
	else if(l2 == l3)\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l1, l2);\
		E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
		E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
		lUV->X = lU;lUV->Y = LVTable[llVI];         EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];\
		LPolygon++;\
	}\
	else\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVertexReindexingTable[pV0], LVertexReindexingTable[pV1], LVertexReindexingTable[pV2]);\
		E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
		E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
		lUV->X = lU;lUV->Y = LVTable[llVI];         EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];\
		LPolygon++;\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVertexReindexingTable[pV2], LVertexReindexingTable[pV3], LVertexReindexingTable[pV0]);\
		lVertexNode = LPolygon->VertexNodes;\
		lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU;lUV->Y = LVTable[llVI + 1];        EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU;lUV->Y = LVTable[llVI];\
		LPolygon++;\
	}\
} while(0)


#define _M_AddQuadUV(pV0, pV1, pV2, pV3,  pUI, pVI) do\
{\
	const EIndex  llUI = pUI, llVI = pVI;\
	const E3dVertexIndex  l0 = LVertexReindexingTable[pV0];\
	const E3dVertexIndex  l1 = LVertexReindexingTable[pV1];\
	const E3dVertexIndex  l2 = LVertexReindexingTable[pV2];\
	const E3dVertexIndex  l3 = LVertexReindexingTable[pV3];\
	if(l0 == l1)\
	{\
		if(l2 != l3)\
		{\
			E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l2, l3);\
			E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
			E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
			lUV->X = lU;lUV->Y = LVTable[llVI];             EStruct_Inc(lUV, LVertexNodeSize);\
			lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];EStruct_Inc(lUV, LVertexNodeSize);\
			lUV->X = lU;lUV->Y = LVTable[llVI + 1];\
			LPolygon++;\
			_EdgesT1(llUI, llVI + 1);\
		}\
	}\
	else if(l2 == l3)\
	{\
		E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, l0, l1, l2);\
		E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
		E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
		lUV->X = lU;lUV->Y = LVTable[llVI];         EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];\
		LPolygon++;\
		_EdgesT2(llUI, llVI);\
	}\
	else\
	{\
		E3dITriangle*  lITriangle;\
\
		E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, l0, l1, l2, l3);\
		ENewN(2, LPolygon->ITriangles);lITriangle = LPolygon->ITriangles;\
		LPolygon->ITriangles_Count = 2;\
		lITriangle->VertexNodes[0] = 0;lITriangle->VertexNodes[1] = 1;lITriangle->VertexNodes[2] = 2;lITriangle++;\
		lITriangle->VertexNodes[0] = 2;lITriangle->VertexNodes[1] = 3;lITriangle->VertexNodes[2] = 0;\
		E3dVertexNode*  lVertexNode = LPolygon->VertexNodes;\
		E3dCoord2*  lUV = (E3dCoord2*)EOffsetToPtr(lVertexNode, LUVOffsetInVtxNode);\
		lUV->X = lU;lUV->Y = LVTable[llVI];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI];    EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU + LUStep;lUV->Y = LVTable[llVI + 1];EStruct_Inc(lUV, LVertexNodeSize);\
		lUV->X = lU;lUV->Y = LVTable[llVI + 1];\
		LPolygon++;\
		LEdge = _EdgesQ(LEdge,  llUI, lPathClosed, lPathLastSegment, LUEdgeVisibilityStep,  llVI, lProfileClosed, lProfileLastSegment, lProfilePointCount, LVEdgesVisibility,  l0, l1, l2, l3);\
	}\
} while(0)


//========================================
// Update method
//========================================
static E3dObject* _Update1(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	E3dMesh*       LMesh;
	E3dPolyGroup*  LPolyGroup;
	E3dInfo*       LComponent;

	if(PObject == NULL)
	{
		E_New(E3dMesh, LMesh);
		PObject = (E3dObject*)LMesh;

		LComponent = (E3dInfo*)E3dObjectComponent_Add(PObject, _ObjectComponent_class);
		if(PComponentTemplate && PComponentTemplate->Class == _ObjectComponent_class)
		{
			_Copy(PObject, PComponentTemplate, PObject, (E3dObjectComponent*)LComponent);
		}

		E3dBaseObject_AddOutput((E3dBaseObject*)LComponent->InputGeometry, &LComponent->InputInterface);

		if((LPolyGroup = E3dMesh_PolyGroupAdd(LMesh)) == NULL) { E_Delete(LMesh);return(NULL); }
		LPolyGroup->VertexNormalType = E3dNormalFROM_SMOOTHING_ANGLE;
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);

		LMesh = (E3dMesh*)PObject;
		LComponent = (E3dInfo*)E3dObjectComponent_ByClass(PObject, _ObjectComponent_class, FALSE);

		if(!PCheckOnly && LComponent->AttributeChangeCount == 0 && LComponent->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail

		if(LMesh->PolyGroups.Count == 0)
		{
			LPolyGroup = E3dMesh_PolyGroupAdd(LMesh);
		}
		else LPolyGroup = LMesh->PolyGroups.Items[0];

// When a Scene is loaded, Update methods are called to create this link
// The Geometry will be up-to-date, so if the link wasn't there yet, just exit
// We should probably add a 3rd argument to this function ("Action=LINK"), so
// even input-less ComponentClasses don't rebuild their Geometry.
//
		if(PCheckOnly && LComponent->InputGeometry)
		{
			if(E3dBaseObject_AddOutput((E3dBaseObject*)LComponent->InputGeometry, &LComponent->InputInterface) == E_SUCCESS)
			{
				return(PObject);
			}
		}
	}

	const E3dSpline*  LProfileSpline = (E3dSpline*)(LComponent->InputGeometry);

	if(LProfileSpline == NULL || LProfileSpline->SNumOfLinearSegments < 1) return(NULL);

	const E3dCoordinate  LStartAngle = LComponent->StartAngle;
	const E3dCoordinate  LEndAngle   = LComponent->EndAngle;

	const EBool          LFull = (LEndAngle - LStartAngle) == 360.0 ? TRUE : FALSE;

	LComponent->Full = LFull;

	const int            LVPoints  = E3dSpline_NumOfLinearSegmentPoints(LProfileSpline);
	const int            LSegments = LProfileSpline->SNumOfLinearSegments;

	const int            LSides = LComponent->Sides;

	if(PCheckOnly || LComponent->InputGeometry == NULL) return(NULL);

	const EIndex  LUPoints = LFull ? LSides : LSides + 1;

	EIndex  LNumOfVertices = LUPoints * LVPoints;
	EIndex  LNumOfEdges = LUPoints * LSegments + LVPoints * LSides;
	EIndex  LNumOfPolygons = LSides * LSegments;

	if(LComponent->Triangles) LNumOfPolygons *= 2;

//printf("LUPoints %d, LVPoints %d, LNumOfVertices %d, LNumOfPolygons %d\n", LUPoints, LVPoints, LNumOfVertices, LNumOfPolygons);fflush(stdout);

	if(LMesh)
	{
#if E3dCREATE_EDGES
		const EBool  LCreateEdges = !LComponent->Triangles;
		LMesh->SaveEdges = TRUE;
#else
		const EBool  LCreateEdges = FALSE;
#endif

		E3dMesh_FreeCaches(LMesh);
		E3dMesh_EdgesFree(LMesh);

		int  LUVOffsetInVtxNode = -1;

		E3dPolyGroup_VertexAttributeAdd(LPolyGroup,  NULL, E3dVtxAttribute_Normal, 0,  NULL);

// Make sure that the VertexNodes have UV
//
		if(LComponent->CreateUVs)
		{
			LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(LPolyGroup,  E3dVtxAttribute_UV, 0);

			if(LUVOffsetInVtxNode < 0)
			{
				E3dPolyGroup_VertexAttributeAdd(LPolyGroup,  NULL, E3dVtxAttribute_UV, 0,  NULL);
				LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(LPolyGroup,  E3dVtxAttribute_UV, 0);
			}
		}
		else E3dPolyGroup_VertexAttributeDel(LPolyGroup,  E3dVtxAttribute_UV, 0);


// Allocate Vertices for the Mesh
//
		if(LNumOfVertices != LMesh->Vertices.Count)
		{
			E3dMesh_VerticesFree(LMesh);
			E3dMesh_VerticesAdd(LMesh, LNumOfVertices);
		}

		EIndex  LUEdgeVisibilityStep = 1;

		if(LCreateEdges)
		{
// Allocate Edges for the Mesh
//
			if(LNumOfEdges != LMesh->Edges.Count) E3dMesh_EdgesNew(LMesh, LNumOfEdges, TRUE);

// Find integer divider of LSegments, to determine which V Edges will be visible
//
			if(LSides >= 8)
			{
				LUEdgeVisibilityStep = 1;
				{
					EIndex  LC = LSides / 4;for(;LC > 0;LC--)
					{
						if(LSides % LC == 0) { LUEdgeVisibilityStep = LC;break; }
					}
				}

				if(LUEdgeVisibilityStep == 1)
				{
					if(LSides % 3 == 0) LUEdgeVisibilityStep = 3;
					else if(LSides % 2 == 0) LUEdgeVisibilityStep = 2;
				}
//printf("%s() %s:%d  LUEdgeVisibilityStep %d\n", __FUNCTION__, __FILE__, __LINE__, (int)LUEdgeVisibilityStep);fflush(stdout);
			}
			else LUEdgeVisibilityStep = 1;
		}


// Allocate Polygons for the Mesh
//
		{
			E3dPolygon*  LPolygons;

			if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);

			LPolygons = E3dPolygons_New(LNumOfPolygons);if(LPolygons)
			{
				LPolyGroup->Polygons.Items = LPolygons;LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LNumOfPolygons;
			}
		}


		E3dCoord3*  LVertexTangents = NULL;
		int*        LVertexReindexingTable = _SetVertices(LMesh, LComponent, &LVertexTangents, TRUE);


// Build the Polygons
//
		EIndex  LNumOfVertexNodes;
		if(LComponent->Triangles) LNumOfVertexNodes = LNumOfPolygons * 3;
		else
		{
// E3dFIXME
// We should take into account Vertex welding at the revolve axis
//
			LNumOfVertexNodes = LNumOfPolygons * 4;
		}


		const E3dCoordinate  LUStep =  1.0 / (E3dCoordinate)LSides;
//		const E3dCoordinate  LVStep = -1.0 / (E3dCoordinate)LSegments;


		E3dCoordinate*  LVTable = NULL;


		{
			E3dPolygon*  LPolygon = LPolyGroup->Polygons.Items;
			E3dEdge*     LEdge = LMesh->Edges.Items;

			if(LProfileSpline && LProfileSpline->CVs.Count > 0)
			{
// Build list of which Edges along the profile Spline should be visible.
// The Edges crossing CVs will be visible. The ones between CVs won't be.
//
				EBool*  LVEdgesVisibility = NULL;

				{
//printf("%s() %s:%d  %lld: ", __FUNCTION__, __FILE__, __LINE__, (long long)(LSegments + 1));fflush(stdout);
					ENewN(LSegments + 1, LVEdgesVisibility);
					EIndex  LCVI = 0, LI = 0;const EIndex  LCVN = LProfileSpline->CVs.Count;const E3dSplineCV*  LCV = LProfileSpline->CVs.Items;for(;LCVI < LCVN;LCVI++, LCV++)
					{
						EIndex  LN = LCV->Segment.NumOfLinearSegments - 1;if(LN < 0) LN = 0;
//printf("%s() %s:%d  CV %3d  lin %3d\n", __FUNCTION__, __FILE__, __LINE__, (int)LCVI, (int)LN);fflush(stdout);
//printf(" 1");fflush(stdout);
						LVEdgesVisibility[LI++] = TRUE;
						EIndex  LC = 0;for(;LC < LN;LC++)
						{
//printf(" 0");fflush(stdout);
							LVEdgesVisibility[LI++] = FALSE;
						}
					}
				}

				const E3dVertexNodeSize  LVertexNodeSize = LPolyGroup->VertexNodeSize;

// Look-up table for V parameter, using arc-length
//
				ENewN(LSegments + 1, LVTable);

				E3dSpline_GetDrawingArcLengthParameters(LProfileSpline, 0.0, TRUE,  LVTable, NULL);

				EBool*  LProfileSegmentVisFlags = NULL;

// If UseProfileSegmentVisibility is enabled, only generate Polygons for visible segments of the profile Spline.
// This means we need to generate an EBool array that indicates which piece-wise linear segments are visible
//
				if(LComponent->UseProfileSegmentVisibility)
				{
					if(LProfileSpline && LProfileSpline->CVs.Count > 1)
					{
						ENewN(LSegments, LProfileSegmentVisFlags);
						EIndex  LCVI = 0, LI = 0;const EIndex  LCVN = LProfileSpline->Closed ? LProfileSpline->CVs.Count : LProfileSpline->CVs.Count - 1;const E3dSplineCV*  LCV = LProfileSpline->CVs.Items;for(;LCVI < LCVN;LCVI++, LCV++)
						{
							const EBool  LSegmentVisible = LCV->Segment.Flags & E3dSegmentVISIBLE ? TRUE : FALSE;
							EIndex  LC = 0;const EIndex  LN = LCV->Segment.NumOfLinearSegments;for(;LC < LN;LC++)
							{
								LProfileSegmentVisFlags[LI++] = LSegmentVisible;
							}
						}
					}
				}

				if(LComponent->CreateUVs)
				{
					if(LComponent->Triangles)
					{
						E3d_ToroidBuildPolygons(_M_Add2TrisUV,  0,  LSides, LFull,  LVPoints, LSegments, LProfileSpline->Closed, LProfileSegmentVisFlags);
					}
					else
					{
						E3d_ToroidBuildPolygons(_M_AddQuadUV,   0,  LSides, LFull,  LVPoints, LSegments, LProfileSpline->Closed, LProfileSegmentVisFlags);
					}
				}
				else
				{
					if(LComponent->Triangles)
					{
						E3d_ToroidBuildPolygons(_M_Add2Tris,    0,  LSides, LFull,  LVPoints, LSegments,  LProfileSpline->Closed, LProfileSegmentVisFlags);
					}
					else
					{
						E3d_ToroidBuildPolygons(_M_AddQuad,     0,  LSides, LFull,  LVPoints, LSegments,  LProfileSpline->Closed, LProfileSegmentVisFlags);
					}
				}

				EFreeChk(LVEdgesVisibility);
				EFreeChk(LProfileSegmentVisFlags);
			}

			LNumOfPolygons = LPolygon - LPolyGroup->Polygons.Items;
			if(LNumOfPolygons < LPolyGroup->Polygons.Count)
			{
				EResizeNT(E3dPolygon, LNumOfPolygons,  LPolyGroup->Polygons.Items);

				LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LNumOfPolygons;
			}

			LMesh->Edges.Count = LEdge - LMesh->Edges.Items;
		}

		EFree(LVertexReindexingTable);

// Set up normals
//
		LPolyGroup->SmoothingAngle = LComponent->SmoothingAngle;


// Create tangents for anisotropic shaders
//  
		if(LComponent->CreateTangents)
		{
			int  LTangentOffset = E3dPolyGroup_VertexAttributeOffset(LPolyGroup, E3dVtxAttribute_Tangent, 0);

			if(LTangentOffset < 0)
			{
//printf("%s()-01 LPolyGroup->VertexNodeSize %d\n", __FUNCTION__, LPolyGroup->VertexNodeSize);fflush(stdout);
				E3dPolyGroup_VertexAttributeAdd(LPolyGroup, NULL, E3dVtxAttribute_Tangent, 0,  NULL);
//printf("%s()-02 LPolyGroup->VertexNodeSize %d\n", __FUNCTION__, LPolyGroup->VertexNodeSize);fflush(stdout);
				LTangentOffset = E3dPolyGroup_VertexAttributeOffset(LPolyGroup, E3dVtxAttribute_Tangent, 0);
			}

			if(LTangentOffset > -1)
			{
				if(LNumOfVertexNodes)
				{
					const E3dVertexNodeSize  LVertexNodeSize = LPolyGroup->VertexNodeSize;

					EIndex  LPI = 0;E3dPolygon*  LPolygon = LPolyGroup->Polygons.Items;for(;LPI < LNumOfPolygons;LPI++, LPolygon++)
					{
						E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
						E3dCoord3*      LTangent = (E3dCoord3*)EOffsetToPtr(LVertexNode, LTangentOffset);
						EIndex  LVI = 0;const EIndex  LVN = LPolygon->NumOfVertices;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize), EStruct_Inc(LTangent, LVertexNodeSize))
						{
							*LTangent = LVertexTangents[LVertexNode->VertexID];
						}
					}
				}
			}
		}
		else E3dPolyGroup_VertexAttributeDel(LPolyGroup,  E3dVtxAttribute_Tangent, 0);


		EFreeChk(LVertexTangents);

		EFreeChk(LVTable);

		if(LComponent->SmoothingAngle > 0.0)
		{
			LPolyGroup->Flags |= E3dUSE_VERTEX_NORMALS;

			LPolyGroup->VertexNormalType = E3dNormalFROM_SMOOTHING_ANGLE;
		}
		else
		{
			LPolyGroup->VertexNormalType = 0;
			LPolyGroup->Flags &= ~E3dUSE_VERTEX_NORMALS;
		}

		EUpdateFlags  LUpdateFlags = LCreateEdges ? E3dUpdateALL & ~E3dUpdateEDGES : E3dUpdateALL;
		if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = LUpdateFlags;

		LComponent->AttributeChangeCount = LComponent->InputChangeCount = 0;
	}

	return(PObject);
}


//========================================
// Dialog box callback handlers
//========================================
static void _DCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*               LObject = LPanel->Object;
	E3dInfo*                 LComponent = (E3dInfo*)E3dObjectComponent_ByClass(LObject, _ObjectComponent_class, FALSE);
	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, _ObjectComponent_class);
	E3dResourcesGUI*         LGUI = (E3dResourcesGUI*)LDrawer->ResourcesGUI;
	EChangeFlags             LChanged = 0;

	if(PW == LGUI->TrianglesW)
	{
		LComponent->Triangles = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->TangentsW)
	{
		LComponent->CreateTangents = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->UVsW)
	{
		LComponent->CreateUVs = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->SmoothingAngleW)
	{
		E3dMesh*       LMesh = (E3dMesh*)LObject;
		E3dPolyGroup*  LPolyGroup = LMesh->PolyGroups.Items[0];

		LPolyGroup->SmoothingAngle = LComponent->SmoothingAngle = ((XeScaleCallbackStruct*)PCallData)->Value.f32;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->StartAngleW)
	{
		LComponent->StartAngle = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
		if(LComponent->StartAngle > LComponent->EndAngle)
		{
			LComponent->EndAngle = LComponent->StartAngle;
			XeScale_SetValue_Coord(LGUI->EndAngleW, LComponent->EndAngle, FALSE);
		}
		if(LComponent->StartAngle < LComponent->EndAngle - 360.0)
		{
			LComponent->EndAngle = LComponent->StartAngle + 360.0;
			XeScale_SetValue_Coord(LGUI->EndAngleW, LComponent->EndAngle, FALSE);
		}

		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->EndAngleW)
	{
		LComponent->EndAngle = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
		if(LComponent->StartAngle > LComponent->EndAngle)
		{
			LComponent->StartAngle = LComponent->EndAngle;
			XeScale_SetValue_Coord(LGUI->StartAngleW, LComponent->StartAngle, FALSE);
		}
		if(LComponent->StartAngle < LComponent->EndAngle-360.0)
		{
			LComponent->StartAngle = LComponent->EndAngle-360.0;
			XeScale_SetValue_Coord(LGUI->StartAngleW, LComponent->StartAngle, FALSE);
		}

		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->SidesW)
	{
		LComponent->Sides = ((XeScaleCallbackStruct*)PCallData)->Value.i32;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}

	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


//========================================
// AxisSelector callback
//========================================
static void _CB_AxisSelector(ECoreObject* PStruct, EPointer PClientData, ECallbackStruct* PCallData)
{
	E3dObjectPanel*    LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*         LObject = LPanel->Object;
	E3dInfo*           LComponent = (E3dInfo*)E3dObjectComponent_ByClass(LObject, _ObjectComponent_class, FALSE);
	E3dpAxisSelector*  LAxisSelector = (E3dpAxisSelector*)PStruct;
	EChangeFlags       LChanged = 0;

	LComponent->Axis = LAxisSelector->Axis;

	LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	E3dScene*  LScene = E3d_Scene;
	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static E3dInfo*         _TmpInfo = NULL;
static E3dObjectPanel*  _TmpPanel = NULL;

#define _PickInputGeometryTypeWarningStr  E3dpLng_Revolve_Pick_a_Spline_or_a_Face_as_the_new_profile
#define _PickInputGeometryObjectTypes     E3dItemSPLINE

#include <E3D/PickInput.ci>

static void _RequestProfilePick(void)
{
	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color = Xe_ForegroundRGBA8pColor;
	E3dp_PickRequest(
	 XeString_NewVa(TRUE,
	 XeFONT, XeFontDEFAULT,  XeCOLOR_RGBA, LLabelRGBAp8Color,  XeTEXT, _ObjectComponent_class->Name,  XeTEXT, ":    ",
	 E3dp_LMouseButtonS,     XeTEXT, ": pick Spline or Face as the new profile  ",
	 E3dp_RMouseButtonEscS,  XeTEXT, ": cancel",
	 0),
	 E3dItemSPLINE|E3dItemFACE|E3dItemMODEL,  _PickCB_InputGeometry, NULL,  0, FALSE);
}


static void _DCB_ProfileShow(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dInfo*         LComponent = (E3dInfo*)E3dObjectComponent_ByClass(LObject, _ObjectComponent_class, FALSE);
	EChangeFlags     LChanged = 0;

	if(LComponent->InputGeometry)
	{
		if(E3dSel_ObjectSelect(E3d_Scene, (E3dObject*)LComponent->InputGeometry, FALSE, FALSE)) LChanged |= E3dChgSELECTION;

		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
		E3dTransformPanel_Update(NULL, E3dDoALL, E3dDoALL, E3dDoALL,  E3dpDO_NOT_CHANGE,  E3dTrPanelALL);
		E3dp_PrintMessage(ELogMESSAGE, 3000, "Revolve: selected profile Geometry <%s>", LComponent->InputGeometry->Name);
	}
	else E3dp_PrintMessage(ELogMESSAGE, 3000, "Revolve: no profile Geometry!");
}


static void _DCB_ProfilePick(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LPanel->Object;
	E3dInfo*         LComponent = (E3dInfo*)E3dObjectComponent_ByClass(LObject, _ObjectComponent_class, FALSE);

	_TmpInfo = LComponent;
	_TmpPanel = LPanel;
	_RequestProfilePick();
}


//========================================
// GUI
//========================================
static void _GUI(E3dObject* PObject, E3dObjectComponent* PInfo, void* PPanel, void* PDrawer, const int PAction)
{
	E3dObjectPanel*          LPanel = (E3dObjectPanel*)PPanel;
	XeComponentClassDrawer*  LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dResourcesGUI*         LResourcesGUI;
	E3dInfo*                 LComponent = (E3dInfo*)PInfo;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				LResourcesGUI = (E3dResourcesGUI*)(LDrawer->ResourcesGUI);

				XeToggleButton_SetState(LResourcesGUI->TrianglesW, LComponent->Triangles, FALSE);
				XeToggleButton_SetState(LResourcesGUI->TangentsW, LComponent->CreateTangents, FALSE);
				XeToggleButton_SetState(LResourcesGUI->UVsW, LComponent->CreateUVs, FALSE);

				XeScale_SetValue_f32(LResourcesGUI->SmoothingAngleW, LComponent->SmoothingAngle, FALSE);

				XeScale_SetValue_Coord(LResourcesGUI->StartAngleW, LComponent->StartAngle, FALSE);
				XeScale_SetValue_Coord(LResourcesGUI->EndAngleW, LComponent->EndAngle, FALSE);

				XeScale_SetValue(LResourcesGUI->SidesW, LComponent->Sides, FALSE);

//printf("%s() %f,%f,%f\n", __FUNCTION__, LComponent->Axis.X, LComponent->Axis.Y, LComponent->Axis.Z);fflush(stdout);
				E3dpAxisSelector_Set(&LResourcesGUI->AxisSelector,  LComponent->Axis.X, LComponent->Axis.Y, LComponent->Axis.Z);
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
				XeWidget  LIndentWs[7];int  LIndentN;      // For indenting the ScaleWidgets
				XeWidget  LTraverseWs[7];int  LTraverseN;  // For TextField traversal
				EArg      Args[256];int  ArgCnt;

				ENew(LResourcesGUI);LDrawer->ResourcesGUI = LResourcesGUI;

// Profile
//
				{
					EXeStart;
					EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
					EXeSetArg(XeNOrientation, XeHORIZONTAL);
					EXeSetArg(XeNMarginSizeY, 5);
					EXeSetArg(XeNSpacingX, 0);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget  LMatrixW = XeWidget_ManagedNew("", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
					EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
					EXeSetArg(XeNFontList, Xe_LabelFontList);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNMarginSizeX, 2);EXeSetArg(XeNMarginSizeY, 3);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget_ManagedNew("Profile", XeLabelWidget_class, LMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNSpacingLeft, 6);
					EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);EXeSetArg(XeNActiveColorPtr, &Xe_BackgroundRGBAiColor);
					EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNLabelType, XeIMAGE);EXeSetArg(XeNImageSizeYPtr, &Xe_IconSize);EXeSetImages2(_Xe_Theme->Image_Find);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeToolTip8("Select the Spline that is used as the profile (so you can find it).");
					LW = XeWidget_ManagedNew("Show", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_ProfileShow, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);EXeSetArg(XeNActiveColorPtr, &Xe_BackgroundRGBAiColor);
					EXeSetArg(XeNMarginSizeX, 0);
					EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNLabelType, XeIMAGE);EXeSetArg(XeNImageSizeYPtr, &Xe_IconSize);EXeSetImages2(_Xe_Theme->Image_Replace);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeToolTip8("Pick a new profile Spline.");
					LW = XeWidget_ManagedNew("Pick", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_ProfilePick, (EPointer)LPanel);
				}


				{
					EXeStart;
					EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNOrientation, XeHORIZONTAL);
					EXeSetArg(XeNSpacingX, 12);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget  LMatrixW = XeWidget_ManagedNew("MatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);


					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNSet, LComponent->Triangles);
					LResourcesGUI->TrianglesW = LW = XeWidget_ManagedNew("Triangles", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);


					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNSet, LComponent->CreateTangents);
					XeToolTip8(
"Create geometric tangents along the rotation.\n\
Geometric tangents are used by anisotropic\nshaders (as opposed to texture-space tangents,\n\
which are used for normal-mapping).");
					LResourcesGUI->TangentsW = LW = XeWidget_ManagedNew("Tangents", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNShadowThickness, 0);
					EXeSetArg(XeNHighlightThickness, 0);
					EXeSetArg(XeNSet, LComponent->CreateUVs);
					XeToolTip8("Create 'evaluator coordinates' U and V.\n\
U increases along the circumference, V along the profile spline.\n\
UVs can be used for among many things, UV texture-mapping.");
					LResourcesGUI->UVsW = LW = XeWidget_ManagedNew("UVs", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);
				}

				LTraverseN = 0;
				LIndentN = 0;
				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 6);EXeSetArg(XeNDecimalPoints, 2);
				EXeSetArg(XeNResizableY, FALSE);
				XeToolTip8("The normal smoothing angle for the PolyGroup created.");
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->SmoothingAngleW = LW = XeWidget_ManagedNew("Smoothing angle:", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
				XeScale_SetValueAndRange_f32(LW,  LComponent->SmoothingAngle,  0.0, 180.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 6);EXeSetArg(XeNDecimalPoints, 2);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->StartAngleW = LW = XeScale_New_Coord(LTopMatrixW, "Start angle",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LComponent->StartAngle,  -360.0, 360.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 6);EXeSetArg(XeNDecimalPoints, 2);
				EXeSetArg(XeNResizableY, FALSE);
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->EndAngleW = LW = XeScale_New_Coord(LTopMatrixW, "End angle",  Args, ArgCnt);
				XeScale_SetValueAndRange_Coord(LW,  LComponent->EndAngle,  -360.0, 360.0,  FALSE);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);


				EXeStart;
				EXeSetArg(XeNTextFieldDigits, 6);
				EXeSetArg(XeNMinimum, 3);EXeSetArg(XeNMaximum, 200);
				EXeSetArg(XeNScaleMaximum, 50);
				EXeSetArg(XeNValue, LComponent->Sides);
				EXeSetArg(XeNResizableY, FALSE);
				XeToolTip8("The number of revolution steps (\"U\" subdivisions).\n\
To set the number of \"V\" subdivisions, edit the Spline's attributes.");
				LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LResourcesGUI->SidesW = LW = XeWidget_ManagedNew("Sides:", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
				XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

				XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 2);


				EXeStart;
				EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
				EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
				EXeSetArg(XeNFontList, Xe_LabelFontList);
				EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
				EXeSetArg(XeNMarginSizeX, 2);EXeSetArg(XeNMarginSizeY, 3);
				EXeSetArg(XeNResizableY, FALSE);
				XeWidget_ManagedNew("Revolve around:", XeLabelWidget_class, LTopMatrixW,  Args, ArgCnt);
				E3dpAxisSelector_New(&LResourcesGUI->AxisSelector, NULL, LTopMatrixW, TRUE,  _CB_AxisSelector, (EPointer)LPanel,  LComponent->Axis.X, LComponent->Axis.Y, LComponent->Axis.Z);


// Set up TextField traversal
//
				XeTraversal_SetRing(LTraverseWs, LTraverseN);
			}
		break;
	}
}


//================================================================
// Callback for Profile-Spline changes
// This function should Not call any 3DWindow redrawing function,
// just return with TRUE if a redraw is needed!
//================================================================
static EChangeFlags _InputChanged(EInterface* PInterface, EInterfaceCallbackStruct* PCBS)
{
	E3dObject*    LObject  = (E3dObject*)PInterface->Object;
	E3dInfo*      LComponent    = (E3dInfo*)PInterface->ObjectComponent;
	EChangeFlags  LChanged = E3dObject_GetChangeFlagsByClass(LObject);

	LComponent->InputChangeCount++;

// Only respond if the Object is instantiated (e.g. not in the undo stack after being deleted)
//
	if(LObject->Models.Count > 0)
	{
		E3dObject_CustomUpdate(LObject, LComponent,  PCBS->CallOutputs ? E3dCallALL : E3dCallALL & ~E3dCallOUTPUTS, &LChanged);
	}

	return(LChanged);
}


//================================================
// Menu/ToolBar callback
//================================================
static void _MCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*   LScene = E3d_Scene;
	E3dObjItem  LItem;

	if(E3dScene_GetSingleSelection(LScene, &LItem, E3dItemSPLINE, E3dVISIBLE) != E3dItemNONE)
	{
		E3dObject*  LInputObject = LItem.Object;
		EBool       LSuccess = FALSE;

		E3dInfo  LTInfo;E3dObjectComponent_InitStatic(&LTInfo, _ObjectComponent_class);
		E_Assign(LInputObject, LTInfo.InputGeometry);

		E3dModel*  LOrigModel = E3dObject_GetSelectingModel(LInputObject, FALSE);
		if(LOrigModel == NULL) LOrigModel = LInputObject->Models.Items[0];

		E3dObject*  LObject = E3dObject_CustomNew(LScene, NULL, LOrigModel, NULL, (E3dObjectComponent*)(&LTInfo), _NewObjectAddingMode, (E3dp_Prefs.Duplicate_Flags & E3dCopyMODEL_UNIQUE_NAME) != 0,  NULL);if(LObject)
		{
			{ E3dGeometry*  LGeometry = E_DynamicCast(LObject, E3dGeometry);if(LGeometry) LGeometry->AutoLOD = FALSE; }

			LSuccess = TRUE;
		  E3dScene_ObjectsUpdateModelList(LScene);
			E3dScene_LayoutOnSchematics(LScene, TRUE);
		}

		E_Delete(LTInfo.InputGeometry);

		if(LSuccess)
		{
			if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) E3dObject_Edit(LObject, _ObjectComponent_class);

			E3dWindows_RedrawByChangeFlags(LScene, E3dChgMeshSHAPE|E3dChgHIERARCHY);
		}
	}
	else E3dp_PrintMessage(ELogWARNING, 5000, "Select a Spline to revolve");
}


//================================================
// Constructor
//================================================
static int _Init(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dInfo*  LThis = (E3dInfo*)PInfo;

	LThis->InputGeometry = NULL;
	LThis->StartAngle = 0.0;LThis->EndAngle = 360.0;
	LThis->Sides = 24;
	LThis->Axis = E3dCoord3_s(0.0, 1.0, 0.0);		// The Y axis
	LThis->AroundWorldAxis = TRUE;
	LThis->Close = FALSE;
	LThis->Triangles = FALSE;
	LThis->CreateTangents = TRUE;
	LThis->UseProfileSegmentVisibility = FALSE;
	LThis->CreateUVs = TRUE;
	LThis->SmoothingAngle = 60.0;

	{
		EInterface*  LInput = &LThis->InputInterface;EInterface_Init(LInput);
		LInput->InputChanged    = _InputChanged;
		LInput->Object          = (ECoreObject*)PObject;
		LInput->ObjectComponent = (ECoreObject*)PInfo;
	}

	LThis->Full = FALSE;

	LThis->GLAxis[0][E3dX] = LThis->GLAxis[0][E3dY] = LThis->GLAxis[0][E3dZ] = 0.0;
	LThis->GLAxis[1][E3dX] = LThis->GLAxis[1][E3dY] = LThis->GLAxis[1][E3dZ] = 0.0;
	LThis->GLAxisUptodate = FALSE;
	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _Copy(const E3dObject* PSObject, const E3dObjectComponent* PSInfo,  E3dObject* PDObject, E3dObjectComponent* PDInfo)
{
	const E3dInfo*  LSInfo = (E3dInfo*)PSInfo;
	E3dInfo*        LDInfo = (E3dInfo*)PDInfo;

	_Init(PDObject, PDInfo);

#define _CopyField(f)  LDInfo->f = LSInfo->f
	_CopyField(StartAngle);
	_CopyField(EndAngle);
	_CopyField(Sides);
	_CopyField(Axis);
	_CopyField(AroundWorldAxis);
	_CopyField(Close);
	_CopyField(Triangles);
	_CopyField(CreateTangents);
	_CopyField(CreateUVs);
	_CopyField(SmoothingAngle);
	_CopyField(GLAxis[0][E3dX]);_CopyField(GLAxis[0][E3dY]);_CopyField(GLAxis[0][E3dZ]);
	_CopyField(GLAxis[1][E3dX]);_CopyField(GLAxis[1][E3dY]);_CopyField(GLAxis[1][E3dZ]);
	_CopyField(GLAxisUptodate);
#undef _CopyField

	E_AssignToMember(LSInfo->InputGeometry, LDInfo,InputGeometry);
	if(LSInfo->InputGeometry)
	{
		E3dBaseObject_AddOutput((E3dBaseObject*)LDInfo->InputGeometry, &LDInfo->InputInterface);
	}
}


//================================================
// Destructor
//================================================
static int _Free(E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dInfo*  LComponent = (E3dInfo*)PInfo;

	E3dBaseObject_RemoveOutput((E3dBaseObject*)LComponent->InputGeometry, &LComponent->InputInterface);

	E_DeleteChk(LComponent->InputGeometry);

	EInterface_Clear(&LComponent->InputInterface);

	return(E_SUCCESS);
}


//========================================
// Draw revolution axis
//========================================
static E3dHWDrawFlags _GLDrawControls(E3dWindow* P3DWindow, const E3dDrawContext* PDrawContext,  E3dScene* PScene, E3dModel* PModel, E3dObject* PObject, E3dObjectComponent* PInfo)
{
	E3dInfo*     LComponent = (E3dInfo*)PInfo;
	const float  LAAMultF = (float)sqrt(E3dp_Prefs.AntiAliasing);

	if(LComponent && PModel->Selection)
	{
#ifdef EGLES
if(E3d_Verbosity & E3dVbOPENGL) { printf("%s() %s:%d  %sGLES IMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout); }
#else
		EpglDisable(GL_LIGHTING);
#endif

		E3dGL_TexturesDisable();

		if(!LComponent->GLAxisUptodate) _SetVertices((E3dMesh*)PObject, LComponent, NULL, FALSE);

		EcM_glColorRGBAf_s(E3dp_NormalDrawContext.NormalRGBAfColor);

		EpglLineWidth(E3dp_Prefs.LineWidth * 2.0 * LAAMultF);

		EpglBegin(GL_LINES);
		 E3dGL_Vertex3v(LComponent->GLAxis[0]);E3dGL_Vertex3v(LComponent->GLAxis[1]);
		EpglEnd();

		EpglLineWidth(E3dp_Prefs.LineWidth * LAAMultF);
	}

	return(0);
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

	{
		E3dObjectComponentClass  LClass =
		{
			.Name=            "Revolve",
			.StructSize=      sizeof(E3dInfo),
			.Resources=       EListS_s(_Resources),

			.Init=            _Init,
			.Copy=            _Copy,
			.Free=            _Free,
			.Update1=          _Update1,
			.GUI=             _GUI,
			.GLDrawControls=  _GLDrawControls
		};
		_ObjectComponent_class = E3dObjectComponent_ClassRegister(&LClass);
	}

	if(_ObjectComponent_class)
	{
	 EpM_CreateIconStates3_sp(Revolve);E_Assign(_Image_Revolve, _ObjectComponent_class->Icon);
	 XePushButton_AddImg3(_ToolBarButton, Revolve, "Tool->Surface", "Revolve", '\0', NULL, NULL, TRUE, "Revolve a Spline around the selected axis to create a surface.\nRight button: dialog box for this tool", _MCB, (EPointer)0);

	 _MenuButton = XePushButton_Add(Xe_App, "Menu->Surface", "Revolve", '\0', NULL, NULL, TRUE, NULL, _MCB, (EPointer)0);
	}
	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		XeItemFree(_MenuButton);
		XeItemFree(_ToolBarButton);
	}

	if(_ObjectComponent_class) { E3dObjectComponent_ClassDeactivate(_ObjectComponent_class);_ObjectComponent_class = NULL; }

	EpIconFree3(Revolve);

	return(E_SUCCESS);
}
