public MeshGradeShape(EdgeBlit edge, ICurve curve, TriangleUtil.Triangle[] tri)
        {
            var box = BoundingBoxD.CreateInvalid();

            foreach (var k in tri)
            {
                box.Include(k.Origin);
                box.Include(k.Origin + k.Edge1);
                box.Include(k.Origin + k.Edge2);
            }

            Box    = box.Inflate(0.5f);
            _tris  = tri;
            _edge  = edge;
            _curve = curve;
        }
            public MeshGradeShape CreateShape(EdgeBlit e, bool invertHeight)
            {
                var h = Height;

                if (invertHeight)
                {
                    h *= -1;
                }
                switch (Type)
                {
                case MyObjectBuilder_RailGradeComponentDefinition.Shape.GradeType.Tunnel:
                    return(MeshGradeShape.CreateTunnelShape(e, Width, RelaxAngleRadians, VerticalOffset, Segments, h, EndPadding));

                case MyObjectBuilder_RailGradeComponentDefinition.Shape.GradeType.Grade:
                default:
                    return(MeshGradeShape.CreateGradeShape(e, Width, RelaxAngleRadians, VerticalOffset, Segments, h, EndPadding));
                }
            }
        public static MeshGradeShape CreateGradeShape(EdgeBlit edge, float width, float relaxAngle, float shiftUp, int segments, float maxDepth, float endPadding)
        {
            var curve = edge.Curve.Convert();

            var tris      = new List <TriangleUtil.Triangle>();
            var lastCross = new Vector3D[4];
            var currCross = new Vector3D[4];

            for (var i = 0; i <= segments; i++)
            {
                var t       = i / (float)segments;
                var loc     = curve.Sample(t);
                var tangent = (Vector3)curve.SampleDerivative(t);
                tangent.Normalize();
                if (i == 0)
                {
                    loc -= tangent * endPadding;
                }
                if (i == segments)
                {
                    loc += tangent * endPadding;
                }
                var up = Vector3.Lerp(edge.FromUp, edge.ToUp, t);
                up.Normalize();
                loc += shiftUp * up;

                var normal = Vector3.Cross(up, tangent);
                normal.Normalize();


                var perturbLow = Vector3.Zero;
                if (i == 0 || i == segments)
                {
                    var n = i == 0 ? -tangent : tangent;
                    perturbLow = (float)Math.Cos(relaxAngle) * n * Math.Abs(maxDepth);
                }

                for (var j = -1; j <= 1; j += 2)
                {
                    var offset = j + 1;

                    var origin = loc + (width / 2) * j * normal;
                    var rot    = Matrix.CreateFromAxisAngle(tangent, -j * Math.Sign(maxDepth) * relaxAngle);
                    var pnorm  = Vector3.TransformNormal(up, rot);

                    var cotan = Vector3.Cross(pnorm, tangent);
                    cotan.Normalize();
                    cotan *= -Math.Sign(Vector3.Dot(cotan, up));
                    var p2 = origin + maxDepth * cotan + perturbLow;

                    if (i > 0)
                    {
                        tris.Add(new TriangleUtil.Triangle(origin, lastCross[offset], lastCross[offset + 1], pnorm));
                        if ((p2 - lastCross[offset + 1]).Dot(tangent) > 0)
                        {
                            tris.Add(new TriangleUtil.Triangle(origin, lastCross[offset + 1], p2, pnorm));
                        }
                        else
                        {
                            p2 = lastCross[offset + 1];
                        }
                    }

                    currCross[offset]     = origin;
                    currCross[offset + 1] = p2;
                }

                if (i > 0)
                {
                    tris.Add(new TriangleUtil.Triangle(currCross[0], lastCross[0], lastCross[2], up));
                    tris.Add(new TriangleUtil.Triangle(currCross[0], lastCross[2], currCross[2], up));

                    tris.Add(new TriangleUtil.Triangle(currCross[1], lastCross[1], lastCross[3], -up));
                    tris.Add(new TriangleUtil.Triangle(currCross[1], lastCross[3], currCross[3], -up));
                }

                if (i == 0 || i == segments)
                {
                    var n = i == 0 ? -tangent : tangent;
                    tris.Add(new TriangleUtil.Triangle(currCross[0], currCross[2], currCross[3], n));
                    tris.Add(new TriangleUtil.Triangle(currCross[0], currCross[3], currCross[1], n));
                }
                var tmp = lastCross;
                lastCross = currCross;
                currCross = tmp;
            }

            return(new MeshGradeShape(edge, curve, tris.ToArray()));
        }
        public static MeshGradeShape CreateTunnelShape(EdgeBlit edge, float width, float relaxAngle, float shiftUp, int segments, float height, float endPadding)
        {
            var curve = edge.Curve.Convert();

            var tris = new List <TriangleUtil.Triangle>();
            // {-Level, -OuterEdge, -Ceiling, +Level, +OuterEdge, +Ceiling}
            var lastCross       = new Vector3D[6];
            var currCross       = new Vector3D[6];
            var halfHeight      = height / 2;
            var dropTop         = height / 5;
            var ceilingPrevious = Vector3D.Zero;

            for (var i = 0; i <= segments; i++)
            {
                var t       = i / (float)segments;
                var loc     = curve.Sample(t);
                var tangent = (Vector3)curve.SampleDerivative(t);
                tangent.Normalize();
                if (i == 0)
                {
                    loc -= tangent * endPadding;
                }
                if (i == segments)
                {
                    loc += tangent * endPadding;
                }
                var up = Vector3.Lerp(edge.FromUp, edge.ToUp, t);
                up.Normalize();
                loc += shiftUp * up;
                var ceilingTotal = loc - up * height;

                var normal = Vector3.Cross(up, tangent);
                normal.Normalize();


                var perturbLow = Vector3.Zero;
                if (i == 0 || i == segments)
                {
                    var n = i == 0 ? -tangent : tangent;
                    perturbLow = (float)Math.Cos(relaxAngle) * n * Math.Abs(halfHeight);
                }

                for (var j = -1; j <= 1; j += 2)
                {
                    var offset = (j + 1) * 3 / 2;

                    var origin = loc + (width / 2) * j * normal;
                    var rot    = Matrix.CreateFromAxisAngle(tangent, -j * Math.Sign(halfHeight) * relaxAngle);
                    var pnorm  = Vector3.TransformNormal(up, rot);

                    var cotan = Vector3.Cross(pnorm, tangent);
                    cotan.Normalize();
                    cotan *= -Math.Sign(Vector3.Dot(cotan, up));
                    var p2 = origin + halfHeight * cotan + perturbLow;

                    var ceilingEdge = origin - up * (height - dropTop);

                    if (i > 0)
                    {
                        // side faces, lower
                        tris.Add(new TriangleUtil.Triangle(origin, lastCross[offset], lastCross[offset + 1], pnorm));

                        var dissolvedOuterEdge = (p2 - lastCross[offset + 1]).Dot(tangent) <= 0;
                        if (dissolvedOuterEdge)
                        {
                            p2 = lastCross[offset + 1];
                        }
                        else
                        {
                            tris.Add(new TriangleUtil.Triangle(origin, lastCross[offset + 1], p2, pnorm));
                        }

                        var dissolvedCeilingEdge = (ceilingEdge - lastCross[offset + 2]).Dot(tangent) <= 0;
                        if (dissolvedCeilingEdge)
                        {
                            ceilingEdge = lastCross[offset + 2];
                        }

                        // side faces, upper:
                        if (!dissolvedCeilingEdge)
                        {
                            tris.Add(new TriangleUtil.Triangle(lastCross[offset + 1], lastCross[offset + 2], ceilingEdge, pnorm));
                        }
                        if (!dissolvedOuterEdge)
                        {
                            tris.Add(new TriangleUtil.Triangle(lastCross[offset + 1], p2, ceilingEdge, pnorm));
                        }
                    }

                    currCross[offset]     = origin;
                    currCross[offset + 1] = p2;
                    currCross[offset + 2] = ceilingEdge;
                }

                if (i > 0)
                {
                    // top
                    tris.Add(new TriangleUtil.Triangle(currCross[0], lastCross[0], lastCross[3], -up));
                    tris.Add(new TriangleUtil.Triangle(currCross[0], lastCross[3], currCross[3], -up));

                    // bottom
                    tris.Add(new TriangleUtil.Triangle(currCross[2], lastCross[2], ceilingPrevious, up));
                    tris.Add(new TriangleUtil.Triangle(currCross[2], ceilingPrevious, ceilingTotal, up));

                    tris.Add(new TriangleUtil.Triangle(currCross[5], lastCross[5], ceilingPrevious, up));
                    tris.Add(new TriangleUtil.Triangle(currCross[5], ceilingPrevious, ceilingTotal, up));
                }

                // end caps
                if (i == 0 || i == segments)
                {
                    var n   = i == 0 ? -tangent : tangent;
                    var avg = (currCross[0] + currCross[1] + currCross[2] + currCross[3] + currCross[4] + currCross[5]) / 6;
                    tris.Add(new TriangleUtil.Triangle(currCross[0], currCross[1], avg, n));
                    tris.Add(new TriangleUtil.Triangle(currCross[1], currCross[2], avg, n));
                    tris.Add(new TriangleUtil.Triangle(currCross[2], ceilingTotal, avg, n));
                    tris.Add(new TriangleUtil.Triangle(ceilingTotal, currCross[5], avg, n));
                    tris.Add(new TriangleUtil.Triangle(currCross[5], currCross[4], avg, n));
                    tris.Add(new TriangleUtil.Triangle(currCross[4], currCross[3], avg, n));
                    tris.Add(new TriangleUtil.Triangle(currCross[3], currCross[0], avg, n));
                }
                var tmp = lastCross;
                lastCross       = currCross;
                currCross       = tmp;
                ceilingPrevious = ceilingTotal;
            }

            return(new MeshGradeShape(edge, curve, tris.ToArray()));
        }