//================================================================================
// EQUINOX-3D(TM) plugin
//
// Create a cylinder, capsule or straight tube and change it interactively
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Nov-12 14:31:54
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <assert.h>

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

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

#include <E3D/PolygonEditing.h>

#include <E3D/UI/CreateMacros.h>
#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>
#include <E3D/UI/BevelPanel.h>



static EPlugin*  _Plugin = NULL;

static EBool        _AddPieSides = FALSE;
static const EBool  _UseRevolve = TRUE;

#define E3dBEVEL_SMOOTHER  0.1
const E3dCoordinate  _TubeMinWallThickness = 0.001;

typedef struct
{
	XeWidget       SmoothW,
                 SideW, TopW, BottomW, OriginAtBottomW, PieW,
                 RadiusW, InnerRadiusW,  HeightW,
                 StartAngleW, EndAngleW,
                 SidesW, RadialSectionsW, HeightSectionsW;

	E3dBevelPanel  StartBevelPanel, StartInnerBevelPanel, EndBevelPanel, EndInnerBevelPanel;
} E3dResourcesGUI;


char*            E_PluginInfo = "Cylinder, capsule and tube primitives";

static XeDWidget  _MenuButtons[16];static int  _MenuButtons_Count = 0;
static XeDWidget  _ToolBarButtons[3];static int  _ToolBarButtons_Count = 0;

enum
{
	E3dCylinderPOLYGONS = 0,
	E3dCylinderNURBS,
	E3dCylinderBEZIER,
};

#define E3dRPI  (1.0 / 180.0 * E3dPI)


EpIconDef3I(static, Cylinder);
EpIconDef3I(static, Capsule);
EpIconDef3I(static, Tube);



typedef struct
{
	E3dObjectComponentCore;

	E3dCoordinate  Radius, InnerRadius, Height, TopDomeHeight, BottomDomeHeight;
	E3dCoordinate  StartAngle, EndAngle;

	unsigned int   Sides, RadialSections, HeightSections;
	unsigned int   Type;

	EBool          Full, Smooth, Side, TopCap, BottomCap, Pie, OriginAtBottom;
	EBool          ShowProfileSpline;

	E3dBevel       StartBevel, EndBevel;            // For Cylinder and Tube
	E3dBevel       StartInnerBevel, EndInnerBevel;  // For Tube
} E3dCylinderComponent;


#define ROffset(field) (void*)(EPtrOffset(E3dCylinderComponent, field))
static EResource _CylinderResources[]=
{
	{ "Radius",          E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "Height",          E3dCOORDINATE_TYPE,  ROffset(Height) },
	{ "StartAngle",      E3dCOORDINATE_TYPE,  ROffset(StartAngle) },
	{ "EndAngle",        E3dCOORDINATE_TYPE,  ROffset(EndAngle) },
	{ "Sides",           EResINT32,           ROffset(Sides) },
	{ "RadialSections",  EResINT32,           ROffset(RadialSections) },
	{ "HeightSections",  EResINT32,           ROffset(HeightSections) },
	{ "Type",            EResINT32,           ROffset(Type) },
	{ "Smooth",          EResBOOLEAN,         ROffset(Smooth) },
	{ "OrigAtBottom",    EResBOOLEAN,         ROffset(OriginAtBottom) },
	{ "Side",            EResBOOLEAN,         ROffset(Side) },
	{ "TopCap",          EResBOOLEAN,         ROffset(TopCap) },
	{ "BottomCap",       EResBOOLEAN,         ROffset(BottomCap) },

	{ "StartBevel.Steps",                  EResINT32,               ROffset(StartBevel.NumOfRoundingSteps) },
	{ "StartBevel.Radius",                 E3dCOORDINATE_TYPE,      ROffset(StartBevel.Radius) },
	{ "StartBevel.Invert",                 EResBOOLEAN,             ROffset(StartBevel.Invert) },
	{ "StartBevel.AddStartSmoother",       EResBOOLEAN,             ROffset(StartBevel.AddStartSmoother) },
	{ "StartBevel.AddEndSmoother",         EResBOOLEAN,             ROffset(StartBevel.AddEndSmoother) },

	{ "EndBevel.Steps",                    EResINT32,               ROffset(EndBevel.NumOfRoundingSteps) },
	{ "EndBevel.Radius",                   E3dCOORDINATE_TYPE,      ROffset(EndBevel.Radius) },
	{ "EndBevel.Invert",                   EResBOOLEAN,             ROffset(EndBevel.Invert) },
	{ "EndBevel.AddStartSmoother",         EResBOOLEAN,             ROffset(EndBevel.AddStartSmoother) },
	{ "EndBevel.AddEndSmoother",           EResBOOLEAN,             ROffset(EndBevel.AddEndSmoother) },
};

static EResource _CapsuleResources[]=
{
	{ "Radius",          E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "Height",          E3dCOORDINATE_TYPE,  ROffset(Height) },
	{ "StartAngle",      E3dCOORDINATE_TYPE,  ROffset(StartAngle) },
	{ "EndAngle",        E3dCOORDINATE_TYPE,  ROffset(EndAngle) },
	{ "Sides",           EResINT32,           ROffset(Sides) },
	{ "RadialSections",  EResINT32,           ROffset(RadialSections) },
	{ "HeightSections",  EResINT32,           ROffset(HeightSections) },
	{ "Type",            EResINT32,           ROffset(Type) },
	{ "Smooth",          EResBOOLEAN,         ROffset(Smooth) },
	{ "OrigAtBottom",    EResBOOLEAN,         ROffset(OriginAtBottom) },
	{ "Side",            EResBOOLEAN,         ROffset(Side) },
	{ "TopCap",          EResBOOLEAN,         ROffset(TopCap) },
	{ "BottomCap",       EResBOOLEAN,         ROffset(BottomCap) },
};

static EResource _TubeResources[]=
{
	{ "Radius",          E3dCOORDINATE_TYPE,  ROffset(Radius) },
	{ "InnerRadius",     E3dCOORDINATE_TYPE,  ROffset(InnerRadius) },
	{ "Height",          E3dCOORDINATE_TYPE,  ROffset(Height) },
	{ "StartAngle",      E3dCOORDINATE_TYPE,  ROffset(StartAngle) },
	{ "EndAngle",        E3dCOORDINATE_TYPE,  ROffset(EndAngle) },
	{ "Sides",           EResINT32,           ROffset(Sides) },
	{ "RadialSections",  EResINT32,           ROffset(RadialSections) },
	{ "HeightSections",  EResINT32,           ROffset(HeightSections) },
	{ "Type",            EResINT32,           ROffset(Type) },
	{ "Smooth",          EResBOOLEAN,         ROffset(Smooth) },
	{ "OrigAtBottom",    EResBOOLEAN,         ROffset(OriginAtBottom) },
	{ "Side",            EResBOOLEAN,         ROffset(Side) },
	{ "TopCap",          EResBOOLEAN,         ROffset(TopCap) },
	{ "BottomCap",       EResBOOLEAN,         ROffset(BottomCap) },

	{ "StartBevel.Steps",                  EResINT32,               ROffset(StartBevel.NumOfRoundingSteps) },
	{ "StartBevel.Radius",                 E3dCOORDINATE_TYPE,      ROffset(StartBevel.Radius) },
	{ "StartBevel.Invert",                 EResBOOLEAN,             ROffset(StartBevel.Invert) },
	{ "StartBevel.AddStartSmoother",       EResBOOLEAN,             ROffset(StartBevel.AddStartSmoother) },
	{ "StartBevel.AddEndSmoother",         EResBOOLEAN,             ROffset(StartBevel.AddEndSmoother) },

	{ "StartInnerBevel.Steps",             EResINT32,               ROffset(StartInnerBevel.NumOfRoundingSteps) },
	{ "StartInnerBevel.Radius",            E3dCOORDINATE_TYPE,      ROffset(StartInnerBevel.Radius) },
	{ "StartInnerBevel.Invert",            EResBOOLEAN,             ROffset(StartInnerBevel.Invert) },
	{ "StartInnerBevel.AddStartSmoother",  EResBOOLEAN,             ROffset(StartInnerBevel.AddStartSmoother) },
	{ "StartInnerBevel.AddEndSmoother",    EResBOOLEAN,             ROffset(StartInnerBevel.AddEndSmoother) },

	{ "EndBevel.Steps",                    EResINT32,               ROffset(EndBevel.NumOfRoundingSteps) },
	{ "EndBevel.Radius",                   E3dCOORDINATE_TYPE,      ROffset(EndBevel.Radius) },
	{ "EndBevel.Invert",                   EResBOOLEAN,             ROffset(EndBevel.Invert) },
	{ "EndBevel.AddStartSmoother",         EResBOOLEAN,             ROffset(EndBevel.AddStartSmoother) },
	{ "EndBevel.AddEndSmoother",           EResBOOLEAN,             ROffset(EndBevel.AddEndSmoother) },

	{ "EndInnerBevel.Steps",               EResINT32,               ROffset(EndInnerBevel.NumOfRoundingSteps) },
	{ "EndInnerBevel.Radius",              E3dCOORDINATE_TYPE,      ROffset(EndInnerBevel.Radius) },
	{ "EndInnerBevel.Invert",              EResBOOLEAN,             ROffset(EndInnerBevel.Invert) },
	{ "EndInnerBevel.AddStartSmoother",    EResBOOLEAN,             ROffset(EndInnerBevel.AddStartSmoother) },
	{ "EndInnerBevel.AddEndSmoother",      EResBOOLEAN,             ROffset(EndInnerBevel.AddEndSmoother) },
};
#undef ROffset


static E3dObjectComponentClass*  _CylinderComponentClass = NULL;
static E3dObjectComponentClass*  _CapsuleComponentClass = NULL;
static E3dObjectComponentClass*  _TubeComponentClass = NULL;

static EIndex  _VNum;

