Awesome. Looking great!
(except perhaps your Pi value: (not sure what difference it makes, just caught my eye!)
CONSTANT PI = 3.141562 <- your code
CONSTANT PI = 3.141593 <- should be)
I attempted a Perlin Noise function / type while working on the Minecraft style voxels here:
http://www.glbasic.com/forum/index.php?topic=9499.msg82316#msg82316Here's the code used:
TYPE TPerlineNoise
maxWidth%
maxHeight%
noise#[]
isNoiseInitialized% = FALSE
FUNCTION Create: width%, height%
self.maxWidth = width
self.maxHeight = height
self.GenerateNoise()
ENDFUNCTION
/// Gets the value for a specific X and Y coordinate
/// results in range [-1, 1] * maxHeight
FUNCTION GetRandomHeight#: x#, y#, maxHeight#, frequency#, amplitude#, persistance#, octaves%
LOCAL i%
LOCAL finalValue# = 0.0
GenerateNoise()
FOR i = 0 TO octaves-1
INC finalValue, GetSmoothNoise(x * frequency, y * frequency) * amplitude
frequency = frequency * 2.0
amplitude = amplitude * persistance
NEXT
IF finalValue < -1.0
finalValue = -1.0
ELSEIF finalValue > 1.0
finalValue = 1.0
ENDIF
RETURN finalValue * maxHeight
ENDFUNCTION
//This function is a simple bilinear filtering function which is good (and easy) enough.
FUNCTION GetSmoothNoise#: x#, y#
LOCAL finalValue# = 0.0
LOCAL fractionX# = x - INTEGER(x)
LOCAL fractionY# = y - INTEGER(y)
LOCAL x1% = MOD(INTEGER(x) + self.maxWidth, self.maxWidth)
LOCAL y1% = MOD(INTEGER(y) + self.maxHeight, self.maxHeight)
//for cool art deco looking images, do +1 for X2 and Y2 instead of -1...
LOCAL x2% = MOD(INTEGER(x) + self.maxWidth - 1, self.maxWidth)
LOCAL y2% = MOD(INTEGER(y) + self.maxHeight - 1, self.maxHeight)
INC finalValue, fractionX * fractionY * self.noise[x1][y1]
INC finalValue, fractionX * (1 - fractionY) * self.noise[x1][y2]
INC finalValue, (1 - fractionX) * fractionY * self.noise[x2][y1]
INC finalValue, (1 - fractionX) * (1 - fractionY) * self.noise[x2][y2]
RETURN finalValue
ENDFUNCTION
/// create a array of randoms
FUNCTION GenerateNoise:
LOCAL x%, y%
IF self.isNoiseInitialized THEN RETURN // A boolean variable in the class to make sure we only do this once
DIM self.noise[self.maxWidth][self.maxHeight] // Create the noise table where MAX_WIDTH and MAX_HEIGHT are set to some value>0
FOR x = 0 TO self.maxWidth-1
FOR y = 0 TO self.maxHeight-1
self.noise[x][y] = (RandomFloat() - 0.5) * 2.0 // Generate noise between -1 and 1
NEXT
NEXT
self.isNoiseInitialized = TRUE
ENDFUNCTION
FUNCTION ToSprite:
LOCAL x%, y%
LOCAL sprite% = GENSPRITE()
LOCAL colour%
CREATESCREEN 0, sprite, self.maxWidth, self.maxHeight
USESCREEN 0
FOR x = 0 TO self.maxWidth-1
FOR y = 0 TO self.maxHeight-1
colour = GetRandomHeight(x,y, 127, 0.15, 1, 0.15, 3) + 128//x#, y#, maxHeight#, frequency#, amplitude#, persistance#, octaves%
SETPIXEL x, y, RGB(colour, colour, colour)
NEXT
NEXT
USESCREEN -1
RETURN sprite
ENDFUNCTION
ENDTYPE
And how I call it:
//x#, y#, maxHeight#, frequency#, amplitude#, persistance#, octaves%
height = noise.GetRandomHeight(x,z, 1, 0.1, 1, 0.15, 3)
See below for a screeny of the results.
Mine is only a 2D version. It would be cool to see a 3D version that creates caves, just like Minecraft. Now, mine only defines the top surface of the landscape.
Keep it up, I love Perlin noise since I heard about it researching how Minecraft generates it's endless landscapes.