Jump to content

Relief Map Generator


Recommended Posts

Here's a plugin I've been tinkering with for years. It makes simple maps at the touch of a few buttons.

 

RMG-Demo.jpg

 

If you want a specifically shaped map, you can use transparency to mask the land & sea.

 

RMG-Alpha-Demo.jpg

The code is not all my own. I'll ask for permission to post it.

 

Comments &/or critiques welcomed :)

 

ReliefMapGenerator.dll

  • Like 6
  • Upvote 1
  • You're a Smart Cookie! 1
Link to comment
Share on other sites

I just remembered @John Stewien's plugins are open source. Yay, because I lifted the ToAngle code from him. Here's the source

Spoiler


// Name: Relief Map Generator
// Submenu: Render
// Author: Scott Stringer aka Ego Eram Reputo
// Title: Relief Map Generator
// Version: 1.3
// Desc: Renders topographical landscapes
// Keywords: Map topographical topography land sea mountains 
// URL:
// Help:
#region UICode
IntSliderControl Amount1 = 250; // [1,1000,5] Scale
IntSliderControl Amount3 = 30; // [10,50,5] Detail
IntSliderControl Amount4 = 50; // [1,200,2] Elevation
IntSliderControl Amount5 = 30; // [1,100,2] Land Mass
IntSliderControl Mountains = 40; // [1,80,5] Mountains
IntSliderControl ToAngleStrength = 5; // [3,20,5] Smoothing
IntSliderControl Amount6 = 0; // [-180,180,2] Hue Adjustment
IntSliderControl Amount7 = 0; // [-100,100,3] Saturation Adjustment
IntSliderControl Amount8 = 0; // [-100,100,5] Lightness Adjustment
CheckboxControl grayScale = false; // Grayscale
CheckboxControl respectAlpha = true; // R-E-S-P-E-C-T Alpha (find out what it means to me)
ReseedButtonControl Amount9 = 0; // Reseed
#endregion

// Working surface
Surface wrk = null;

// Auxillary surface
Surface aux = null;

// Setup for Overlay blending op
private BinaryPixelOp overlayOp = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Overlay);
// Setup for Hue, Saturation & Lightness ops
private UnaryPixelOps.HueSaturationLightness saturationOp;

protected override void OnDispose(bool disposing)
{
    if (disposing)
    {
        // Release any surfaces or effects you've created
        wrk?.Dispose(); wrk = null;
        aux?.Dispose(); aux = null;
    }
    base.OnDispose(disposing);
}