//================================================
// Compute vertex coordinates of the cylinder
//================================================
static void _SetVertices(E3dMesh* PMesh, E3dCylinderComponent* PComponent, E3dCoord2* PUnitArcPoints)
{
	E3dCoord2*  LUnitArcPoints;

	if(PUnitArcPoints) LUnitArcPoints = PUnitArcPoints;
	else LUnitArcPoints = E3d_CreateUnitArc(PComponent->Sides, PComponent->StartAngle, PComponent->EndAngle,  NULL);

	const E3dCoordinate  LRadius = PComponent->Radius;
	const E3dCoordinate  LHeight = PComponent->Height;
	const E3dCoordinate  LTopDomeHeight = PComponent->TopDomeHeight;
	const E3dCoordinate  LBottomDomeHeight = PComponent->BottomDomeHeight;

	const EBool    LFull = PComponent->Full;
	EIndex         LSides = PComponent->Sides;
	EIndex         LRadialSections = PComponent->RadialSections;
	const EIndex   LHeightSections = PComponent->HeightSections;

	const E3dCoordinate  LUStep = 1.0 / (E3dCoordinate)LSides;
	const E3dCoordinate  LVStep = 1.0 / (E3dCoordinate)LHeightSections;

	const E3dCoordinate  LRadStep = LRadius / (E3dCoordinate)LRadialSections;

	EBool  LPie;
	if((!LFull) && PComponent->Pie) LPie = TRUE;
	else LPie = FALSE;

	E3dCoordinate  LTopY, LBottomY;
	E3dCoordinate  LY;

	if(PComponent->OriginAtBottom)
	{
		LY = LHeight;LTopY = LHeight;LBottomY = 0.0;
	}
	else
	{
		LY = LHeight * 0.5;LTopY = LHeight * 0.5;LBottomY = -LHeight * 0.5;
	}


	E3dVertex*  LVertices = PMesh->Vertices.Items;const E3dVertexSize  LVertexSize = PMesh->VertexSize;
	E3dVertex*  LVertex = LVertices;

	switch(PComponent->Type)
	{
		case E3dCylinderPOLYGONS:
			{
				const E3dCoordinate  LHStep = LHeight / (double)LHeightSections;
// Sides
//
				if(LPie)
				{
					if(LRadialSections < 1) LRadialSections = 1;
					LSides++;
				}
				else
				{
					if(!LFull) LSides++;
				}

				{
					EIndex  LI = 0;E3dCoordinate  LVi = 0.0;for(;LI <= LHeightSections;LI++, LVi += LVStep)
					{
						int  LC1 = 0;E3dCoordinate  LU = 0.0;const E3dCoord2*  LPoint = LUnitArcPoints;for(;LC1 < LSides;LC1++, LU += LUStep)
						{
							LVertex->Position.X = LRadius * LPoint->X;LVertex->Position.Y = LY;LVertex->Position.Z = LRadius * LPoint->Y;

							LPoint++;
							EStruct_Inc(LVertex, LVertexSize);
						}
						LY -= LHStep;
					}
				}

				LY = LTopY;
				const EIndex  LRN = LRadialSections - 1;

				if(LRadialSections > 0)
				{
					if(PComponent->TopCap)
					{
// Middle Vertex
//
						LVertex->Position.X = 0.0;LVertex->Position.Y = LY + LTopDomeHeight * LRadius;LVertex->Position.Z = 0.0;EStruct_Inc(LVertex, LVertexSize);

						if(LRadialSections > 1 && E3d_Abs(LTopDomeHeight) > 0.0)
						{
							E3dCoordinate  LAngle, LAngleStep, LHt;

							LAngleStep = E3dPI * 0.5 / ((E3dCoordinate)LRadialSections);
							LAngle = E3dPI * 0.5 - LAngleStep;

							EIndex  LI = 0;for(;LI < LRN;LI++)
							{
								E3dCoordinate  LRad = E3d_Cos(LAngle) * LRadius;LHt = LRadius * E3d_Sin(LAngle);
								EIndex  LC1 = 0;const E3dCoord2*  LPoint = LUnitArcPoints;for(;LC1 < LSides;LC1++)
								{
									LVertex->Position.X = LRad * LPoint->X;LVertex->Position.Y = LY + LTopDomeHeight * LHt;LVertex->Position.Z = LRad * LPoint->Y;
									LPoint++;
									EStruct_Inc(LVertex, LVertexSize);
								}
								LAngle -= LAngleStep;
							}
						}
						else
						{
							EIndex  LI = 0;E3dCoordinate  LRad = LRadStep;for(;LI < LRN;LI++)
							{
								EIndex  LC1 = 0;const E3dCoord2*  LPoint = LUnitArcPoints;for(;LC1 < LSides;LC1++)
								{
									LVertex->Position.X = LRad * LPoint->X;LVertex->Position.Y = LY;LVertex->Position.Z = LRad * LPoint->Y;
									LPoint++;
									EStruct_Inc(LVertex, LVertexSize);
								}
								LRad += LRadStep;
							}
						}
					}


					LY = LBottomY;

// Radial segments in bottom cap
//
					if(PComponent->BottomCap)
					{
// Middle Vertex
//
						LVertex->Position.X = 0.0;LVertex->Position.Y = LY - LBottomDomeHeight*LRadius;LVertex->Position.Z = 0.0;EStruct_Inc(LVertex, LVertexSize);

						if(LRadialSections > 1 && (E3d_Abs(LTopDomeHeight) > 0.0))
						{
							E3dCoordinate  LAngle, LAngleStep, LHt;

							LAngleStep = E3dPI * 0.5 / ((E3dCoordinate)LRadialSections);
							LAngle = E3dPI * 0.5 - LAngleStep;

							EIndex  LI = 0;for(;LI < LRN;LI++)
							{
								E3dCoordinate  LRad = E3d_Cos(LAngle) * LRadius;LHt = LRadius * E3d_Sin(LAngle);
								EIndex  LC1 = 0;const E3dCoord2*  LPoint = LUnitArcPoints;for(;LC1 < LSides;LC1++)
								{
									LVertex->Position.X = LRad * LPoint->X;LVertex->Position.Y = LY - LTopDomeHeight * LHt;LVertex->Position.Z = LRad * LPoint->Y;
									LPoint++;
									EStruct_Inc(LVertex, LVertexSize);
								}
								LAngle -= LAngleStep;
							}
						}
						else
						{
							EIndex  LI = 0;E3dCoordinate  LRad = LRadStep;for(;LI < LRN;LI++)
							{
								EIndex  LC1 = 0;const E3dCoord2*  LPoint = LUnitArcPoints;for(;LC1 < LSides;LC1++)
								{
									LVertex->Position.X = LRad * LPoint->X;LVertex->Position.Y = LY;LVertex->Position.Z = LRad * LPoint->Y;
									LPoint++;
									EStruct_Inc(LVertex, LVertexSize);
								}
								LRad += LRadStep;
							}
						}
					}
				}

// Inner Vertices on the two "pie cut" sides
//
				if(LPie && LHeightSections > 1 && _AddPieSides)
				{
					{
						LY = LTopY - LHStep;
						EIndex  LI = 1;const E3dCoord2*  LPoint = LUnitArcPoints;E3dCoordinate  LX = LPoint->X, LZ = LPoint->Y;for(;LI < LHeightSections;LI++)
						{
							EIndex  LC1 = 0;E3dCoordinate  LRad = 0.0;for(;LC1 < LRadialSections;LC1++)
							{
								LVertex->Position.X = LRad * LX;LVertex->Position.Y = LY;LVertex->Position.Z = LRad * LZ;
								LRad += LRadStep;
								EStruct_Inc(LVertex, LVertexSize);
							}
							LY -= LHStep;
						}
					}

					if(LRadialSections > 1)
					{
						const EIndex  LN1 = LRadialSections - 1;

						LY = LTopY - LHStep;

						EIndex  LI = 1;const E3dCoord2*  LPoint = LUnitArcPoints + LSides - 1;E3dCoordinate  LX = LPoint->X, LZ = LPoint->Y;for(;LI < LHeightSections;LI++)
						{
							int  LC1 = 0;E3dCoordinate  LRad = LRadStep;for(;LC1 < LN1;LC1++)
							{
								LVertex->Position.X = LRad * LX;LVertex->Position.Y = LY;LVertex->Position.Z = LRad * LZ;
								LRad += LRadStep;
								EStruct_Inc(LVertex, LVertexSize);
							}
							LY -= LHStep;
						}
					}
				}

// This is for debugging only
// If an existing cylinder is edited, _VNum mignt not be initialized, this is normal!!!
//
/*
				const EIndex  LVIndex = EStruct_GetIndex(PMesh->Vertices, LVertexSize, LVertex);if(LVIndex > _VNum)
				{
					printf("%s() %d vs %d\n", __FUNCTION__, LVIndex, _VNum);fflush(stdout);
					assert(0);
				}
*/
			}
		break;
	}

	if(LUnitArcPoints && LUnitArcPoints == NULL) EFree(LUnitArcPoints);
}



void _CreatePolygonsForCylinder(E3dCylinderComponent* PComponent, E3dMesh* PMesh, E3dPolyGroup* PPolyGroup, const EIndex PNSideVertices, E3dCoord2* PUnitArcPoints)
{
	E3dPolyGroup_VertexAttributeAdd(PPolyGroup,  NULL, E3dVtxAttribute_Normal, 0,  NULL);

// Make sure the VertexNodes have UVs
//
	E3dVertexNodeSize  LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(PPolyGroup,  E3dVtxAttribute_UV, 0);
	if(LUVOffsetInVtxNode < 0)
	{
		E3dPolyGroup_VertexAttributeAdd(PPolyGroup,  NULL, E3dVtxAttribute_UV, 0,  NULL);
		LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(PPolyGroup,  E3dVtxAttribute_UV, 0);
	}

	const EIndex    LHeightSections = PComponent->HeightSections;
	const EIndex    LSides = PNSideVertices;

	EIndex          LNumOfVertexNodes = 0;(void)LNumOfVertexNodes;
	if(PComponent->Side) LNumOfVertexNodes += LHeightSections * LSides * 4;

	const EIndex    LRadialSections = PComponent->RadialSections;

	EIndex  LNCapVertexNodes;
	if(LRadialSections == 0) LNCapVertexNodes = LSides;
	else
	{
		LNCapVertexNodes = LSides * 3 + (LRadialSections - 1) * LSides * 4;
	}
	if(PComponent->TopCap) LNumOfVertexNodes += LNCapVertexNodes;
	if(PComponent->BottomCap) LNumOfVertexNodes += LNCapVertexNodes;


	const E3dCoordinate  LInvR = 1.0 / PComponent->Radius;
	EIndex               LVi = 0;

// Side
//
	E3dCoordinate  LUStep = -1.0 / (E3dCoordinate)LSides;
	E3dCoordinate  LVStep =  1.0 / (E3dCoordinate)LHeightSections;

	const E3dVertexNodeSize  LVertexNodeSize = PPolyGroup->VertexNodeSize;

	E3dCoordinate   LSideHeightH, LDomeHeight,
	                LU, LV;

	E3dPolygon*     LPolygon = PPolyGroup->Polygons.Items;

	E3dVertex*      LVertices = PMesh->Vertices.Items;const E3dVertexSize  LVertexSize = PMesh->VertexSize;

	const int       LNormalOffset = E3dVertexNodeAttribute_Offset(&PPolyGroup->VertexAttributes,  E3dVtxAttribute_Normal, 0);

	if(PComponent->Side)
	{
//	PMesh->PolyGroups[0]->Polygons.Count += PNSideVertices * LHeightSections;

		LV = 0.0;
		EIndex  LI = 0;for(;LI < LHeightSections;LI++, LVi += PNSideVertices, LV += LVStep)
		{
			LU = 1.0;

// Use last vtx on this section
//
			E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4,  PNSideVertices - 1 + LVi, LVi, LVi + PNSideVertices, LVi + PNSideVertices - 1 + PNSideVertices);

			{
				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				const E3dVertex*  LVertex0 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[PNSideVertices - 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[PNSideVertices - 1].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = -LUStep;LUV->Y = LV;
				EStruct_Inc(LVertexNode, LVertexNodeSize);EStruct_Inc(LUV, LVertexNodeSize);

				const E3dVertex*  LVertex1 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[0].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[0].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = 0.0;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex2 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[0].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[0].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = 0.0;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[PNSideVertices - 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[PNSideVertices - 1].Y; }
				LUV->X = -LUStep;LUV->Y = LV + LVStep;

				E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

				LPolygon++;
			}

			LU += LUStep;

			EIndex  LH = 1;for(;LH < LSides;LH++, LU += LUStep)
			{
				E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4,  LH - 1 + LVi, LH + LVi, LVi + LH + PNSideVertices, LVi + LH - 1 + PNSideVertices);
				E3dVertexNode*  LVertexNode   = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV           = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				const E3dVertex*  LVertex0 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH - 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH - 1].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU - LUStep;LUV->Y = LV;
				EStruct_Inc(LVertexNode, LVertexNodeSize);EStruct_Inc(LUV, LVertexNodeSize);

				const E3dVertex*  LVertex1 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH].X;    LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex2 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH].X;    LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH - 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH - 1].Y; }
				LUV->X = LU-LUStep;LUV->Y = LV + LVStep;

				E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);
				LPolygon++;
			}
		}
	}


	LVStep = 1.0 / (E3dCoordinate)LRadialSections;

	if(PComponent->TopCap)
	{
		E3dCoord2*  LUnitCirclePoint;

		if(LRadialSections == 0)
		{
			E3dVertexNode*  LVertexNode = E3dPolygon_VertexNodesNew(LPolygon, LVertexNodeSize, PNSideVertices);if(LVertexNode)
			{
				E3dCoord3*  LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*  LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				LUnitCirclePoint = PUnitArcPoints + PNSideVertices - 1;
				EIndex  LI = 0, LH = PNSideVertices - 1;for(;LI < PNSideVertices;LI++, --LUnitCirclePoint)
				{
					LVertexNode->VertexID = LH;
					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LUnitCirclePoint->X * 0.5 + 0.5;LUV->Y = LUnitCirclePoint->Y * 0.5 + 0.5;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);
					LH--;
				}
				LPolygon->NumOfVertices = LPolygon->NumOfExteriorVertices = PNSideVertices;
				LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = 1.0;LPolygon->Normal.Z = 0.0;
				LPolygon++;
			}
		}
		else
		{
			EIndex  LCenterV = PNSideVertices * (LHeightSections + 1);

			EIndex  LNextCircleV;
			if(LRadialSections == 1) LNextCircleV = 0;
			else LNextCircleV = LCenterV+1;

			LVi = LNextCircleV;
			LV = 0.0;
			LU = 1.0;
			if((LDomeHeight = PComponent->TopDomeHeight) == 0.0)
			{
// Flat top
				EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
				{
					if(LVC < PNSideVertices - 1) E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVi + 1, LVi, LCenterV);
					else E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LNextCircleV, LVi, LCenterV);
					LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = 1.0;LPolygon->Normal.Z = 0.0;

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0; }
					LUV->X = LU + LUStep * 0.5;LUV->Y = LV;

					LPolygon++;
				}
			}
			else
			{
// Rounded top
				if(PComponent->OriginAtBottom) LSideHeightH = PComponent->Height;
				else LSideHeightH = PComponent->Height * 0.5;

				EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
				{
					if(LVC < PNSideVertices - 1) E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVi + 1, LVi, LCenterV);
					else E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LNextCircleV, LVi, LCenterV);

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					const E3dVertex*  LVertex;
					const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
					LUV->X = LU + LUStep * 0.5;LUV->Y = LV;

					E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

					LPolygon++;
				}
			}

			LV += LVStep;

			if(LRadialSections > 1)
			{
				const EIndex  LRN = LRadialSections - 1;

				if(LDomeHeight == 0.0)
				{
// Flat top
					EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
					{
						LVi = LNextCircleV;
						if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
						else LNextCircleV = 0;    // Top outer side vertices start at index 0

						LU = 1.0;
						EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
						{
							if(LVC < PNSideVertices - 1) E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi + 1, LVC + LNextCircleV + 1, LVC + LNextCircleV, LVi);
							else E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi - PNSideVertices + 1, LVC + LNextCircleV - PNSideVertices + 1, LVC + LNextCircleV, LVi);

							LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = 1.0;LPolygon->Normal.Z = 0.0;

							E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
							E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
							E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0; }
							LUV->X = LU;LUV->Y = LV;

							LPolygon++;
						}
					}
				}
				else
				{
// Rounded top
					if(PComponent->OriginAtBottom) LSideHeightH = PComponent->Height;
					else LSideHeightH = PComponent->Height * 0.5;

					EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
					{
						LVi = LNextCircleV;
						if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
						else LNextCircleV = 0;    // Top outer side vertices start at index 0

						LU = 1.0;
						EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
						{
							if(LVC < PNSideVertices - 1) E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi + 1, LVC + LNextCircleV + 1, LVC + LNextCircleV, LVi);
							else E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi - PNSideVertices + 1, LVC + LNextCircleV - PNSideVertices + 1, LVC + LNextCircleV, LVi);

							E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
							E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
							E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

							const E3dVertex*  LVertex;

							const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV;

							E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

							LPolygon++;
						}
					}
				}

			}

		}
	}

	LVStep = -1.0 / (E3dCoordinate)LRadialSections;

	if(PComponent->BottomCap)
	{
		E3dCoord2*	LUnitCirclePoint;

		if(LRadialSections == 0)
		{
			E3dVertexNode*  LVertexNode = E3dPolygon_VertexNodesNew(LPolygon, LVertexNodeSize, PNSideVertices);if(LVertexNode)
			{
				E3dCoord3*  LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*  LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);
				LVi = PNSideVertices * LHeightSections;
				LUnitCirclePoint = PUnitArcPoints;
				EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LUnitCirclePoint++)
				{
					LVertexNode->VertexID = LVi;
					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LUnitCirclePoint->X * 0.5 + 0.5;LUV->Y = LUnitCirclePoint->Y * 0.5 + 0.5;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);
				}
				LPolygon->NumOfVertices = LPolygon->NumOfExteriorVertices = PNSideVertices;
				LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = -1.0;LPolygon->Normal.Z = 0.0;
				LPolygon++;
			}
		}
		else
		{
			EIndex	LCenterV, LNextCircleV;

			if(PComponent->TopCap) LCenterV = PNSideVertices * (LHeightSections + 1 + LRadialSections - 1) + 1;
			else LCenterV = PNSideVertices * (LHeightSections + 1);

			if(LRadialSections == 1) LNextCircleV = PNSideVertices * LHeightSections;
			else LNextCircleV = LCenterV + 1;

			LVi = LNextCircleV;
			LV = 1.0;
			LU = 1.0;
			if((LDomeHeight = PComponent->TopDomeHeight) == 0.0)
			{
// Flat bottom
				EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
				{
					if(LVC < PNSideVertices - 1) E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LCenterV, LVi, LVi + 1);
					else E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LCenterV, LVi, LNextCircleV);
					LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = -1.0;LPolygon->Normal.Z = 0.0;

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep * 0.5;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0; }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;

					LPolygon++;
				}
			}
			else
			{
// Rounded bottom
				if(PComponent->OriginAtBottom) LSideHeightH = 0.0;
				else LSideHeightH = -PComponent->Height * 0.5;

				EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
				{
					if(LVC < PNSideVertices - 1) E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LCenterV, LVi, LVi + 1);
					else E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LCenterV, LVi, LNextCircleV);

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					const E3dVertex*  LVertex;

					const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep * 0.5;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;

					E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

					LPolygon++;
				}
			}

			LV += LVStep;

			if(LRadialSections > 1)
			{
				const EIndex  LRN = LRadialSections - 1;

				if(LDomeHeight == 0.0)
				{
// Flat bottom
					EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
					{
						LVi = LNextCircleV;
						if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
						else LNextCircleV = PNSideVertices * LHeightSections;    // Bottom-outer side vertices start at index PNSideVertices * LHeightSections

						LU = 1.0;
						EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
						{
							if(LVC < PNSideVertices - 1) E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV + 1, LVi + 1);
							else E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV - PNSideVertices + 1, LVi - PNSideVertices + 1);

							LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = -1.0;LPolygon->Normal.Z = 0.0;

							E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
							E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
							E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV+LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0; }
							LUV->X = LU + LUStep;LUV->Y = LV;

							LPolygon++;
						}
					}
				}
				else
				{
// Rounded bottom
					if(PComponent->OriginAtBottom) LSideHeightH = 0.0;
					else LSideHeightH = -PComponent->Height * 0.5;

					EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
					{
						LVi = LNextCircleV;
						if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
						else LNextCircleV = PNSideVertices * LHeightSections;    // Bottom-outer side vertices start at index PNSideVertices * LHeightSections

						LU = 1.0;
						EIndex  LVC = 0;for(;LVC < PNSideVertices;LVC++, LVi++, LU += LUStep)
						{
							if(LVC < PNSideVertices - 1) E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV + 1, LVi + 1);
							else E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV - PNSideVertices + 1, LVi - PNSideVertices + 1);

							E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
							E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
							E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

							const E3dVertex*  LVertex;

							const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
							LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
							EStruct_Inc(LVertexNode, LVertexNodeSize);

							LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
							if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
							LUV->X = LU + LUStep;LUV->Y = LV;

							E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

							LPolygon++;
						}
					}
				}
			}
		}
	}
}


