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