About Monkey 2 › Forums › Monkey 2 Projects › Miscellaneous Procedural Generation Code
This topic contains 18 replies, has 5 voices, and was last updated by
codifies
2 years, 2 months ago.
-
AuthorPosts
-
January 23, 2017 at 2:42 am #6743
Perlin noise
(See attachment for runnable example)
[/crayon]Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788[crayon-5cb9d7a859902782035018 inline="true" ]'http://devmag.org.za/2009/04/25/perlin-noise/Function GeneratePerlinMap(array:Double[,], octaveCount:Int)If Not array Then ReturnLocal Interpolate := Lambda:Double(val0:Double, val1:Double, alpha:Double)Return val0 * (1.0 - alpha) + alpha * val1EndLocal width := array.GetSize(0)Local height := array.GetSize(1)Local baseNoise := New Double[width, height]For Local x := 0 Until widthFor Local y := 0 Until heightbaseNoise[x, y] = Rnd(0.0, 1.0)NextNextLocal persistance:Double = 0.7Local smoothNoise := New Double[octaveCount, width, height]For Local i:= 0 Until octaveCountFor Local x := 0 Until widthFor Local y := 0 Until heightsmoothNoise[i, x, y] = 0.0NextNextNextFor Local octave:=0 Until octaveCountLocal samplePeriod:Int = 1 Shl octaveLocal sampleFrequency:Double = 1.0 / samplePeriodFor Local x:= 0 Until widthLocal sampleX0:Int = (x / samplePeriod) * samplePeriodLocal sampleX1:Int = (sampleX0 + samplePeriod) Mod Double(width)Local horizontalBlend:Double = (Double(x) - sampleX0) * sampleFrequencyFor Local y:= 0 Until heightLocal sampleY0:Int = (y / samplePeriod) * samplePeriodLocal sampleY1:Int = (sampleY0 + samplePeriod) Mod Double(height)Local verticalBlend:Double = (Double(y) - sampleY0) * sampleFrequencyLocal top:Double = Interpolate(baseNoise[sampleX0, sampleY0], baseNoise[sampleX1, sampleY0], horizontalBlend)Local bottom:Double = Interpolate(baseNoise[sampleX0, sampleY1], baseNoise[sampleX1, sampleY1], horizontalBlend)smoothNoise[octave, x, y] = Interpolate(top, bottom, verticalBlend)NextNextNextFor Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] = 0.0NextNextLocal amplitude:Double = 1.0Local totalAmplitude:Double = 0.0For Local octave := (octaveCount - 1) To 0 Step -1amplitude *= persistancetotalAmplitude += amplitudeFor Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] += smoothNoise[octave, x, y] * amplitudeNextNextNextFor Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] /= totalAmplitudeNextNextEndAttachments:
January 23, 2017 at 5:51 am #6745Hill algorithm:
(See attachment for runnable example)
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778[crayon-5cb9d7a860638662215239 inline="true" ]Function GenerateHillMap:void(array:Double[,], numHills:UInt, maxHillRadius:Double, island:Bool = false)'Generate a heightmap in a 2d array using the "hill" algorithm.'numHills: number of "hills". Recommended range: 250 to 2000.'maxHillRadiussize: maximum hill radius. For best results, value should be significantly'less than the map's shortest dimension.'island: create an island-like result. True/FalseIf Not array Then ReturnLocal width := array.GetSize(0)Local height := array.GetSize(1)For Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] = 0.0NextNextIf island Then maxHillRadius *= 0.5For Local n := 1 To numHillsLocal radius :Double = Floor(Rnd(0, maxHillRadius))Local centrex:Double = Floor(Rnd(0, width))Local centrey:Double = Floor(Rnd(0, height))If islandLocal theta := Rnd(0.0, 359.0)Local distx := Floor(Rnd(0, width * 0.5 - radius))Local disty := Floor(Rnd(0, height * 0.5 - radius))centrex = width * 0.5 + (Cos(theta) * distx)centrey = height * 0.5 + (Sin(theta) * disty)EndifLocal startx := centrex - radiusIf startx < 0 Then startx = 0Local endx := centrex + radiusIf endx > width Then endx = widthLocal starty := centrey - radiusIf starty < 0 Then starty = 0Local endy := centrey + radiusIf endy > height Then endy = heightFor Local x := startx Until endxFor Local y := starty Until endyLocal h:Double = (radius * radius) - (((x - centrex) * (x - centrex)) + ((y - centrey) * (y - centrey)))If (h > 0.0) Then array[x, y] += hNextNextNext'''NormaliseLocal minv := array[0, 0]Local maxv := array[0, 0]For Local x := 0 Until widthFor Local y:= 0 Until heightIf (array[x, y] < minv)minv = array[x, y]ElseIf (array[x, y] > maxv) Then maxv = array[x, y]EndifNextNextFor Local x := 0 Until widthFor Local y:= 0 Until heightarray[x, y] = (array[x, y] - minv) / (maxv - minv)NextNext'''EndAttachments:
January 23, 2017 at 6:04 am #6746Midpoint Displacement
(See attachment for runnable example)
[/crayon]Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394[crayon-5cb9d7a8669a4208564158 inline="true" ]Function GenerateMPDMap:Void(array:Double[,], grain:Float)'Generate a heightmap in a 2d array using the "midpoint displacement" algorithm.'grain: graininess. Suggested range 0.1 to 2.0If Not array Then ReturnLocal width := array.GetSize(0)Local height := array.GetSize(1)For Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] = -1.0NextNextarray[0, 0] = Rnd(0.0, 1.0)array[width - 1, 0] = Rnd(0.0, 1.0)array[0, height - 1] = Rnd(0.0, 1.0)array[width - 1, height - 1] = Rnd(0.0, 1.0)recurseMPD(array, 0, 0, width - 1, height - 1, grain, 1.0)'''NormaliseLocal minv := array[0, 0]Local maxv := array[0, 0]For Local x := 0 Until widthFor Local y:= 0 Until heightIf (array[x, y] < minv)minv = array[x, y]ElseIf (array[x, y] > maxv) Then maxv = array[x, y]EndifNextNextFor Local x := 0 Until widthFor Local y:= 0 Until heightarray[x, y] = (array[x, y] - minv) / (maxv - minv)NextNext'''EndFunction recurseMPD:Void(array:Double[,], x0:Int, y0:Int, x2:Int, y2:Int, grain:Double, level:Double)'Recursive function used by the GenerateMPDMap function.Local MPD := Lambda:Double(x0:Int, y0:Int, x1:Int, y1:Int, x2:Int, y2:Int)Local r:Double = Rnd(-grain, grain) / levelr += ((array[x0, y0] + array[x2, y2]) / 2.0)r = Clamp(r, Cast<Double>(0.0), Cast<Double>(1.0))array[x1, y1] = rReturn rEndIf (Not(((x2 - x0) < 2) And ((y2 - y0) < 2)))Local v:Double, i:Doublelevel = 2.0 * levelLocal x1:Int = Ceil((x0 + x2) / 2.0)Local y1:Int = Ceil((y0 + y2) / 2.0)v = array[x1, y0]If (v = -1.0) Then v = MPD(x0, y0, x1, y0, x2, y0)i = vv = array[x2, y1]If (v = -1.0) Then v = MPD(x2, y0, x2, y1, x2, y2)i += vv = array[x1, y2]If (v = -1.0) Then v = MPD(x0, y2, x1, y2, x2, y2)i += vv = array[x0, y1]If (v = -1.0) Then v = MPD(x0, y0, x0, y1, x0, y2)i += vIf (array[x1, y1] = -1.0) Then array[x1, y1] = i/4.0recurseMPD(array, x0, y0, x1, y1, grain, level)recurseMPD(array, x1, y0, x2, y1, grain, level)recurseMPD(array, x1, y1, x2, y2, grain, level)recurseMPD(array, x0, y1, x1, y2, grain, level)EndifEndAttachments:
January 23, 2017 at 7:57 am #6747Any chance of a short description on how they work? (Couple of lines maybe?)
My current method for making heightmaps is like this :
Draw randomly sized rectangles on top of each other. When drawn ontop add the value together. At a certain high value stop the procedure and you are done.
The result looks pretty much like a heightmap. Is pretty easy to code too.
January 23, 2017 at 10:18 am #6749very nice, I especially like the hill method, but notice sometimes a definite plus sign shape in them sometimes.
I assume you realise that seeding with millisecs will often have almost identical results (its millisecs from app start)
but nice work.
January 23, 2017 at 10:19 am #6751Here are some links that explain, in depth, the algorithms that the above code is inspired/based on:
Perlin
http://devmag.org.za/2009/04/25/perlin-noise/Hill algorithm.
http://www.stuffwithstuff.com/robot-frog/3d/hills/hill.htmlMidpoint displacement (aka Diamond Square algorithm)
https://en.wikipedia.org/wiki/Diamond-square_algorithmYour technique sounds similar to the “hill algorithm”, except you use rectangles instead of circles/ovals. I’d be interested in seeing your code!
January 23, 2017 at 10:25 am #6752Yes, if you seed the RNG with the same number you should see the same results regardless of platform, since Monkey 2’s RNG algorithm is platform agnostic.
EDIT. Sorry, I misunderstood your statement. Yes, I realise Millisecs and Microsecs are derived from the application start time so the results are likely to be the same…
January 23, 2017 at 11:03 am #6753don’t suppose you can improve on this?
[/crayon]Monkey1234567[crayon-5cb9d7a87758a928267105 inline="true" ]' boiler plate just to ensure random is different each runlocal tv:timevalgettimeofday( varptr(tv) )local t:long = tv.tv_sec * 1000 + tv.tv_usecSeedRnd(t)January 23, 2017 at 6:26 pm #6756This looks good.
January 24, 2017 at 12:07 am #6763That’s good enough, no improvement necessary.
For simple proof of concept code I usually don’t bother seeding with a unique number per run but I should. So I’m going to incorporate your code snippet, though slightly simplified.
Another way to do it, if you’re not generating something right at the start, is to seed with Microsecs at the point of first user interaction. That’s what I do for my game projects and simulations.
January 24, 2017 at 12:40 am #6767Particle deposition:
(See attachment for runnable example)
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131[crayon-5cb9d7a87de44801340969 inline="true" ]Function GenerateParticleMap:Void(array:Double[,], particles:Int, clusters:Int, sticky:Bool = True, amts:Int[] = Null)'Generate a heightmap in a 2d array using the "particle" algorithms.'particles: number of particles to calculate per cluster. Minimum 1.'clusters: number of clusters: higher number gives greater map density. Minimum 1.'sticky: algorithm variation: True = sticky, False = rolling.'amts: particle variation. Should be an integer array of 4 values. Suggested range -5 to 5.If Not array Then ReturnLocal width := array.GetSize(0)Local height := array.GetSize(1)For Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] = 0.0NextNextIf amts = Null Or amts.Length < 4 Then amts = New Int[](-1, 1, -1, 1)Local disp:Float = 1.0If sticky 'Sticky styleFor Local m := 1 To clustersLocal x := Floor(Rnd(0, width))Local y := Floor(Rnd(0, height))For Local n := 1 To particlesIf (((x < width) And (y < height) And (x >= 0) And (y >= 0)))array[x, y] += dispSelect Floor(Rnd(0, amts.Length))Case 0x += amts[0]Case 1x += amts[1]Case 2y += amts[2]Case 3y += amts[3]EndEndifNextNextElse 'Rolling styleFor Local m := 1 To clustersLocal x := Floor(Rnd(0, width))Local y := Floor(Rnd(0, height))For Local n := 1 To particlesIf (((x < width) And (y < height) And (x >= 0) And (y >= 0)))Local h := array[x, y] + disparray[x, y] = hLocal startx := x - 1If (startx < 0) Then startx = 0Local endx := x + 1If (endx > width) Then endx = widthLocal starty := y - 1If (starty < 0) Then starty = 0Local endy := y + 1If (endy > height) Then endy = heightFor Local x2 := startx Until endxFor Local y2 := starty Until endyLocal h2 := array[x2, y2]If (h2 < h) Then array[x2, y2] += dispNextNextEndifSelect Floor(Rnd(0, amts.Length))Case 0x += amts[0]Case 1x += amts[1]Case 2y += amts[2]Case 3y += amts[3]EndNextNextEndif'''NormaliseLocal minv := array[0, 0]Local maxv := array[0, 0]For Local x := 0 Until widthFor Local y:= 0 Until heightIf (array[x, y] < minv)minv = array[x, y]ElseIf (array[x, y] > maxv) Then maxv = array[x, y]EndifNextNextFor Local x := 0 Until widthFor Local y:= 0 Until heightarray[x, y] = (array[x, y] - minv) / (maxv - minv)NextNext'''EndAttachments:
January 24, 2017 at 12:42 am #6769Fault Algorithm:
(See attachment for runnable example)
[/crayon]Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061[crayon-5cb9d7a885976262291933 inline="true" ]'http://www.lighthouse3d.com/opengl/terrain/index.php?faultFunction GenerateFaultMap:Void(array:Double[,], realism:Float)'Generate a heightmap in a 2d array using the "fault line" algorithm.'realism: number of fault lines: higher number (> 500) gives greater realism. Minimum 1.'NOTE: This is a comparatively slow algorithm.If Not array Then ReturnLocal width := array.GetSize(0)Local height := array.GetSize(1)For Local x := 0 Until widthFor Local y := 0 Until heightarray[x, y] = 0.0NextNextLocal d := Sqrt((width * width) + (height * height))Local disp:Double = 10.0For Local n := 1 To realismLocal v := Rnd(0, 359)Local a := Sin((v * (Pi / 180.0)))Local b := Cos((v * (Pi / 180.0)))Local c := (Rnd(0, 1) * d) - (d / 2)For Local x := 0 Until widthFor Local y := 0 Until heightIf (((a * x) + (b * y) - c) > 0.0)array[x, y] += dispElsearray[x, y] -= dispEndifNextNextNext'''NormaliseLocal minv := array[0, 0]Local maxv := array[0, 0]For Local x := 0 Until widthFor Local y:= 0 Until heightIf (array[x, y] < minv)minv = array[x, y]ElseIf (array[x, y] > maxv) Then maxv = array[x, y]EndifNextNextFor Local x := 0 Until widthFor Local y:= 0 Until heightarray[x, y] = (array[x, y] - minv) / (maxv - minv)NextNext'''EndAttachments:
January 24, 2017 at 12:46 am #6770Relevant theory links for the previous two examples…
Particle Deposition:
Generating heightmaps using particle depositionFault Algorithm:
http://www.lighthouse3d.com/opengl/terrain/index.php?faultJanuary 24, 2017 at 3:22 am #6772Very nice!
One note: looking at these walls of code I have a question – is there way to make code box smaller? And I looked into “code” popup and found “Height” checkbox, that disabled by default.
I didn’t try it yet but it seems to be useful for big code blocks.
January 24, 2017 at 9:04 am #6788 -
AuthorPosts
You must be logged in to reply to this topic.