예제 #1
0
        public static bool GenerateCapsule(ref ChiselBrushContainer brushContainer, ref ChiselCapsuleDefinition definition)
        {
            definition.Validate();
            Vector3[] vertices = null;
            if (!BrushMeshFactory.GenerateCapsuleVertices(ref definition, ref vertices))
            {
                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;

            brushContainer.EnsureSize(1);

            return(BrushMeshFactory.GenerateSegmentedSubMesh(ref brushContainer.brushMeshes[0],
                                                             sides, segments,
                                                             vertices,
                                                             topCap, bottomCap,
                                                             topVertex, bottomVertex,
                                                             definition.surfaceDefinition));
        }
예제 #2
0
        public bool Generate(ref ChiselBrushContainer brushContainer)
        {
            if (!IsValid)
            {
                return(false);
            }

            brushContainer.EnsureSize(1);

            brushContainer.brushMeshes[0] = new BrushMesh(brushOutline);

            brushContainer.brushMeshes[0].CalculatePlanes();

            // Detect if outline is inside-out and if so, just invert all polygons.
            isInsideOut = brushContainer.brushMeshes[0].IsInsideOut();
            if (isInsideOut)
            {
                brushContainer.brushMeshes[0].Invert();
            }

            // Split non planar polygons into convex pieces
            brushContainer.brushMeshes[0].SplitNonPlanarPolygons();

            return(brushContainer.brushMeshes[0].Validate());
        }
        public static bool GenerateHemisphere(ref ChiselBrushContainer brushContainer, ref ChiselHemisphereDefinition definition)
        {
            definition.Validate();

            brushContainer.EnsureSize(1);

            var transform = Matrix4x4.TRS(Vector3.zero, Quaternion.AngleAxis(definition.rotation, Vector3.up), Vector3.one);
            return GenerateHemisphereSubMesh(ref brushContainer.brushMeshes[0], definition.diameterXYZ, transform, definition.horizontalSegments, definition.verticalSegments, definition.surfaceDefinition);
        }
예제 #4
0
        public static bool GenerateSphere(ref ChiselBrushContainer brushContainer, ref ChiselSphereDefinition definition)
        {
            definition.Validate();

            brushContainer.EnsureSize(1);

            var transform = float4x4.TRS(Vector3.zero, quaternion.AxisAngle(new Vector3(0, 1, 0), definition.rotation), Vector3.one);

            return(BrushMeshFactory.GenerateSphere(ref brushContainer.brushMeshes[0], definition.diameterXYZ, definition.offsetY, definition.generateFromCenter, transform, definition.horizontalSegments, definition.verticalSegments, definition.surfaceDefinition));
        }
        public bool Generate(ref ChiselBrushContainer brushContainer)
        {
            Profiler.BeginSample("GenerateBrush");
            try
            {
                if (!IsValid)
                {
                    return(false);
                }

                Profiler.BeginSample("EnsureSize");
                brushContainer.EnsureSize(1);
                Profiler.EndSample();

                Profiler.BeginSample("new BrushMesh");
                BrushMesh brushMesh;
                if (brushContainer.brushMeshes[0] == null)
                {
                    brushMesh = new BrushMesh(brushOutline);
                    brushContainer.brushMeshes[0] = brushMesh;
                }
                else
                {
                    brushContainer.brushMeshes[0].CopyFrom(brushOutline);
                    brushMesh = brushContainer.brushMeshes[0];
                }
                Profiler.EndSample();

                Profiler.BeginSample("Definition.Validate");
                Validate();
                Profiler.EndSample();

                Profiler.BeginSample("Assign Materials");
                for (int p = 0; p < brushMesh.polygons.Length; p++)
                {
                    brushMesh.polygons[p].surface = surfaceDefinition.surfaces[p];
                }
                Profiler.EndSample();

                Profiler.BeginSample("BrushMesh.Validate");
                var valid = brushMesh.Validate();
                Profiler.EndSample();
                return(valid);
            }
            finally
            {
                Profiler.EndSample();
            }
        }
        public static bool GenerateStadium(ref ChiselBrushContainer brushContainer, ref ChiselStadiumDefinition definition)
        {
            definition.Validate();
            Vector3[] vertices = null;
            if (!GenerateStadiumVertices(definition, ref vertices))
            {
                return(false);
            }

            brushContainer.EnsureSize(1);

            var surfaceIndices = new int[vertices.Length + 2];

            return(BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[0], definition.sides, surfaceIndices, 0, 1, vertices, definition.surfaceDefinition));
        }
