VoxelServer/ServerCore/PerlinGenerator.py

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)