I was messing a bit with procedural generation and finally could implement a Perlin Noise inside Skyline.

- Code: Select all
-- ======================================================================
-- original code by Ken Perlin: http://mrl.nyu.edu/~perlin/noise/
-- ======================================================================
local function BitAND(a,b)--Bitwise and
local p,c=1,0
while a>0 and b>0 do
local ra,rb=a%2,b%2
if ra+rb>1 then c=c+p end
a,b,p=(a-ra)/2,(b-rb)/2,p*2
end
return c
end
perlin = {}
perlin.p = {}
perlin.permutation = {}
perlin.size = 256
perlin.gx = {}
perlin.gy = {}
perlin.randMax = 256
-- Random Pattern
function randomSeed()
local n = 256
local t = {}
for i = 1, n do
t[i] = i
end
for i = 1, n do
local j = math.random(i, n)
t[i], t[j] = t[j], t[i]
perlin.permutation[i] = t[i]
--sky.lprint("permutation: "..t[i] )
end
end
function perlin:load()
-- Random Seed
randomSeed()
for i=1,self.size do
self.p[i] = self.permutation[i]
self.p[255+i] = self.p[i]
end
end
function perlin:noise( x, y, z )
local X = BitAND(math.floor(x), 255) + 1
local Y = BitAND(math.floor(y), 255) + 1
local Z = BitAND(math.floor(z), 255) + 1
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
local u = fade(x)
local v = fade(y)
local w = fade(z)
local A = self.p[X]+Y
local AA = self.p[A]+Z
local AB = self.p[A+1]+Z
local B = self.p[X+1]+Y
local BA = self.p[B]+Z
local BB = self.p[B+1]+Z
return lerp(w, lerp(v, lerp(u, grad(self.p[AA ], x , y , z ),
grad(self.p[BA ], x-1, y , z )),
lerp(u, grad(self.p[AB ], x , y-1, z ),
grad(self.p[BB ], x-1, y-1, z ))),
lerp(v, lerp(u, grad(self.p[AA+1], x , y , z-1),
grad(self.p[BA+1], x-1, y , z-1)),
lerp(u, grad(self.p[AB+1], x , y-1, z-1),
grad(self.p[BB+1], x-1, y-1, z-1))))
end
function fade( t )
return t * t * t * (t * (t * 6 - 15) + 10)
end
function lerp( t, a, b )
return a + t * (b - a)
end
function grad( hash, x, y, z )
local h = BitAND(hash, 15)
local u = h < 8 and x or y
local v = h < 4 and y or ((h == 12 or h == 14) and x or z)
return ((h and 1) == 0 and u or -u) + ((h and 2) == 0 and v or -v)
end
function Perlin_onInit()
perlin:load()
doPerlin()
end
function doPerlin()
for i=1,200 do
for j=1,200 do
local x = i-1
local z = j-1
local y = perlin:noise(i/10, j/10, 0.3)
if ( y > 0.05 ) then
entity.spawn("node_cube",x,10,z,1,1,1)
end
end
end
end
Since I'm unable to modify the vertices from a mesh, all I can get is a minecraft style terrain...
But there are some values I can use in my own functions, like the density of the noise...
... and certain heights:
That allowed me to populate an entire island with vegetation randomly, following some interesting patterns.
First I've created the palms, using a Perlin noise at a certain level, then I throw a ray down and spawn only those palms which are 2 meters over the sea level. That avoid the palms getting too close to the shore.
Each palm will hold a cluster of grass meshes around, at a random distance.
Then I spawn the bushes with the same Perlin noise but a lower level, to avoid colliding with the palms.
Finally, the rocks don't have a perlin noise, just spawned around randomly, but they are clusters as well.
This is one of the random levels. Every time I click play, these values change:
total_palm: 253
total_grass: 7590
total_bush: 8199
total_rock: 200
I even can make a dynamic map... Darker dots are the palms and the lighter ones are the bushes.
There are still some work to do regarding to object placement in order to avoid overlapping, but already got a decent procedural environment. It's a pitty that I can not generate a terrain in the same way, with different biomes and much bigger. I only can use a custom terrain mesh, which is a huge limitation.
Anyway, here's a short video of a character walking around the island. Hope you like it!

Cheers