public static bool GenerateCapsuleAsset(CSGBrushMeshAsset brushMeshAsset, ref CSGCapsuleDefinition definition)
        {
            Vector3[] vertices = null;
            if (!GenerateCapsuleVertices(ref definition, ref vertices))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            // TODO: share this with GenerateCapsuleVertices
            var bottomCap    = !definition.haveRoundedBottom;
            var topCap       = !definition.haveRoundedTop;
            var sides        = definition.sides;
            var segments     = definition.segments;
            var bottomVertex = definition.bottomVertex;
            var topVertex    = definition.topVertex;

            var subMeshes = new[] { new CSGBrushSubMesh() };

            if (!GenerateSegmentedSubMesh(subMeshes[0],
                                          sides, segments,
                                          vertices,
                                          topCap, bottomCap,
                                          topVertex, bottomVertex,
                                          definition.surfaceAssets, definition.surfaceDescriptions))
            {
                brushMeshAsset.Clear();
                return(false);
            }


            brushMeshAsset.SubMeshes = subMeshes;
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        // possible situations:
        //	capsule with top AND bottom set to >0 height
        //	capsule with top OR bottom set to 0 height
        //	capsule with both top AND bottom set to 0 height
        //	capsule with height equal to top and bottom height
        public static bool GenerateCapsuleVertices(ref CSGCapsuleDefinition definition, ref Vector3[] vertices)
        {
            definition.Validate();
            var haveTopHemisphere    = definition.haveRoundedTop;
            var haveBottomHemisphere = definition.haveRoundedBottom;
            var haveMiddleCylinder   = definition.haveCylinder;

            if (!haveBottomHemisphere && !haveTopHemisphere && !haveMiddleCylinder)
            {
                return(false);
            }

            var radiusX        = definition.diameterX * 0.5f;
            var radiusZ        = definition.diameterZ * 0.5f;
            var topHeight      = haveTopHemisphere    ? definition.topHeight    : 0;
            var bottomHeight   = haveBottomHemisphere ? definition.bottomHeight : 0;
            var totalHeight    = definition.height;
            var cylinderHeight = definition.cylinderHeight;

            var sides = definition.sides;

            var extraVertices = definition.extraVertexCount;

            var bottomRings = definition.bottomRingCount;
            var topRings    = definition.topRingCount;
            var ringCount   = definition.ringCount;
            var vertexCount = definition.vertexCount;

            var bottomVertex = definition.bottomVertex;
            var topVertex    = definition.topVertex;

            var topOffset    = definition.topOffset + definition.offsetY;
            var bottomOffset = definition.bottomOffset + definition.offsetY;

            if (vertices == null ||
                vertices.Length != vertexCount)
            {
                vertices = new Vector3[vertexCount];
            }

            if (haveBottomHemisphere)
            {
                vertices[bottomVertex] = Vector3.up * (bottomOffset - bottomHeight);                       // bottom
            }
            if (haveTopHemisphere)
            {
                vertices[topVertex] = Vector3.up * (topOffset + topHeight);                                // top
            }
            var degreePerSegment = (360.0f / sides) * Mathf.Deg2Rad;
            var angleOffset      = definition.rotation + (((sides & 1) == 1) ? 0.0f : 0.5f * degreePerSegment);

            var topVertexOffset    = extraVertices + ((topRings - 1) * sides);
            var bottomVertexOffset = extraVertices + ((ringCount - bottomRings) * sides);
            var unitCircleOffset   = topVertexOffset;
            var vertexIndex        = unitCircleOffset;

            {
                for (int h = sides - 1; h >= 0; h--, vertexIndex++)
                {
                    var hRad = (h * degreePerSegment) + angleOffset;
                    vertices[vertexIndex] = new Vector3(Mathf.Cos(hRad) * radiusX,
                                                        0.0f,
                                                        Mathf.Sin(hRad) * radiusZ);
                }
            }
            for (int v = 1; v < topRings; v++)
            {
                vertexIndex = topVertexOffset - (v * sides);
                var segmentFactor = ((v - (topRings * 0.5f)) / topRings) + 0.5f;        // [0.0f ... 1.0f]
                var segmentDegree = (segmentFactor * 90);                               // [0 .. 90]
                var segmentHeight = topOffset +
                                    (Mathf.Sin(segmentDegree * Mathf.Deg2Rad) *
                                     topHeight);
                var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad);                   // [0 .. 0.707 .. 1 .. 0.707 .. 0]
                for (int h = 0; h < sides; h++, vertexIndex++)
                {
                    vertices[vertexIndex].x = vertices[h + unitCircleOffset].x * segmentRadius;
                    vertices[vertexIndex].y = segmentHeight;
                    vertices[vertexIndex].z = vertices[h + unitCircleOffset].z * segmentRadius;
                }
            }
            vertexIndex = bottomVertexOffset;
            {
                for (int h = 0; h < sides; h++, vertexIndex++)
                {
                    vertices[vertexIndex] = new Vector3(vertices[h + unitCircleOffset].x,
                                                        bottomOffset,
                                                        vertices[h + unitCircleOffset].z);
                }
            }
            for (int v = 1; v < bottomRings; v++)
            {
                var segmentFactor = ((v - (bottomRings * 0.5f)) / bottomRings) + 0.5f;          // [0.0f ... 1.0f]
                var segmentDegree = (segmentFactor * 90);                                       // [0 .. 90]
                var segmentHeight = bottomOffset - bottomHeight +
                                    ((1 - Mathf.Sin(segmentDegree * Mathf.Deg2Rad)) *
                                     bottomHeight);
                var segmentRadius = Mathf.Cos(segmentDegree * Mathf.Deg2Rad);                                   // [0 .. 0.707 .. 1 .. 0.707 .. 0]
                for (int h = 0; h < sides; h++, vertexIndex++)
                {
                    vertices[vertexIndex].x = vertices[h + unitCircleOffset].x * segmentRadius;
                    vertices[vertexIndex].y = segmentHeight;
                    vertices[vertexIndex].z = vertices[h + unitCircleOffset].z * segmentRadius;
                }
            }
            {
                for (int h = 0; h < sides; h++, vertexIndex++)
                {
                    vertices[h + unitCircleOffset].y = topOffset;
                }
            }
            return(true);
        }