static void _CreatePolygonsForPie(const E3dCylinderComponent* PComponent, E3dMesh* PMesh, E3dPolyGroup* PPolyGroup, const EIndex PSides, const EIndex PNSideVertices, const E3dCoord2* PUnitArcPoints)
{
	E3dCoordinate   LSideHeightH, LDomeHeight;
	EIndex          LNumOfVertexNodes = 0;(void)LNumOfVertexNodes;
	EIndex          LNCapVertexNodes;
	EIndex          LRadialSections = PComponent->RadialSections;
	const EIndex    LHeightSections = PComponent->HeightSections;

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

// Make sure the VertexNodes have UVs
//
	E3dVertexNodeSize  LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_UV, 0);
	if(LUVOffsetInVtxNode < 0)
	{
		E3dPolyGroup_VertexAttributeAdd(PPolyGroup,  NULL, E3dVtxAttribute_UV, 0,  NULL);
		LUVOffsetInVtxNode = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_UV, 0);
	}

	if(PComponent->Side) LNumOfVertexNodes += LHeightSections * PSides * 4;

	if(LRadialSections == 0) LNCapVertexNodes = PSides;
	else
	{
		LNCapVertexNodes = PSides * 3 + (LRadialSections - 1) * PSides * 4;
	}
	if(PComponent->TopCap) LNumOfVertexNodes += LNCapVertexNodes;
	if(PComponent->BottomCap) LNumOfVertexNodes += LNCapVertexNodes;


	if(LRadialSections < 1) LRadialSections = 1;

	const E3dCoordinate  LUStep = -1.0 / (E3dCoordinate)PSides;
	E3dCoordinate        LVStep = 1.0 / (E3dCoordinate)LHeightSections;

	const E3dCoordinate  LInvR = 1.0/PComponent->Radius;
	EIndex               LVi = 0;

	const int            LNormalOffset = E3dVertexNodeAttribute_Offset(&PPolyGroup->VertexAttributes,  E3dVtxAttribute_Normal, 0);

	E3dVertex*       LVertices = PMesh->Vertices.Items;const E3dVertexSize  LVertexSize = PMesh->VertexSize;

// Side
//
	const E3dVertexNodeSize  LVertexNodeSize = PPolyGroup->VertexNodeSize;
	E3dPolygon*  LPolygon = PPolyGroup->Polygons.Items;
	if(PComponent->Side)
	{
		EIndex  LI = 0;E3dCoordinate  LV = 0.0;for(;LI < LHeightSections;LI++, LVi += PNSideVertices, LV += LVStep)
		{
			EIndex  LH = 0;E3dCoordinate  LU = 1.0;for(;LH < PSides;LH++, LU += LUStep)
			{
				E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LH + LVi, LH + 1 + LVi, LVi + LH + 1 + PNSideVertices, LVi + LH + PNSideVertices);

				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				const E3dVertex*  LVertex0 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH].X;    LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex1 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH + 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH + 1].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU + LUStep;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex2 = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH + 1].X;LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH + 1].Y;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = PUnitArcPoints[LH].X;    LVertexNormal->Y = 0.0;LVertexNormal->Z = PUnitArcPoints[LH].Y; }
				LUV->X = LU;LUV->Y = LV + LVStep;

				E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);
				LPolygon++;
			}
		}
	}

	LVStep = 1.0 / (E3dCoordinate)LRadialSections;

	if(PComponent->TopCap)
	{
		EIndex  LCenterV = PNSideVertices * (LHeightSections + 1);
		EIndex  LNextCircleV;

		if(LRadialSections == 1) LNextCircleV = 0;
		else LNextCircleV = LCenterV + 1;

// Triangles in the middle
//
		LVi = LNextCircleV;
		E3dCoordinate  LV = 0.0;
		E3dCoordinate  LU = 1.0;
		if((LDomeHeight = PComponent->TopDomeHeight) == 0.0)
		{
// Flat top
			EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
			{
				E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVi + 1, LVi, LCenterV);
				LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = 1.0;LPolygon->Normal.Z = 0.0;

				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0; }
				LUV->X = LU + LUStep * 0.5;LUV->Y = LV;

				LPolygon++;
			}
		}
		else
		{
// Rounded top
			if(PComponent->OriginAtBottom) LSideHeightH = PComponent->Height;
			else LSideHeightH = PComponent->Height * 0.5;

			EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
			{
				E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LVi + 1, LVi, LCenterV);

				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				const E3dVertex*  LVertex;

				const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
				LUV->X = LU + LUStep * 0.5;LUV->Y = LV;

				E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

				LPolygon++;
			}
		}

		LV += LVStep;
		const EIndex  LRN = LRadialSections - 1;

		if(LDomeHeight == 0.0)
		{
// Flat top
			EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
			{
				LVi = LNextCircleV;
				if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
				else LNextCircleV = 0;			// Top outer side vertices start at index 0

				LU = 1.0;
				EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
				{
					E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi + 1, LVC + LNextCircleV + 1, LVC + LNextCircleV, LVi);

					LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = 1.0;LPolygon->Normal.Z = 0.0;

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = 1.0;LVertexNormal->Z = 0.0; }
					LUV->X = LU;LUV->Y = LV;

					LPolygon++;
				}
			}
		}
		else
		{
// Rounded top
			if(PComponent->OriginAtBottom) LSideHeightH = PComponent->Height;
			else LSideHeightH = PComponent->Height * 0.5;

			EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
			{
				LVi = LNextCircleV;
				if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
				else LNextCircleV = 0;  // Top outer side vertices start at index 0

				EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
				{
					E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi + 1, LVC + LNextCircleV + 1, LVC + LNextCircleV, LVi);
					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					const E3dVertex*  LVertex;

					const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
					LUV->X = LU;LUV->Y = LV;

					E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

					LPolygon++;
				}
			}

		}
	}

	LVStep = -1.0 / (E3dCoordinate)LRadialSections;

	if(PComponent->BottomCap)
	{
		EIndex  LCenterV;
		if(PComponent->TopCap) LCenterV = PNSideVertices*(LHeightSections + 1 + LRadialSections - 1) + 1;
		else LCenterV = PNSideVertices * (LHeightSections + 1);

		EIndex  LNextCircleV;
		if(LRadialSections == 1) LNextCircleV = PNSideVertices * LHeightSections;
		else LNextCircleV = LCenterV + 1;

		LVi = LNextCircleV;
		E3dCoordinate  LV = 1.0;
		E3dCoordinate  LU = 1.0;
		if((LDomeHeight = PComponent->TopDomeHeight) == 0.0)
		{
// Flat bottom
			EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
			{
				E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 3, LCenterV, LVi, LVi + 1);
				LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = -1.0;LPolygon->Normal.Z = 0.0;

				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU + LUStep * 0.5;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0; }
				LUV->X = LU + LUStep;LUV->Y = LV + LVStep;

				LPolygon++;
			}
		}
		else
		{
// Rounded bottom
			if(PComponent->OriginAtBottom) LSideHeightH = 0.0;
			else LSideHeightH = -PComponent->Height * 0.5;

			EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
			{
				E3dPolygon_SetAsTriangle(LPolygon, LVertexNodeSize, LCenterV, LVi, LVi + 1);

				E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
				E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
				E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

				const E3dVertex*  LVertex;

				const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU+LUStep*0.5;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
				LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
				EStruct_Inc(LVertexNode, LVertexNodeSize);

				const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
				if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
				LUV->X = LU + LUStep;LUV->Y = LV + LVStep;

				E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

				LPolygon++;
			}
		}

		LV += LVStep;
		const EIndex  LRN = LRadialSections - 1;

		if(LDomeHeight == 0.0)
		{
// Flat bottom
			EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
			{
				LVi = LNextCircleV;
				if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
				else LNextCircleV = PNSideVertices * LHeightSections;  // Bottom-outer side vertices start at index PNSideVertices * LHeightSections

				LU = 1.0;
				EIndex  LVC = 0;for(;LVC < PSides;LVC++, LVi++, LU += LUStep)
				{
					E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV + 1, LVi + 1);

					LPolygon->Normal.X = 0.0;LPolygon->Normal.Y = -1.0;LPolygon->Normal.Z = 0.0;

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					if(LVertexNormal) { LVertexNormal->X = 0.0;LVertexNormal->Y = -1.0;LVertexNormal->Z = 0.0; }
					LUV->X = LU + LUStep;LUV->Y = LV;

					LPolygon++;
				}
			}
		}
		else
		{
// Rounded bottom
			if(PComponent->OriginAtBottom) LSideHeightH = 0.0;
			else LSideHeightH = -PComponent->Height * 0.5;

			EIndex  LRC = 0;for(;LRC < LRN;LRC++, LV += LVStep)
			{
				LVi = LNextCircleV;
				if(LRC < LRN - 1) LNextCircleV = LVi + PNSideVertices;
				else LNextCircleV = PNSideVertices * LHeightSections;	// Bottom-outer side vertices start at index PNSideVertices * LHeightSections

				LU = 1.0;
				EIndex  LVC = 0;for(;LVC<PSides;LVC++, LVi++, LU += LUStep)
				{
					E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV + 1, LVi + 1);

					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*      LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dCoord2*      LUV = (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffsetInVtxNode);

					const E3dVertex*  LVertex;

					const E3dVertex*  LVertex0 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex1 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					const E3dVertex*  LVertex2 = LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR;EStruct_Inc(LVertexNormal, LVertexNodeSize); }
					LUV->X = LU + LUStep;LUV->Y = LV + LVStep;EStruct_Inc(LUV, LVertexNodeSize);
					EStruct_Inc(LVertexNode, LVertexNodeSize);

					LVertex = EStruct_ByIndex(LVertices, LVertexSize, LVertexNode->VertexID);
					if(LVertexNormal) { LVertexNormal->X = LVertex->Position.X * LInvR;LVertexNormal->Y = (LVertex->Position.Y - LSideHeightH) * LInvR;LVertexNormal->Z = LVertex->Position.Z * LInvR; }
					LUV->X = LU + LUStep;LUV->Y = LV;

					E3d_TriangleNormal(&LVertex0->Position, &LVertex1->Position, &LVertex2->Position,  &LPolygon->Normal);

					LPolygon++;
				}
			}
		}
	}