예제 #7
0
        public static bool GenerateBox(ref ChiselBrushContainer brushContainer, ref ChiselBoxDefinition definition)
        {
            definition.Validate();

            var min = definition.min;
            var max = definition.max;

            if (!BoundsExtensions.IsValid(min, max))
            {
                return(false);
            }

            brushContainer.EnsureSize(1);

            return(GenerateBox(ref brushContainer.brushMeshes[0], definition.min, definition.max, definition.surfaceDefinition));
        }
예제 #8
0
        public static bool GenerateLinearStairs(ref ChiselBrushContainer brushContainer, ref ChiselLinearStairsDefinition definition)
        {
            definition.Validate();

            int requiredSubMeshCount = BrushMeshFactory.GetLinearStairsSubMeshCount(definition, definition.leftSide, definition.rightSide);

            if (requiredSubMeshCount == 0)
            {
                return(false);
            }

            int subMeshOffset = 0;

            brushContainer.EnsureSize(requiredSubMeshCount);

            return(GenerateLinearStairsSubMeshes(ref brushContainer, definition, definition.leftSide, definition.rightSide, subMeshOffset));
        }
예제 #9
0
        public static bool GenerateCylinder(ref ChiselBrushContainer brushContainer, ref ChiselCylinderDefinition definition)
        {
            definition.Validate();

            var tempTop    = definition.top;
            var tempBottom = definition.bottom;

            if (!definition.isEllipsoid)
            {
                tempTop.diameterZ    = tempTop.diameterX;
                tempBottom.diameterZ = tempBottom.diameterX;
            }

            brushContainer.EnsureSize(1);

            bool result = false;

            switch (definition.type)
            {
            case CylinderShapeType.Cylinder:       result = BrushMeshFactory.GenerateCylinder(ref brushContainer.brushMeshes[0], tempBottom, tempTop.height, definition.rotation, definition.sides, in definition.surfaceDefinition); break;
예제 #10
0
 public bool Generate(ref ChiselBrushContainer brushContainer)
 {
     return(BrushMeshFactory.GenerateSpiralStairs(ref brushContainer, ref this));
 }
 public bool Generate(ref ChiselBrushContainer brushContainer)
 {
     return(BrushMeshFactory.GenerateCapsule(ref brushContainer, ref this));
 }
예제 #12
0
        public static bool GenerateTorus(ref ChiselBrushContainer brushContainer, ref ChiselTorusDefinition definition)
        {
            definition.Validate();
            Vector3[] vertices = null;
            if (!GenerateTorusVertices(definition, ref vertices))
            {
                return(false);
            }

            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;

            brushContainer.EnsureSize(horzSegments);


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

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

            var circleVertices = new Vector3[vertSegments];

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

            for (int v = 0; v < vertSegments; v++)
            {
                var vRad = tubeAngleOffset + (v * vertDegreePerSegment);
                circleVertices[v] = new Vector3((math.cos(vRad) * tubeRadiusX) - torusRadius,
                                                (math.sin(vRad) * tubeRadiusY), 0);
                min.x = math.min(min.x, circleVertices[v].x);
                min.y = math.min(min.y, circleVertices[v].y);
                max.x = math.max(max.x, circleVertices[v].x);
                max.y = math.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 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.AxisAngle(new Vector3(0, 1, 0), hDegree0);
                var rotation1       = quaternion.AxisAngle(new Vector3(0, 1, 0), hDegree1);
                var subMeshVertices = new Vector3[vertSegments * 2];
                for (int v = 0; v < vertSegments; v++)
                {
                    subMeshVertices[v + vertSegments] = math.mul(rotation0, circleVertices[v]);
                    subMeshVertices[v] = math.mul(rotation1, circleVertices[v]);
                }

                var brushMesh = new BrushMesh();
                BrushMeshFactory.CreateExtrudedSubMesh(ref brushMesh, vertSegments, descriptionIndex, 0, 1, subMeshVertices, in definition.surfaceDefinition);
                if (!brushMesh.Validate())
                {
                    return(false);
                }

                brushContainer.brushMeshes[h - 1] = brushMesh;
            }
            return(true);
        }
        public static bool GenerateRevolvedShape(ref ChiselBrushContainer brushContainer, ref ChiselRevolvedShapeDefinition definition)
        {
            definition.Validate();


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

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

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

            if (!Decomposition.ConvexPartition(shapeVertices, shapeSegmentIndices,
                                               out polygonVerticesArray,
                                               out polygonIndicesArray))
            {
                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 brushMeshesList      = new List <BrushMesh>();
            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.AxisAngle(Vector3.forward, hDegree0);
                    var rotation1       = quaternion.AxisAngle(Vector3.forward, hDegree1);
                    var subMeshVertices = new Vector3[vertSegments * 2];
                    for (int v = 0; v < vertSegments; v++)
                    {
                        subMeshVertices[v + vertSegments] = math.mul(rotation0, new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y));
                        subMeshVertices[v] = math.mul(rotation1, new Vector3(polygonVertices[v].x, 0, polygonVertices[v].y));
                    }

                    var brushMesh = new BrushMesh();
                    if (!BrushMeshFactory.CreateExtrudedSubMesh(ref brushMesh, vertSegments, descriptionIndex, 0, 1, subMeshVertices, in definition.surfaceDefinition))
                    {
                        continue;
                    }

                    if (!brushMesh.Validate())
                    {
                        return(false);
                    }
                    brushMeshesList.Add(brushMesh);
                }
            }

            brushContainer.CopyFrom(brushMeshesList);
            return(true);
        }
        public static bool GeneratePathedStairs(ref ChiselBrushContainer brushContainer, ref ChiselPathedStairsDefinition 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 += BrushMeshFactory.GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
            }
            if (totalSubMeshCount == 0)
            {
                return(false);
            }

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

            brushContainer.EnsureSize(totalSubMeshCount);

            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 = BrushMeshFactory.GetLinearStairsSubMeshCount(definition.stairs, leftSide, rightSide);
                if (subMeshCount == 0)
                {
                    continue;
                }

                if (!BrushMeshFactory.GenerateLinearStairsSubMeshes(ref brushContainer, definition.stairs, leftSide, rightSide, subMeshIndex))
                {
                    return(false);
                }

                var halfWidth = maxWidth1 * 0.5f;
                for (int m = 0; m < subMeshCount; m++)
                {
                    var vertices = brushContainer.brushMeshes[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;
            }
            return(false);
        }