// This single-threaded function is called after the UI changes and before the Render function is called
// The purpose is to prepare anything you'll need in the Render function
// Don't render to the dst surface in PreRender (BoltBait 18 Sept 2021)
void PreRender(Surface dst, Surface src)
{
    Rectangle selection = EnvironmentParameters.SelectionBounds; EnvironmentParameters.GetSelectionAsPdnRegion();

    saturationOp = new UnaryPixelOps.HueSaturationLightness(Amount6, Amount7+100, Amount8);
    try
    {
        r = new Random(Convert.ToInt32(Amount9));
        r1 = r.Next(1000, 10000);
        r2 = r.Next(100000, 1000000);
        r3 = r.Next(1000000000, 2000000000);
    }
    catch { }

    if (wrk == null)
    {
        wrk = new Surface(src.Size);
    }

    if (aux == null)
    {
        aux = new Surface(src.Size);
    }

    // Fill the aux surface with desaturated value noise (i.e. grayscale)
    for (int y = aux.Bounds.Top; y < aux.Bounds.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = aux.Bounds.Left; x < aux.Bounds.Right; x++)
        {
            int c = (int)fBm(x, y);  // get noise value from fractal brownian motion (fBm) routine
            int d = (int)(c / 3.6); // normalize into range from 0 to 255 i.e 256 colors.   
                                   //int d = (int)(278-(9* Math.Sqrt(900-c)));
            if (d < 0) d = 0;
            if (respectAlpha)
            {
                // use Alpha of source pixel to lower the height value.             
                ColorBgra SrcPixel = src[x, y];
                d *= SrcPixel.A;
                d /= 255;
            }
            aux[x, y] = ColorBgra.FromBgra((byte)(d), (byte)(d), (byte)(d), 0xff);
        }
    }

    // run ToAngle on the aux surface and store the result in wrk
    int radius = (int)(ToAngleStrength / (Amount3*0.09));
    int[] xdx = new int[2];
    int[] ydy = new int[2];

    for (int y = aux.Bounds.Top; y < aux.Bounds.Bottom; y++)
    {
        for (int x = aux.Bounds.Left ; x < aux.Bounds.Right; x++)
        {
            ColorBgra CurrentPixel = aux[x, y];

            if (CurrentPixel.B < 81-Mountains+40) // don't render mountains below this level (default = sea level). Could have used any of BGR as value is grayscale.
            {
                wrk[x, y] = ColorBgra.FromBgra(0,0,0,0);
            }
            else{
                float xdiffR = 0f;
                float xdiffG = 0f;
                float xdiffB = 0f;
                float ydiffR = 0f;
                float ydiffG = 0f;
                float ydiffB = 0f;

                for (int dx = 0; dx <= radius; ++dx)
                {
                    xdx[0] = x - dx;
                    if (xdx[0] < aux.Bounds.Left)
                    {
                        xdx[0] = aux.Bounds.Left + aux.Bounds.Left - xdx[0];
                    }
                    xdx[1] = x + dx;

                    if (xdx[1] >= aux.Bounds.Right)
                    {
                        xdx[1] = aux.Bounds.Right - 2 - xdx[1] + aux.Bounds.Right;
                    }
                    for (int dy = 0; dy <= radius; ++dy)
                    {
                        ydy[0] = y - dy;
                        if (ydy[0] < aux.Bounds.Top)
                        {
                            ydy[0] = aux.Bounds.Top + aux.Bounds.Top - ydy[0];
                        }
                        ydy[1] = y + dy;
                        if (ydy[1] >= aux.Bounds.Bottom)
                        {
                            ydy[1] = aux.Bounds.Bottom - 2 - ydy[1] + aux.Bounds.Bottom;
                        }
                        xdiffR += aux[xdx[0], ydy[0]].R;
                        xdiffR -= aux[xdx[1], ydy[0]].R;
                        xdiffG += aux[xdx[0], ydy[0]].G;
                        xdiffG -= aux[xdx[1], ydy[0]].G;
                        xdiffB += aux[xdx[0], ydy[0]].B;
                        xdiffB -= aux[xdx[1], ydy[0]].B;

                        if (dy != 0)
                        {
                            xdiffR += aux[xdx[0], ydy[1]].R;
                            xdiffR -= aux[xdx[1], ydy[1]].R;
                            xdiffG += aux[xdx[0], ydy[1]].G;
                            xdiffG -= aux[xdx[1], ydy[1]].G;
                            xdiffB += aux[xdx[0], ydy[1]].B;
                            xdiffB -= aux[xdx[1], ydy[1]].B;
                        }
                        ydiffR += aux[xdx[0], ydy[0]].R;
                        ydiffR -= aux[xdx[0], ydy[1]].R;
                        ydiffG += aux[xdx[0], ydy[0]].G;
                        ydiffG -= aux[xdx[0], ydy[1]].G;
                        ydiffB += aux[xdx[0], ydy[0]].B;
                        ydiffB -= aux[xdx[0], ydy[1]].B;

                        if (dx != 0)
                        {
                            ydiffR += aux[xdx[1], ydy[0]].R;
                            ydiffR -= aux[xdx[1], ydy[1]].R;
                            ydiffG += aux[xdx[1], ydy[0]].G;
                            ydiffG -= aux[xdx[1], ydy[1]].G;
                            ydiffB += aux[xdx[1], ydy[0]].B;
                            ydiffB -= aux[xdx[1], ydy[1]].B;
                        }
                    }
                }

                CurrentPixel.R = (byte)(255 * Math.Abs(Math.Atan2(ydiffR, xdiffR) / Math.PI));
                CurrentPixel.G = (byte)(255 * Math.Abs(Math.Atan2(ydiffG, xdiffG) / Math.PI));
                CurrentPixel.B = (byte)(255 * Math.Abs(Math.Atan2(ydiffB, xdiffB) / Math.PI));
                CurrentPixel.A = (byte)(aux[x,y].G-(81-Mountains)*1.5);
                wrk[x, y] = CurrentPixel;
            }
        }
    }


    // Recolor the aux surface using the grayscale value as an index into the palette.
    if (!grayScale){
    int d2=0;
    for (int y = aux.Bounds.Top; y < aux.Bounds.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = aux.Bounds.Left; x < aux.Bounds.Right; x++)
        {
            ColorBgra CurrentPixel = aux[x,y];
            d2 = CurrentPixel.R;
            d2 *= 3;  // byte offset in palette array = ElevationPalette 
                aux[x, y] = saturationOp.Apply(ColorBgra.FromBgra(ElevationPalette[d2], ElevationPalette[++d2], ElevationPalette[++d2], 0xff));
        }
    }
}
}

