Something I did today, might be useful to some. Interested in feedback or improvements.
It’s ugly, I know - once its more final, I’m gonna wrap it up in a class.
Goal is to procedurally generate, from a single seed value, a “nice looking” planet. If you don’t think this is nice - you shoulda seen it before! If you think you can do better - do! And share code. Please. Don’t make me beg. It’s not pretty.
Here’s a screenshot… https://twitter.com/#!/bortels/status/197206189606584320/photo/1
This takes about 4 seconds to run on my new ipad. If you’re impatient, or bored, reduce “radius” to 50 and it’ll run in a second or less. In the long run, I want to break out the loop so it can run a bit at a time, in the “background”, while Codea does other stuff. This seems like a prime place for a coroutine, if I understood how they work.
-- Procedural planets
-- Tom Bortels - bortels@gmail.com
--
-- todo:
-- clouds
-- more color ranges (tint?)
-- gas giants (banding)
-- ice caps
function setup()
radius = 150
ambient = 0.05
i = image(radius*2+5, radius*2+5)
sphere(i, radius, 2, ambient)
end
function sphere(img, radius, k, ambient)
--math.randomseed(2)
local topolevel = {-11000,-5500,-3000,-2000,-750,-70,-20,0,
0,250,500,1200,1700,2800,4000,6000 }
local topocolor = {
{36,38,175}, {56,58,195}, {70,72,214}, {81,102,217},
{100,129,223}, {131,161,230}, {164,192,240}, {170,200,255},
{0,97,71}, {16,122,47}, {232,215,125}, {161,67,0},
{130,30,30}, {110,110,110}, {255,255,255}, {255,255,255} }
local col = {}
for n=0,255 do
nn = ((n/255)*10000) - 5000
col[n] = {}
for d=1,15 do
low=topolevel[d]
hi = topolevel[d+1]
if (low < nn) and (hi >= nn) then
local r1,g1,b1 = unpack(topocolor[d])
local r2,g2,b2 = unpack(topocolor[d+1])
local range = hi - low
local fract = (nn-low)/range
col[n].r = ((r2-r1)*fract)+r1
col[n].g = ((g2-g1)*fract)+g1
col[n].b = ((b2-b1)*fract)+b1
end
end
end
local function normalize (vec)
len = math.sqrt(vec[1]^2 + vec[2]^2 + vec[3]^2)
return {vec[1]/len, vec[2]/len, vec[3]/len}
end
local light = normalize{30, -30, -50}
local function dot (vec1, vec2)
d = vec1[1]*vec2[1] + vec1[2]*vec2[2] + vec1[3]*vec2[3]
return d < 0 and -d or 0
end
local function m255(a, b)
local r = a * b
if (r>255) then r=255 end
return r
end
ofs = radius + math.random(50)
ofss = radius + math.random(50)
nf = radius / math.random(1,3) -- continents
-- nff = radius / 7.7 -- coastlines
nff = radius / math.random(6,9)
trange = math.random()/2+0.5
toffset = (1-trange)*math.random()
--print(trange .. " " .. toffset)
local i,x,j,vec,b
for i = math.floor(-radius),-math.floor(-radius) do
x = i + 0.5
for j = math.floor(-2*radius),-math.floor(-2*radius) do
y = j / 2 + 0.5
if x^2 + y^2 <= radius^2 then
z = math.sqrt(radius^2 - x^2 - y^2)
c = (noise(ofs+x/nf, ofs+y/nf, ofs+z/nf) + 1) * 120
c = c + noise(ofss+x/nff, ofss+y/nff, ofss+z/nff) * 16
c = c * trange + (toffset * 255)
c = math.floor(c)
local rr=col[c].r
local gg=col[c].g
local bb=col[c].b
vec = normalize{x, y, z}
b = dot(light,vec) ^ k + ambient
local ba = b+ambient
img:set(x+img.width/2, y+img.height/2,
m255(rr, ba), m255(gg, ba),
m255(bb, ba), 255)
end
end
end
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(19, 19, 21, 255)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
sprite(i, WIDTH/2, HEIGHT/2)
end