// Extra sides
//
	{
//		EIndex  LNextCircleV = PNSideVertices;
		EIndex  LRC = 0;const EIndex  LRN = LRadialSections - 1;for(;LRC < LRN;LRC++)
		{
			EIndex  LVC = 0;for(;LVC < LHeightSections;LVC++)
			{
//				E3dPolygon_SetVa(LPolygon, LVertexNodeSize, 4, LVi, LVC + LNextCircleV, LVC + LNextCircleV + 1, LVi + 1);
			}
		}
	}

	PPolyGroup->Polygons.Count = LPolygon - PPolyGroup->Polygons.Items;
}


//================================================
// Copy editable fields
//================================================
static void _CopyFields(const E3dCylinderComponent* PSrc, E3dCylinderComponent* PDst)
{
#define _CopyField(p)  PDst->p = PSrc->p
	_CopyField(TopDomeHeight);
	_CopyField(RadialSections);
	_CopyField(Radius);
	_CopyField(Height);
	_CopyField(Sides);
	_CopyField(HeightSections);
	_CopyField(Type);
	_CopyField(StartAngle);
	_CopyField(EndAngle);
	_CopyField(Full);
	_CopyField(Smooth);
	_CopyField(Side);
	_CopyField(TopCap);
	_CopyField(BottomCap);
	_CopyField(Pie);
	_CopyField(OriginAtBottom);
#undef _CopyField

	E3dBevel_Copy(&PSrc->StartBevel, &PDst->StartBevel);
	E3dBevel_Copy(&PSrc->EndBevel,   &PDst->EndBevel);
}


//================================================
// Add a Revolve ObjectComponent to the Mesh
//================================================
static E3dObjectComponent* _RevolveComponent_Add(E3dObject* PObject, E3dObjectComponentClass* PRevolveClass)
{
	E3dObjectComponent*  LRevolveInfo = E3dObjectComponent_Add(PObject, PRevolveClass);if(LRevolveInfo)
	{
// Revolve is an internal representation of the Cylinder, so hide its ObjectComponent in the GUI and mark it non-saveable
//
		LRevolveInfo->InternalFlags &= ~(EComponentCOPY|EComponentSAVE|EComponentVISIBLE|EComponentREMOVABLE);
		LRevolveInfo->InternalFlags = 0;

		E3dSpline*  LSpline;E_NewNamed(E3dSpline, "Profile",  LSpline);if(LSpline)
		{
			LSpline->SplineType = E3dSplineBEZIER;  // Make the Spline Bezier, for flexibility. But, we'll just create linear segments here.

			EObject_NamedResourceSetObject(LRevolveInfo, "ProfileGeometry", LSpline);  // This is how you pass an Object as a Resource. In this case, the profile Spline to be revolved
			return(LRevolveInfo);
		}
	}
	return(NULL);
}