// Here is the main multi-threaded render function
// The dst canvas is broken up into rectangles and
// your job is to write to each pixel of that rectangle
void Render(Surface dst, Surface src, Rectangle rect)
{
    // wrk has the topography in grayscale & alpha
    // aux has coloured landscape
    // Overlay Blend the wrk surface and the aux surface to the dst surface
    overlayOp.Apply(dst, aux ,wrk, rect);

    // If you want to see the individual layers, uncomment the following
    //for (int y = rect.Top; y < rect.Bottom; y++)
    //{
    //    if (IsCancelRequested) return;
    //    for (int x = rect.Left; x < rect.Right; x++)
    //    {
    //        ColorBgra SrcPixel = src[x, y];
    //        ColorBgra WrkPixel = wrk[x, y];
    //        ColorBgra AuxPixel = aux[x, y];
    //        ColorBgra DstPixel = dst[x, y];
    //
    //        dst[x, y] = CurrentPixel;
    //    }
    //}
}

// fBm = Fractal Brownian motion - a triple Value Noise routine.
double fBm(int x, int y)
{
    int HeightScale = 900; // was 1024;
    double k = (PerlinNoise2d(x, y) * HeightScale) * 12 / 15;
    k += (PerlinNoise2d(x + Amount9, y + Amount9) * HeightScale) * 2 / 15; // adds a bit more Noise, with less weighting.
    k += (int)(PerlinNoise2d(x + Amount9 / 4, y + Amount9 / 4) * HeightScale) / 15;  // adds a tiny bit more, with even less weighting.
    return k;
}

static Random r = new Random();
int r1 = r.Next(1000, 10000);
int r2 = r.Next(100000, 1000000);
int r3 = r.Next(1000000000, 2000000000);

double PerlinNoise2d(int x, int y)
{
    double total = 0.0;
    double frequency = 0.075f / Math.Sqrt(Amount1);     // was .015 smaller values = larger landmass 
    double persistence = (32 + Amount3 / 2) / 100.0;   // was .65 aka smoothness smaller values = finer detail
    double octaves = 8;           // was  8;         // USER ADJUSTABLE
    double amplitude = Amount4 / 60.0;         // was 1 use larger values to eliminate mountain 'rings'
    double cloudCoverage = -2.0 + (Math.Sqrt(Math.Sqrt(Amount5)));     // was 0 aka amount of land vs sea
    double cloudDensity = 0.6666666; //(Amount5 / 30.0) - 1;      // was 1 aka feature density

    for (int lcv = 0; lcv < octaves; lcv++)
    {
        total += Smooth(x * frequency, y * frequency) * amplitude;
        frequency *= 2;
        amplitude *= persistence;
    }

    total += cloudCoverage;
    total *= cloudDensity;

    if (total > 1) total = 1 - (total - 1); // creating a downward slope from highest points
    total = (total < 0) ? 0 : total;

    return total;
}

