forked from fogleman/Minecraft
-
Notifications
You must be signed in to change notification settings - Fork 0
/
noise.py
147 lines (113 loc) · 4.25 KB
/
noise.py
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# Imports, sorted alphabetically.
# Python packages
from math import sqrt, floor
import random
# Third-party packages
from perlin import SimplexNoise
# Modules from this project
from utils import FastRandom
# Modules from this project
__all__ = ('SimplexNoiseGen', 'PerlinNoise')
# Factory class utilizing perlin.SimplexNoise
class SimplexNoiseGen(object):
def __init__(self, seed, octaves=6, zoom_level=0.002): # octaves = 6,
perm = range(255)
random.Random(seed).shuffle(perm)
self.noise = SimplexNoise(permutation_table=perm).noise2
self.PERSISTENCE = 2.1379201 # AKA lacunarity
self.H = 0.836281
self.OCTAVES = octaves # Higher linearly increases calc time; increases apparent 'randomness'
self.weights = [self.PERSISTENCE ** (-self.H * n) for n in xrange(self.OCTAVES)]
self.zoom_level = zoom_level # Smaller will create gentler, softer transitions. Larger is more mountainy
def fBm(self,x,z):
x *= self.zoom_level
z *= self.zoom_level
y = 0
for weight in self.weights:
y += self.noise(x, z) * weight
x *= self.PERSISTENCE
z *= self.PERSISTENCE
return y
# Improved Perlin Noise based on Improved Noise reference implementation by Ken Perlin
class PerlinNoise(object):
def __init__(self, seed):
rand = FastRandom(seed)
self.perm = [None] * 512
noise_tbl = [None] * 256
self.PERSISTENCE = 2.1379201
self.H = 0.836281
self.OCTAVES = 9
self.weights = [None] * self.OCTAVES
self.regen_weight = True
for i in range(256):
noise_tbl[i] = i
for i in range(256):
j = rand.randint() % 256
j = abs(j)
noise_tbl[i], noise_tbl[j] = noise_tbl[j], noise_tbl[i]
for i in range(256):
self.perm[i] = self.perm[i + 256] = noise_tbl[i]
def fade(self, t):
return (t ** 3) * (t * (t * 6 - 15) + 10)
# linear interpolate
def lerp(self, t, a, b):
return a + t * (b - a)
def grad(self, hash, x, y, z):
h = hash & 15
u = x if h < 8 else y
if h < 4:
v = y
elif h in (12, 14):
v = x
else:
v = z
return (-u if h & 1 else u) + (-v if h & 2 else v)
def noise(self, x, y, z):
X = int(floor(x)) & 255
Y = int(floor(y)) & 255
Z = int(floor(z)) & 255
x -= floor(x)
y -= floor(y)
z -= floor(z)
u = self.fade(x)
v = self.fade(y)
w = self.fade(z)
A = self.perm[X] + Y
AA = self.perm[A] + Z
AB = self.perm[(A + 1)] + Z
B = self.perm[(X + 1)] + Y
BA = self.perm[B] + Z
BB = self.perm[(B + 1)] + Z
return self.lerp(w,
self.lerp(v, self.lerp(u,
self.grad(self.perm[AA], x, y, z),
self.grad(self.perm[BA], x - 1.0, y, z)),
self.lerp(u,
self.grad(self.perm[AB], x, y - 1.0, z),
self.grad(self.perm[BB], x - 1.0, y - 1.0, z))),
self.lerp(v, self.lerp(u,
self.grad(self.perm[(AA + 1)], x, y, z - 1.0),
self.grad(self.perm[(BA + 1)], x - 1.0, y, z - 1.0)),
self.lerp(u,
self.grad(self.perm[(AB + 1)], x, y - 1.0, z - 1.0),
self.grad(self.perm[(BB + 1)], x - 1.0, y - 1.0, z - 1.0))))
def fBm(self, x, y, z):
total = 0.0
if self.regen_weight:
self.weights = [None] * self.OCTAVES
for n in range(self.OCTAVES):
self.weights[n] = self.PERSISTENCE ** (-self.H * n)
self.regen_weight = False
for n in range(self.OCTAVES):
total += self.noise(x, y, z) * self.weights[n]
x *= self.PERSISTENCE
y *= self.PERSISTENCE
z *= self.PERSISTENCE
return total
@property
def octave(self):
return self.OCTAVES
@octave.setter
def octave(self, value):
self.OCTAVES = value
self.regen_weight = True