//================================================
// Create profile Spline for Revolve
//================================================
static void _ProfileSplineUpdate(E3dSpline* PSpline,  const E3dCoordinate POuterRadius, const E3dCoordinate PInnerRadius, const EIndex PRadialSections, const E3dCoordinate PHeight,  const EIndex PHeightSections,  const E3dBevel* PStartBevel, const E3dBevel* PStartInnerBevel,  const E3dBevel* PEndBevel,  const E3dBevel* PEndInnerBevel,  E3dCylinderComponent* PComponent)
{
	const EIndex  LRadialSections = PRadialSections >= 1 ? PRadialSections : 1;
	const EIndex  LHeightSections = PHeightSections;
	const EBool   LIsTube = PInnerRadius != 0.0;

	EIndex  LNCVs = 0;

	{
		LNCVs += LRadialSections;
		if(PStartBevel)
		{
			EIndex  LNCVsT = 0;
			if(PStartBevel->AddStartSmoother) LNCVsT++;
			LNCVsT += PStartBevel->NumOfRoundingSteps;
			if(PStartBevel->AddEndSmoother) LNCVsT++;
			LNCVs += LNCVsT;
		}

		if(PStartInnerBevel)
		{
			EIndex  LNCVsT = 0;
			if(PStartInnerBevel->AddStartSmoother) LNCVsT++;
			LNCVsT += PStartInnerBevel->NumOfRoundingSteps;
			if(PStartInnerBevel->AddEndSmoother) LNCVsT++;
			LNCVs += LNCVsT;
		}

		if(LIsTube) LNCVs += LHeightSections * 2 - 1;
		else LNCVs += LHeightSections;

		if(PEndBevel)
		{
			EIndex  LNCVsT = 0;
			if(PEndBevel->AddStartSmoother) LNCVsT++;
			LNCVsT += PEndBevel->NumOfRoundingSteps;
			if(PEndBevel->AddEndSmoother) LNCVsT++;
			LNCVs += LNCVsT;
		}

		if(PEndInnerBevel)
		{
			EIndex  LNCVsT = 0;
			if(PEndInnerBevel->AddStartSmoother) LNCVsT++;
			LNCVsT += PEndInnerBevel->NumOfRoundingSteps;
			if(PEndInnerBevel->AddEndSmoother) LNCVsT++;
			LNCVs += LNCVsT;
		}

		LNCVs += LRadialSections;
		LNCVs++;

printf("%s() %s:%d  %sLNCVs%s  %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LNCVs);fflush(stdout);

		E3dSplineCV*  LCV = E3dSplineCVs_New(E3dSplineLINEAR, LNCVs);E3dSpline_SetCVs(PSpline, LCV, LNCVs);

		E3dCoord2*  LDomeUnitArcPoints = NULL;
		if(PComponent->BottomDomeHeight > 0.0 || PComponent->TopDomeHeight > 0.0) LDomeUnitArcPoints = E3d_CreateUnitArc(LRadialSections, 0.0, 90.0,  NULL);


		{
			E3dCoordinate  LY = PComponent->OriginAtBottom ? 0.0 : -PHeight / 2.0;

			{
				const uint16_t  LSegmentFlags = PComponent->BottomCap ? E3dSegmentDEFAULT : E3dSegmentDEFAULT & ~E3dSegmentVISIBLE;EIndex  LI = 0;
				E3dCoordinate  LX = PInnerRadius;

				if(PComponent->BottomDomeHeight > 0.0)
				{
// Capsule bottom
//
					const E3dCoord2*  LUnitArcPoint = LDomeUnitArcPoints + LRadialSections;for(;LI < LRadialSections;LI++, LUnitArcPoint--)
					{
						LCV->Position = E3dCoord3_s(LX + LUnitArcPoint->X * POuterRadius, LY - LUnitArcPoint->Y * POuterRadius, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
					}
				}
				else
				{
					E3dCoordinate  LRadialRange = POuterRadius - PInnerRadius;
					if(PStartBevel)
					{
						const EBool    LBevelInvert = PStartBevel->Invert;
						E3dCoordinate  LReductionByBevel = LBevelInvert ? -PStartBevel->Radius : PStartBevel->Radius;
						if(PStartBevel->AddStartSmoother) LReductionByBevel += PStartBevel->Radius * E3dBEVEL_SMOOTHER;
						LRadialRange -= LReductionByBevel;
					}
					if(PStartInnerBevel)
					{
						const EBool    LBevelInvert = PStartInnerBevel->Invert;
						E3dCoordinate  LReductionByBevel = LBevelInvert ? -PStartInnerBevel->Radius : PStartInnerBevel->Radius;
						if(PStartInnerBevel->AddStartSmoother) LReductionByBevel += PStartInnerBevel->Radius * E3dBEVEL_SMOOTHER;
						LRadialRange -= LReductionByBevel;
					}

					const E3dCoordinate  LRadialStep = LRadialRange / (E3dCoordinate)LRadialSections;

					if(PStartInnerBevel)
					{
						const EIndex         LAN = PStartInnerBevel->NumOfRoundingSteps;
						const E3dCoordinate  LBevelRadius = PStartInnerBevel->Radius;
						const EBool          LBevelInvert = PStartInnerBevel->Invert;

						if(PStartInnerBevel->AddEndSmoother) { LCV->Position = E3dCoord3_s(PInnerRadius, LY + LBevelRadius * (1.0 + E3dBEVEL_SMOOTHER), 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++; }

						{
							E3dCoord2*  LUnitArcPoints = E3d_CreateUnitArc(LAN, -180.0, -90.0,  NULL);
							EIndex  LAI = 0;const E3dCoord2*  LUnitArcPoint = LUnitArcPoints;for(;LAI < LAN;LAI++, LUnitArcPoint++)
							{
								if(LBevelInvert) LCV->Position = E3dCoord3_s(-LUnitArcPoint->X * LBevelRadius + PInnerRadius - LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY + LBevelRadius, 0.0);
								else LCV->Position = E3dCoord3_s(LUnitArcPoint->X * LBevelRadius + PInnerRadius + LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY + LBevelRadius, 0.0);
								LCV->Segment.Flags = LSegmentFlags;LCV++;
							}
							EFree(LUnitArcPoints);
						}

						if(LIsTube)
						{
							if(LBevelInvert) LX = PInnerRadius - LBevelRadius;
							else LX = PInnerRadius + LBevelRadius;
							if(PStartInnerBevel->AddStartSmoother) { LCV->Position = E3dCoord3_s(LX, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;LX += LBevelRadius * E3dBEVEL_SMOOTHER; }
						}
					}

					for(;LI < LRadialSections;LI++, LX += LRadialStep)
					{
						LCV->Position = E3dCoord3_s(LX, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
					}

					if(PStartBevel)
					{
						const EIndex         LAN = PStartBevel->NumOfRoundingSteps;
						const E3dCoordinate  LBevelRadius = PStartBevel->Radius;
						const EBool          LBevelInvert = PStartBevel->Invert;

						if(PStartBevel->AddStartSmoother)
						{
							if(LBevelInvert) LCV->Position = E3dCoord3_s(POuterRadius + LBevelRadius * (1.0 - E3dBEVEL_SMOOTHER), LY, 0.0);
							else LCV->Position = E3dCoord3_s(POuterRadius - LBevelRadius * (1.0 + E3dBEVEL_SMOOTHER), LY, 0.0);
							LCV->Segment.Flags = LSegmentFlags;LCV++;
						}

						{
							E3dCoord2*  LUnitArcPoints = E3d_CreateUnitArc(LAN, -90.0, 0.0,  NULL);
							EIndex  LAI = 0;const E3dCoord2*  LUnitArcPoint = LUnitArcPoints;for(;LAI < LAN;LAI++, LUnitArcPoint++)
							{
								if(LBevelInvert) LCV->Position = E3dCoord3_s(-LUnitArcPoint->X * LBevelRadius + POuterRadius + LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY + LBevelRadius, 0.0);
								else LCV->Position = E3dCoord3_s(LUnitArcPoint->X * LBevelRadius + POuterRadius - LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY + LBevelRadius, 0.0);
								LCV->Segment.Flags = LSegmentFlags;LCV++;
							}
							EFree(LUnitArcPoints);
						}
						LY += LBevelRadius;

						if(PStartBevel->AddEndSmoother)
						{
							LCV->Position = E3dCoord3_s(POuterRadius, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;LY += LBevelRadius * E3dBEVEL_SMOOTHER;
						}
					}
				}
			}

			{
				E3dCoordinate  LHeightRange = PHeight;
				if(PStartBevel)
				{
					LHeightRange -= PStartBevel->Radius;
					if(PStartBevel->AddEndSmoother) LHeightRange -= PStartBevel->Radius * E3dBEVEL_SMOOTHER;
				}

				if(PEndBevel)
				{
					LHeightRange -= PEndBevel->Radius;
					if(PEndBevel->AddEndSmoother) LHeightRange -= PEndBevel->Radius * E3dBEVEL_SMOOTHER;
				}

				const E3dCoordinate  LHeightStep = LHeightRange / (E3dCoordinate)LHeightSections;
				const uint16_t  LSegmentFlags = PComponent->Side ? E3dSegmentDEFAULT : E3dSegmentDEFAULT & ~E3dSegmentVISIBLE;EIndex  LI = 0;for(;LI < LHeightSections;LI++, LY += LHeightStep)
				{
					LCV->Position = E3dCoord3_s(POuterRadius, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
				}
			}
		}

		{
			E3dCoordinate  LY = PComponent->OriginAtBottom ? PHeight : PHeight / 2.0;

			{
				const uint16_t  LSegmentFlags = PComponent->TopCap ? E3dSegmentDEFAULT : E3dSegmentDEFAULT & ~E3dSegmentVISIBLE;EIndex  LI = 0;E3dCoordinate  LX = POuterRadius;

				if(PComponent->TopDomeHeight > 0.0)
				{
// Capsule top
//
					const E3dCoord2*  LUnitArcPoint = LDomeUnitArcPoints;for(;LI < LRadialSections + 1;LI++, LUnitArcPoint++)
					{
						LCV->Position = E3dCoord3_s(LX + (LUnitArcPoint->X - 1.0) * POuterRadius, LY + LUnitArcPoint->Y * POuterRadius, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
					}
				}
				else
				{
					E3dCoordinate  LRadialRange = POuterRadius - PInnerRadius;
					if(PEndBevel)
					{
						const EBool    LBevelInvert = PEndBevel->Invert;
						E3dCoordinate  LReductionByBevel = LBevelInvert ? -PEndBevel->Radius : PEndBevel->Radius;
						if(PEndBevel->AddStartSmoother) LReductionByBevel += PEndBevel->Radius * E3dBEVEL_SMOOTHER;
						LRadialRange -= LReductionByBevel;
					}
					if(PEndInnerBevel)
					{
						const EBool    LBevelInvert = PEndInnerBevel->Invert;
						E3dCoordinate  LReductionByBevel = LBevelInvert ? -PEndInnerBevel->Radius : PEndInnerBevel->Radius;
						if(PEndInnerBevel->AddStartSmoother) LReductionByBevel += PEndInnerBevel->Radius * E3dBEVEL_SMOOTHER;
						LRadialRange -= LReductionByBevel;
					}


					const E3dCoordinate  LRadialStep = LRadialRange / (E3dCoordinate)LRadialSections;

					if(PEndBevel)
					{
						const E3dCoordinate  LBevelRadius = PEndBevel->Radius;
						const EBool          LBevelInvert = PEndBevel->Invert;

						if(PEndBevel->AddEndSmoother) { LCV->Position = E3dCoord3_s(POuterRadius, LY - LBevelRadius * (1.0 + E3dBEVEL_SMOOTHER), 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++; }

						const EIndex         LAN = PEndBevel->NumOfRoundingSteps;
						E3dCoord2*           LUnitArcPoints = E3d_CreateUnitArc(LAN, 0.0, 90.0,  NULL);

						{
							EIndex  LAI = 0;const E3dCoord2*  LUnitArcPoint = LUnitArcPoints;for(;LAI < LAN;LAI++, LUnitArcPoint++)
							{
								if(LBevelInvert) LCV->Position = E3dCoord3_s(-LUnitArcPoint->X * LBevelRadius + POuterRadius + LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY - LBevelRadius, 0.0);
								else LCV->Position = E3dCoord3_s(LUnitArcPoint->X * LBevelRadius + POuterRadius - LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY - LBevelRadius, 0.0);
								LCV->Segment.Flags = LSegmentFlags;LCV++;
							}
						}

						EFree(LUnitArcPoints);

						if(LBevelInvert) LX = POuterRadius + LBevelRadius;
						else LX = POuterRadius - LBevelRadius;

						if(PEndBevel->AddStartSmoother)
						{
							LCV->Position = E3dCoord3_s(LX, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
							LX -= LBevelRadius * E3dBEVEL_SMOOTHER;
						}
					}

					for(;LI < LRadialSections;LI++, LX -= LRadialStep)
					{
						LCV->Position = E3dCoord3_s(LX, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
					}

					if(PEndInnerBevel)
					{
						const E3dCoordinate  LBevelRadius = PEndInnerBevel->Radius;
						const EBool          LBevelInvert = PEndInnerBevel->Invert;

						if(PEndInnerBevel->AddStartSmoother)
						{
							if(LBevelInvert) LCV->Position = E3dCoord3_s(PInnerRadius - LBevelRadius * (1.0 - E3dBEVEL_SMOOTHER), LY, 0.0);
							else LCV->Position = E3dCoord3_s(PInnerRadius + LBevelRadius * (1.0 + E3dBEVEL_SMOOTHER), LY, 0.0);
							LCV->Segment.Flags = LSegmentFlags;LCV++;
						}

						const EIndex  LAN = PEndInnerBevel->NumOfRoundingSteps;
						E3dCoord2*    LUnitArcPoints = E3d_CreateUnitArc(LAN, 90.0, 180.0,  NULL);

						{
							EIndex  LAI = 0;const E3dCoord2*  LUnitArcPoint = LUnitArcPoints;for(;LAI < LAN;LAI++, LUnitArcPoint++)
							{
								if(LBevelInvert) LCV->Position = E3dCoord3_s(PInnerRadius - LBevelRadius - LUnitArcPoint->X * LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY - LBevelRadius, 0.0);
								else LCV->Position = E3dCoord3_s(PInnerRadius + LBevelRadius + LUnitArcPoint->X * LBevelRadius, LUnitArcPoint->Y * LBevelRadius + LY - LBevelRadius, 0.0);
								LCV->Segment.Flags = LSegmentFlags;LCV++;
							}
						}

						EFree(LUnitArcPoints);

						LCV->Position = E3dCoord3_s(PInnerRadius, LY - LBevelRadius , 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;

						if(PEndInnerBevel->AddEndSmoother) { LCV->Position = E3dCoord3_s(PInnerRadius, LY - LBevelRadius * (1.0 + E3dBEVEL_SMOOTHER), 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++; }
					}
					else { LCV->Position = E3dCoord3_s(PInnerRadius, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++; }
				}
			}
		}

		if(LIsTube)
		{
			E3dCoordinate  LY = PComponent->OriginAtBottom ? PHeight : PHeight / 2.0;
			E3dCoordinate  LHeightRange = PHeight;
			if(PStartInnerBevel)
			{
				LHeightRange -= PStartInnerBevel->Radius;
				if(PStartInnerBevel->AddEndSmoother) LHeightRange -= PStartInnerBevel->Radius * E3dBEVEL_SMOOTHER;
			}

			if(PEndInnerBevel)
			{
				{
					const E3dCoordinate  LD = PEndInnerBevel->Radius;
					LY -= LD;LHeightRange -= LD;
				}
				if(PEndInnerBevel->AddEndSmoother)
				{
					const E3dCoordinate  LD = PEndInnerBevel->Radius * E3dBEVEL_SMOOTHER;
					LY -= LD;LHeightRange -= LD;
				}
			}

			const E3dCoordinate  LHeightStep = LHeightRange / (E3dCoordinate)LHeightSections;

			const uint16_t  LSegmentFlags = PComponent->Side ? E3dSegmentDEFAULT : E3dSegmentDEFAULT & ~E3dSegmentVISIBLE;EIndex  LI = 1;LY -= LHeightStep;for(;LI < LHeightSections;LI++, LY -= LHeightStep)
			{
				LCV->Position = E3dCoord3_s(PInnerRadius, LY, 0.0);LCV->Segment.Flags = LSegmentFlags;LCV++;
			}
		}
//printf("%s() %s:%d  %sCVs%s  %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)(LCV - PSpline->CVs.Items));fflush(stdout);
	}

	if(PInnerRadius != 0.0) PSpline->Closed = TRUE;

	E3dSpline_CreateLinearSegments(PSpline);
}


//========================================
// Update() (create/change) method
//========================================
static E3dObject* _Update(E3dObject* PObject, E3dObjectComponent* PComponent, E3dObjectComponentClass* PClass,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags)
{
	E3dMesh*               LMesh;
	E3dPolyGroup*          LPolyGroup;
	E3dCylinderComponent*  LComponent;

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

		LComponent = (E3dCylinderComponent*)E3dObjectComponent_Add((E3dObject*)LMesh, PClass);
		if(PComponent && PComponent->Class == PClass) _CopyFields((E3dCylinderComponent*)PComponent, LComponent);
		LPolyGroup = E3dMesh_PolyGroupAdd(LMesh);if(LPolyGroup == NULL)
		{
			E_Delete(LMesh);return(NULL);
		}
		LPolyGroup->VertexNormalType = E3dNormalPROCESS;
	}
	else
	{
		if(!EObject_IsSubClass(PObject, E3dMesh)) return(NULL);
		LMesh = (E3dMesh*)PObject;
		LComponent = (E3dCylinderComponent*)E3dObjectComponent_ByClass(PObject, PClass, FALSE);
		if(LComponent->AttributeChangeCount == 0 && LComponent->InputChangeCount == 0) return(NULL);    // If no attributes or inputs changed, bail
		LPolyGroup = LMesh->PolyGroups.Items[0];
	}

/*
	if(LRadialSections < 1)
	{
		E3dObjectComponentClass*  LExtrudeClass = E3dObjectComponent_ClassByName("Extrude");if(LExtrudeClass == NULL) return(NULL);
	}
*/

	if(_UseRevolve)
	{
// Create a profile Spline and use the Revolve tool to create the cylinder
//
		E3dObjectComponentClass*  LRevolveClass = E3dObjectComponent_ClassByName("Revolve");if(LRevolveClass)
		{
			E3dObjectComponent*  LRevolveInfo = NULL;
			if(PObject == NULL)
			{
				LRevolveInfo = _RevolveComponent_Add((E3dObject*)LMesh, LRevolveClass);
			}
			else
			{
				LRevolveInfo = E3dObjectComponent_ByClass((E3dObject*)LMesh, LRevolveClass, FALSE);
				if(LRevolveInfo == NULL) LRevolveInfo = _RevolveComponent_Add((E3dObject*)LMesh, LRevolveClass);
			}

			if(LRevolveInfo)
			{
				E3dSpline*  LSpline = NULL;EObject_NamedResourceGetObject(LRevolveInfo, "ProfileGeometry", &LSpline);  // This is how you get an Object as a Resource. In this case, the profile Spline to be revolved

// Create Spline CVs in counter-clockwise order, so the revolved surface will be the correct side out.
//
				if(LSpline)
				{
					if(LComponent->ShowProfileSpline)
					{
						E3dModel*  LModel = LMesh->Models.Count > 0 ? LMesh->Models.Items[0] : NULL;
printf("%s() %s:%d  %sLModel%s %p\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, LModel);fflush(stdout);
						if(LModel) E3dModel_ObjectAppend(LModel, (E3dObject*)LSpline, TRUE);
					}

					const E3dBevel*  LStartBevel = LComponent->BottomDomeHeight == 0.0 && LComponent->StartBevel.NumOfRoundingSteps > 0 ? &LComponent->StartBevel : NULL;
					const E3dBevel*  LStartInnerBevel = LComponent->BottomDomeHeight == 0.0 && LComponent->StartInnerBevel.NumOfRoundingSteps > 0 ? &LComponent->StartInnerBevel : NULL;
					const E3dBevel*  LEndBevel = LComponent->TopDomeHeight == 0.0 && LComponent->EndBevel.NumOfRoundingSteps > 0 ? &LComponent->EndBevel : NULL;
					const E3dBevel*  LEndInnerBevel = LComponent->TopDomeHeight == 0.0 && LComponent->EndInnerBevel.NumOfRoundingSteps > 0 ? &LComponent->EndInnerBevel : NULL;
					_ProfileSplineUpdate(LSpline,  LComponent->Radius, LComponent->InnerRadius, LComponent->RadialSections,  LComponent->Height, LComponent->HeightSections,  LStartBevel, LStartInnerBevel,  LEndBevel, LEndInnerBevel,  LComponent);

					EObject_NamedResourceSet1i(LRevolveInfo, "Sides", LComponent->Sides);        // Steps for the revolve
					EObject_NamedResourceSet1f(LRevolveInfo, "SmoothingAngle", 48.0);            // Normal smoothing angle for the Revolve
					EObject_NamedResourceSet1f(LRevolveInfo, "StartAngle", LComponent->StartAngle);
					EObject_NamedResourceSet1f(LRevolveInfo, "EndAngle", LComponent->EndAngle);
					EObject_NamedResourceSet1b(LRevolveInfo, "UseProfileSegmentVisibility", TRUE);

					LRevolveInfo->AttributeChangeCount++;
					LRevolveClass->Update1((E3dObject*)LMesh, NULL,  PTriggeredUpdateFlags, PTriggeredSceneChangeFlags,  FALSE);

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

					return((E3dObject*)LMesh);
				}
			}
		}
		else
		{
printf("%s() %s:%d  %sRevolve%s component class not found\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
		}
	}
	else
	{
printf("%s() %s:%d  %sNon-Revolve%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
	}


	const EIndex   LSides = LComponent->Sides, LHeightSections = LComponent->HeightSections;
	E3dCoordinate  LStartAngle = LComponent->StartAngle, LEndAngle = LComponent->EndAngle;


	EBool  LFull;
	if(LEndAngle - LStartAngle == 360.0) LFull = TRUE;else LFull = FALSE;

	EBool  LPie;
	if(LFull || (!(LComponent->Pie))) LPie = FALSE;else LPie = TRUE;

	LComponent->Full = LFull;

	E3dCoord2*  LUnitArcPoints = NULL;
	EIndex      LSidesN = 0;
	EIndex      LVNum = 0, LPNum = 0;

	EIndex  LRadialSections = LComponent->RadialSections;

	switch(LComponent->Type)
	{
		case E3dCylinderPOLYGONS:
			LVNum = LPNum = 0;

// Create number of Vertices and Polygons and initialize the unit cross-section
//
			if(LFull || (!LComponent->Pie))
			{
				if(LFull) LSidesN = LSides;
				else LSidesN = LSides + 1;

				LVNum += LSidesN * (LHeightSections + 1);
				if(LComponent->Side)
				{
					LPNum += LSidesN * LHeightSections;
				}

				if(LRadialSections < 1)
				{
					if(LComponent->TopCap) LPNum++;
					if(LComponent->BottomCap) LPNum++;
				}
				else
				{
					if(LComponent->TopCap)
					{
						LVNum += ((LRadialSections - 1) * LSidesN + 1);    // "+1" middle vertex
						LPNum += LRadialSections * LSidesN;
					}

					if(LComponent->BottomCap)
					{
						LVNum += ((LRadialSections - 1) * LSidesN + 1);    // "+1" middle vertex
						LPNum += LRadialSections * LSidesN;
					}
				}
			}
			else
			{
// "Pie"
//
				LPNum = LSides * LHeightSections;
				LSidesN = LSides + 1;

				if(LRadialSections < 1) LRadialSections = 1;
				LVNum = (LSides + 1) * (LHeightSections + 1);

				if(LComponent->TopCap)
				{
					LVNum += (LSides + 1) * (LRadialSections - 1) + 1;LPNum += LRadialSections * LSides;
				}
				else if(_AddPieSides) LVNum++;

				if(LComponent->BottomCap)
				{
					LVNum += (LSides + 1) * (LRadialSections - 1) + 1;LPNum += LRadialSections * LSides;
				}
				else if(_AddPieSides) LVNum++;
			}

			LUnitArcPoints = E3d_CreateUnitArc(LSides, LStartAngle, LEndAngle,  NULL);

			_VNum = LVNum;
		break;
	}

	E3dMesh_FreeCaches(LMesh);
	E3dMesh_EdgesFree(LMesh);

	if(LVNum != LMesh->Vertices.Count)
	{
		E3dMesh_VerticesFree(LMesh);
		E3dMesh_VerticesAdd(LMesh, LVNum);
	}

	if(LMesh->Vertices.Count > 0)
	{
		switch(LComponent->Type)
		{
			case E3dCylinderPOLYGONS:
				{
					E3dPolygon*  LPolygon;

					if(LPNum == LPolyGroup->Polygons.Count) LPolygon = LPolyGroup->Polygons.Items;
					else
					{
						if(LPolyGroup->Polygons.Items) E3dPolyGroup_FreePolygons(LPolyGroup);
						LPolygon = LPolyGroup->Polygons.Items = E3dPolygons_New(LPNum);
						LPolyGroup->Polygons.Count = LPolyGroup->Polygons.CountAllocated = LPNum;
					}

					_SetVertices(LMesh, LComponent, LUnitArcPoints);

					if(LPolygon)
					{
						if(LPie) _CreatePolygonsForPie(LComponent, LMesh, LPolyGroup, LSides, LSidesN, LUnitArcPoints);
						else _CreatePolygonsForCylinder(LComponent, LMesh, LPolyGroup, LSidesN, LUnitArcPoints);

						if(LComponent->Smooth) LPolyGroup->Flags |= E3dUSE_VERTEX_NORMALS;
						else LPolyGroup->Flags &= ~E3dUSE_VERTEX_NORMALS;
						LPolyGroup->SmoothingAngle = 30.0;

						if(PTriggeredUpdateFlags) *PTriggeredUpdateFlags = E3dUpdateALL;
					}
				}
			break;
		}
	}

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

	EFreeChk(LUnitArcPoints);

	return((E3dObject*)LMesh);
}


static E3dObject* _CylinderUpdate(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);
	else return(_Update(PObject, PComponentTemplate, _CylinderComponentClass,  PTriggeredUpdateFlags, PTriggeredSceneChangeFlags));
}


static E3dObject* _CapsuleUpdate(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);
	else return(_Update(PObject, PComponentTemplate, _CapsuleComponentClass,  PTriggeredUpdateFlags, PTriggeredSceneChangeFlags));
}


static E3dObject* _TubeUpdate(E3dObject* PObject, E3dObjectComponent* PComponentTemplate,  EUpdateFlags* PTriggeredUpdateFlags, EChangeFlags* PTriggeredSceneChangeFlags,  const EBool PCheckOnly)
{
	if(PCheckOnly) return(NULL);
	else return(_Update(PObject, PComponentTemplate, _TubeComponentClass,  PTriggeredUpdateFlags, PTriggeredSceneChangeFlags));
}


//========================================
// Set GUI for cylinder type
//========================================
static void _SetGUIToType(E3dCylinderComponent* PComponent, E3dResourcesGUI* PResourcesGUI)
{
/*
	XeWidget  LIndentWs[12];int  LIndentN;      // For indenting the ScaleWidgets
	XeWidget  LTraverseWs[12];int  LTraverseN;  // For TextField traversal

	switch(PComponent->Type)
	{
		case E3dCylinderNURBS:
		case E3dCylinderPOLYGONS:
			LIndentN = LTraverseN = 0;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->RadiusW;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->HeightW;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->StartAngleW;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->EndAngleW;
			XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);

			LIndentN = 0;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->SidesW;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->RadialSectionsW;
			LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = PResourcesGUI->HeightSectionsW;
			XeWidgets_IndentFromLeft(LIndentWs, LIndentN, 4);

			XeTraversal_SetRing(LTraverseWs, LTraverseN);
		break;
	}
*/
}


/*
//========================================
// Set Cylinder type
//========================================
static void _CBSetType(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dCylinderComponent*  PComponent = (E3dCylinderComponent*)E3dObjectComponent_ByClass((E3dObject*)_Geometry, _ComponentClass, FALSE);

	if(LComponent->Type != (int)PClientData)
	{
		EFreeChkClr(_UnitCirclePoints);
		LComponent->Type = (int)PClientData;
		_SetGUIToType(LComponent);

		_Create(_Model, _Geometry, LComponent, E3dUpdateALL);

		E3dWindows_RedrawByChangeFlags(E3dChgSHAPE);
	}
}
*/


//========================================
// Dialog box callbacks
//========================================
static void _DCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*                LObject = LPanel->Object;
	E3dMesh*                  LMesh = (E3dMesh*)LObject;
	E3dObjectComponentClass*  LClass = NULL;Xe_GetValuesVa(PW,  XeNUserData0, &LClass,  NULL);
	E3dCylinderComponent*     LComponent = (E3dCylinderComponent*)E3dObjectComponent_ByClass(LObject, LClass, FALSE);
	XeComponentClassDrawer*   LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LClass);
	E3dResourcesGUI*          LGUI = (E3dResourcesGUI*)LDrawer->ResourcesGUI;
	EChangeFlags              LChanged = 0;
	E3dScene*                 LScene = E3d_Scene;
	const EBool               LIsTube = LClass == _TubeComponentClass;

	if(PW == LGUI->SmoothW)
	{
		LComponent->Smooth = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		if(LComponent->Smooth) LMesh->PolyGroups.Items[0]->Flags |= E3dUSE_VERTEX_NORMALS;
		else LMesh->PolyGroups.Items[0]->Flags &= ~E3dUSE_VERTEX_NORMALS;

		E3dMesh_RecomputeNormals(LMesh, FALSE, FALSE);
		LChanged |= E3dChgVERTEX_NORMAL;

		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		E3dObject_UpdateForDisplay((E3dObject*)LMesh,  E3dUpdateVERTEX_NORMAL, E3dCallCALLBACKS,  LScene);
	}
	else if(PW == LGUI->OriginAtBottomW)
	{
		LComponent->OriginAtBottom = ((XeToggleButtonCallbackStruct*)PCallData)->Set;

		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);

		if(_UseRevolve) { LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged); }
		else
		{
			_SetVertices((E3dMesh*)LObject, LComponent, NULL);
			LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE, 0,  LScene);
		}
	}
	else if(PW == LGUI->SideW)
	{
		LComponent->Side = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->TopW)
	{
		LComponent->TopCap = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->BottomW)
	{
		LComponent->BottomCap = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->PieW)
	{
		LComponent->Pie = ((XeToggleButtonCallbackStruct*)PCallData)->Set;
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->RadiusW)
	{
		LComponent->Radius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

		if(LIsTube)
		{
			if(LComponent->InnerRadius + _TubeMinWallThickness > LComponent->Radius)
			{
				LComponent->InnerRadius = LComponent->Radius - _TubeMinWallThickness;
				XeScale_SetValue_Coord(LGUI->InnerRadiusW, LComponent->InnerRadius, FALSE);
			}
		}

		if(_UseRevolve) { LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged); }
		else
		{
			_SetVertices((E3dMesh*)LObject, LComponent, NULL);
			LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE, 0,  LScene);
		}
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
	}
	else if(PW == LGUI->InnerRadiusW)
	{
		LComponent->InnerRadius = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;

		if(LIsTube)
		{
			if(LComponent->InnerRadius + _TubeMinWallThickness > LComponent->Radius)
			{
				LComponent->Radius = LComponent->InnerRadius + _TubeMinWallThickness;
				XeScale_SetValue_Coord(LGUI->RadiusW, LComponent->Radius, FALSE);
			}
		}

		if(_UseRevolve) { LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged); }
		else
		{
			_SetVertices((E3dMesh*)LObject, LComponent, NULL);
			LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE, 0,  LScene);
		}
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
	}
	else if(PW == LGUI->HeightW)
	{
		LComponent->Height = ((XeScaleCallbackStruct*)PCallData)->Value.m_Coordinate;
		if(_UseRevolve) { LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged); }
		else
		{
			_SetVertices((E3dMesh*)LObject, LComponent, NULL);
			LChanged |= E3dObject_UpdateForDisplay(LObject,  E3dUpdateMeshSHAPE, 0,  LScene);
		}
		LChanged |= E3dObject_GetChangeFlagsByClass(LObject);
	}
	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, (double)LComponent->EndAngle, FALSE);
		}
		if(LComponent->StartAngle < LComponent->EndAngle - 360.0)
		{
			LComponent->EndAngle = LComponent->StartAngle + 360.0;
			XeScale_SetValue_Coord(LGUI->EndAngleW, (double)LComponent->EndAngle, FALSE);
		}

//printf("%s() %f-%f  span: %f\n", __FUNCTION__, LComponent->StartAngle, LComponent->EndAngle, LComponent->EndAngle-LComponent->StartAngle);fflush(stdout);
		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, (double)(LComponent->StartAngle), FALSE);
		}
		if(LComponent->StartAngle < LComponent->EndAngle - 360.0)
		{
			LComponent->StartAngle = LComponent->EndAngle - 360.0;
			XeScale_SetValue_Coord(LGUI->StartAngleW, (double)(LComponent->StartAngle), FALSE);
		}

//	EBool  LFull;if((LComponent->EndAngle - LComponent->StartAngle) == 360.0) LFull = TRUE;else LFull = 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);
	}
	else if(PW == LGUI->RadialSectionsW)
	{
		LComponent->RadialSections = ((XeScaleCallbackStruct*)PCallData)->Value.i32;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}
	else if(PW == LGUI->HeightSectionsW)
	{
		LComponent->HeightSections = ((XeScaleCallbackStruct*)PCallData)->Value.i32;
		LComponent->AttributeChangeCount++;E3dObject_CustomUpdate(LObject, LComponent,  E3dCallALL,  &LChanged);
	}

	E3dWindows_RedrawByChangeFlags(LScene, LChanged);
}


