summaryrefslogtreecommitdiff
path: root/server/noise.lua
blob: 6fd745f851f2d7505e821a5264b3ef2e5cdff05c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
local Pos = require"common.coords".Pos
local class = require"common.class"
local bit = require"bit"
local tau = 2*math.pi

math.randomseed(os.time())


local function hash_list(t)
	local h = #t
	for _,x in ipairs(t) do
		x = bit.bxor(bit.rshift(x,16),x) * 0x7feb352d
		x = bit.bxor(bit.rshift(x,15),x) * 0x846ca68b
		x = bit.bxor(bit.rshift(x,16),x)
		h = bit.bxor(h,x + 0x9e3779b9 + bit.lshift(h,6) + bit.rshift(h,2))
	end
	return h
end

local function hash_to_unit_vec(t)
	local h = hash_list(t)
	math.randomseed(h)
	local theta = math.random()*tau
	return Pos:make(math.cos(theta),math.sin(theta))
end

local function lerp(a,b,t) return (1-t)*a + t*b end
local function smoothstep(x)
	if x<0 then return 0 end
	if x>1 then return 1 end
	return x*x*(3 - 2*x)
end
local function slerp(a,b,t) return lerp(a,b,smoothstep(t)) end

local PerlinNoise = class()
function PerlinNoise.make(cls,seed)
	return setmetatable({seed=seed},cls)
end
function PerlinNoise.vertex(self,ix,iy)
	return hash_to_unit_vec{ix,iy,self.seed}
end

function PerlinNoise.at(self,x,y)
	local x0 = math.floor(x)
	local y0 = math.floor(y)
	local x1 = x0 + 1
	local y1 = y0 + 1

	local v00 = self:vertex(x0,y0)
	local v01 = self:vertex(x0,y1)
	local v10 = self:vertex(x1,y0)
	local v11 = self:vertex(x1,y1)

	local p = function(...) return Pos:make(...) end
	local d00 = v00:dot(p(x-x0,y-y0))
	local d01 = v01:dot(p(x-x0,y-y1))
	local d10 = v10:dot(p(x-x1,y-y0))
	local d11 = v11:dot(p(x-x1,y-y1))

	local q0 = slerp(d00,d01,y-y0)
	local q1 = slerp(d10,d11,y-y0)
	local z  = slerp(q0,q1,x-x0)
	return z
end

local NoiseAgg = class()
function NoiseAgg.make(cls,things)
	return setmetatable({things=things or {}},cls)
end
function NoiseAgg.at(self,x,y)
	local n = 0
	local t = 0
	assert(#self.things>0,"can't generate noise with no noise things")
	for _,thing in ipairs(self.things) do
		local gen,scale,amp = thing.gen,thing.scale,thing.amp
		n = n + amp
		t = t + gen:at(x/scale,y/scale)*amp
	end
	return t/n
end

return {PerlinNoise=PerlinNoise,NoiseAgg=NoiseAgg}