double Smooth(double x, double y)
{
    double n1 = Noise((int)x, (int)y);
    double n2 = Noise((int)x + 1, (int)y);
    double n3 = Noise((int)x, (int)y + 1);
    double n4 = Noise((int)x + 1, (int)y + 1);

    double i1 = Interpolate(n1, n2, x - (int)x, 1);
    double i2 = Interpolate(n3, n4, x - (int)x, 1);

    return Interpolate(i1, i2, y - (int)y, 1);
}

double Noise(int x, int y)
{
    int n = x + y * 57; //smoothfactor;
    n = (n << 13) ^ (n-3); // was ^ n

    return (1.0 - ((n * (n * n * r1 + r2) + r3) & 0x6fffffff) / 1073741824.0);
    // was 0x7fffffff
}


double Interpolate(double x, double y, double a, int iType)
{
    double iVal, val;

    switch (iType)
    {
        case 0: // Linear_Interpolate
            iVal = x * (1 - a) + y * a;
            break;

        case 1:     // Cosine_Interpolate
            val = (1 - Math.Cos(a * Math.PI)) * .5;
            iVal = x * (1 - val) + y * val;
            break;

        default:
            iVal = 0;
            break;
    }
    return iVal;
}


// Array for colorization = 32x BGR triplets
byte[] ElevationPalette = {
    //  Sub-tropical coloring
    // deep blue = sea
    // source:  2022 Elevation palette.pdn & 2022 Elevation palette.csv
214,089,049,
214,091,049,
214,096,051,
215,100,054,
215,103,056,
216,109,060,
217,113,061,
217,117,064,
218,123,066,
219,127,069,
219,131,071,
219,136,073,
219,140,074,
220,142,077,
220,147,080,
221,153,082,
222,157,085,
222,161,086,
222,166,089,
224,170,092,
224,174,095,
224,179,097,
225,182,099,
225,185,100,
226,189,102,
154,168,079,
020,079,026,
020,079,026,
020,079,026,
021,080,026,
021,080,026,
020,081,026,
020,081,026,
021,082,027,
021,082,027,
021,082,027,
021,083,027,
021,083,027,
021,085,027,
021,085,027,
021,086,027,
021,086,027,
021,086,027,
021,088,028,
022,089,028,
021,090,028,
021,090,028,
021,090,028,
021,090,028,
021,090,028,
022,092,028,
022,092,028,
022,094,029,
022,094,029,
022,094,029,
022,094,029,
022,094,029,
022,095,031,
022,095,031,
023,096,032,
023,096,032,
023,098,032,
022,100,033,
022,100,033,
022,100,033,
022,100,033,
023,101,034,
023,102,036,
023,102,036,
023,102,036,
023,102,038,
024,104,039,
024,104,039,
024,105,040,
024,106,041,
024,107,041,
024,109,043,
024,109,043,
024,109,043,
025,109,045,
024,110,046,
024,111,047,
024,111,047,
024,111,047,
025,112,047,
025,113,047,
025,113,047,
025,113,048,
025,113,048,
025,114,049,
025,114,050,
025,114,050,
025,114,050,
025,114,050,
025,114,050,
025,114,050,
025,114,050,
025,114,050,
026,116,051,
026,117,052,
026,118,053,
026,118,053,
026,118,053,
026,118,053,
026,118,053,
026,118,053,
026,118,053,
026,119,055,
026,120,056,
026,121,057,
026,121,057,
027,121,058,
026,121,060,
026,123,060,
026,123,060,
026,123,060,
027,123,063,
027,123,066,
028,126,068,
028,126,068,
027,125,070,
028,127,073,
028,128,074,
028,130,076,
028,130,076,
028,129,080,
028,130,084,
029,132,086,
029,132,088,
029,132,088,
029,132,090,
030,133,094,
030,135,097,
030,135,097,
030,135,099,
030,135,100,
031,137,104,
030,137,105,
030,137,105,
031,139,109,
032,140,113,
033,140,113,
033,140,114,
035,143,116,
042,149,122,
047,153,126,
050,155,129,
050,155,129,
055,159,132,
062,165,139,
065,167,140,
068,168,142,
068,168,142,
076,173,148,
084,178,153,
088,181,156,
088,181,156,
091,182,157,
100,187,164,
107,193,168,
110,194,170,
105,211,200,
113,214,209,
122,214,218,
126,213,221,
127,211,225,
124,209,224,
123,206,223,
121,205,223,
119,203,223,
117,201,224,
115,199,222,
114,197,223,
113,194,222,
112,192,222,
109,190,221,
107,188,220,
104,186,219,
105,186,220,
102,181,220,
100,180,219,
100,178,219,
098,177,220,
095,173,219,
094,173,219,
092,169,219,
091,167,219,
090,164,218,
089,162,215,
088,158,212,
089,155,212,
089,153,209,
088,152,206,
088,148,206,
089,144,204,
087,141,200,
087,139,198,
088,138,196,
086,133,194,
087,130,193,
086,126,192,
086,125,189,
086,121,189,
086,118,186,
086,117,186,
086,113,182,
086,111,180,
086,109,178,
086,106,177,
092,108,173,
099,112,170,
106,115,167,
113,116,166,
120,121,162,
129,126,158,
135,129,154,
141,131,153,
149,132,149,
153,135,145,
159,138,143,
163,142,148,
169,151,155,
173,155,159,
179,162,166,
184,168,172,
188,174,179,
192,179,185,
198,186,190,
202,192,194,
206,198,200,
212,205,206,
216,209,211,
221,216,217,
227,223,224,
231,230,230,
235,235,235,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
237,237,237,
230,230,230,
219,219,219,
220,220,220,
224,224,224,
229,229,229,
233,233,233,
234,234,234,
234,234,234,
233,233,233,
233,233,233,
233,233,233,
};

 

