Friday, December 9, 2016

On the Road to Splines 4

Yesterday I posted about the spline progress.  I made only a little progress coding tonight, as I spent much of the evening out enjoying the Christmas lights.  Still, I have made progress towards making the mountains work right.  Elevation is computed by calculating distance from the center line of the spline outwards, and using simple linear interpolation between base elevation and elevation at the ridge line.

Mountain spline is now colored to represent elevation.  Note lighter color along ridge line.

This is equivalent to the absolute simplest form of mountain generation by Soon Tee Teoh in his 2008 paper "River and Coastal Action in Automatic Terrain Generation."  All points along the ridge line are of the same height.  The rest of Teoh's ideas should be easy enough to incorporate, as should some of my own ideas.

However, much of that will have to wait.  This application increasingly needs refactoring.  For example, I think it needs to incorporate some limited level-of-detail (LOD) capability.  At minimum, it needs a symbolic cartographic representation for when viewed zoomed out, and a detailed geometric representation when zoomed in.  Also, there is that whole laundry list of items from my earlier post.  Those also need to be addressed.

Beyond that, though, I have that town analysis I mentioned in a post yesterday that needs to continue. I'm putting together a small database to let me record detailed data collected from digital Sanborn maps.  I'm interested in generating terrain, but I'd also like to be able to generate believable towns upon those terrains, as well.

For those curious about the underlying algorithm, I present the C# code snippet below.  A segment of mountain is rendered to a height map using the code.  This is the initial version of the code used for the mountain in the screenshot above.  A mountain is defined by a spline that represents the ridge line of the mountain.  The spline is segmented into individual line segments.  Height declines linearly based upon a point's distance from the ridge line segment.

private void RenderLineSegment(Vector3 start, Vector3 end, float height1, float height2, 
    float slope, int offsetX, int offsetY, float[,] heights)
{
    // Calculate the maximum distance the mountain may extend from 
    // the ridge line.
    float maxExtent = (1 / slope) * Math.Max(height1, height2);
    float maxExtentSq = maxExtent * maxExtent;

    // Calculate the dimensions of this segment of mountain.
    Vector3 min = Vector3.Min(start, end) - 
        new Vector3(maxExtent, maxExtent, 0);
    Vector3 max = Vector3.Max(start, end) + 
        new Vector3(maxExtent, maxExtent, 0);

    // Calculate segment length, and difference in height between 
    // start and end points.
    LineSegment seg = new LineSegment(start, end);
    float segLength = Vector3.Distance(start, end);
    float deltaH = height2 - height1;

    // Render the height (elevation) data.
    for (float y = min.Y; y < max.Y; y++)
    {
        for (float x = min.X; x < max.X; x++)
        {
            Vector3 curr = new Vector3(x, y, 0);
            Vector3 nearest = seg.NearestPointOnSegment(curr);
            float distSq = Vector3.DistanceSquared(curr, nearest);
            if (distSq <= maxExtentSq)
            {
                // linear interpolate height at point on segment.
                float ndist = Vector3.Distance(nearest, start);
                float nPercent = ndist / segLength;
                float h = height1 + (deltaH * nPercent);

                // linear interpolate height at current point.
                float extent = (1 / slope) * h;
                float currPercent = 1 - ((float)Math.Sqrt(distSq) / extent);
                float currH = currPercent * h;

                // set elevation only if greater than elevation already 
                // present at point.
                int gridX = (int)x + offsetX;
                int gridY = (int)y + offsetY;
                heights[gridX, gridY] = Math.Max(heights[gridX, gridY], currH);
            }                  
        }
    }
}

No comments:

Post a Comment