static void _CB_Bevel(E3dBevelPanel* PBevelPanel, EPointer PClientData)
{
	E3dObjectPanel*        LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*             LObject = LPanel->Object;
	E3dCylinderComponent*  LComponent = (E3dCylinderComponent*)E3dObjectComponent_ByClass(LObject, _CylinderComponentClass, FALSE);if(LComponent == NULL) return;
	EChangeFlags           LChanged = 0;

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


static void _CB_TubeBevel(E3dBevelPanel* PBevelPanel, EPointer PClientData)
{
	E3dObjectPanel*        LPanel = (E3dObjectPanel*)PClientData;
	E3dObject*             LObject = LPanel->Object;
	E3dCylinderComponent*  LComponent = (E3dCylinderComponent*)E3dObjectComponent_ByClass(LObject, _TubeComponentClass, FALSE);if(LComponent == NULL) return;
	EChangeFlags           LChanged = 0;

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


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

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

					XeToggleButton_SetState(LGUI->SmoothW, LComponent->Smooth, FALSE);

/*
					if(LComponent->TopDomeHeight == 0.0) { XeWidget_Manage(LGUI->CylinderBigW); XeWidget_Unmanage(LGUI->CapsuleBigW); }
					else { XeWidget_Unmanage(LGUI->CylinderBigW); XeWidget_Manage(LGUI->CapsuleBigW); }
*/

					XeToggleButton_SetState(LGUI->SideW, LComponent->Side, FALSE);
					XeToggleButton_SetState(LGUI->TopW, LComponent->TopCap, FALSE);
					XeToggleButton_SetState(LGUI->BottomW, LComponent->BottomCap, FALSE);
					XeToggleButton_SetState(LGUI->OriginAtBottomW, LComponent->OriginAtBottom, FALSE);
					XeToggleButton_SetState(LGUI->PieW, LComponent->Pie, FALSE);

					XeScale_SetValue_Coord(LGUI->RadiusW, LComponent->Radius, FALSE);
					if(LGUI->InnerRadiusW) XeScale_SetValue_Coord(LGUI->InnerRadiusW, LComponent->InnerRadius, FALSE);
					XeScale_SetValue_Coord(LGUI->HeightW, LComponent->Height, FALSE);
					XeScale_SetValue_Coord(LGUI->StartAngleW, LComponent->StartAngle, FALSE);
					XeScale_SetValue_Coord(LGUI->EndAngleW, LComponent->EndAngle, FALSE);
					XeScale_SetValue(LGUI->SidesW, LComponent->Sides, FALSE);
					XeScale_SetValue(LGUI->RadialSectionsW, LComponent->RadialSections, FALSE);
					XeScale_SetValue(LGUI->HeightSectionsW, LComponent->HeightSections, FALSE);

					if(LGUI->StartBevelPanel.RadiusW)
					{
						E3dBevelPanel_Update(&LGUI->StartBevelPanel, &LComponent->StartBevel);
						if(LComponent->Class == _TubeComponentClass) E3dBevelPanel_Update(&LGUI->StartInnerBevelPanel, &LComponent->StartInnerBevel);

						E3dBevelPanel_Update(&LGUI->EndBevelPanel, &LComponent->EndBevel);
						if(LComponent->Class == _TubeComponentClass) E3dBevelPanel_Update(&LGUI->EndInnerBevelPanel, &LComponent->EndInnerBevel);
					}

					_SetGUIToType(LComponent, LGUI);
				}
				else
				{
					XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW, LW;
					XeWidget  LIndentWs[16];int  LIndentN;      // For indenting the ScaleWidgets
					XeWidget  LTraverseWs[16];int  LTraverseN;  // For TextField traversal
					EArg      Args[256];int  ArgCnt;

					E3dResourcesGUI*  LGUI;ENew(LGUI);LDrawer->ResourcesGUI = LGUI;

					const XeCoordinate  LSpacingLeft = XeDrawer_LeftIndent, LSpacingRight = 4;

					{
						EXeStart;
//EXeSetArg(XeNBackgroundColor, &Xe_CyanRGBAiColor);
						EXeSetArg(XeNSpacingLeft, LSpacingLeft);EXeSetArg(XeNSpacingRight, LSpacingRight);
						EXeSetArg(XeNOrientation, XeHORIZONTAL);
						EXeSetArg(XeNSpacingX, 8);
						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->Smooth);
						EXeSetArg(XeNResizableY, FALSE);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->SmoothW = LW = XeWidget_ManagedNew("Smooth", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LComponent->OriginAtBottom);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->OriginAtBottomW = LW = XeWidget_ManagedNew("Origin at bottom", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);
					}

					{
						EXeStart;
						EXeSetArg(XeNSpacingLeft, LSpacingLeft);EXeSetArg(XeNSpacingRight, LSpacingRight);
						EXeSetArg(XeNOrientation, XeHORIZONTAL);
						EXeSetArg(XeNSpacingX, 8);
						EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						XeWidget  LMatrixW = XeWidget_ManagedNew("LMatrixW", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LComponent->Side);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->SideW = LW = XeWidget_ManagedNew("Side", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LComponent->TopCap);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->TopW = LW = XeWidget_ManagedNew("Cap top", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LComponent->BottomCap);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->BottomW = LW = XeWidget_ManagedNew("Cap bottom", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

						EXeStart;
						EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNHighlightThickness, 0);
						EXeSetArg(XeNSet, LComponent->Pie);
						EXeSetArg(XeNUserData0, LComponent->Class);
						LGUI->PieW = LW = XeWidget_ManagedNew("Pie", XeToggleButton_class, LMatrixW,  Args, ArgCnt);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);
					}

					EXeStart;
					EXeSetArg(XeNShadowThickness, 2);
					EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);

					LIndentN = 0;
					LTraverseN = 0;
					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->RadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Radius",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LComponent->Radius,  _TubeMinWallThickness, 1000000.0,  _TubeMinWallThickness, 100.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					if(LComponent->Class == _TubeComponentClass)
					{
						EXeStart;
						EXeSetArg(XeNSpacingRight, LSpacingRight);
						EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
						EXeSetArg(XeNIncrement, 10);
						EXeSetArg(XeNUserData0, LComponent->Class);
						EXeSetArg(XeNResizableY, FALSE);
						LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->InnerRadiusW = LW = XeScale_New_Coord(LTopMatrixW, "Inner radius",  Args, ArgCnt);
						XeScale_SetValueAndRanges_Coord(LW,  LComponent->InnerRadius,  0.0, 1000000.0,  0.0, 100.0,  FALSE);
						XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);
					}
					else LGUI->InnerRadiusW = NULL;

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 4);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->HeightW = LW = XeScale_New_Coord(LTopMatrixW, "Height",  Args, ArgCnt);
					XeScale_SetValueAndRanges_Coord(LW,  LComponent->Height,  0.0001, 1000000.0,  0.0001, 100.0,  FALSE);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, LSpacingLeft);


					LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNSpacingTop, 8);
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
					EXeSetArg(XeNIncrement, 10);
					EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNUserData0, LComponent->Class);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->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(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 8);EXeSetArg(XeNDecimalPoints, 2);
					EXeSetArg(XeNIncrement, 100);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->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);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, LSpacingLeft);


					LIndentN = 0;
					EXeStart;
					EXeSetArg(XeNSpacingTop, 8);
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 4);
					EXeSetArg(XeNMinimum, 3);EXeSetArg(XeNMaximum, 360);EXeSetArg(XeNValue, LComponent->Sides);
					EXeSetArg(XeNScaleMaximum, 60);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->SidesW = LW = XeWidget_ManagedNew("Sides", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 4);
					EXeSetArg(XeNMinimum, 0);EXeSetArg(XeNMaximum, 100);EXeSetArg(XeNValue, LComponent->RadialSections);
					EXeSetArg(XeNScaleMinimum, 0);EXeSetArg(XeNScaleMaximum, 24);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->RadialSectionsW = LW = XeWidget_ManagedNew("Radial segments", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					EXeStart;
					EXeSetArg(XeNSpacingRight, LSpacingRight);
					EXeSetArg(XeNTextFieldDigits, 4);
					EXeSetArg(XeNMinimum, 1);EXeSetArg(XeNMaximum, 100);EXeSetArg(XeNValue, LComponent->HeightSections);
					EXeSetArg(XeNScaleMinimum, 1);EXeSetArg(XeNScaleMaximum, 20);
					EXeSetArg(XeNUserData0, LComponent->Class);
					EXeSetArg(XeNResizableY, FALSE);
					LIndentWs[LIndentN++] = LTraverseWs[LTraverseN++] = LGUI->HeightSectionsW = LW = XeWidget_ManagedNew("Height sections", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)LPanel);

					XeWidgets_IndentFromLeft(LIndentWs, LIndentN, LSpacingLeft);

					if(LComponent->Class == _CylinderComponentClass || LComponent->Class == _TubeComponentClass)
					{
						{
							E3dBevelPanel*  LBevelPanel = &LGUI->StartBevelPanel;
							EXeStart;
							EXeSetArg(XeNShadowThickness, 2);
							EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
							EXeSetArg(XeNResizableY, FALSE);
							XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);
							E3dBevelPanel_CreateDrawer(LBevelPanel, LTopMatrixW, "Bevel bottom",  LComponent->Class == _CylinderComponentClass ? _CB_Bevel : _CB_TubeBevel, (EPointer)LPanel,  &LComponent->StartBevel);
							Xe_SetValuesVa(LBevelPanel->Drawer.OpenCloseButtonW,  XeNSpacingLeft, XeDrawer_LeftIndent,  NULL);
							Xe_SetValuesVa(LBevelPanel->Drawer.DrawerW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, LSpacingRight,  NULL);
							LTraverseWs[LTraverseN++] = LBevelPanel->RadiusW;
							LTraverseWs[LTraverseN++] = LBevelPanel->RoundingStepsW;
						}

						if(LComponent->Class == _TubeComponentClass)
						{
							E3dBevelPanel*  LBevelPanel = &LGUI->StartInnerBevelPanel;
							EXeStart;
							EXeSetArg(XeNShadowThickness, 2);
							EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
							EXeSetArg(XeNResizableY, FALSE);
							XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);
							E3dBevelPanel_CreateDrawer(LBevelPanel, LTopMatrixW, "Bevel bottom inner",  LComponent->Class == _CylinderComponentClass ? _CB_Bevel : _CB_TubeBevel, (EPointer)LPanel,  &LComponent->StartInnerBevel);
							Xe_SetValuesVa(LBevelPanel->Drawer.OpenCloseButtonW,  XeNSpacingLeft, XeDrawer_LeftIndent,  NULL);
							Xe_SetValuesVa(LBevelPanel->Drawer.DrawerW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, LSpacingRight,  NULL);
							LTraverseWs[LTraverseN++] = LBevelPanel->RadiusW;
							LTraverseWs[LTraverseN++] = LBevelPanel->RoundingStepsW;
						}

						{
							E3dBevelPanel*  LBevelPanel = &LGUI->EndBevelPanel;
							EXeStart;
							EXeSetArg(XeNShadowThickness, 2);
							EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
							EXeSetArg(XeNResizableY, FALSE);
							XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);
							E3dBevelPanel_CreateDrawer(LBevelPanel, LTopMatrixW, "Bevel top",  LComponent->Class == _CylinderComponentClass ? _CB_Bevel : _CB_TubeBevel, (EPointer)LPanel,  &LComponent->EndBevel);
							Xe_SetValuesVa(LBevelPanel->Drawer.OpenCloseButtonW,  XeNSpacingLeft, XeDrawer_LeftIndent,  NULL);
							Xe_SetValuesVa(LBevelPanel->Drawer.DrawerW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, LSpacingRight,  NULL);
							LTraverseWs[LTraverseN++] = LBevelPanel->RadiusW;
							LTraverseWs[LTraverseN++] = LBevelPanel->RoundingStepsW;
						}

						if(LComponent->Class == _TubeComponentClass)
						{
							E3dBevelPanel*  LBevelPanel = &LGUI->EndInnerBevelPanel;
							EXeStart;
							EXeSetArg(XeNShadowThickness, 2);
							EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
							EXeSetArg(XeNResizableY, FALSE);
							XeWidget_ManagedNew("", XeSeparatorWidget_class, LTopMatrixW,  Args, ArgCnt);
							E3dBevelPanel_CreateDrawer(LBevelPanel, LTopMatrixW, "Bevel top inner",  LComponent->Class == _CylinderComponentClass ? _CB_Bevel : _CB_TubeBevel, (EPointer)LPanel,  &LComponent->EndInnerBevel);
							Xe_SetValuesVa(LBevelPanel->Drawer.OpenCloseButtonW,  XeNSpacingLeft, XeDrawer_LeftIndent,  NULL);
							Xe_SetValuesVa(LBevelPanel->Drawer.DrawerW,  XeNSpacingLeft, XeDrawer_LeftIndent,  XeNSpacingRight, LSpacingRight,  NULL);
							LTraverseWs[LTraverseN++] = LBevelPanel->RadiusW;
							LTraverseWs[LTraverseN++] = LBevelPanel->RoundingStepsW;
						}
					}
					else
					{
						EClr_s(LGUI->StartBevelPanel);
						EClr_s(LGUI->StartInnerBevelPanel);
						EClr_s(LGUI->EndBevelPanel);
						EClr_s(LGUI->EndInnerBevelPanel);
					}

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

					_SetGUIToType(LComponent, LGUI);
				}
			}
		break;
	}
}