Link to comment
Share on other sites

This is really cool @Ego Eram Reputo.  Mine doesn't look quite like yours, but....welcome to Lynx Island.  😄

 

RMG_lynxisland.png

  • Like 3
  • Haha 1
Link to comment
Share on other sites

👍

 

To get a more organic coastline, use dents at a largish scale to distort the mask outline, then run Gaussian blur to smooth it out.

  • Like 1
Link to comment
Share on other sites

@Ego Eram Reputo  Don't mean to sound dumb here but I'm having a brunette moment.  😆  I haven't used an alpha mask in such a long time, I forget.

 

Could you please outline some steps to achieve your beautiful coastline?  Then I'll  probably say 'aha'.  Thank you!

Link to comment
Share on other sites

Okay EER, I 'aha'd' myself.  I was making it way more difficult than it needed to be.

I figured it out with your tips, from 19 hours ago, and have a nice organic coastline with a smoother transition from the light blue to the darker blue ocean.

Thanks!  😂

Link to comment
Share on other sites

For those wondering about this bit....

 

On 9/12/2022 at 10:02 AM, Ego Eram Reputo said:

To get a more organic coastline, use dents at a largish scale to distort the mask outline, then run Gaussian blur to smooth it out.

 

....if you're using a shape as a map-mask, soften the edge to get a more organic coastline. Otherwise the falloff from land to sea will be too abrupt. In the images below, notice how the hard Eastern edge of the shape produces a cookie-cutter coast. The softened Western side of the shape produces a more meandering coastline with a much more believable slope from the highlands down to the sea (and also light blue shallows).

 

Edge-demo1.png >>> Edge-demo2.png

The deeper the alpha blurring is, the gentler the taper from land to sea. See?

 

  • Like 3
Link to comment
Share on other sites

This would be a fun plug-in to generate old world map like The Lord of the Rings or Avatar: The Last Airbender.

  • Like 1

knightsig.png

Paint.NET Gallery | Remove Foreground Object Tutorial | Dispersion Effect Tutorial

”An artist should never be a prisoner of himself, prisoner of style, prisoner of reputation, prisoner of success.” ~ Henri Matisee

Link to comment
Share on other sites

16 hours ago, TrevorOutlaw said:

This would be a fun plug-in to generate old world map like The Lord of the Rings or Avatar: The Last Airbender.

 

Run the plugin with a Saturation of -75 and increase the Lightness to 15. Then run the Sepia Adjustment & you're half way there!

 

 

Old-world.jpg

With a little tweaking, https://postimg.cc/hXnL94F4

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...