117 lines
5.0 KiB
Python
117 lines
5.0 KiB
Python
import numpy as np
|
||
from opensimplex import OpenSimplex
|
||
from settings import WorldSettings
|
||
from numba import njit
|
||
|
||
class ClimateGenerator:
|
||
def __init__(self, height_map: np.ndarray, settings: WorldSettings):
|
||
self.height_map = height_map
|
||
self.settings = settings
|
||
self.noise = OpenSimplex(seed=settings.get_valid_seed())
|
||
|
||
# Палитра биомов
|
||
self.biome_colors = {
|
||
'water': (0.2, 0.4, 0.8, 1.0),
|
||
'ice': (0.95, 0.95, 0.98, 0.8),
|
||
'tundra': (0.4, 0.6, 0.9, 0.9),
|
||
'taiga': (0.1, 0.3, 0.15, 1.0),
|
||
'forest': (0.3, 0.6, 0.3, 1.0),
|
||
'desert': (0.9, 0.6, 0.2, 1.0),
|
||
'steppe': (0.8, 0.75, 0.5, 1.0),
|
||
'swamp': (0.4, 0.35, 0.2, 0.9),
|
||
'jungle': (0.1, 0.5, 0.1, 1.0),
|
||
'savanna': (0.8, 0.7, 0.3, 1.0)
|
||
}
|
||
|
||
def generate_biome_map(self, enabled_biomes=None) -> np.ndarray:
|
||
"""Генерация карты биомов с учетом выбранных биомов"""
|
||
if enabled_biomes is None:
|
||
enabled_biomes = list(self.biome_colors.keys())
|
||
|
||
height, width = self.height_map.shape
|
||
|
||
# Генерация шумов
|
||
temp_noise = self._generate_noise(width, height, 40)
|
||
moist_noise = self._generate_noise(width, height, 50)
|
||
|
||
biome_map = np.zeros((height, width, 4), dtype=np.float32)
|
||
self._fill_biome_map(
|
||
biome_map,
|
||
self.height_map,
|
||
temp_noise,
|
||
moist_noise,
|
||
enabled_biomes
|
||
)
|
||
return biome_map
|
||
|
||
def _generate_noise(self, width, height, scale):
|
||
"""Генерация многооктавного шума"""
|
||
noise = np.zeros((height, width))
|
||
for octave in [1.0, 0.5, 0.25]:
|
||
for y in range(height):
|
||
for x in range(width):
|
||
nx = x / (scale * octave)
|
||
ny = y / (scale * octave)
|
||
noise[y, x] += self.noise.noise2(nx, ny) * octave
|
||
return (noise - noise.min()) / (noise.max() - noise.min())
|
||
|
||
@staticmethod
|
||
@njit
|
||
def _fill_biome_map(biome_map, height_map, temp_noise, moist_noise, enabled_biomes):
|
||
height, width = height_map.shape
|
||
water_level = 0.05
|
||
|
||
for y in range(height):
|
||
latitude = abs(y/height - 0.5) * 2 # 0 на экваторе, 1 на полюсах
|
||
|
||
for x in range(width):
|
||
h = height_map[y,x]
|
||
|
||
# Вода (всегда включена, без прозрачности)
|
||
if h < water_level:
|
||
biome_map[y,x] = (0.2, 0.4, 0.8, 1.0)
|
||
continue
|
||
|
||
# Климатические параметры
|
||
temp = (0.7 - latitude * 0.5) * (1.0 - h * 0.5) * (0.8 + temp_noise[y,x] * 0.4)
|
||
moist = moist_noise[y,x]
|
||
|
||
# Расчёт прозрачности: сильнее зависит от высоты
|
||
alpha = 1.0 - h**2 * 0.7 # h^2 делает прозрачность резче на вершинах
|
||
|
||
# Выбор биома
|
||
if temp < 0.3: # Холодные зоны
|
||
if h > 0.8 and 'ice' in enabled_biomes:
|
||
biome = (0.95, 0.95, 0.98, alpha * 0.7) # Лёд прозрачнее
|
||
elif moist > 0.65 and 'tundra' in enabled_biomes:
|
||
biome = (0.4, 0.6, 0.9, alpha)
|
||
elif 'taiga' in enabled_biomes:
|
||
biome = (0.1, 0.3, 0.15, alpha)
|
||
else:
|
||
biome = (0.3, 0.6, 0.3, alpha)
|
||
|
||
elif temp < 0.6: # Умеренные зоны
|
||
if moist > 0.7 and 'swamp' in enabled_biomes:
|
||
biome = (0.4, 0.35, 0.2, alpha)
|
||
elif moist > 0.5 and 'forest' in enabled_biomes:
|
||
biome = (0.3, 0.6, 0.3, alpha)
|
||
elif moist > 0.3 and 'steppe' in enabled_biomes:
|
||
biome = (0.8, 0.75, 0.5, alpha)
|
||
elif 'desert' in enabled_biomes:
|
||
biome = (0.9, 0.6, 0.2, alpha)
|
||
else:
|
||
biome = (0.3, 0.6, 0.3, alpha)
|
||
|
||
else: # Теплые зоны
|
||
if moist < 0.3 and 'desert' in enabled_biomes:
|
||
biome = (0.9, 0.6, 0.2, alpha)
|
||
elif moist < 0.5 and 'savanna' in enabled_biomes:
|
||
biome = (0.8, 0.7, 0.3, alpha)
|
||
elif moist > 0.7 and 'jungle' in enabled_biomes:
|
||
biome = (0.1, 0.5, 0.1, alpha)
|
||
elif 'forest' in enabled_biomes:
|
||
biome = (0.3, 0.6, 0.3, alpha)
|
||
else:
|
||
biome = (0.8, 0.75, 0.5, alpha)
|
||
|
||
biome_map[y,x] = biome |