예제 #15
0
 public bool Generate(ref ChiselBrushContainer brushContainer)
 {
     return(BrushMeshFactory.GenerateRevolvedShape(ref brushContainer, ref this));
 }
예제 #16
0
        // TODO: create helper method to cut brushes, use that instead of intersection + subtraction brushes
        // TODO: create spiral sides support

        public static bool GenerateSpiralStairs(ref ChiselBrushContainer brushContainer, ref ChiselSpiralStairsDefinition definition)
        {
            definition.Validate();
            ref readonly var surfaceDefinition = ref definition.surfaceDefinition;
예제 #17
0
        public static bool GenerateExtrudedShape(ref ChiselBrushContainer brushContainer, ref ChiselExtrudedShapeDefinition definition)
        {
            definition.Validate();

            ref readonly var shape = ref definition.shape;
예제 #18
0
 // TODO: remove all stairs specific parameters
 static void GenerateBottomRamp(ref ChiselBrushContainer brushContainer, int startIndex, int stepCount, Vector3 min, Vector3 max, Vector3 extrusion, StairsRiserType riserType, float riserDepth, float extraDepth, float maxDepth, ChiselLinearStairsDefinition definition, in ChiselSurfaceDefinition surfaceDefinition)
예제 #19
0
        public static bool GenerateLinearStairsSubMeshes(ref ChiselBrushContainer brushContainer, ChiselLinearStairsDefinition definition, StairsSideType leftSideDefinition, StairsSideType rightSideDefinition, int subMeshOffset = 0)
        {
            // TODO: properly assign all materials

            if (definition.surfaceDefinition.surfaces.Length != (int)ChiselLinearStairsDefinition.SurfaceSides.TotalSides)
            {
                return(false);
            }

            // TODO: implement smooth riser-type

            const float kEpsilon = 0.001f;

            // TODO: put these values in a shared location since they need to match in multiple locations

            var treadHeight   = (definition.treadHeight < kEpsilon) ? 0 : definition.treadHeight;
            var riserType     = (treadHeight == 0 && definition.riserType == StairsRiserType.ThinRiser) ? StairsRiserType.ThickRiser : definition.riserType;
            var leftSideType  = (riserType == StairsRiserType.None && definition.leftSide == StairsSideType.Up) ? StairsSideType.DownAndUp : leftSideDefinition;
            var rightSideType = (riserType == StairsRiserType.None && definition.rightSide == StairsSideType.Up) ? StairsSideType.DownAndUp : rightSideDefinition;

            if (riserType == StairsRiserType.Smooth)
            {
                switch (leftSideType)
                {
                case StairsSideType.Up: leftSideType = StairsSideType.DownAndUp; break;

                case StairsSideType.None: leftSideType = StairsSideType.Down; break;
                }
                switch (rightSideType)
                {
                case StairsSideType.Up: rightSideType = StairsSideType.DownAndUp; break;

                case StairsSideType.None: rightSideType = StairsSideType.Down; break;
                }
            }
            var boundsMin = definition.bounds.min;
            var boundsMax = definition.bounds.max;

            if (boundsMin.y > boundsMax.y)
            {
                var t = boundsMin.y; boundsMin.y = boundsMax.y; boundsMax.y = t;
            }
            if (boundsMin.x > boundsMax.x)
            {
                var t = boundsMin.x; boundsMin.x = boundsMax.x; boundsMax.x = t;
            }
            if (boundsMin.z > boundsMax.z)
            {
                var t = boundsMin.z; boundsMin.z = boundsMax.z; boundsMax.z = t;
            }

            var haveRiser        = riserType != StairsRiserType.None;
            var haveLeftSideDown = riserType != StairsRiserType.FillDown &&
                                   (leftSideType == StairsSideType.Down || leftSideType == StairsSideType.DownAndUp);
            var haveLeftSideUp    = (leftSideType == StairsSideType.Up || leftSideType == StairsSideType.DownAndUp);
            var haveRightSideDown = riserType != StairsRiserType.FillDown &&
                                    (rightSideType == StairsSideType.Down || rightSideType == StairsSideType.DownAndUp);
            var haveRightSideUp = (rightSideType == StairsSideType.Up || rightSideType == StairsSideType.DownAndUp);
            var sideWidth       = definition.sideWidth;
            var sideHeight      = definition.sideHeight;
            var leftSideDepth   = (haveLeftSideDown) ? definition.sideDepth : 0;
            var rightSideDepth  = (haveRightSideDown) ? definition.sideDepth : 0;
            var thickRiser      = riserType == StairsRiserType.ThickRiser || riserType == StairsRiserType.Smooth;
            var riserDepth      = (haveRiser && !thickRiser) ? definition.riserDepth : 0;

            var stepCount   = definition.StepCount;
            var offsetZ     = (definition.StepDepthOffset < kEpsilon) ? 0 : definition.StepDepthOffset;
            var offsetY     = definition.plateauHeight;
            var nosingDepth = definition.nosingDepth;

            var haveTread   = (treadHeight >= kEpsilon);
            var haveTopSide = (sideHeight > kEpsilon);

            var leftNosingWidth     = haveLeftSideUp ? -sideWidth : definition.nosingWidth;
            var rightNosingWidth    = haveRightSideUp ? -sideWidth : definition.nosingWidth;
            var leftTopNosingWidth  = (haveLeftSideUp && (!haveTopSide)) ? definition.nosingWidth : leftNosingWidth;
            var rightTopNosingWidth = (haveRightSideUp && (!haveTopSide)) ? definition.nosingWidth : rightNosingWidth;

            var subMeshCount = 0; if (haveRiser)
            {
                subMeshCount = stepCount;
            }
            var startTread = subMeshCount; if (haveTread)
            {
                subMeshCount += stepCount;
            }
            var startLeftSideDown = subMeshCount; if (haveLeftSideDown)
            {
                subMeshCount += stepCount;
            }
            var startRightSideDown = subMeshCount; if (haveRightSideDown)
            {
                subMeshCount += stepCount;
            }
            var startLeftSideUp = subMeshCount; if (haveLeftSideUp)
            {
                subMeshCount += (stepCount - 1) + (haveTopSide ? 1 : 0) + 1;                                                        //(haveLeftSideDown  ? 0 : 1);
            }
            var startRightSideUp = subMeshCount; if (haveRightSideUp)
            {
                subMeshCount += (stepCount - 1) + (haveTopSide ? 1 : 0) + 1;                                                         //(haveRightSideDown ? 0 : 1);
            }
            var stepOffset = new Vector3(0, -definition.stepHeight, definition.stepDepth);

            if (stepCount > 0)
            {
                if (haveRiser)
                {
                    var min = boundsMin;
                    var max = boundsMax;
                    max.z = min.z + definition.StepDepthOffset + definition.stepDepth;
                    if (riserType != StairsRiserType.FillDown)
                    {
                        if (riserType == StairsRiserType.ThinRiser)
                        {
                            min.z = max.z - riserDepth;
                        }
                        else
                        {
                            min.z = min.z + definition.StepDepthOffset;
                        }
                        if (thickRiser)
                        {
                            min.z -= offsetZ;
                        }
                    }
                    min.y  = max.y - definition.stepHeight;
                    min.y -= treadHeight;
                    max.y -= treadHeight;
                    min.x += haveRightSideUp ? sideWidth : 0;
                    max.x -= haveLeftSideUp ? sideWidth : 0;
                    var extrusion = new Vector3(max.x - min.x, 0, 0);
                    for (int i = 0; i < stepCount; i++)
                    {
                        if (i == 1 &&
                            thickRiser)
                        {
                            min.z += offsetZ;
                        }
                        if (i == stepCount - 1)
                        {
                            min.y += treadHeight - offsetY;
                        }

                        Vector3[] vertices;
                        if (i == 0 || riserType != StairsRiserType.Smooth)
                        {
                            vertices = new[] {
                                new Vector3(min.x, min.y, min.z),                   // 0
                                new Vector3(min.x, min.y, max.z),                   // 1
                                new Vector3(min.x, max.y, max.z),                   // 2
                                new Vector3(min.x, max.y, min.z),                   // 3
                            };
                        }
                        else
                        {
                            vertices = new[] {
                                new Vector3(min.x, min.y, min.z),                        // 0
                                new Vector3(min.x, min.y, max.z),                        // 1
                                new Vector3(min.x, max.y, max.z),                        // 2
                                new Vector3(min.x, max.y, min.z - definition.stepDepth), // 3
                            };
                        }

                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + i], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 3, 3, 3 }, // TODO: fix this
                                                               definition.surfaceDefinition);

                        if (riserType != StairsRiserType.FillDown)
                        {
                            min.z += definition.stepDepth;
                        }
                        max.z += definition.stepDepth;
                        min.y -= definition.stepHeight;
                        max.y -= definition.stepHeight;
                    }
                }
                if (haveTread)
                {
                    var min = new Vector3(boundsMin.x + sideWidth, boundsMax.y - definition.treadHeight, boundsMin.z);
                    var max = new Vector3(boundsMax.x - sideWidth, boundsMax.y, boundsMin.z + definition.StepDepthOffset + definition.stepDepth + nosingDepth);
                    for (int i = 0; i < stepCount; i++)
                    {
                        min.x = boundsMin.x - ((i == 0) ? rightTopNosingWidth : rightNosingWidth);
                        max.x = boundsMax.x + ((i == 0) ? leftTopNosingWidth : leftNosingWidth);
                        if (i == 1)
                        {
                            min.z = max.z - (definition.stepDepth + nosingDepth);
                        }
                        var vertices = new[] {
                            new Vector3(min.x, min.y, min.z),                       // 0
                            new Vector3(min.x, min.y, max.z),                       // 1
                            new Vector3(min.x, max.y, max.z),                       // 2
                            new Vector3(min.x, max.y, min.z),                       // 3
                        };
                        var extrusion = new Vector3(max.x - min.x, 0, 0);
                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + startTread + i], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 2, 2, 2 }, // TODO: fix this
                                                               definition.surfaceDefinition);
                        min += stepOffset;
                        max += stepOffset;
                    }
                }
                if (haveLeftSideDown)
                {
                    var min = new Vector3(boundsMax.x - sideWidth, boundsMax.y - definition.stepHeight - definition.treadHeight, boundsMin.z + definition.StepDepthOffset);
                    var max = new Vector3(boundsMax.x, boundsMax.y - definition.treadHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth);

                    var extrusion  = new Vector3(sideWidth, 0, 0);
                    var extraDepth = (thickRiser ? definition.stepDepth : riserDepth) + leftSideDepth;
                    var maxDepth   = boundsMin.z;

                    GenerateBottomRamp(ref brushContainer, subMeshOffset + startLeftSideDown, stepCount, min, max, extrusion, riserType, definition.stepDepth - riserDepth, extraDepth, maxDepth, definition, definition.surfaceDefinition);
                }
                if (haveRightSideDown)
                {
                    var min = new Vector3(boundsMin.x, boundsMax.y - definition.stepHeight - definition.treadHeight, boundsMin.z + definition.StepDepthOffset);
                    var max = new Vector3(boundsMin.x + sideWidth, boundsMax.y - definition.treadHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth);

                    var extrusion  = new Vector3(sideWidth, 0, 0);
                    var extraDepth = (thickRiser ? definition.stepDepth : riserDepth) + rightSideDepth;
                    var maxDepth   = boundsMin.z;

                    GenerateBottomRamp(ref brushContainer, subMeshOffset + startRightSideDown, stepCount, min, max, extrusion, riserType, definition.stepDepth - riserDepth, extraDepth, maxDepth, definition, definition.surfaceDefinition);
                }
                if (haveLeftSideUp)
                {
                    var min        = new Vector3(boundsMax.x - sideWidth, boundsMax.y - definition.treadHeight - definition.stepHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth);
                    var max        = new Vector3(boundsMax.x, boundsMax.y - definition.treadHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth + definition.stepDepth);
                    var extrusion  = new Vector3(sideWidth, 0, 0);
                    var extraDepth = (thickRiser ? definition.stepDepth : riserDepth) + leftSideDepth;
                    var maxDepth   = boundsMin.z;

                    GenerateTopRamp(ref brushContainer, subMeshOffset + startLeftSideUp, stepCount - 1, min, max, extrusion, sideHeight, extraDepth, maxDepth, riserType, definition, definition.surfaceDefinition);

                    if (haveTopSide)
                    {
                        var vertices = new[] {
                            new Vector3(min.x, max.y + sideHeight + definition.treadHeight, min.z),                         // 0
                            new Vector3(min.x, max.y + sideHeight + definition.treadHeight, boundsMin.z),                   // 1
                            new Vector3(min.x, max.y, boundsMin.z),                                                         // 2
                            new Vector3(min.x, max.y, min.z),                                                               // 3
                        };

                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + startLeftSideUp + (stepCount - 1)], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 3, 3, 3 }, // TODO: fix this
                                                               definition.surfaceDefinition);
                    }
                    //if (!haveLeftSideDown)
                    {
                        var       stepHeight = definition.stepHeight;
                        Vector3[] vertices;
                        if (riserType == StairsRiserType.FillDown)
                        {
                            vertices = new[] {
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z),              // 0
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMin.z),              // 1
                                new Vector3(min.x, boundsMin.y, boundsMin.z),                           // 2
                                new Vector3(min.x, boundsMin.y, boundsMax.z),                           // 3
                            };
                        }
                        else
                        {
                            vertices = new[] {
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z),                      // 0
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z - extraDepth),         // 1
                                new Vector3(min.x, boundsMin.y, boundsMax.z - extraDepth),                      // 2
                                new Vector3(min.x, boundsMin.y, boundsMax.z),                                   // 3
                            };
                        }

                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + startLeftSideUp + stepCount], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 3, 3, 3 }, // TODO: fix this
                                                               definition.surfaceDefinition);
                    }
                }
                if (haveRightSideUp)
                {
                    var min        = new Vector3(boundsMin.x, boundsMax.y - definition.treadHeight - definition.stepHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth);
                    var max        = new Vector3(boundsMin.x + sideWidth, boundsMax.y - definition.treadHeight, boundsMin.z + definition.StepDepthOffset + definition.stepDepth + definition.stepDepth);
                    var extrusion  = new Vector3(sideWidth, 0, 0);
                    var extraDepth = (thickRiser ? definition.stepDepth : riserDepth) + rightSideDepth;
                    var maxDepth   = boundsMin.z;

                    GenerateTopRamp(ref brushContainer, subMeshOffset + startRightSideUp, stepCount - 1, min, max, extrusion, sideHeight, extraDepth, maxDepth, riserType, definition, definition.surfaceDefinition);

                    if (haveTopSide)
                    {
                        var vertices = new[] {
                            new Vector3(min.x, max.y + sideHeight + definition.treadHeight, min.z),                         // 0
                            new Vector3(min.x, max.y + sideHeight + definition.treadHeight, boundsMin.z),                   // 1
                            new Vector3(min.x, max.y, boundsMin.z),                                                         // 2
                            new Vector3(min.x, max.y, min.z),                                                               // 3
                        };

                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + startRightSideUp + (stepCount - 1)], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 3, 3, 3 }, // TODO: fix this
                                                               definition.surfaceDefinition);
                    }
                    //if (!haveRightSideDown)
                    {
                        var       stepHeight = definition.stepHeight;
                        Vector3[] vertices;
                        if (riserType == StairsRiserType.FillDown)
                        {
                            vertices = new[] {
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z),               // 0
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMin.z),               // 1
                                new Vector3(min.x, boundsMin.y, boundsMin.z),                            // 2
                                new Vector3(min.x, boundsMin.y, boundsMax.z),                            // 3
                            };
                        }
                        else
                        {
                            vertices = new[] {
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z),                      // 0
                                new Vector3(min.x, boundsMin.y + stepHeight, boundsMax.z - extraDepth),         // 1
                                new Vector3(min.x, boundsMin.y, boundsMax.z - extraDepth),                      // 2
                                new Vector3(min.x, boundsMin.y, boundsMax.z),                                   // 3
                            };
                        }

                        BrushMeshFactory.CreateExtrudedSubMesh(ref brushContainer.brushMeshes[subMeshOffset + startRightSideUp + stepCount], vertices, extrusion,
                                                               new int[] { 0, 1, 2, 3, 3, 3 }, // TODO: fix this
                                                               definition.surfaceDefinition);
                    }
                }
            }
            return(true);
        }