//================================================
// Menu callback for Cylinder
//================================================
static void _MCB_Cylinder(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_CylinderComponentClass);
}


//================================================
// Menu callback for Capsule
//================================================
static void _MCB_Capsule(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_CapsuleComponentClass);
}


//================================================
// Menu callback for Tube
//================================================
static void _MCB_Tube(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dTpl_ObjectComponentActivateCB_Geometry(_TubeComponentClass);
}


//================================================
//================================================
static int _CylinderInit(E3dObject* PObject, E3dObjectComponent* PComponent)
{
	E3dCylinderComponent*  LThis = (E3dCylinderComponent*)PComponent;

	LThis->TopDomeHeight = 0.0;LThis->BottomDomeHeight = 0.0;
	LThis->RadialSections = 0;
	LThis->Radius = 1.0;
	LThis->InnerRadius = 0.0;
	LThis->Height = 2.0;

	LThis->Sides = 18;
	LThis->HeightSections = 1;

	LThis->Type = E3dCylinderPOLYGONS;

	LThis->StartAngle = 0.0;LThis->EndAngle = 360.0;LThis->Full = TRUE;
	LThis->Smooth = TRUE;
	LThis->Side = TRUE;LThis->TopCap = TRUE;LThis->BottomCap = TRUE;
	LThis->Pie = TRUE;
	LThis->OriginAtBottom = FALSE;

	LThis->ShowProfileSpline = FALSE;

	E3dBevel_Init(&LThis->StartBevel);
	E3dBevel_Init(&LThis->EndBevel);

	E3dBevel_Init(&LThis->StartInnerBevel);
	E3dBevel_Init(&LThis->EndInnerBevel);

	return(E_SUCCESS);
}


