Esempio n. 1
0
        public static bool GenerateBoxAsset(CSGBrushMeshAsset brushMeshAsset, UnityEngine.Vector3 min, UnityEngine.Vector3 max, CSGSurfaceAsset[] surfaceAssets, SurfaceFlags surfaceFlags = SurfaceFlags.None)
        {
            if (!BoundsExtensions.IsValid(min, max))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            if (surfaceAssets.Length != 6)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            if (min.x > max.x)
            {
                float x = min.x; min.x = max.x; max.x = x;
            }
            if (min.y > max.y)
            {
                float y = min.y; min.y = max.y; max.y = y;
            }
            if (min.z > max.z)
            {
                float z = min.z; min.z = max.z; max.z = z;
            }

            brushMeshAsset.Polygons  = CreateBoxAssetPolygons(surfaceAssets, surfaceFlags);
            brushMeshAsset.HalfEdges = boxHalfEdges.ToArray();
            brushMeshAsset.Vertices  = BrushMeshFactory.CreateBoxVertices(min, max);
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
Esempio n. 2
0
        public static bool GenerateConicalFrustumAsset(CSGBrushMeshAsset brushMeshAsset, CSGCircleDefinition bottom, CSGCircleDefinition top, float rotation, int segments, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (segments < 3 || (top.height - bottom.height) == 0 || (bottom.diameterX == 0 && top.diameterX == 0) || (bottom.diameterZ == 0 && top.diameterZ == 0))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            if (surfaceAssets.Length != 3 ||
                surfaceDescriptions.Length != segments + 2)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            var subMesh = new CSGBrushSubMesh();

            if (!GenerateConicalFrustumSubMesh(subMesh, bottom, top, rotation, segments, surfaceAssets, surfaceDescriptions))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = new CSGBrushSubMesh[] { subMesh };
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
Esempio n. 3
0
        public static bool GenerateBoxAsset(CSGBrushMeshAsset brushMeshAsset, UnityEngine.Vector3 min, UnityEngine.Vector3 max, CSGSurfaceAsset[] surfaceAssets, SurfaceDescription[] surfaceDescriptions)
        {
            if (!BoundsExtensions.IsValid(min, max))
            {
                brushMeshAsset.Clear();
                Debug.LogError("bounds is of an invalid size " + (max - min));
                return(false);
            }

            if (surfaceDescriptions == null || surfaceDescriptions.Length != 6)
            {
                brushMeshAsset.Clear();
                Debug.LogError("surfaceDescriptions needs to be an array of length 6");
                return(false);
            }

            if (surfaceAssets == null || surfaceAssets.Length != 6)
            {
                brushMeshAsset.Clear();
                Debug.LogError("surfaceAssets needs to be an array of length 6");
                return(false);
            }

            if (min.x > max.x)
            {
                float x = min.x; min.x = max.x; max.x = x;
            }
            if (min.y > max.y)
            {
                float y = min.y; min.y = max.y; max.y = y;
            }
            if (min.z > max.z)
            {
                float z = min.z; min.z = max.z; max.z = z;
            }

            brushMeshAsset.Polygons  = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
            brushMeshAsset.HalfEdges = boxHalfEdges.ToArray();
            brushMeshAsset.Vertices  = BrushMeshFactory.CreateBoxVertices(min, max);
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        public static bool GenerateLinearStairsAsset(CSGBrushMeshAsset brushMeshAsset, CSGLinearStairsDefinition definition)
        {
            definition.Validate();
            int subMeshCount = GetLinearStairsSubMeshCount(definition, definition.leftSide, definition.rightSide);

            if (subMeshCount == 0)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != subMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[subMeshCount];
                for (int i = 0; i < subMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            if (!GenerateLinearStairsSubMeshes(subMeshes, definition, definition.leftSide, definition.rightSide, 0))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        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);
        }
        public static bool GenerateSphereAsset(CSGBrushMeshAsset brushMeshAsset, CSGSphereDefinition definition)
        {
            var subMesh = new CSGBrushSubMesh();

            if (!GenerateSphereSubMesh(subMesh, definition))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            brushMeshAsset.SubMeshes = new[] { subMesh };
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
Esempio n. 7
0
        public static bool GenerateStadiumAsset(CSGBrushMeshAsset brushMeshAsset, CSGStadiumDefinition definition)
        {
            Vector3[] vertices = null;
            if (!GenerateStadiumVertices(definition, ref vertices))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            var subMeshes      = new[] { new CSGBrushSubMesh() };
            var surfaceIndices = new int[vertices.Length + 2];

            CreateExtrudedSubMesh(subMeshes[0], definition.sides, surfaceIndices, surfaceIndices, 0, 1, vertices, definition.surfaceAssets, definition.surfaceDescriptions);

            brushMeshAsset.SubMeshes = subMeshes;
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
        public static bool GeneratePathedStairsAsset(CSGBrushMeshAsset brushMeshAsset, CSGPathedStairsDefinition definition)
        {
            definition.Validate();

            var shapeVertices       = new List <Vector2>();
            var shapeSegmentIndices = new List <int>();

            GetPathVertices(definition.shape, definition.curveSegments, shapeVertices, shapeSegmentIndices);

            var totalSubMeshCount = 0;

            for (int i = 0; i < shapeVertices.Count; i++)
            {
                if (i == 0 && !definition.shape.closed)
                {
                    continue;
                }

                var leftSide  = (!definition.shape.closed && i == 1) ? definition.stairs.leftSide  : StairsSideType.None;
                var rightSide = (!definition.shape.closed && i == shapeVertices.Count - 1) ? definition.stairs.rightSide : StairsSideType.None;

                totalSubMeshCount += GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
            }
            if (totalSubMeshCount == 0)
            {
                brushMeshAsset.Clear();
                return(false);
            }

//			var stairDirections = definition.shape.closed ? shapeVertices.Count : (shapeVertices.Count - 1);

            // TODO: use list instead?
            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != totalSubMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[totalSubMeshCount];
                for (int i = 0; i < totalSubMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            var depth  = definition.stairs.depth;
            var height = definition.stairs.height;

            var halfDepth  = depth * 0.5f;
            var halfHeight = height * 0.5f;

            int subMeshIndex = 0;

            for (int vi0 = shapeVertices.Count - 3, vi1 = shapeVertices.Count - 2, vi2 = shapeVertices.Count - 1, vi3 = 0; vi3 < shapeVertices.Count; vi0 = vi1, vi1 = vi2, vi2 = vi3, vi3++)
            {
                if (vi2 == 0 && !definition.shape.closed)
                {
                    continue;
                }

                // TODO: optimize this, we're probably redoing a lot of stuff for every iteration
                var v0 = shapeVertices[vi0];
                var v1 = shapeVertices[vi1];
                var v2 = shapeVertices[vi2];
                var v3 = shapeVertices[vi3];

                var m0 = (v0 + v1) * 0.5f;
                var m1 = (v1 + v2) * 0.5f;
                var m2 = (v2 + v3) * 0.5f;

                var d0 = (v1 - v0);
                var d1 = (v2 - v1);
                var d2 = (v3 - v2);

                var maxWidth0  = d0.magnitude;
                var maxWidth1  = d1.magnitude;
                var maxWidth2  = d2.magnitude;
                var halfWidth1 = d1 * 0.5f;

                d0 /= maxWidth0;
                d1 /= maxWidth1;
                d2 /= maxWidth2;

                var depthVector = new Vector3(d1.y, 0, -d1.x);
                var lineCenter  = new Vector3(m1.x, halfHeight, m1.y) - (depthVector * halfDepth);

                var depthVector0 = new Vector2(d0.y, -d0.x) * depth;
                var depthVector1 = new Vector2(d1.y, -d1.x) * depth;
                var depthVector2 = new Vector2(d2.y, -d2.x) * depth;

                m0 -= depthVector0;
                m1 -= depthVector1;
                m2 -= depthVector2;

                Vector2 output;
                var     leftShear  = Intersect(m1, d1, m0, d0, out output) ?  Vector2.Dot(d1, (output - (m1 - halfWidth1))) : 0;
                var     rightShear = Intersect(m1, d1, m2, d2, out output) ? -Vector2.Dot(d1, (output - (m1 + halfWidth1))) : 0;

                var transform = Matrix4x4.TRS(lineCenter,                                       // move to center of line
                                              Quaternion.LookRotation(depthVector, Vector3.up), // rotate to align with line
                                              Vector3.one);

                // set the width to the width of the line
                definition.stairs.width       = maxWidth1;
                definition.stairs.nosingWidth = 0;

                var leftSide     = (!definition.shape.closed && vi2 == 1) ? definition.stairs.leftSide  : StairsSideType.None;
                var rightSide    = (!definition.shape.closed && vi2 == shapeVertices.Count - 1) ? definition.stairs.rightSide : StairsSideType.None;
                var subMeshCount = GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
                if (subMeshCount == 0)
                {
                    continue;
                }

                if (!GenerateLinearStairsSubMeshes(subMeshes, definition.stairs, leftSide, rightSide, subMeshIndex))
                {
                    brushMeshAsset.Clear();
                    return(false);
                }

                var halfWidth = maxWidth1 * 0.5f;
                for (int m = 0; m < subMeshCount; m++)
                {
                    var vertices = subMeshes[subMeshIndex + m].Vertices;
                    for (int v = 0; v < vertices.Length; v++)
                    {
                        // TODO: is it possible to put all of this in a single matrix?
                        // lerp the stairs to go from less wide to wider depending on the depth of the vertex
                        var depthFactor = 1.0f - ((vertices[v].z / definition.stairs.depth) + 0.5f);
                        var wideFactor  = (vertices[v].x / halfWidth) + 0.5f;
                        var scale       = (vertices[v].x / halfWidth);

                        // lerp the stairs width depending on if it's on the left or right side of the stairs
                        vertices[v].x = Mathf.Lerp(scale * (halfWidth - (rightShear * depthFactor)),
                                                   scale * (halfWidth - (leftShear * depthFactor)),
                                                   wideFactor);
                        vertices[v] = transform.MultiplyPoint(vertices[v]);
                    }
                }

                subMeshIndex += subMeshCount;
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(false);
        }
        public static bool GenerateRevolvedShapeAsset(CSGBrushMeshAsset brushMeshAsset, CSGRevolvedShapeDefinition definition)
        {
            definition.Validate();
            var surfaces     = definition.surfaceAssets;
            var descriptions = definition.surfaceDescriptions;


            var shapeVertices       = new List <Vector2>();
            var shapeSegmentIndices = new List <int>();

            GetPathVertices(definition.shape, definition.curveSegments, shapeVertices, shapeSegmentIndices);

            Vector2[][] polygonVerticesArray;
            int[][]     polygonIndicesArray;

            if (!Decomposition.ConvexPartition(shapeVertices, shapeSegmentIndices,
                                               out polygonVerticesArray,
                                               out polygonIndicesArray))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            // TODO: splitting it before we do the composition would be better
            var polygonVerticesList = polygonVerticesArray.ToList();

            for (int i = polygonVerticesList.Count - 1; i >= 0; i--)
            {
                SplitPolygon(polygonVerticesList, i);
            }

            var subMeshes = new List <CSGBrushSubMesh>();

            var horzSegments         = definition.revolveSegments;           //horizontalSegments;
            var horzDegreePerSegment = definition.totalAngle / horzSegments;


            // TODO: make this work when intersecting rotation axis
            //			1. split polygons along rotation axis
            //			2. if edge lies on rotation axis, make sure we don't create infinitely thin quad
            //					collapse this quad, or prevent this from happening
            // TODO: share this code with torus generator
            for (int p = 0; p < polygonVerticesList.Count; p++)
            {
                var polygonVertices = polygonVerticesList[p];
//				var segmentIndices		= polygonIndicesArray[p];
                var shapeSegments = polygonVertices.Length;

                var vertSegments     = polygonVertices.Length;
                var descriptionIndex = new int[2 + vertSegments];

                descriptionIndex[0] = 0;
                descriptionIndex[1] = 1;

                for (int v = 0; v < vertSegments; v++)
                {
                    descriptionIndex[v + 2] = 2;
                }

                var horzOffset = definition.startAngle;
                for (int h = 1, pr = 0; h < horzSegments + 1; pr = h, h++)
                {
                    var hDegree0        = (pr * horzDegreePerSegment) + horzOffset;
                    var hDegree1        = (h * horzDegreePerSegment) + horzOffset;
                    var rotation0       = Quaternion.AngleAxis(hDegree0, Vector3.forward);
                    var rotation1       = Quaternion.AngleAxis(hDegree1, Vector3.forward);
                    var subMeshVertices = new Vector3[vertSegments * 2];
                    for (int v = 0; v < vertSegments; v++)
                    {
                        subMeshVertices[v + vertSegments] = rotation0 * new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y);
                        subMeshVertices[v] = rotation1 * new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y);
                    }

                    var subMesh = new CSGBrushSubMesh();
                    if (!CreateExtrudedSubMesh(subMesh, vertSegments, descriptionIndex, descriptionIndex, 0, 1, subMeshVertices, surfaces, descriptions))
                    {
                        continue;
                    }

                    if (!subMesh.Validate())
                    {
                        brushMeshAsset.Clear();
                        return(false);
                    }
                    subMeshes.Add(subMesh);
                }
            }

            brushMeshAsset.SubMeshes = subMeshes.ToArray();

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
Esempio n. 10
0
        public static bool GenerateTorusAsset(CSGBrushMeshAsset brushMeshAsset, CSGTorusDefinition definition)
        {
            Vector3[] vertices = null;
            if (!GenerateTorusVertices(definition, ref vertices))
            {
                brushMeshAsset.Clear();
                return(false);
            }

            definition.Validate();
            var surfaces     = definition.surfaceAssets;
            var descriptions = definition.surfaceDescriptions;
            var tubeRadiusX  = (definition.tubeWidth * 0.5f);
            var tubeRadiusY  = (definition.tubeHeight * 0.5f);
            var torusRadius  = (definition.outerDiameter * 0.5f) - tubeRadiusX;


            var horzSegments = definition.horizontalSegments;
            var vertSegments = definition.verticalSegments;

            var horzDegreePerSegment = (definition.totalAngle / horzSegments);
            var vertDegreePerSegment = (360.0f / vertSegments) * Mathf.Deg2Rad;
            var descriptionIndex     = new int[2 + vertSegments];

            descriptionIndex[0] = 0;
            descriptionIndex[1] = 1;

            var circleVertices = new Vector2[vertSegments];

            var min             = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
            var max             = new Vector2(float.NegativeInfinity, float.NegativeInfinity);
            var tubeAngleOffset = ((((vertSegments & 1) == 1) ? 0.0f : ((360.0f / vertSegments) * 0.5f)) + definition.tubeRotation) * Mathf.Deg2Rad;

            for (int v = 0; v < vertSegments; v++)
            {
                var vRad = tubeAngleOffset + (v * vertDegreePerSegment);
                circleVertices[v] = new Vector2((Mathf.Cos(vRad) * tubeRadiusX) - torusRadius,
                                                (Mathf.Sin(vRad) * tubeRadiusY));
                min.x = Mathf.Min(min.x, circleVertices[v].x);
                min.y = Mathf.Min(min.y, circleVertices[v].y);
                max.x = Mathf.Max(max.x, circleVertices[v].x);
                max.y = Mathf.Max(max.y, circleVertices[v].y);
                descriptionIndex[v + 2] = 2;
            }

            if (definition.fitCircle)
            {
                var center = (max + min) * 0.5f;
                var size   = (max - min) * 0.5f;
                size.x = tubeRadiusX / size.x;
                size.y = tubeRadiusY / size.y;
                for (int v = 0; v < vertSegments; v++)
                {
                    circleVertices[v].x  = (circleVertices[v].x - center.x) * size.x;
                    circleVertices[v].y  = (circleVertices[v].y - center.y) * size.y;
                    circleVertices[v].x -= torusRadius;
                }
            }

            var subMeshes  = new CSGBrushSubMesh[horzSegments];
            var horzOffset = definition.startAngle;

            for (int h = 1, p = 0; h < horzSegments + 1; p = h, h++)
            {
                var hDegree0        = (p * horzDegreePerSegment) + horzOffset;
                var hDegree1        = (h * horzDegreePerSegment) + horzOffset;
                var rotation0       = Quaternion.AngleAxis(hDegree0, Vector3.up);
                var rotation1       = Quaternion.AngleAxis(hDegree1, Vector3.up);
                var subMeshVertices = new Vector3[vertSegments * 2];
                for (int v = 0; v < vertSegments; v++)
                {
                    subMeshVertices[v + vertSegments] = rotation0 * circleVertices[v];
                    subMeshVertices[v] = rotation1 * circleVertices[v];
                }

                var subMesh = new CSGBrushSubMesh();
                CreateExtrudedSubMesh(subMesh, vertSegments, descriptionIndex, descriptionIndex, 0, 1, subMeshVertices, surfaces, descriptions);
                if (!subMesh.Validate())
                {
                    brushMeshAsset.Clear();
                    return(false);
                }
                subMeshes[h - 1] = subMesh;
            }

            brushMeshAsset.SubMeshes = subMeshes;

            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }
Esempio n. 11
0
        public static bool GenerateSpiralStairsAsset(CSGBrushMeshAsset brushMeshAsset, ref CSGSpiralStairsDefinition definition, CSGSurfaceAsset[] surfaceAssets, ref SurfaceDescription[] surfaceDescriptions)
        {
            if (surfaceAssets == null ||
                surfaceDescriptions == null ||
                surfaceAssets.Length != 6 ||
                surfaceDescriptions.Length != 6)
            {
                brushMeshAsset.Clear();
                return(false);
            }

            definition.Validate();

            const float kEpsilon = 0.001f;

            var nosingDepth = definition.nosingDepth;
            var treadHeight = (nosingDepth < kEpsilon) ? 0 : definition.treadHeight;
            var haveTread   = (treadHeight >= kEpsilon);

            var innerDiameter = definition.innerDiameter;
            var haveInnerCyl  = (innerDiameter >= kEpsilon);

            var riserType = definition.riserType;
            var haveRiser = riserType != StairsRiserType.None;

            if (!haveRiser && !haveTread)
            {
                brushMeshAsset.Clear();
                return(false);
            }


            var origin = definition.origin;

            var startAngle   = definition.startAngle * Mathf.Deg2Rad;
            var anglePerStep = definition.AnglePerStep * Mathf.Deg2Rad;

            var nosingWidth   = definition.nosingWidth;
            var outerDiameter = definition.outerDiameter;

            var p0 = new Vector2(Mathf.Sin(0), Mathf.Cos(0));
            var p1 = new Vector2(Mathf.Sin(anglePerStep), Mathf.Cos(anglePerStep));
            var pm = new Vector2(Mathf.Sin(anglePerStep * 0.5f), Mathf.Cos(anglePerStep * 0.5f));
            var pn = (p0 + p1) * 0.5f;

            var stepOuterDiameter = outerDiameter + ((pm.magnitude - pn.magnitude) * (outerDiameter * 1.25f));               // TODO: figure out why we need the 1.25 magic number to fit the step in the outerDiameter?
            var stepOuterRadius   = stepOuterDiameter * 0.5f;
            var stepInnerRadius   = 0.0f;
            var stepHeight        = definition.stepHeight;
            var height            = definition.height;
            var stepCount         = definition.StepCount;

            if (height < 0)
            {
                origin.y += height;
                height    = -height;
            }

            // TODO: expose this to user
            var smoothSubDivisions = 3;

            var cylinderSubMeshCount = haveInnerCyl ? 2 : 1;
            var subMeshPerRiser      = (riserType == StairsRiserType.None) ? 0 :
                                       (riserType == StairsRiserType.Smooth) ? (2 * smoothSubDivisions)
                                                                                : 1;
            var riserSubMeshCount = (stepCount * subMeshPerRiser) + ((riserType == StairsRiserType.None) ? 0 : cylinderSubMeshCount);
            var treadSubMeshCount = (haveTread ? stepCount + cylinderSubMeshCount : 0);
            var subMeshCount      = (treadSubMeshCount + riserSubMeshCount);

            var treadStart = !haveRiser ? 0 : riserSubMeshCount;
            var innerSides = definition.innerSegments;
            var outerSides = definition.outerSegments;
            var riserDepth = definition.riserDepth;

            CSGBrushSubMesh[] subMeshes;
            if (brushMeshAsset.SubMeshCount != subMeshCount)
            {
                subMeshes = new CSGBrushSubMesh[subMeshCount];
                for (int i = 0; i < subMeshCount; i++)
                {
                    subMeshes[i] = new CSGBrushSubMesh();
                }
            }
            else
            {
                subMeshes = brushMeshAsset.SubMeshes;
            }

            if (haveRiser)
            {
                if (riserType == StairsRiserType.ThinRiser)
                {
                    var     minY = origin.y;
                    var     maxY = origin.y + stepHeight - treadHeight;
                    Vector2 o0, o1;
                    float   angle = startAngle;
                    var     c1    = Mathf.Sin(angle) * stepOuterRadius;
                    var     s1    = Mathf.Cos(angle) * stepOuterRadius;
                    for (int i = 0; i < stepCount; i++)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        angle += anglePerStep;
                        c1     = Mathf.Sin(angle) * stepOuterRadius;
                        s1     = Mathf.Cos(angle) * stepOuterRadius;

                        o0 = new Vector2(origin.x + c0, origin.z + s0);
                        o1 = new Vector2(origin.x, origin.z);

                        var riserVector = (new Vector2((c0 - c1), (s0 - s1)).normalized) * riserDepth;

                        var i0 = o0 - riserVector;
                        var i1 = o1 - riserVector;

                        var vertices = new[] {
                            new Vector3(i0.x, maxY, i0.y),                      // 0
                            new Vector3(i1.x, maxY, i1.y),                      // 1
                            new Vector3(o1.x, maxY, o1.y),                      // 2
                            new Vector3(o0.x, maxY, o0.y),                      // 3

                            new Vector3(i0.x, minY, i0.y),                      // 4
                            new Vector3(i1.x, minY, i1.y),                      // 5
                            new Vector3(o1.x, minY, o1.y),                      // 6
                            new Vector3(o0.x, minY, o0.y),                      // 7
                        };

                        if (i == 0)
                        {
                            subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
                            minY -= treadHeight;
                        }
                        else
                        {
                            subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray();
                        }
                        subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray();
                        subMeshes[i].Vertices  = vertices;

                        minY += stepHeight;
                        maxY += stepHeight;
                    }
                }
                else
                if (riserType == StairsRiserType.Smooth)
                {
                    //var stepY = stepHeight;
                    var   minY  = origin.y;
                    var   maxY  = origin.y + stepHeight - treadHeight;
                    var   maxY2 = origin.y + (stepHeight * 2) - treadHeight;
                    float angle = startAngle;
                    var   c1    = Mathf.Sin(angle);
                    var   s1    = Mathf.Cos(angle);
                    angle += anglePerStep;
                    var c2 = Mathf.Sin(angle);
                    var s2 = Mathf.Cos(angle);

                    for (int i = 0; i < riserSubMeshCount; i += subMeshPerRiser)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        c1     = c2;
                        s1     = s2;
                        angle += anglePerStep;
                        c2     = Mathf.Sin(angle);
                        s2     = Mathf.Cos(angle);

                        var c0o = c0 * stepOuterRadius;
                        var c1o = c1 * stepOuterRadius;
                        var s0o = s0 * stepOuterRadius;
                        var s1o = s1 * stepOuterRadius;

                        var o0 = new Vector2(origin.x + c0o, origin.z + s0o);
                        var o1 = new Vector2(origin.x + c1o, origin.z + s1o);

                        var i0 = o0;
                        var i1 = o1;

                        int subMeshIndex = i;
                        for (int subDiv = 1; subDiv < smoothSubDivisions; subDiv++)
                        {
                            // TODO: need to space the subdivisions from smallest spaces to bigger spaces
                            float stepMidRadius;
                            stepMidRadius = (((outerDiameter * 0.5f) * (1.0f / (smoothSubDivisions + 1))) * ((smoothSubDivisions - 1) - (subDiv - 1)));
                            if (subDiv == (smoothSubDivisions - 1))
                            {
                                var innerRadius = (innerDiameter * 0.5f) - 0.1f;
                                stepMidRadius = (innerRadius < 0.1f) ? stepMidRadius : innerRadius;
                            }

                            var c0i = c0 * stepMidRadius;
                            var c1i = c1 * stepMidRadius;
                            var s0i = s0 * stepMidRadius;
                            var s1i = s1 * stepMidRadius;

                            i0 = new Vector2(origin.x + c0i, origin.z + s0i);
                            i1 = new Vector2(origin.x + c1i, origin.z + s1i);

                            {
                                var vertices = new[] {
                                    new Vector3(i0.x, maxY, i0.y),                        // 0
                                    new Vector3(i0.x, minY, i0.y),                        // 1
                                    new Vector3(o0.x, minY, o0.y),                        // 2
                                    new Vector3(o0.x, maxY, o0.y),                        // 3

                                    new Vector3(o1.x, maxY, o1.y),                        // 4
                                };

                                if (i == 0)
                                {
                                    subMeshes[subMeshIndex].Polygons = CreateSquarePyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                                }
                                else
                                {
                                    subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                                }
                                subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedSquarePyramidHalfEdges.ToArray() : squarePyramidHalfEdges.ToArray();
                                subMeshes[subMeshIndex].Vertices  = vertices;
                                subMeshIndex++;
                            }

                            {
                                var vertices = new[] {
                                    new Vector3(i0.x, maxY, i0.y),                        // 0
                                    new Vector3(i0.x, minY, i0.y),                        // 1
                                    new Vector3(i1.x, maxY, i1.y),                        // 2

                                    new Vector3(o1.x, maxY, o1.y),                        // 3
                                };

                                if (i == 0)
                                {
                                    subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                                }
                                else
                                {
                                    subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                                }
                                subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                                subMeshes[subMeshIndex].Vertices  = vertices;
                                subMeshIndex++;
                            }

                            o0 = i0;
                            o1 = i1;
                        }

                        {
                            var vertices = new[] {
                                new Vector3(i0.x, maxY, i0.y),                        // 0
                                new Vector3(i1.x, maxY, i1.y),                        // 2
                                new Vector3(i0.x, minY, i0.y),                        // 1

                                new Vector3(origin.x, minY, origin.y),                // 3
                            };

                            if (i == 0)
                            {
                                subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                            }
                            else
                            {
                                subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                            }
                            subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                            subMeshes[subMeshIndex].Vertices  = vertices;
                            subMeshIndex++;
                        }

                        {
                            var vertices = new[] {
                                new Vector3(i1.x, maxY, i1.y),                        // 2
                                new Vector3(i0.x, maxY, i0.y),                        // 0
                                new Vector3(origin.x, maxY, origin.y),                // 1

                                new Vector3(origin.x, minY, origin.y),                // 3
                            };

                            if (i == 0)
                            {
                                subMeshes[subMeshIndex].Polygons = CreateTriangularPyramidAssetPolygons(surfaceAssets, surfaceDescriptions);
                            }
                            else
                            {
                                subMeshes[subMeshIndex].Polygons = subMeshes[subMeshIndex - i].Polygons.ToArray();
                            }
                            subMeshes[subMeshIndex].HalfEdges = (anglePerStep > 0) ? invertedTriangularPyramidHalfEdges.ToArray() : triangularPyramidHalfEdges.ToArray();
                            subMeshes[subMeshIndex].Vertices  = vertices;
                            subMeshIndex++;
                        }

                        if (i == 0)
                        {
                            minY -= treadHeight;
                        }

                        minY  += stepHeight;
                        maxY  += stepHeight;
                        maxY2 += stepHeight;
                    }
                }
                else
                {
                    var     minY = origin.y;
                    var     maxY = origin.y + stepHeight - treadHeight;
                    Vector2 o0, o1;
                    float   angle = startAngle;
                    var     c1    = Mathf.Sin(angle) * stepOuterRadius;
                    var     s1    = Mathf.Cos(angle) * stepOuterRadius;
                    for (int i = 0; i < stepCount; i++)
                    {
                        var c0 = c1;
                        var s0 = s1;
                        angle += anglePerStep;
                        c1     = Mathf.Sin(angle) * stepOuterRadius;
                        s1     = Mathf.Cos(angle) * stepOuterRadius;

                        o0 = new Vector2(origin.x + c0, origin.z + s0);
                        o1 = new Vector2(origin.x + c1, origin.z + s1);
                        var vertices = new[] {
                            new Vector3(origin.x, maxY, origin.z),                // 0
                            new Vector3(o1.x, maxY, o1.y),                        // 1
                            new Vector3(o0.x, maxY, o0.y),                        // 2

                            new Vector3(origin.x, minY, origin.z),                // 3
                            new Vector3(o1.x, minY, o1.y),                        // 4
                            new Vector3(o0.x, minY, o0.y),                        // 5
                        };

                        if (i == 0)
                        {
                            subMeshes[i].Polygons = CreateWedgeAssetPolygons(surfaceAssets, surfaceDescriptions);
                            minY -= treadHeight;
                        }
                        else
                        {
                            subMeshes[i].Polygons = subMeshes[0].Polygons.ToArray();
                        }
                        subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedWedgeHalfEdges.ToArray() : wedgeHalfEdges.ToArray();
                        subMeshes[i].Vertices  = vertices;

                        if (riserType != StairsRiserType.FillDown)
                        {
                            minY += stepHeight;
                        }
                        maxY += stepHeight;
                    }
                }

                {
                    var subMeshIndex          = treadStart - cylinderSubMeshCount;
                    var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                        surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                    };
                    var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0]
                    cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                    cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                    for (int i = 0; i < outerSides; i++)
                    {
                        cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                    }
                    GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                    subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting;
                }

                if (haveInnerCyl)
                {
                    var subMeshIndex          = treadStart - 1;
                    var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                        surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                    };
                    var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0]
                    cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                    cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                    for (int i = 0; i < innerSides; i++)
                    {
                        cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                    }
                    GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                    subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive;
                }
            }

            if (haveTread)
            {
                var     minY = origin.y + stepHeight - treadHeight;
                var     maxY = origin.y + stepHeight;
                Vector2 i0, i1, o0, o1;
                float   angle      = startAngle;
                var     c1         = Mathf.Sin(angle);
                var     s1         = Mathf.Cos(angle);
                var     startIndex = treadStart;
                for (int n = 0, i = startIndex; n < stepCount; n++, i++)
                {
                    var c0 = c1;
                    var s0 = s1;
                    angle += anglePerStep;
                    c1     = Mathf.Sin(angle);
                    s1     = Mathf.Cos(angle);

                    i0 = new Vector2(origin.x + (c0 * (stepInnerRadius)), origin.z + (s0 * (stepInnerRadius)));
                    i1 = new Vector2(origin.x + (c1 * (stepInnerRadius)), origin.z + (s1 * (stepInnerRadius)));
                    o0 = new Vector2(origin.x + (c0 * (stepOuterRadius + nosingWidth)), origin.z + (s0 * (stepOuterRadius + nosingWidth)));
                    o1 = new Vector2(origin.x + (c1 * (stepOuterRadius + nosingWidth)), origin.z + (s1 * (stepOuterRadius + nosingWidth)));

                    var noseSizeDeep = (new Vector2((c0 - c1), (s0 - s1)).normalized) * nosingDepth;
                    i0 += noseSizeDeep;
                    o0 += noseSizeDeep;

                    var vertices = new[] {
                        new Vector3(i1.x, maxY, i1.y),                      // 1
                        new Vector3(i0.x, maxY, i0.y),                      // 0
                        new Vector3(o0.x, maxY, o0.y),                      // 3
                        new Vector3(o1.x, maxY, o1.y),                      // 2

                        new Vector3(i1.x, minY, i1.y),                      // 5
                        new Vector3(i0.x, minY, i0.y),                      // 4
                        new Vector3(o0.x, minY, o0.y),                      // 7
                        new Vector3(o1.x, minY, o1.y),                      // 6
                    };

                    if (n == 0)
                    {
                        subMeshes[i].Polygons = CreateBoxAssetPolygons(surfaceAssets, surfaceDescriptions);
                    }
                    else
                    {
                        subMeshes[i].Polygons = subMeshes[startIndex].Polygons.ToArray();
                    }

                    subMeshes[i].HalfEdges = (anglePerStep > 0) ? invertedBoxHalfEdges.ToArray() : boxHalfEdges.ToArray();
                    subMeshes[i].Vertices  = vertices;

                    minY += stepHeight;
                    maxY += stepHeight;
                }
            }


            {
                var subMeshIndex          = subMeshCount - cylinderSubMeshCount;
                var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                    surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                };
                var cylinderSurfaceDescriptions = new SurfaceDescription[outerSides + 2]; //surfaceDescriptions[0]
                cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                for (int i = 0; i < outerSides; i++)
                {
                    cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                }
                GenerateCylinderSubMesh(subMeshes[subMeshIndex], outerDiameter + nosingWidth, origin.y, origin.y + height, 0, outerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                subMeshes[subMeshIndex].Operation = CSGOperationType.Intersecting;
            }

            if (haveInnerCyl)
            {
                var subMeshIndex          = subMeshCount - 1;
                var cylinderSurfaceAssets = new CSGSurfaceAsset[3] {
                    surfaceAssets[0], surfaceAssets[1], surfaceAssets[2]
                };
                var cylinderSurfaceDescriptions = new SurfaceDescription[innerSides + 2]; //surfaceDescriptions[0]
                cylinderSurfaceDescriptions[0] = surfaceDescriptions[0];
                cylinderSurfaceDescriptions[1] = surfaceDescriptions[1];
                for (int i = 0; i < innerSides; i++)
                {
                    cylinderSurfaceDescriptions[i + 2] = surfaceDescriptions[2];
                }
                GenerateCylinderSubMesh(subMeshes[subMeshIndex], innerDiameter - nosingWidth, origin.y, origin.y + height, 0, innerSides, cylinderSurfaceAssets, cylinderSurfaceDescriptions);
                subMeshes[subMeshIndex].Operation = CSGOperationType.Subtractive;
            }


            brushMeshAsset.SubMeshes = subMeshes;
            brushMeshAsset.CalculatePlanes();
            brushMeshAsset.SetDirty();
            return(true);
        }