Perlin Noise Class
Monkey Forums/Monkey Code/Perlin Noise Class
| ||
basic perlin class
'==========================================
' CLASS
' BASIC PERLIN CLASS WITHOUT BITMAP
'==========================================
Class TPerlinNoiseBasic
Field sizeX = 640
Field sizeY = 480
'first random noise table
Field noiseArr:Float[]
'output table
Field terrainArr:Float[]
'frequency, the lower the larger terrains of same level
Field frequency:Float = 1.0
'starting amplitude
Field amplitude:Float = 1.0
'change of amplitude of next octave
Field persistance:Float = 0.6
'number of octaves
Field octaves = 8
'=========================================
' just make some data, using fake 2D arrays
'===========================================
Method New(_x, _y)
Self.sizeX = _x
Self.sizeY = _y
Self.noiseArr = new float[_x * _y]
Self.terrainArr = new float[_x * _y]
End
'==========================================
' to init perlin noise parameters
'==========================================
Method ChangeParams(fre:Float, amp:Float, pers:Float, oct)
frequency = fre
amplitude = amp
persistance = pers
octaves = oct
End
'==========================================
' single field noise generation
'=========================================
Method GetRandomNoise:Float(x:Float, y:Float)
Local fre:Float = frequency
Local amp:Float = amplitude
Local finalValue:Float = 0.0
For Local i = 0 To octaves
finalValue = finalValue + LinearFilterNoise(x * fre, y * fre) * amp
fre = fre * 2.0
amp = amp * persistance
Next
If(finalValue < - 1.0) finalValue = -1.0
If(finalValue > 1.0) finalValue = 1.0
finalValue = finalValue * 0.5 + 0.5
Return finalValue
End
'==========================================
' create output terrain array
'==========================================
Method MakeTerrainMap()
For Local x = 0 To sizeX - 1
For Local y = 0 To sizeY - 1
terrainArr[x + y * sizeX] = GetRandomNoise(x, y)
Next
Next
End
'============================================
' perlin noise with linear interpolation
'===========================================
Method LinearFilterNoise:Float(x:Float, y:Float)
Local fractionX:Float = x - Int(x)
Local fractionY:Float = y - Int(y)
Local x1 = (Int(x) + sizeX) Mod sizeX
Local y1 = (Int(y) + sizeY) Mod sizeY
Local x2 = (Int(x) + sizeX - 1) Mod sizeX
Local y2 = (Int(y) + sizeY - 1) Mod sizeY
If(x1 < 0) x1 = x1 + sizeX
If(x2 < 0) x2 = x2 + sizeX
If(y1 < 0) y1 = y1 + sizeY
If(y2 < 0) y2 = y2 + sizeY
Local finVal:Float = 0
finVal = finVal + fractionX * fractionY * noiseArr[x1 + y1*sizeX ]
finVal = finVal + fractionX * (1 - fractionY) * noiseArr[x1 + y2*sizeX]
finVal = finVal + (1 - fractionX) * fractionY * noiseArr[x2 + y1 * sizeX]
finVal = finVal + (1 - fractionX) * (1 - fractionY) * noiseArr[x2 + y2*sizeX ]
Return finVal
End
'===========================================
' to fill noise array with white noise
'===========================================
Method InitNoise()
noiseArr = New Float[sizeX * sizeY]
For Local x = 0 To sizeX - 1
For Local y = 0 To sizeY - 1
noiseArr[x + y * sizeX] = (Rnd() - 0.5) * 2.0
Next
Next
End
'===========================================
' might be usefull, process whole array with sinus
'============================================
Method terrainSinus(p:Float)
For Local x = 0 To sizeX - 1
For Local y = 0 To sizeY - 1
Local md:Float = Sin(y * 180 / sizeY) * 2 - 1
terrainArr[x + sizeX * y] = md * p + terrainArr[x + sizeX * y] * (1.0 - p)
Next
Next
End
End
extended class to draw data on screen and make some quantization due to fact we cannot draw to image / backbuffer so this has limited usage
'==========================================
' CLASS
' EXTENDED PERLIN CLASS WITH BITMAP AND LEVELS
'==========================================
Class TPerlinNoise Extends TPerlinNoiseBasic
'min and max colors
Field colMin:TColor = new TColor(0, 0, 0)
Field colMax:TColor = new TColor(200,200,200)
Field levels[] = New Int[100]
Field levelsColor:TColor[] = new TColor[20]
'=========================================
' just make new data
'===========================================
Method New(_x, _y)
Super.New(_x,_y)
'init levels
For Local i = 0 To 99
Self.levels[i] = 0
Next
End
'==========================================
' draw image
'==========================================
Method OnRender(scale, dx, dy)
For Local x = 0 To sizeX - 1
For Local y = 0 To sizeY - 1
Local val# = terrainArr[x + sizeX * y]
Local R = colMax.R * val + colMin.R * (1 - val)
Local G = colMax.G * val + colMin.G * (1 - val)
Local B = colMax.B * val + colMin.B * (1 - val)
SetColor(R, G, B)
DrawRect(x * scale + dx, y * scale + dy, scale, scale)
Next
Next
End
'==========================================
' draw image according to levels, like izolines
'==========================================
Method OnRenderLevel(scale, dx, dy)
For Local x = 0 To sizeX - 1
For Local y = 0 To sizeY - 1
Local val:TColor = levelsColor[levels[terrainArr[x + sizeX * y]*99]]
if(val = null) Continue
SetColor(val.R, val.G, val.B)
DrawRect(x * scale + dx, y * scale + dy, scale, scale)
Next
Next
End
'============================================
' start process
'===========================================
Method OnCreate()
InitNoise()
MakeTerrainMap()
End
'============================================
' setup terrain N from lvl min to lvl max
' this is quantization of terrain to one color in range
' this creates area of same value = izo lines
'===========================================
Method SetupLevel(levelMin, levelMax, val, R,G,B)
levelsColor[val] = New TColor(R,G,B)
For Local i = levelMin To levelMax - 1
levels[i] = val
Next
End
End
example how to use Local Perlinek:TPerlinNoise Perlinek = New TPerlinNoise(64,48) 'to fill screen Perlinek.ChangeParams(0.1, 0.99, 0.65, 6) 'experiment !! Perlinek.colMin = New TColor(0, 50, 0) 'dark green Perlinek.colMax = New TColor(150, 200, 150) 'light green Perlinek.SetupLevel(0,40,0,0,40,80) 'low level water Perlinek.SetupLevel(40,80,1,40,240,40) 'medium grass Perlinek.SetupLevel(80,100,2,140,140,0) 'high rocks Perlinek.OnCreate() '=========================================================== 'draw levels quantization or draw just all '10 is scale 64 * 10 = 640 if(SpaceKeyIsHit = 1) Perlinek.OnRenderLevel(10,0,0) if(SpaceKeyIsHit = 0) Perlinek.OnRender(10,0,0) output ![]() |
| ||
| Nice one, thanks. |
| ||
| if any one can help me how to create such image on the fly in monkey (grab image from backbuffer) |
| ||
| There's no way to access the framebuffer in the standard Monkey libraries. If you want to be able to do that you will have to write the target code and Monkey abstraction layer yourself. |
| ||
| I'd like to test this out, but the TColor class is undefined. Can you add your code for TColor please? |
| ||
Class TColor Field R,G,B '**************************** '* '**************************** Method New(R,G,B) Self.R = R Self.G = G Self.B = B End '**************************** '* '**************************** Method New(col) Self.R = col & $00FF0000 Self.R = Self.R / $10000 Self.G = col & $0000FF00 Self.G = Self.G / $100 Self.B = col & $000000FF End '**************************** '* '**************************** Method GenerateColor:Int() Return $FF000000 + (R * $10000 + G * $100 + B) End End |
| ||
| use it with temperatur / altitude / humidity layers and pick if low temp and high hum then its tundra etc... |
| ||
| if any one can help me how to create such image on the fly in monkey (grab image from backbuffer) Forget rendering and then grabbing an image. You have all the data you need in your TPerlinNoiseBasic class arrays. :) Just calculate the pixel color values from your floats and put these in a new array, which is then put into an image: Strict 'Come on.. Don't forget this.. 'Create an image Local img:= CreateImage(width, height) 'And an array Local pixels:= New Int[width * height] '...calculate pixels's color values ' and then img.WritePixels(pixels, x, y, width, height) It is also possible to create terrain images bigger than the screen, thanks to WritePixels parameters! I use this code to get my tilemap-layers into images, and tilemaps usually are bigger than the screen: Method SetupChunks:Void()
chunkWidth = fullWidth
chunkHeight = fullHeight
While chunkWidth > DeviceWidth()
chunkWidth /= 2
Wend
While chunkHeight > DeviceHeight()
chunkHeight /= 2
Wend
chunkCols = fullWidth / chunkWidth
chunkRows = fullHeight / chunkHeight
End
Method PreRender:Void()
Local x:Int, y:Int
Local w:Int = data.width * tileWidth 'Layer's width * Tile width
Local h:Int = data.height * tileHeight 'Layer's height * Tile height
graphic = CreateImage(w, h)
Local pixels:= New Int[chunkWidth * chunkHeight] ' We only need an array for a chunk, not whole image...
For Local chunkX:Int = 0 Until chunkCols
For Local chunkY:Int = 0 Until chunkRows
Cls(0, 0, 0)
x = chunkX * chunkWidth
y = chunkY * chunkHeight
Select renderingType
Case ORTHOGONAL
RenderOrtho(x, y, chunkWidth, chunkHeight)
Case ISOMETRIC
RenderIsometric() ' This doesn't even work yet, so don't ask... :)
End
ReadPixels(pixels, 0, 0, chunkWidth, chunkHeight) 'Read where we rendered
MaskPixels(pixels, 0, 0, 0)
graphic.WritePixels(pixels, x, y, chunkWidth, chunkHeight) '...but put it in right place in our image.
Next
Next
EndEDIT: Sorry.. Didn't notice this was so old... T__T |
| ||
| Okay, so I played around a little and got it working with images. It "renders" the pixels straight into the image. Should there be no need for any pre-rendering. Also the terrain can be any size, even bigger than the rendering screen. I will still be optimizing the code since there's some left-overs from the earlier rendering.. :) [DONE] Also will be adding colormap, that isn't yet implemented. Will be using gradients which you are able to set key-colors to. [DONE] EDIT: How it looks with 2 gradient colors (red and lighter blue): ![]() Usage: 'Use these, in for example OnCreate()
Local Perlinek = New TPerlinNoise(640, 480)
Perlinek.SetParams(0.05, 0.99, 0.65, 8) ' Experiment with these.. ;)
Perlinek.MakeTerrainMap()
Perlinek.SetLevelColor(0.0, New Color(255, 0, 0))
Perlinek.SetLevelColor(1.1, New Color(0, 128, 255)) ' Have to fix this, doesn't work as it should with key 1.0...
Perlinek.CreateTerrain()
'....
'And simply draw the image in OnRender():
DrawImage(Perlinek.image, 0, 0)
Code (updated with gradient): |
| ||
Here is another with Temperate-like colors:![]() Perlinek.SetLevelColor(1.1, New Color(2, 43, 68)) ' Water Perlinek.SetLevelColor(0.55, New Color(9, 62, 92)) Perlinek.SetLevelColor(0.51, New Color(17, 82, 112)) Perlinek.SetLevelColor(0.5, New Color(69, 108, 118)) ' Shore Perlinek.SetLevelColor(0.49, New Color(42, 102, 41)) ' Land Perlinek.SetLevelColor(0.25, New Color(115, 128, 77)) Perlinek.SetLevelColor(0.15, New Color(153, 143, 92)) Perlinek.SetLevelColor(0.05, New Color(179, 160, 120)) Perlinek.SetLevelColor(0.0, New Color(220, 200, 180)) ' Mountains |