//================================================
//================================================
static int _CapsuleInit(E3dObject* PObject, E3dObjectComponent* PComponent)
{
	E3dCylinderComponent*  LThis = (E3dCylinderComponent*)PComponent;

	LThis->TopDomeHeight = 1.0;LThis->BottomDomeHeight = 1.0;
	LThis->RadialSections = 8;
	LThis->Radius = 1.0;
	LThis->InnerRadius = 0.0;
	LThis->Height = 2.0;

	LThis->Sides = 18;
	LThis->HeightSections = 1;

	LThis->Type = E3dCylinderPOLYGONS;

	LThis->StartAngle = 0.0;LThis->EndAngle = 360.0;LThis->Full = TRUE;
	LThis->Smooth = TRUE;
	LThis->Side = TRUE;LThis->TopCap = TRUE;LThis->BottomCap = TRUE;
	LThis->Pie = TRUE;
	LThis->OriginAtBottom = FALSE;

	LThis->ShowProfileSpline = FALSE;

	E3dBevel_Init(&LThis->StartBevel);
	E3dBevel_Init(&LThis->EndBevel);

	E3dBevel_Init(&LThis->StartInnerBevel);
	E3dBevel_Init(&LThis->EndInnerBevel);

	return(E_SUCCESS);
}


//================================================
//================================================
static int _TubeInit(E3dObject* PObject, E3dObjectComponent* PComponent)
{
	E3dCylinderComponent*  LThis = (E3dCylinderComponent*)PComponent;

	LThis->TopDomeHeight = 0.0;LThis->BottomDomeHeight = 0.0;
	LThis->RadialSections = 0;
	LThis->Radius = 1.0;
	LThis->InnerRadius = 0.7;
	LThis->Height = 2.0;

	LThis->Sides = 18;
	LThis->HeightSections = 1;

	LThis->Type = E3dCylinderPOLYGONS;

	LThis->StartAngle = 0.0;LThis->EndAngle = 360.0;LThis->Full = TRUE;
	LThis->Smooth = TRUE;
	LThis->Side = TRUE;LThis->TopCap = TRUE;LThis->BottomCap = TRUE;
	LThis->Pie = TRUE;
	LThis->OriginAtBottom = FALSE;

	LThis->ShowProfileSpline = FALSE;  // This is a debug feature because _Update() can't properly instantiate the Spline under the Model because when it's called the first time, there is no Model (Mesh->Models is empty)

	E3dBevel_Init(&LThis->StartBevel);
	E3dBevel_Init(&LThis->EndBevel);

	E3dBevel_Init(&LThis->StartInnerBevel);
	E3dBevel_Init(&LThis->EndInnerBevel);

	return(E_SUCCESS);
}


//========================================
// Copy-constructor
//========================================
static void _Copy(const E3dObject* LSObject, const E3dObjectComponent* LSInfoIn, E3dObject* LDObject, E3dObjectComponent* LDInfoIn)
{
	const E3dCylinderComponent*  LSInfo = (E3dCylinderComponent*)LSInfoIn;
	E3dCylinderComponent*        LDInfo = (E3dCylinderComponent*)LDInfoIn;

	_CopyFields(LSInfo, LDInfo);
}


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

	{
		E3dObjectComponentClass  LClass =
		{
			.Name=        "Cylinder",
			.StructSize=  sizeof(E3dCylinderComponent),
			.Resources=   EListS_s(_CylinderResources),

			.Init=        _CylinderInit,
			.Copy=        _Copy,
			.Update1=      _CylinderUpdate,
			.GUI=         _GUI,
		};

		_CylinderComponentClass = E3dObjectComponent_ClassRegister(&LClass);if(_CylinderComponentClass)
		{
			EpM_CreateIconStates3_sp(Cylinder);E_Assign(_Image_Cylinder, _CylinderComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Cylinder, "Tool->Create", "Cylinder", '\0', NULL, NULL, FALSE, "Create cylinder\nRight button:  dialog box for this tool", _MCB_Cylinder, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Cylinder", '\0', NULL, NULL, TRUE, NULL, _MCB_Cylinder, (EPointer)0);
		}
	}

	{
		E3dObjectComponentClass  LClass =
		{
			.Name=        "Capsule",
			.StructSize=  sizeof(E3dCylinderComponent),
			.Resources=   EListS_s(_CapsuleResources),

			.Init=        _CapsuleInit,
			.Copy=        _Copy,
			.Update1=      _CapsuleUpdate,
			.GUI=         _GUI,
		};

		_CapsuleComponentClass = E3dObjectComponent_ClassRegister(&LClass);if(_CapsuleComponentClass)
		{
			EpM_CreateIconStates3_sp(Capsule);E_Assign(_Image_Capsule, _CapsuleComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Capsule, "Tool->Create", "Capsule", '\0', NULL, NULL, FALSE, "Create capsule\nRight button:  dialog box for this tool", _MCB_Capsule, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Capsule", '\0', NULL, NULL, TRUE, NULL, _MCB_Capsule, (EPointer)0);
		}
	}

	{
		E3dObjectComponentClass  LClass =
		{
			.Name=        "Tube",
			.StructSize=  sizeof(E3dCylinderComponent),
			.Resources=   EListS_s(_TubeResources),

			.Init=        _TubeInit,
			.Copy=        _Copy,
			.Update1=      _TubeUpdate,
			.GUI=         _GUI,
		};

		_TubeComponentClass = E3dObjectComponent_ClassRegister(&LClass);if(_TubeComponentClass)
		{
			EpM_CreateIconStates3_sp(Tube);E_Assign(_Image_Tube, _TubeComponentClass->Icon);
			XePushButton_AddImg3(_ToolBarButtons[_ToolBarButtons_Count++], Tube, "Tool->Create", "Tube", '\0', NULL, NULL, FALSE, "Create a tube\nRight button:  dialog box for this tool", _MCB_Tube, (EPointer)0);
			_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Create", "Tube", '\0', NULL, NULL, TRUE, NULL, _MCB_Tube, (EPointer)0);
		}
	}

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		{ int  LI = 0;for(;LI < _MenuButtons_Count;LI++) { XeItemFree(_MenuButtons[LI]); } _MenuButtons_Count = 0; }
		{ int  LI = 0;for(LI = 0;LI < _ToolBarButtons_Count;LI++) { XeItemFree(_ToolBarButtons[LI]); } _ToolBarButtons_Count = 0; }
	}

	if(_CylinderComponentClass) { E3dObjectComponent_ClassDeactivate(_CylinderComponentClass);_CylinderComponentClass = NULL; }
	EpIconFree3(Cylinder);

	if(_CapsuleComponentClass) { E3dObjectComponent_ClassDeactivate(_CapsuleComponentClass);_CapsuleComponentClass = NULL; }
	EpIconFree3(Capsule);

	if(_TubeComponentClass) { E3dObjectComponent_ClassDeactivate(_TubeComponentClass);_TubeComponentClass = NULL; }
	EpIconFree3(Tube);

	return(E_SUCCESS);
}
