public static void ClampTerrainToRoad(HeightField terrain, Path3F road,
                                              float defaultWidth, float defaultSideFalloff,
                                              int maxNumberOfIterations, float tolerance)
        {
            if (terrain == null)
            {
                throw new ArgumentNullException("terrain");
            }
            if (road == null)
            {
                throw new ArgumentNullException("road");
            }

            // Compute list of line segments. (2 points per line segment!)
            var flattenedPoints = new List <Vector3F>();

            road.Flatten(flattenedPoints, maxNumberOfIterations, tolerance);

            // Abort if path is empty.
            int numberOfLineSegments = flattenedPoints.Count / 2;

            if (numberOfLineSegments <= 0)
            {
                return;
            }

            // Compute accumulated lengths. (One entry for each entry in flattenedPoints.)
            float[] accumulatedLengths = new float[flattenedPoints.Count];
            accumulatedLengths[0] = 0;
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
                Vector3F previous = flattenedPoints[i - 1];
                Vector3F current  = flattenedPoints[i];
                float    length   = (current - previous).Length;

                accumulatedLengths[i] = accumulatedLengths[i - 1] + length;
                if (i + 1 < flattenedPoints.Count)
                {
                    accumulatedLengths[i + 1] = accumulatedLengths[i];
                }
            }

            // Create a mapping between accumulatedLength and the path keys.
            // (accumulatedLength --> key)
            var pathLengthsAndKeys = new List <Pair <float, TerrainRoadPathKey> >();
            {
                int index = 0;
                foreach (var key in road)
                {
                    Vector3F position = key.Point;
                    var      roadKey  = key as TerrainRoadPathKey;
                    if (roadKey == null)
                    {
                        roadKey = new TerrainRoadPathKey
                        {
                            Point       = key.Point,
                            Width       = defaultWidth,
                            SideFalloff = defaultSideFalloff,
                        };
                    }

                    for (; index < flattenedPoints.Count; index++)
                    {
                        if (Vector3F.AreNumericallyEqual(position, flattenedPoints[index]))
                        {
                            pathLengthsAndKeys.Add(new Pair <float, TerrainRoadPathKey>(accumulatedLengths[index], roadKey));
                            break;
                        }

                        bool isLastLineSegment = (index + 2 == flattenedPoints.Count);
                        if (!isLastLineSegment)
                        {
                            index++;
                        }
                    }

                    index++;
                }
            }

            // Create a list of interpolated road widths and side falloffs. (One entry for each entry in flattenedPoints.)
            var halfWidths       = new float[flattenedPoints.Count];
            var sideFalloffs     = new float[flattenedPoints.Count];
            int previousKeyIndex = 0;
            var previousKey      = pathLengthsAndKeys[0];
            var nextKey          = pathLengthsAndKeys[1];

            halfWidths[0]   = 0.5f * pathLengthsAndKeys[0].Second.Width;
            sideFalloffs[0] = pathLengthsAndKeys[0].Second.SideFalloff;
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
                if (accumulatedLengths[i] > nextKey.First)
                {
                    previousKey = nextKey;
                    previousKeyIndex++;
                    nextKey = pathLengthsAndKeys[previousKeyIndex + 1];
                }

                float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First);
                halfWidths[i]   = 0.5f * InterpolationHelper.Lerp(previousKey.Second.Width, nextKey.Second.Width, p);
                sideFalloffs[i] = InterpolationHelper.Lerp(previousKey.Second.SideFalloff, nextKey.Second.SideFalloff, p);

                if (i + 1 < flattenedPoints.Count)
                {
                    halfWidths[i + 1]   = halfWidths[i];
                    sideFalloffs[i + 1] = sideFalloffs[i];
                }
            }

            // Get AABB of road with the side falloff.
            Aabb aabbWithSideFalloffs;
            {
                Vector3F p = flattenedPoints[0];
                float    r = halfWidths[0] + sideFalloffs[0];
                aabbWithSideFalloffs = new Aabb(new Vector3F(p.X - r, 0, p.Z - r),
                                                new Vector3F(p.X + r, 0, p.Z + r));
                for (int i = 1; i < flattenedPoints.Count; i += 2)
                {
                    p = flattenedPoints[i];
                    r = halfWidths[i] + sideFalloffs[i];
                    aabbWithSideFalloffs.Grow(new Vector3F(p.X - r, 0, p.Z - r));
                    aabbWithSideFalloffs.Grow(new Vector3F(p.X + r, 0, p.Z + r));
                }
            }

            // Terrain properties.
            int   numberOfSamplesX = terrain.NumberOfSamplesX;
            int   numberOfSamplesZ = terrain.NumberOfSamplesZ;
            int   numberOfCellsX   = numberOfSamplesX - 1;
            int   numberOfCellsZ   = numberOfSamplesZ - 1;
            float widthX           = terrain.WidthX;
            float cellSizeX        = widthX / numberOfCellsX;
            float widthZ           = terrain.WidthZ;
            float cellSizeZ        = widthZ / numberOfCellsZ;
            float cellSizeDiagonal = (float)Math.Sqrt(cellSizeX * cellSizeX + cellSizeZ * cellSizeZ);

            bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]);

            {
                // Get the line segments which of the road border.
                List <Vector4F> segments        = new List <Vector4F>(); // 2 points per segment.
                Vector3F        lastOrthonormal = Vector3F.Right;
                Vector4F        previousV1      = Vector4F.Zero;
                Vector4F        previousV2      = Vector4F.Zero;
                for (int i = 0; i < flattenedPoints.Count; i++)
                {
                    Vector3F start = flattenedPoints[i];

                    Vector3F previous;
                    bool     isFirstPoint = (i == 0);
                    if (!isFirstPoint)
                    {
                        previous = flattenedPoints[i - 1];
                    }
                    else if (isClosed && road.SmoothEnds)
                    {
                        previous = flattenedPoints[flattenedPoints.Count - 2];
                    }
                    else
                    {
                        previous = start;
                    }

                    Vector3F next;
                    bool     isLastPoint = (i + 1 == flattenedPoints.Count);
                    if (!isLastPoint)
                    {
                        next = flattenedPoints[i + 1];
                    }
                    else if (isClosed && road.SmoothEnds)
                    {
                        next = flattenedPoints[1];
                    }
                    else
                    {
                        next = start;
                    }

                    Vector3F tangent = next - previous;

                    Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
                    if (!orthonormal.TryNormalize())
                    {
                        orthonormal = lastOrthonormal;
                    }

                    // Add 2 vertices two segments for the road side border.
                    //
                    //  pV1        pV2 (previous vertices)
                    //  x           x
                    //  |           |
                    //  x           x
                    //  v1          v2 (current vertices)
                    //
                    // We store the side falloff with the vertex:
                    // Vectors are 4D. Height is y. Side falloff is w.
                    Vector4F v1 = new Vector4F(start - orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);
                    Vector4F v2 = new Vector4F(start + orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);

                    if (i > 0)
                    {
                        segments.Add(previousV1);
                        segments.Add(v1);
                        segments.Add(previousV2);
                        segments.Add(v2);

                        if (isLastPoint && !isClosed)
                        {
                            // A segment for the end of the road.
                            segments.Add(v1);
                            segments.Add(v2);
                        }
                    }
                    else
                    {
                        if (!isClosed)
                        {
                            // A segment for the start of the road.
                            segments.Add(v1);
                            segments.Add(v2);
                        }
                    }

                    previousV1 = v1;
                    previousV2 = v2;

                    lastOrthonormal = orthonormal;

                    // The flattened points list contains 2 points per line segment, which means that there
                    // are duplicate intermediate points, which we skip.
                    bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
                    if (!isLastLineSegment)
                    {
                        i++;
                    }
                }

                // Apply the side falloff to the terrain heights.
                // We use a padding where the road influence is 100% because we want road width to be flat
                // but that means that we also have to set some triangle vertices outside the road width to
                // full 100% road height.
                float padding = cellSizeDiagonal;
                ClampHeightsToLineSegments(terrain, aabbWithSideFalloffs, segments, padding);
            }

            // Clamp the terrain heights to the inner part of the road.
            // We create quads for the road mesh and clamp the heights to the quad triangles.
            {
                Vector3F previousV1      = Vector3F.Zero;
                Vector3F previousV2      = Vector3F.Zero;
                Vector3F lastOrthonormal = Vector3F.Right;
                for (int i = 0; i < flattenedPoints.Count; i++)
                {
                    Vector3F start = flattenedPoints[i];

                    Vector3F previous;
                    bool     isFirstPoint = (i == 0);
                    if (!isFirstPoint)
                    {
                        previous = flattenedPoints[i - 1];
                    }
                    else if (isClosed && road.SmoothEnds)
                    {
                        previous = flattenedPoints[flattenedPoints.Count - 2];
                    }
                    else
                    {
                        previous = start;
                    }

                    Vector3F next;
                    bool     isLastPoint = (i + 1 == flattenedPoints.Count);
                    if (!isLastPoint)
                    {
                        next = flattenedPoints[i + 1];
                    }
                    else if (isClosed && road.SmoothEnds)
                    {
                        next = flattenedPoints[1];
                    }
                    else
                    {
                        next = start;
                    }

                    Vector3F tangent = next - previous;

                    Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
                    if (!orthonormal.TryNormalize())
                    {
                        orthonormal = lastOrthonormal;
                    }

                    // Add 2 vertices to create a mesh like this:
                    //
                    //  pV1             pV2 (previous vertices)
                    //  x---------------x
                    //  |               |
                    //  x---------------x
                    //  v1              v2 (current vertices)
                    //
                    // Then we check all height samples against these triangles.

                    // Vectors are 4D. Height is y. Influence is w.
                    Vector3F v1 = start - orthonormal * halfWidths[i];
                    Vector3F v2 = start + orthonormal * halfWidths[i];

                    if (i > 0)
                    {
                        ClampHeightsToQuad(terrain, previousV1, previousV2, v1, v2);
                    }

                    previousV1 = v1;
                    previousV2 = v2;

                    lastOrthonormal = orthonormal;

                    // The flattened points list contains 2 points per line segment, which means that there
                    // are duplicate intermediate points, which we skip.
                    bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
                    if (!isLastLineSegment)
                    {
                        i++;
                    }
                }
            }

            terrain.Invalidate();
        }
        public static void ClampTerrainToRoad(HeightField terrain, Path3F road,
            float defaultWidth, float defaultSideFalloff,
            int maxNumberOfIterations, float tolerance)
        {
            if (terrain == null)
            throw new ArgumentNullException("terrain");
              if (road == null)
            throw new ArgumentNullException("road");

              // Compute list of line segments. (2 points per line segment!)
              var flattenedPoints = new List<Vector3F>();
              road.Flatten(flattenedPoints, maxNumberOfIterations, tolerance);

              // Abort if path is empty.
              int numberOfLineSegments = flattenedPoints.Count / 2;
              if (numberOfLineSegments <= 0)
            return;

              // Compute accumulated lengths. (One entry for each entry in flattenedPoints.)
              float[] accumulatedLengths = new float[flattenedPoints.Count];
              accumulatedLengths[0] = 0;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            Vector3F previous = flattenedPoints[i - 1];
            Vector3F current = flattenedPoints[i];
            float length = (current - previous).Length;

            accumulatedLengths[i] = accumulatedLengths[i - 1] + length;
            if (i + 1 < flattenedPoints.Count)
              accumulatedLengths[i + 1] = accumulatedLengths[i];
              }

              // Create a mapping between accumulatedLength and the path keys.
              // (accumulatedLength --> key)
              var pathLengthsAndKeys = new List<Pair<float, TerrainRoadPathKey>>();
              {
            int index = 0;
            foreach (var key in road)
            {
              Vector3F position = key.Point;
              var roadKey = key as TerrainRoadPathKey;
              if (roadKey == null)
              {
            roadKey = new TerrainRoadPathKey
            {
              Point = key.Point,
              Width = defaultWidth,
              SideFalloff = defaultSideFalloff,
            };
              }

              for (; index < flattenedPoints.Count; index++)
              {
            if (Vector3F.AreNumericallyEqual(position, flattenedPoints[index]))
            {
              pathLengthsAndKeys.Add(new Pair<float, TerrainRoadPathKey>(accumulatedLengths[index], roadKey));
              break;
            }

            bool isLastLineSegment = (index + 2 == flattenedPoints.Count);
            if (!isLastLineSegment)
              index++;
              }

              index++;
            }
              }

              // Create a list of interpolated road widths and side falloffs. (One entry for each entry in flattenedPoints.)
              var halfWidths = new float[flattenedPoints.Count];
              var sideFalloffs = new float[flattenedPoints.Count];
              int previousKeyIndex = 0;
              var previousKey = pathLengthsAndKeys[0];
              var nextKey = pathLengthsAndKeys[1];
              halfWidths[0] = 0.5f * pathLengthsAndKeys[0].Second.Width;
              sideFalloffs[0] = pathLengthsAndKeys[0].Second.SideFalloff;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            if (accumulatedLengths[i] > nextKey.First)
            {
              previousKey = nextKey;
              previousKeyIndex++;
              nextKey = pathLengthsAndKeys[previousKeyIndex + 1];
            }

            float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First);
            halfWidths[i] = 0.5f * InterpolationHelper.Lerp(previousKey.Second.Width, nextKey.Second.Width, p);
            sideFalloffs[i] = InterpolationHelper.Lerp(previousKey.Second.SideFalloff, nextKey.Second.SideFalloff, p);

            if (i + 1 < flattenedPoints.Count)
            {
              halfWidths[i + 1] = halfWidths[i];
              sideFalloffs[i + 1] = sideFalloffs[i];
            }
              }

              // Get AABB of road with the side falloff.
              Aabb aabbWithSideFalloffs;
              {
            Vector3F p = flattenedPoints[0];
            float r = halfWidths[0] + sideFalloffs[0];
            aabbWithSideFalloffs = new Aabb(new Vector3F(p.X - r, 0, p.Z - r),
                                        new Vector3F(p.X + r, 0, p.Z + r));
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
              p = flattenedPoints[i];
              r = halfWidths[i] + sideFalloffs[i];
              aabbWithSideFalloffs.Grow(new Vector3F(p.X - r, 0, p.Z - r));
              aabbWithSideFalloffs.Grow(new Vector3F(p.X + r, 0, p.Z + r));
            }
              }

              // Terrain properties.
              int numberOfSamplesX = terrain.NumberOfSamplesX;
              int numberOfSamplesZ = terrain.NumberOfSamplesZ;
              int numberOfCellsX = numberOfSamplesX - 1;
              int numberOfCellsZ = numberOfSamplesZ - 1;
              float widthX = terrain.WidthX;
              float cellSizeX = widthX / numberOfCellsX;
              float widthZ = terrain.WidthZ;
              float cellSizeZ = widthZ / numberOfCellsZ;
              float cellSizeDiagonal = (float)Math.Sqrt(cellSizeX * cellSizeX + cellSizeZ * cellSizeZ);

              bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]);

              {
            // Get the line segments which of the road border.
            List<Vector4F> segments = new List<Vector4F>();  // 2 points per segment.
            Vector3F lastOrthonormal = Vector3F.Right;
            Vector4F previousV1 = Vector4F.Zero;
            Vector4F previousV2 = Vector4F.Zero;
            for (int i = 0; i < flattenedPoints.Count; i++)
            {
              Vector3F start = flattenedPoints[i];

              Vector3F previous;
              bool isFirstPoint = (i == 0);
              if (!isFirstPoint)
            previous = flattenedPoints[i - 1];
              else if (isClosed && road.SmoothEnds)
            previous = flattenedPoints[flattenedPoints.Count - 2];
              else
            previous = start;

              Vector3F next;
              bool isLastPoint = (i + 1 == flattenedPoints.Count);
              if (!isLastPoint)
            next = flattenedPoints[i + 1];
              else if (isClosed && road.SmoothEnds)
            next = flattenedPoints[1];
              else
            next = start;

              Vector3F tangent = next - previous;

              Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
              if (!orthonormal.TryNormalize())
            orthonormal = lastOrthonormal;

              // Add 2 vertices two segments for the road side border.
              //
              //  pV1        pV2 (previous vertices)
              //  x           x
              //  |           |
              //  x           x
              //  v1          v2 (current vertices)
              //
              // We store the side falloff with the vertex:
              // Vectors are 4D. Height is y. Side falloff is w.
              Vector4F v1 = new Vector4F(start - orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);
              Vector4F v2 = new Vector4F(start + orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);

              if (i > 0)
              {
            segments.Add(previousV1);
            segments.Add(v1);
            segments.Add(previousV2);
            segments.Add(v2);

            if (isLastPoint && !isClosed)
            {
              // A segment for the end of the road.
              segments.Add(v1);
              segments.Add(v2);
            }
              }
              else
              {
            if (!isClosed)
            {
              // A segment for the start of the road.
              segments.Add(v1);
              segments.Add(v2);
            }
              }

              previousV1 = v1;
              previousV2 = v2;

              lastOrthonormal = orthonormal;

              // The flattened points list contains 2 points per line segment, which means that there
              // are duplicate intermediate points, which we skip.
              bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
              if (!isLastLineSegment)
            i++;
            }

            // Apply the side falloff to the terrain heights.
            // We use a padding where the road influence is 100% because we want road width to be flat
            // but that means that we also have to set some triangle vertices outside the road width to
            // full 100% road height.
            float padding = cellSizeDiagonal;
            ClampHeightsToLineSegments(terrain, aabbWithSideFalloffs, segments, padding);
              }

              // Clamp the terrain heights to the inner part of the road.
              // We create quads for the road mesh and clamp the heights to the quad triangles.
              {
            Vector3F previousV1 = Vector3F.Zero;
            Vector3F previousV2 = Vector3F.Zero;
            Vector3F lastOrthonormal = Vector3F.Right;
            for (int i = 0; i < flattenedPoints.Count; i++)
            {
              Vector3F start = flattenedPoints[i];

              Vector3F previous;
              bool isFirstPoint = (i == 0);
              if (!isFirstPoint)
            previous = flattenedPoints[i - 1];
              else if (isClosed && road.SmoothEnds)
            previous = flattenedPoints[flattenedPoints.Count - 2];
              else
            previous = start;

              Vector3F next;
              bool isLastPoint = (i + 1 == flattenedPoints.Count);
              if (!isLastPoint)
            next = flattenedPoints[i + 1];
              else if (isClosed && road.SmoothEnds)
            next = flattenedPoints[1];
              else
            next = start;

              Vector3F tangent = next - previous;

              Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
              if (!orthonormal.TryNormalize())
            orthonormal = lastOrthonormal;

              // Add 2 vertices to create a mesh like this:
              //
              //  pV1             pV2 (previous vertices)
              //  x---------------x
              //  |               |
              //  x---------------x
              //  v1              v2 (current vertices)
              //
              // Then we check all height samples against these triangles.

              // Vectors are 4D. Height is y. Influence is w.
              Vector3F v1 = start - orthonormal * halfWidths[i];
              Vector3F v2 = start + orthonormal * halfWidths[i];

              if (i > 0)
            ClampHeightsToQuad(terrain, previousV1, previousV2, v1, v2);

              previousV1 = v1;
              previousV2 = v2;

              lastOrthonormal = orthonormal;

              // The flattened points list contains 2 points per line segment, which means that there
              // are duplicate intermediate points, which we skip.
              bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
              if (!isLastLineSegment)
            i++;
            }
              }

              terrain.Invalidate();
        }
        public static void CreateMesh(GraphicsDevice graphicsDevice, Path3F path, float defaultWidth,
                                      int maxNumberOfIterations, float tolerance,
                                      out Submesh submesh, out Aabb aabb, out float roadLength)
        {
            if (graphicsDevice == null)
            {
                throw new ArgumentNullException("graphicsDevice");
            }
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            // Compute list of line segments. (2 points per line segment!)
            var flattenedPoints = new List <Vector3F>();

            path.Flatten(flattenedPoints, maxNumberOfIterations, tolerance);

            // Abort if path is empty.
            int numberOfLineSegments = flattenedPoints.Count / 2;

            if (numberOfLineSegments <= 0)
            {
                submesh    = null;
                aabb       = new Aabb();
                roadLength = 0;
                return;
            }

            // Compute accumulated lengths. (One entry for each entry in flattenedPoints.)
            float[] accumulatedLengths = new float[flattenedPoints.Count];
            accumulatedLengths[0] = 0;
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
                Vector3F previous = flattenedPoints[i - 1];
                Vector3F current  = flattenedPoints[i];
                float    length   = (current - previous).Length;

                accumulatedLengths[i] = accumulatedLengths[i - 1] + length;
                if (i + 1 < flattenedPoints.Count)
                {
                    accumulatedLengths[i + 1] = accumulatedLengths[i];
                }
            }

            // Total road length.
            roadLength = accumulatedLengths[accumulatedLengths.Length - 1];

            // Create a mapping between accumulatedLength and the path key widths.
            // (accumulatedLength --> TerrainRoadPathKey.Width)
            var widthKeys = new List <Pair <float, float> >();
            {
                int index = 0;
                foreach (var key in path)
                {
                    Vector3F position = key.Point;
                    var      roadKey  = key as TerrainRoadPathKey;
                    float    width    = (roadKey != null) ? roadKey.Width : defaultWidth;

                    for (; index < flattenedPoints.Count; index++)
                    {
                        if (Vector3F.AreNumericallyEqual(position, flattenedPoints[index]))
                        {
                            widthKeys.Add(new Pair <float, float>(accumulatedLengths[index], width));
                            break;
                        }

                        bool isLastLineSegment = (index + 2 == flattenedPoints.Count);
                        if (!isLastLineSegment)
                        {
                            index++;
                        }
                    }

                    index++;
                }
            }

            // Create a list of interpolated road widths. (One entry for each entry in flattenedPoints.)
            var widths           = new float[flattenedPoints.Count];
            int previousKeyIndex = 0;
            var previousKey      = widthKeys[0];
            var nextKey          = widthKeys[1];

            widths[0] = widthKeys[0].Second;
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
                if (accumulatedLengths[i] > nextKey.First)
                {
                    previousKey = nextKey;
                    previousKeyIndex++;
                    nextKey = widthKeys[previousKeyIndex + 1];
                }

                float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First);
                widths[i] = InterpolationHelper.Lerp(previousKey.Second, nextKey.Second, p);

                if (i + 1 < flattenedPoints.Count)
                {
                    widths[i + 1] = widths[i];
                }
            }

            // Compute vertices and indices.
            var      vertices        = new List <TerrainLayerVertex>(numberOfLineSegments * 2 + 2);
            var      indices         = new List <int>(numberOfLineSegments * 6); // Two triangles per line segment.
            Vector3F lastOrthonormal = Vector3F.UnitX;

            aabb = new Aabb(flattenedPoints[0], flattenedPoints[0]);
            bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]);

            for (int i = 0; i < flattenedPoints.Count; i++)
            {
                Vector3F start = flattenedPoints[i];

                Vector3F previous;
                bool     isFirstPoint = (i == 0);
                if (!isFirstPoint)
                {
                    previous = flattenedPoints[i - 1];
                }
                else if (isClosed && path.SmoothEnds)
                {
                    previous = flattenedPoints[flattenedPoints.Count - 2];
                }
                else
                {
                    previous = start;
                }

                Vector3F next;
                bool     isLastPoint = (i + 1 == flattenedPoints.Count);
                if (!isLastPoint)
                {
                    next = flattenedPoints[i + 1];
                }
                else if (isClosed && path.SmoothEnds)
                {
                    next = flattenedPoints[1];
                }
                else
                {
                    next = start;
                }

                Vector3F tangent = next - previous;

                Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
                if (!orthonormal.TryNormalize())
                {
                    orthonormal = lastOrthonormal;
                }

                // Add indices to add two triangles between the current and the next vertices.
                if (!isLastPoint)
                {
                    int baseIndex = vertices.Count;

                    //  2      3
                    //   x----x
                    //   |\   |   ^
                    //   | \  |   | road
                    //   |  \ |   | direction
                    //   |   \|   |
                    //   x----x
                    //  0      1

                    indices.Add(baseIndex);
                    indices.Add(baseIndex + 1);
                    indices.Add(baseIndex + 2);

                    indices.Add(baseIndex + 1);
                    indices.Add(baseIndex + 3);
                    indices.Add(baseIndex + 2);
                }

                // Add two vertices.
                Vector3F leftVertex  = start - orthonormal * (widths[i] / 2);
                Vector3F rightVertex = start + orthonormal * (widths[i] / 2);
                vertices.Add(new TerrainLayerVertex(new Vector2(leftVertex.X, leftVertex.Z), new Vector2(0, accumulatedLengths[i])));
                vertices.Add(new TerrainLayerVertex(new Vector2(rightVertex.X, rightVertex.Z), new Vector2(1, accumulatedLengths[i])));

                // Grow AABB
                aabb.Grow(leftVertex);
                aabb.Grow(rightVertex);

                lastOrthonormal = orthonormal;

                // The flattened points list contains 2 points per line segment, which means that there
                // are duplicate intermediate points, which we skip.
                bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
                if (!isLastLineSegment)
                {
                    i++;
                }
            }

            Debug.Assert(vertices.Count == (numberOfLineSegments * 2 + 2));
            Debug.Assert(indices.Count == (numberOfLineSegments * 6));

            // The road is projected onto the terrain, therefore the computed y limits are not correct.
            // (unless the terrain was clamped to the road).
            aabb.Minimum.Y = 0;
            aabb.Maximum.Y = 0;

            // Convert to submesh.
            submesh = new Submesh
            {
                PrimitiveCount = indices.Count / 3,
                PrimitiveType  = PrimitiveType.TriangleList,
                VertexCount    = vertices.Count,
                VertexBuffer   = new VertexBuffer(graphicsDevice, TerrainLayerVertex.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly),
                IndexBuffer    = new IndexBuffer(graphicsDevice, IndexElementSize.ThirtyTwoBits, indices.Count, BufferUsage.WriteOnly)
            };
            submesh.VertexBuffer.SetData(vertices.ToArray());
            submesh.IndexBuffer.SetData(indices.ToArray());
        }
        public static void CreateMesh(GraphicsDevice graphicsDevice, Path3F path, float defaultWidth,
            int maxNumberOfIterations, float tolerance,
            out Submesh submesh, out Aabb aabb, out float roadLength)
        {
            if (graphicsDevice == null)
            throw new ArgumentNullException("graphicsDevice");
              if (path == null)
            throw new ArgumentNullException("path");

              // Compute list of line segments. (2 points per line segment!)
              var flattenedPoints = new List<Vector3F>();
              path.Flatten(flattenedPoints, maxNumberOfIterations, tolerance);

              // Abort if path is empty.
              int numberOfLineSegments = flattenedPoints.Count / 2;
              if (numberOfLineSegments <= 0)
              {
            submesh = null;
            aabb = new Aabb();
            roadLength = 0;
            return;
              }

              // Compute accumulated lengths. (One entry for each entry in flattenedPoints.)
              float[] accumulatedLengths = new float[flattenedPoints.Count];
              accumulatedLengths[0] = 0;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            Vector3F previous = flattenedPoints[i - 1];
            Vector3F current = flattenedPoints[i];
            float length = (current - previous).Length;

            accumulatedLengths[i] = accumulatedLengths[i - 1] + length;
            if (i + 1 < flattenedPoints.Count)
              accumulatedLengths[i + 1] = accumulatedLengths[i];
              }

              // Total road length.
              roadLength = accumulatedLengths[accumulatedLengths.Length - 1];

              // Create a mapping between accumulatedLength and the path key widths.
              // (accumulatedLength --> TerrainRoadPathKey.Width)
              var widthKeys = new List<Pair<float, float>>();
              {
            int index = 0;
            foreach (var key in path)
            {
              Vector3F position = key.Point;
              var roadKey = key as TerrainRoadPathKey;
              float width = (roadKey != null) ? roadKey.Width : defaultWidth;

              for (; index < flattenedPoints.Count; index++)
              {
            if (Vector3F.AreNumericallyEqual(position, flattenedPoints[index]))
            {
              widthKeys.Add(new Pair<float, float>(accumulatedLengths[index], width));
              break;
            }

            bool isLastLineSegment = (index + 2 == flattenedPoints.Count);
            if (!isLastLineSegment)
              index++;
              }

              index++;
            }
              }

              // Create a list of interpolated road widths. (One entry for each entry in flattenedPoints.)
              var widths = new float[flattenedPoints.Count];
              int previousKeyIndex = 0;
              var previousKey = widthKeys[0];
              var nextKey = widthKeys[1];
              widths[0] = widthKeys[0].Second;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            if (accumulatedLengths[i] > nextKey.First)
            {
              previousKey = nextKey;
              previousKeyIndex++;
              nextKey = widthKeys[previousKeyIndex + 1];
            }

            float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First);
            widths[i] = InterpolationHelper.Lerp(previousKey.Second, nextKey.Second, p);

            if (i + 1 < flattenedPoints.Count)
              widths[i + 1] = widths[i];
              }

              // Compute vertices and indices.
              var vertices = new List<TerrainLayerVertex>(numberOfLineSegments * 2 + 2);
              var indices = new List<int>(numberOfLineSegments * 6);  // Two triangles per line segment.
              Vector3F lastOrthonormal = Vector3F.UnitX;
              aabb = new Aabb(flattenedPoints[0], flattenedPoints[0]);
              bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]);
              for (int i = 0; i < flattenedPoints.Count; i++)
              {
            Vector3F start = flattenedPoints[i];

            Vector3F previous;
            bool isFirstPoint = (i == 0);
            if (!isFirstPoint)
              previous = flattenedPoints[i - 1];
            else if (isClosed && path.SmoothEnds)
              previous = flattenedPoints[flattenedPoints.Count - 2];
            else
              previous = start;

            Vector3F next;
            bool isLastPoint = (i + 1 == flattenedPoints.Count);
            if (!isLastPoint)
              next = flattenedPoints[i + 1];
            else if (isClosed && path.SmoothEnds)
              next = flattenedPoints[1];
            else
              next = start;

            Vector3F tangent = next - previous;

            Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
            if (!orthonormal.TryNormalize())
              orthonormal = lastOrthonormal;

            // Add indices to add two triangles between the current and the next vertices.
            if (!isLastPoint)
            {
              int baseIndex = vertices.Count;

              //  2      3
              //   x----x
              //   |\   |   ^
              //   | \  |   | road
              //   |  \ |   | direction
              //   |   \|   |
              //   x----x
              //  0      1

              indices.Add(baseIndex);
              indices.Add(baseIndex + 1);
              indices.Add(baseIndex + 2);

              indices.Add(baseIndex + 1);
              indices.Add(baseIndex + 3);
              indices.Add(baseIndex + 2);
            }

            // Add two vertices.
            Vector3F leftVertex = start - orthonormal * (widths[i] / 2);
            Vector3F rightVertex = start + orthonormal * (widths[i] / 2);
            vertices.Add(new TerrainLayerVertex(new Vector2(leftVertex.X, leftVertex.Z), new Vector2(0, accumulatedLengths[i])));
            vertices.Add(new TerrainLayerVertex(new Vector2(rightVertex.X, rightVertex.Z), new Vector2(1, accumulatedLengths[i])));

            // Grow AABB
            aabb.Grow(leftVertex);
            aabb.Grow(rightVertex);

            lastOrthonormal = orthonormal;

            // The flattened points list contains 2 points per line segment, which means that there
            // are duplicate intermediate points, which we skip.
            bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
            if (!isLastLineSegment)
              i++;
              }

              Debug.Assert(vertices.Count == (numberOfLineSegments * 2 + 2));
              Debug.Assert(indices.Count == (numberOfLineSegments * 6));

              // The road is projected onto the terrain, therefore the computed y limits are not correct.
              // (unless the terrain was clamped to the road).
              aabb.Minimum.Y = 0;
              aabb.Maximum.Y = 0;

              // Convert to submesh.
              submesh = new Submesh
              {
            PrimitiveCount = indices.Count / 3,
            PrimitiveType = PrimitiveType.TriangleList,
            VertexCount = vertices.Count,
            VertexBuffer = new VertexBuffer(graphicsDevice, TerrainLayerVertex.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly),
            IndexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.ThirtyTwoBits, indices.Count, BufferUsage.WriteOnly)
              };
              submesh.VertexBuffer.SetData(vertices.ToArray());
              submesh.IndexBuffer.SetData(indices.ToArray());
        }