141 lines
4.5 KiB
Python
141 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import numpy as np
|
|
from typing import Tuple
|
|
from Tools import hash2d
|
|
|
|
|
|
def lerp(a0, a1, w):
|
|
return a0 + w*(a1 - a0)
|
|
|
|
|
|
def dotGridGradient(ix, iy, x, y, raw_val_x, raw_val_y):
|
|
dx = x - ix
|
|
dy = y - iy
|
|
|
|
return dx * raw_val_x + dy * raw_val_y
|
|
|
|
|
|
class PerlinGenerator(object):
|
|
|
|
def __init__(self, raw_noise_cell_size: int = 8,
|
|
raw_noise_chunk_size: int = 8,
|
|
world_seed: int = 0):
|
|
self.raw_noise_cell_size: int = raw_noise_cell_size
|
|
self.raw_noise_chunk_size: int = raw_noise_chunk_size
|
|
self.world_seed: int = world_seed
|
|
|
|
self.raw_noise_chunks = {}
|
|
|
|
def getCellCoords(self,
|
|
perlin_x: int,
|
|
perlin_y: int) -> Tuple[float, float]:
|
|
return (perlin_x / self.raw_noise_cell_size,
|
|
perlin_y / self.raw_noise_cell_size)
|
|
|
|
def getCellChunk(self,
|
|
raw_cell_x: int,
|
|
raw_cell_y: int) -> Tuple[int, int]:
|
|
return (raw_cell_x // self.raw_noise_chunk_size,
|
|
raw_cell_y // self.raw_noise_chunk_size)
|
|
|
|
def existRawChunk(self, chunk_x: int, chunk_y: int) -> Tuple[int, int]:
|
|
return (chunk_x in self.raw_noise_chunks and
|
|
chunk_y in self.raw_noise_chunks[chunk_x])
|
|
|
|
def createRawChunk(self, chunk_x: int, chunk_y: int) -> None:
|
|
if chunk_x not in self.raw_noise_chunks:
|
|
self.raw_noise_chunks[chunk_x] = {}
|
|
|
|
c = self.raw_noise_chunks[chunk_x][chunk_y] = np.zeros(
|
|
shape=(self.raw_noise_chunk_size,
|
|
self.raw_noise_chunk_size,
|
|
2),
|
|
dtype=float)
|
|
|
|
# init random state
|
|
chunk_seed = (hash2d(chunk_x, chunk_y) + self.world_seed) % 2**32
|
|
np.random.seed(chunk_seed)
|
|
|
|
# creating chunk:
|
|
for x in range(self.raw_noise_chunk_size):
|
|
for y in range(self.raw_noise_chunk_size):
|
|
|
|
# create random vector on unit circle
|
|
re = np.random.normal()
|
|
im = np.random.normal()
|
|
|
|
# TODO: check whether mag == 0 is possible!
|
|
mag = (re ** 2 + im ** 2) ** .5
|
|
|
|
c[x, y, 0] = re / mag
|
|
c[x, y, 1] = im / mag
|
|
|
|
def perlin(self, perlin_x: int, perlin_y: int) -> float:
|
|
|
|
raw_coords_x, raw_coords_y = self.getCellCoords(
|
|
perlin_x=perlin_x, perlin_y=perlin_y)
|
|
|
|
ix0: int = int(raw_coords_x)
|
|
iy0: int = int(raw_coords_y)
|
|
|
|
ix1: int = ix0 + 1
|
|
iy1: int = iy0 + 1
|
|
|
|
sx: float = raw_coords_x - ix0
|
|
sy: float = raw_coords_y - iy0
|
|
|
|
cx0, cy0 = self.getCellChunk(raw_cell_x=ix0, raw_cell_y=iy0)
|
|
cx1, cy1 = self.getCellChunk(raw_cell_x=ix1, raw_cell_y=iy1)
|
|
|
|
if not self.existRawChunk(cx0, cy0):
|
|
self.createRawChunk(cx0, cy0)
|
|
|
|
if not self.existRawChunk(cx0, cy1):
|
|
self.createRawChunk(cx0, cy1)
|
|
|
|
if not self.existRawChunk(cx1, cy0):
|
|
self.createRawChunk(cx1, cy0)
|
|
|
|
if not self.existRawChunk(cx1, cy1):
|
|
self.createRawChunk(cx1, cy1)
|
|
|
|
local_x0: int = ix0 % self.raw_noise_chunk_size
|
|
local_y0: int = iy0 % self.raw_noise_chunk_size
|
|
local_x1: int = ix1 % self.raw_noise_chunk_size
|
|
local_y1: int = iy1 % self.raw_noise_chunk_size
|
|
|
|
chunk_x0y0 = self.raw_noise_chunks[cx0][cy0]
|
|
chunk_x0y1 = self.raw_noise_chunks[cx0][cy1]
|
|
chunk_x1y0 = self.raw_noise_chunks[cx1][cy0]
|
|
chunk_x1y1 = self.raw_noise_chunks[cx1][cy1]
|
|
|
|
n0_x: float = chunk_x0y0[local_x0, local_y0, 0]
|
|
n0_y: float = chunk_x0y0[local_x0, local_y0, 1]
|
|
|
|
n1_x: float = chunk_x1y0[local_x1, local_y0, 0]
|
|
n1_y: float = chunk_x1y0[local_x1, local_y0, 1]
|
|
|
|
n2_x: float = chunk_x0y1[local_x0, local_y1, 0]
|
|
n2_y: float = chunk_x0y1[local_x0, local_y1, 1]
|
|
|
|
n3_x: float = chunk_x1y1[local_x1, local_y1, 0]
|
|
n3_y: float = chunk_x1y1[local_x1, local_y1, 1]
|
|
|
|
n0: float = dotGridGradient(
|
|
ix0, iy0, raw_coords_x, raw_coords_y, n0_x, n0_y)
|
|
n1: float = dotGridGradient(
|
|
ix1, iy0, raw_coords_x, raw_coords_y, n1_x, n1_y)
|
|
|
|
n2: float = dotGridGradient(
|
|
ix0, iy1, raw_coords_x, raw_coords_y, n2_x, n2_y)
|
|
n3: float = dotGridGradient(
|
|
ix1, iy1, raw_coords_x, raw_coords_y, n3_x, n3_y)
|
|
|
|
x0: float = lerp(n0, n1, sx)
|
|
x1: float = lerp(n2, n3, sx)
|
|
|
|
# TODO: this interpolation method is bad!
|
|
|
|
return lerp(x0, x1, sy)
|