function V3(x,y,z) return {x=x,y=y,z=z} end function dot(v,w) return v.x*w.x + v.y*w.y + v.z*w.z end function norm(v) local m=math.sqrt(dot(v,v)) return V3(v.x/m, v.y/m, v.z/m) end function clamp(n,lo,hi) return math.floor(math.min(math.max(lo,n),hi)) end function hittest(s, x, y) local z = s.r^2 - (x-s.x)^2 - (y-s.y)^2 if z >= 0 then z = math.sqrt(z) return true, s.z-z, s.z+z end return false, 0, 0 end function deathstar(pos, neg, sun, k, amb) shades = {[0]=" ",".",":","!","*","o","e","&","#","%","@"} for y = pos.x-pos.r-0.5, pos.x+pos.r+0.5 do for x = pos.x-pos.r-0.5, pos.x+pos.r+0.5, 0.5 do local hitpos, pz1, pz2 = hittest(pos, x, y) local result, hitneg, nz1, nz2 = 0 if hitpos then hitneg, nz1, nz2 = hittest(neg, x, y) if not hitneg or nz1 > pz1 then result = 1 elseif nz2 > pz2 then result = 0 elseif nz2 > pz1 then result = 2 else result = 1 end end local shade = 0 if result > 0 then if result == 1 then shade = clamp((1-dot(sun, norm(V3(x-pos.x, y-pos.y, pz1-pos.z)))^k+amb) * #shades, 1, #shades) else shade = clamp((1-dot(sun, norm(V3(neg.x-x, neg.y-y, neg.z-nz2)))^k+amb) * #shades, 1, #shades) end end io.write(shades[shade]) end io.write("\n") end end deathstar({x=20, y=20, z=0, r=20}, {x=10, y=10, z=-15, r=10}, norm(V3(-2,1,3)), 2, 0.1)