-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathopensimplex_cgo.py
150 lines (116 loc) · 3.95 KB
/
opensimplex_cgo.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
148
149
150
from random import randint
from pathlib import Path
from ctypes import cdll, c_int64, c_float
# pylint: disable=R0801
BASE_PATH = Path(__file__).parent.resolve()
__opensimplex = cdll.LoadLibrary(f'{BASE_PATH}/noise_cgo.so')
_noise_set_seed = __opensimplex.set_seed
_noise_set_seed.argtypes = [c_int64]
_noise_2d = __opensimplex.get_2d
_noise_2d.argtypes = [c_float, c_float]
_noise_2d.restype = c_float
_noise_3d = __opensimplex.get_3d
_noise_3d.argtypes = [c_float, c_float, c_float]
_noise_3d.restype = c_float
_noise_4d = __opensimplex.get_4d
_noise_4d.argtypes = [c_float, c_float, c_float, c_float]
_noise_4d.restype = c_float
class OpenSimplex:
def __init__(self, seed: int = None):
if seed is None:
self.seed = randint(0, 100_000_000)
else:
self.seed = seed
_noise_set_seed(c_int64(self.seed))
@staticmethod
def _abs(n: float) -> float:
if n < 0:
return n * -1
return n
def get_2d(self, x: float, y: float) -> float:
return self._abs(_noise_2d(
c_float(x),
c_float(y),
))
def get_3d(self, x: float, y: float, z: float) -> float:
return self._abs(_noise_3d(
c_float(x),
c_float(y),
c_float(z),
))
def get_4d(self, x: float, y: float, z: float, w: float) -> float:
return self._abs(_noise_4d(
c_float(x),
c_float(y),
c_float(z),
c_float(w),
))
class OpenSimplexConfig:
def __init__(
self, seed: int = None, octaves: int = 5, persistence: float = 0.0, lacunarity: float = 0.0,
exponentiation: float = 1.0, height: float = 1.0, scale: float = 1.0,
):
self.seed = seed
self.octaves = octaves
self.persistence = persistence
self.lacunarity = lacunarity
self.exponentiation = exponentiation
self.height = height
self.scale = scale
class OpenSimplexExtended:
A = 0.5
def __init__(self, config: OpenSimplexConfig):
self.cnf = config
self._noise = OpenSimplex(seed=self.cnf.seed)
def get_2d(self, x: float, y: float) -> float:
return self._get(x, y)
def get_3d(self, x: float, y: float, z: float) -> float:
return self._get(x, y, z)
def get_4d(self, x: float, y: float, z: float, w: float) -> float:
return self._get(x, y, z, w)
# based on: https://github.com/simondevyoutube/ProceduralTerrain_Part10/blob/main/src/noise.js#L15
def _get(self, x: float, y: float, z: float = None, w: float = None) -> float:
d = 2
xs = x / self.cnf.scale
ys = y / self.cnf.scale
zs = 0
ws = 0
if z is not None:
d = 3
zs = z / self.cnf.scale
if w is not None:
d = 4
ws = w / self.cnf.scale
g = 2.0 ** (-self.cnf.persistence)
amplitude = 1.0
frequency = 1.0
normalization = 0
total = 0
for _ in range(self.cnf.octaves):
if d == 4:
noise_value = self._noise.get_4d(
xs * frequency,
ys * frequency,
zs * frequency,
ws * frequency,
)
elif d == 3:
noise_value = self._noise.get_3d(
xs * frequency,
ys * frequency,
zs * frequency,
)
else:
noise_value = self._noise.get_2d(
xs * frequency,
ys * frequency,
)
noise_value = noise_value * self.A + self.A
total += noise_value * amplitude
normalization += amplitude
amplitude *= g
frequency *= self.cnf.lacunarity
total /= normalization
if total < 0:
total *= -1
return float(total ** self.cnf.exponentiation) * self.cnf.height