1275 lines
50 KiB
Python
1275 lines
50 KiB
Python
"""
|
||
生物群落生成器
|
||
负责生成程序化地形的生物群落分布
|
||
"""
|
||
|
||
import numpy as np
|
||
import math
|
||
from typing import Dict, Any, List, Tuple
|
||
|
||
class BiomeGenerator:
|
||
"""
|
||
生物群落生成器
|
||
负责生成程序化地形的生物群落分布,基于高度、湿度、温度等因素
|
||
"""
|
||
|
||
def __init__(self, plugin):
|
||
"""
|
||
初始化生物群落生成器
|
||
|
||
Args:
|
||
plugin: 程序化地形生成插件实例
|
||
"""
|
||
self.plugin = plugin
|
||
self.enabled = False
|
||
self.initialized = False
|
||
|
||
# 生物群落配置
|
||
self.seed = plugin.config.get('seed', 12345)
|
||
|
||
# 生物群落类型定义
|
||
self.biome_types = {
|
||
0: {'name': 'ocean', 'color': (0, 0, 128), 'humidity': 0.8, 'temperature': 0.5},
|
||
1: {'name': 'beach', 'color': (255, 255, 0), 'humidity': 0.6, 'temperature': 0.7},
|
||
2: {'name': 'plains', 'color': (144, 238, 144), 'humidity': 0.5, 'temperature': 0.6},
|
||
3: {'name': 'forest', 'color': (34, 139, 34), 'humidity': 0.7, 'temperature': 0.6},
|
||
4: {'name': 'jungle', 'color': (0, 100, 0), 'humidity': 0.9, 'temperature': 0.8},
|
||
5: {'name': 'desert', 'color': (255, 165, 0), 'humidity': 0.2, 'temperature': 0.9},
|
||
6: {'name': 'mountain', 'color': (139, 137, 137), 'humidity': 0.4, 'temperature': 0.4},
|
||
7: {'name': 'snow', 'color': (255, 255, 255), 'humidity': 0.3, 'temperature': 0.2},
|
||
8: {'name': 'taiga', 'color': (0, 128, 128), 'humidity': 0.6, 'temperature': 0.3},
|
||
9: {'name': 'tundra', 'color': (173, 216, 230), 'humidity': 0.4, 'temperature': 0.2},
|
||
10: {'name': 'swamp', 'color': (128, 128, 0), 'humidity': 0.9, 'temperature': 0.7},
|
||
11: {'name': 'savanna', 'color': (255, 215, 0), 'humidity': 0.4, 'temperature': 0.8}
|
||
}
|
||
|
||
# 生物群落分布参数
|
||
self.distribution_params = {
|
||
'humidity_scale': 20.0,
|
||
'temperature_scale': 20.0,
|
||
'height_influence': 0.3,
|
||
'humidity_influence': 0.4,
|
||
'temperature_influence': 0.3,
|
||
'edge_blending': 0.1
|
||
}
|
||
|
||
# 气候参数
|
||
self.climate_params = {
|
||
'equator_temperature': 1.0,
|
||
'pole_temperature': 0.0,
|
||
'ocean_temperature_modifier': 0.1,
|
||
'mountain_temperature_drop': 0.3,
|
||
'humidity_persistence': 0.6,
|
||
'humidity_lacunarity': 2.0
|
||
}
|
||
|
||
# 噪声生成器引用
|
||
self.noise_generator = None
|
||
|
||
# 统计信息
|
||
self.stats = {
|
||
'biomes_generated': 0,
|
||
'total_generation_time': 0.0,
|
||
'average_generation_time': 0.0,
|
||
'biome_distribution': {}
|
||
}
|
||
|
||
print("✓ 生物群落生成器已创建")
|
||
|
||
def initialize(self) -> bool:
|
||
"""
|
||
初始化生物群落生成器
|
||
|
||
Returns:
|
||
是否初始化成功
|
||
"""
|
||
try:
|
||
# 获取噪声生成器引用
|
||
self.noise_generator = self.plugin.noise_generator
|
||
|
||
self.initialized = True
|
||
print("✓ 生物群落生成器初始化完成")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落生成器初始化失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def enable(self) -> bool:
|
||
"""
|
||
启用生物群落生成器
|
||
|
||
Returns:
|
||
是否启用成功
|
||
"""
|
||
try:
|
||
if not self.initialized:
|
||
print("✗ 生物群落生成器未初始化")
|
||
return False
|
||
|
||
self.enabled = True
|
||
print("✓ 生物群落生成器已启用")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落生成器启用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def disable(self):
|
||
"""禁用生物群落生成器"""
|
||
try:
|
||
self.enabled = False
|
||
print("✓ 生物群落生成器已禁用")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落生成器禁用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def finalize(self):
|
||
"""清理生物群落生成器资源"""
|
||
try:
|
||
self.disable()
|
||
self.initialized = False
|
||
print("✓ 生物群落生成器资源已清理")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落生成器资源清理失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def update(self, dt: float):
|
||
"""
|
||
更新生物群落生成器状态
|
||
|
||
Args:
|
||
dt: 时间增量
|
||
"""
|
||
# 处理更新逻辑
|
||
pass
|
||
|
||
def generate_biomes(self, heightmap: np.ndarray, seed: int = None) -> Dict[str, np.ndarray]:
|
||
"""
|
||
生成生物群落分布
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
包含生物群落图、湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 生物群落生成器未启用")
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成生物群落分布...")
|
||
|
||
# 生成气候数据
|
||
print(" → 生成气候数据...")
|
||
climate_data = self._generate_climate_data(heightmap)
|
||
moisture_map = climate_data['moisture']
|
||
temperature_map = climate_data['temperature']
|
||
|
||
# 生成生物群落图
|
||
print(" → 生成生物群落图...")
|
||
biome_map = self._generate_biome_map(heightmap, moisture_map, temperature_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['biomes_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
|
||
# 更新生物群落分布统计
|
||
self._update_biome_distribution_stats(biome_map)
|
||
|
||
print(f"✓ 生物群落生成完成,耗时: {generation_time:.2f}秒")
|
||
return {
|
||
'biome_map': biome_map,
|
||
'moisture_map': moisture_map,
|
||
'temperature_map': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
def _generate_climate_data(self, heightmap: np.ndarray) -> Dict[str, np.ndarray]:
|
||
"""
|
||
生成气候数据(湿度和温度)
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
|
||
Returns:
|
||
包含湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
height, width = heightmap.shape
|
||
|
||
# 创建气候图
|
||
moisture_map = np.zeros((height, width), dtype=np.float32)
|
||
temperature_map = np.zeros((height, width), dtype=np.float32)
|
||
|
||
# 计算中心点(用于纬度计算)
|
||
center_y = height / 2.0
|
||
|
||
# 生成基础噪声
|
||
if self.noise_generator:
|
||
# 生成湿度噪声
|
||
for y in range(height):
|
||
for x in range(width):
|
||
moisture_noise = self.noise_generator.generate_fractal_noise(
|
||
x / self.distribution_params['humidity_scale'],
|
||
y / self.distribution_params['humidity_scale'],
|
||
self.seed,
|
||
octaves=4,
|
||
persistence=self.climate_params['humidity_persistence'],
|
||
lacunarity=self.climate_params['humidity_lacunarity']
|
||
)
|
||
moisture_map[y, x] = (moisture_noise + 1.0) / 2.0 # 转换到0-1范围
|
||
|
||
# 生成温度噪声
|
||
for y in range(height):
|
||
for x in range(width):
|
||
temperature_noise = self.noise_generator.generate_noise(
|
||
x / self.distribution_params['temperature_scale'],
|
||
y / self.distribution_params['temperature_scale'],
|
||
self.seed + 10000
|
||
)
|
||
temperature_map[y, x] = (temperature_noise + 1.0) / 2.0
|
||
|
||
else:
|
||
# 如果没有噪声生成器,使用简单的梯度
|
||
moisture_map = np.random.rand(height, width).astype(np.float32)
|
||
temperature_map = np.random.rand(height, width).astype(np.float32)
|
||
|
||
# 应用纬度影响(赤道热,两极冷)
|
||
for y in range(height):
|
||
# 计算纬度因子(赤道=1,两极=0)
|
||
latitude_factor = 1.0 - abs(y - center_y) / center_y
|
||
base_temperature = self.climate_params['pole_temperature'] + \
|
||
(self.climate_params['equator_temperature'] - self.climate_params['pole_temperature']) * latitude_factor
|
||
|
||
for x in range(width):
|
||
# 应用基础温度
|
||
temperature_map[y, x] = base_temperature + (temperature_map[y, x] - 0.5) * 0.2
|
||
|
||
# 根据高度调整温度(越高越冷)
|
||
height_factor = heightmap[y, x]
|
||
temperature_map[y, x] -= height_factor * self.climate_params['mountain_temperature_drop']
|
||
|
||
# 海洋温度调整
|
||
if heightmap[y, x] < 0.2: # 假设低于0.2为海洋
|
||
temperature_map[y, x] += self.climate_params['ocean_temperature_modifier']
|
||
|
||
# 确保值在0-1范围内
|
||
moisture_map = np.clip(moisture_map, 0.0, 1.0)
|
||
temperature_map = np.clip(temperature_map, 0.0, 1.0)
|
||
|
||
return {
|
||
'moisture': moisture_map,
|
||
'temperature': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 气候数据生成失败: {e}")
|
||
height, width = heightmap.shape
|
||
return {
|
||
'moisture': np.random.rand(height, width).astype(np.float32),
|
||
'temperature': np.random.rand(height, width).astype(np.float32)
|
||
}
|
||
|
||
def _generate_biome_map(self, heightmap: np.ndarray, moisture_map: np.ndarray,
|
||
temperature_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
生成生物群落图
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
moisture_map: 湿度图数据
|
||
temperature_map: 温度图数据
|
||
|
||
Returns:
|
||
生物群落图
|
||
"""
|
||
try:
|
||
height, width = heightmap.shape
|
||
biome_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 为每个像素确定生物群落类型
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 获取当前像素的属性
|
||
height_value = heightmap[y, x]
|
||
moisture_value = moisture_map[y, x]
|
||
temperature_value = temperature_map[y, x]
|
||
|
||
# 根据属性确定生物群落
|
||
biome_type = self._determine_biome(height_value, moisture_value, temperature_value)
|
||
biome_map[y, x] = biome_type
|
||
|
||
# 应用边缘混合以减少生物群落之间的硬边界
|
||
biome_map = self._apply_edge_blending(biome_map, heightmap, moisture_map, temperature_map)
|
||
|
||
return biome_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落图生成失败: {e}")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def _determine_biome(self, height: float, moisture: float, temperature: float) -> int:
|
||
"""
|
||
根据属性确定生物群落类型
|
||
|
||
Args:
|
||
height: 高度值 (0-1)
|
||
moisture: 湿度值 (0-1)
|
||
temperature: 温度值 (0-1)
|
||
|
||
Returns:
|
||
生物群落类型ID
|
||
"""
|
||
try:
|
||
# 海洋
|
||
if height < 0.2:
|
||
return 0 # ocean
|
||
|
||
# 海滩
|
||
if height < 0.25:
|
||
return 1 # beach
|
||
|
||
# 根据湿度和温度组合确定生物群落
|
||
if temperature > 0.7: # 热
|
||
if moisture > 0.7: # 湿
|
||
return 4 # jungle
|
||
elif moisture > 0.3: # 中等湿度
|
||
return 11 # savanna
|
||
else: # 干燥
|
||
return 5 # desert
|
||
elif temperature > 0.4: # 温暖
|
||
if moisture > 0.7: # 湿
|
||
if height > 0.7: # 高海拔
|
||
return 3 # forest (山地森林)
|
||
else:
|
||
return 3 # forest
|
||
elif moisture > 0.4: # 中等湿度
|
||
return 2 # plains
|
||
else: # 干燥
|
||
return 2 # plains
|
||
else: # 冷
|
||
if moisture > 0.6: # 湿
|
||
if height > 0.8: # 高海拔
|
||
return 7 # snow
|
||
else:
|
||
return 8 # taiga
|
||
elif moisture > 0.3: # 中等湿度
|
||
if height > 0.6: # 高海拔
|
||
return 7 # snow
|
||
else:
|
||
return 9 # tundra
|
||
else: # 干燥
|
||
if height > 0.5: # 高海拔
|
||
return 7 # snow
|
||
else:
|
||
return 9 # tundra
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落确定失败: {e}")
|
||
return 2 # 默认为平原
|
||
|
||
def _apply_edge_blending(self, biome_map: np.ndarray, heightmap: np.ndarray,
|
||
moisture_map: np.ndarray, temperature_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
应用边缘混合以减少生物群落之间的硬边界
|
||
|
||
Args:
|
||
biome_map: 原始生物群落图
|
||
heightmap: 高度图数据
|
||
moisture_map: 湿度图数据
|
||
temperature_map: 温度图数据
|
||
|
||
Returns:
|
||
边缘混合后的生物群落图
|
||
"""
|
||
try:
|
||
height, width = biome_map.shape
|
||
blended_map = biome_map.copy()
|
||
|
||
# 混合边缘宽度
|
||
blend_width = max(1, int(min(height, width) * self.distribution_params['edge_blending']))
|
||
|
||
# 简化的边缘混合(只在生物群落边界应用)
|
||
for y in range(height):
|
||
for x in range(width):
|
||
current_biome = biome_map[y, x]
|
||
|
||
# 检查周围像素是否属于不同生物群落
|
||
different_neighbors = 0
|
||
total_neighbors = 0
|
||
|
||
for dy in range(-blend_width, blend_width + 1):
|
||
for dx in range(-blend_width, blend_width + 1):
|
||
if dy == 0 and dx == 0:
|
||
continue
|
||
|
||
ny, nx = y + dy, x + dx
|
||
if 0 <= ny < height and 0 <= nx < width:
|
||
total_neighbors += 1
|
||
if biome_map[ny, nx] != current_biome:
|
||
different_neighbors += 1
|
||
|
||
# 如果有不同邻居,则有一定概率混合
|
||
if total_neighbors > 0 and different_neighbors > 0:
|
||
blend_probability = different_neighbors / total_neighbors
|
||
if np.random.random() < blend_probability * 0.5: # 50%的混合概率
|
||
# 随机选择一个邻居的生物群落
|
||
neighbor_biomes = []
|
||
for dy in range(-1, 2):
|
||
for dx in range(-1, 2):
|
||
if dy == 0 and dx == 0:
|
||
continue
|
||
ny, nx = y + dy, x + dx
|
||
if 0 <= ny < height and 0 <= nx < width:
|
||
neighbor_biomes.append(biome_map[ny, nx])
|
||
|
||
if neighbor_biomes:
|
||
blended_map[y, x] = np.random.choice(neighbor_biomes)
|
||
|
||
return blended_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 边缘混合应用失败: {e}")
|
||
return biome_map
|
||
|
||
def _update_biome_distribution_stats(self, biome_map: np.ndarray):
|
||
"""更新生物群落分布统计"""
|
||
try:
|
||
unique, counts = np.unique(biome_map, return_counts=True)
|
||
for biome_id, count in zip(unique, counts):
|
||
biome_name = self.biome_types.get(biome_id, {}).get('name', f'biome_{biome_id}')
|
||
self.stats['biome_distribution'][biome_name] = self.stats['biome_distribution'].get(biome_name, 0) + count
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落分布统计更新失败: {e}")
|
||
|
||
def set_seed(self, seed: int):
|
||
"""
|
||
设置随机种子
|
||
|
||
Args:
|
||
seed: 随机种子
|
||
"""
|
||
self.seed = seed
|
||
print(f"✓ 生物群落生成器随机种子设置为: {seed}")
|
||
|
||
def set_distribution_parameters(self, params: Dict[str, float]):
|
||
"""
|
||
设置分布参数
|
||
|
||
Args:
|
||
params: 分布参数字典
|
||
"""
|
||
self.distribution_params.update(params)
|
||
print(f"✓ 生物群落分布参数已更新: {self.distribution_params}")
|
||
|
||
def set_climate_parameters(self, params: Dict[str, float]):
|
||
"""
|
||
设置气候参数
|
||
|
||
Args:
|
||
params: 气候参数字典
|
||
"""
|
||
self.climate_params.update(params)
|
||
print(f"✓ 气候参数已更新: {self.climate_params}")
|
||
|
||
def get_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取统计信息
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
# 更新平均生成时间
|
||
if self.stats['biomes_generated'] > 0:
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
return self.stats.copy()
|
||
|
||
def get_biome_info(self, biome_id: int) -> Dict[str, Any]:
|
||
"""
|
||
获取生物群落信息
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
|
||
Returns:
|
||
生物群落信息字典
|
||
"""
|
||
return self.biome_types.get(biome_id, {
|
||
'name': f'unknown_{biome_id}',
|
||
'color': (128, 128, 128),
|
||
'humidity': 0.5,
|
||
'temperature': 0.5
|
||
})
|
||
|
||
def get_available_biomes(self) -> List[Dict[str, Any]]:
|
||
"""
|
||
获取可用的生物群落列表
|
||
|
||
Returns:
|
||
生物群落信息列表
|
||
"""
|
||
return [self.get_biome_info(biome_id) for biome_id in sorted(self.biome_types.keys())]
|
||
|
||
def generate_biome_with_zones(self, heightmap: np.ndarray, zones: Dict[str, Any],
|
||
seed: int = None) -> Dict[str, np.ndarray]:
|
||
"""
|
||
根据区域定义生成生物群落
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
zones: 区域定义字典
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
包含生物群落图、湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 生物群落生成器未启用")
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成区域化生物群落分布...")
|
||
|
||
# 生成基础气候数据
|
||
climate_data = self._generate_climate_data(heightmap)
|
||
moisture_map = climate_data['moisture']
|
||
temperature_map = climate_data['temperature']
|
||
|
||
# 创建生物群落图
|
||
height, width = heightmap.shape
|
||
biome_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 首先生成基础生物群落
|
||
print(" → 生成基础生物群落...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
moisture_value = moisture_map[y, x]
|
||
temperature_value = temperature_map[y, x]
|
||
biome_map[y, x] = self._determine_biome(height_value, moisture_value, temperature_value)
|
||
|
||
# 然后应用区域定义
|
||
print(" → 应用区域定义...")
|
||
for zone_name, zone_params in zones.items():
|
||
zone_biome = zone_params.get('biome', 2) # 默认为平原
|
||
zone_position = zone_params.get('position', (0, 0))
|
||
zone_size = zone_params.get('size', (width, height))
|
||
|
||
x_start, y_start = zone_position
|
||
zone_width, zone_height = zone_size
|
||
|
||
# 确保区域在地图范围内
|
||
x_start = max(0, min(x_start, width - 1))
|
||
y_start = max(0, min(y_start, height - 1))
|
||
x_end = min(width, x_start + zone_width)
|
||
y_end = min(height, y_start + zone_height)
|
||
|
||
# 在区域中应用指定的生物群落
|
||
for y in range(y_start, y_end):
|
||
for x in range(x_start, x_end):
|
||
biome_map[y, x] = zone_biome
|
||
|
||
# 应用边缘混合
|
||
biome_map = self._apply_edge_blending(biome_map, heightmap, moisture_map, temperature_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['biomes_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
|
||
# 更新生物群落分布统计
|
||
self._update_biome_distribution_stats(biome_map)
|
||
|
||
print(f"✓ 区域化生物群落生成完成,耗时: {generation_time:.2f}秒")
|
||
return {
|
||
'biome_map': biome_map,
|
||
'moisture_map': moisture_map,
|
||
'temperature_map': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 区域化生物群落生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
height, width = heightmap.shape
|
||
return {
|
||
'biome_map': np.zeros((height, width), dtype=np.int32),
|
||
'moisture_map': np.zeros((height, width), dtype=np.float32),
|
||
'temperature_map': np.zeros((height, width), dtype=np.float32)
|
||
}
|
||
|
||
def generate_biome_from_rules(self, heightmap: np.ndarray, rules: List[Dict[str, Any]],
|
||
seed: int = None) -> Dict[str, np.ndarray]:
|
||
"""
|
||
根据规则生成生物群落
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
rules: 生物群落生成规则列表
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
包含生物群落图、湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 生物群落生成器未启用")
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始基于规则生成生物群落...")
|
||
|
||
# 生成基础气候数据
|
||
climate_data = self._generate_climate_data(heightmap)
|
||
moisture_map = climate_data['moisture']
|
||
temperature_map = climate_data['temperature']
|
||
|
||
# 创建生物群落图
|
||
height, width = heightmap.shape
|
||
biome_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 根据规则生成生物群落
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
moisture_value = moisture_map[y, x]
|
||
temperature_value = temperature_map[y, x]
|
||
|
||
# 应用规则
|
||
biome_type = self._apply_biome_rules(
|
||
height_value, moisture_value, temperature_value, rules
|
||
)
|
||
biome_map[y, x] = biome_type
|
||
|
||
# 应用边缘混合
|
||
biome_map = self._apply_edge_blending(biome_map, heightmap, moisture_map, temperature_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['biomes_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
|
||
# 更新生物群落分布统计
|
||
self._update_biome_distribution_stats(biome_map)
|
||
|
||
print(f"✓ 基于规则的生物群落生成完成,耗时: {generation_time:.2f}秒")
|
||
return {
|
||
'biome_map': biome_map,
|
||
'moisture_map': moisture_map,
|
||
'temperature_map': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 基于规则的生物群落生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
height, width = heightmap.shape
|
||
return {
|
||
'biome_map': np.zeros((height, width), dtype=np.int32),
|
||
'moisture_map': np.zeros((height, width), dtype=np.float32),
|
||
'temperature_map': np.zeros((height, width), dtype=np.float32)
|
||
}
|
||
|
||
def _apply_biome_rules(self, height: float, moisture: float, temperature: float,
|
||
rules: List[Dict[str, Any]]) -> int:
|
||
"""
|
||
根据规则确定生物群落类型
|
||
|
||
Args:
|
||
height: 高度值
|
||
moisture: 湿度值
|
||
temperature: 温度值
|
||
rules: 规则列表
|
||
|
||
Returns:
|
||
生物群落类型ID
|
||
"""
|
||
try:
|
||
# 遍历规则
|
||
for rule in rules:
|
||
# 检查条件是否满足
|
||
conditions = rule.get('conditions', {})
|
||
match = True
|
||
|
||
# 检查高度条件
|
||
if 'height' in conditions:
|
||
height_range = conditions['height']
|
||
if not (height_range[0] <= height <= height_range[1]):
|
||
match = False
|
||
|
||
# 检查湿度条件
|
||
if 'moisture' in conditions:
|
||
moisture_range = conditions['moisture']
|
||
if not (moisture_range[0] <= moisture <= moisture_range[1]):
|
||
match = False
|
||
|
||
# 检查温度条件
|
||
if 'temperature' in conditions:
|
||
temperature_range = conditions['temperature']
|
||
if not (temperature_range[0] <= temperature <= temperature_range[1]):
|
||
match = False
|
||
|
||
# 如果所有条件都满足,应用该规则
|
||
if match:
|
||
return rule.get('biome', 2) # 默认为平原
|
||
|
||
# 如果没有规则匹配,使用默认方法
|
||
return self._determine_biome(height, moisture, temperature)
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落规则应用失败: {e}")
|
||
return self._determine_biome(height, moisture, temperature)
|
||
|
||
def generate_biome_with_influence_maps(self, heightmap: np.ndarray,
|
||
influence_maps: Dict[str, np.ndarray],
|
||
seed: int = None) -> Dict[str, np.ndarray]:
|
||
"""
|
||
使用影响力图生成生物群落
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
influence_maps: 影响力图字典(如'forest_influence', 'desert_influence'等)
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
包含生物群落图、湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 生物群落生成器未启用")
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始使用影响力图生成生物群落...")
|
||
|
||
# 生成基础气候数据
|
||
climate_data = self._generate_climate_data(heightmap)
|
||
moisture_map = climate_data['moisture']
|
||
temperature_map = climate_data['temperature']
|
||
|
||
# 创建生物群落图
|
||
height, width = heightmap.shape
|
||
biome_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 为每个像素根据影响力图确定生物群落
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
moisture_value = moisture_map[y, x]
|
||
temperature_value = temperature_map[y, x]
|
||
|
||
# 获取影响力值
|
||
influences = {}
|
||
for biome_name, influence_map in influence_maps.items():
|
||
if 0 <= y < influence_map.shape[0] and 0 <= x < influence_map.shape[1]:
|
||
influences[biome_name] = influence_map[y, x]
|
||
else:
|
||
influences[biome_name] = 0.0
|
||
|
||
# 根据影响力确定生物群落
|
||
biome_type = self._determine_biome_by_influence(
|
||
height_value, moisture_value, temperature_value, influences
|
||
)
|
||
biome_map[y, x] = biome_type
|
||
|
||
# 应用边缘混合
|
||
biome_map = self._apply_edge_blending(biome_map, heightmap, moisture_map, temperature_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['biomes_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
|
||
# 更新生物群落分布统计
|
||
self._update_biome_distribution_stats(biome_map)
|
||
|
||
print(f"✓ 基于影响力图的生物群落生成完成,耗时: {generation_time:.2f}秒")
|
||
return {
|
||
'biome_map': biome_map,
|
||
'moisture_map': moisture_map,
|
||
'temperature_map': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 基于影响力图的生物群落生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
height, width = heightmap.shape
|
||
return {
|
||
'biome_map': np.zeros((height, width), dtype=np.int32),
|
||
'moisture_map': np.zeros((height, width), dtype=np.float32),
|
||
'temperature_map': np.zeros((height, width), dtype=np.float32)
|
||
}
|
||
|
||
def _determine_biome_by_influence(self, height: float, moisture: float, temperature: float,
|
||
influences: Dict[str, float]) -> int:
|
||
"""
|
||
根据影响力确定生物群落类型
|
||
|
||
Args:
|
||
height: 高度值
|
||
moisture: 湿度值
|
||
temperature: 温度值
|
||
influences: 各生物群落的影响力
|
||
|
||
Returns:
|
||
生物群落类型ID
|
||
"""
|
||
try:
|
||
# 如果有任何影响力大于阈值,选择影响力最大的生物群落
|
||
max_influence = 0.0
|
||
selected_biome = None
|
||
|
||
for biome_name, influence in influences.items():
|
||
if influence > max_influence and influence > 0.3: # 阈值
|
||
max_influence = influence
|
||
# 将生物群落名称映射到ID
|
||
for biome_id, biome_info in self.biome_types.items():
|
||
if biome_info['name'] == biome_name:
|
||
selected_biome = biome_id
|
||
break
|
||
|
||
# 如果有选定的生物群落,返回它
|
||
if selected_biome is not None:
|
||
return selected_biome
|
||
|
||
# 否则使用默认方法
|
||
return self._determine_biome(height, moisture, temperature)
|
||
|
||
except Exception as e:
|
||
print(f"✗ 基于影响力的生物群落确定失败: {e}")
|
||
return self._determine_biome(height, moisture, temperature)
|
||
|
||
def generate_biome_with_erosion_influence(self, heightmap: np.ndarray,
|
||
erosion_map: np.ndarray,
|
||
sediment_map: np.ndarray,
|
||
seed: int = None) -> Dict[str, np.ndarray]:
|
||
"""
|
||
考虑侵蚀和沉积影响生成生物群落
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
erosion_map: 侵蚀图数据
|
||
sediment_map: 沉积图数据
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
包含生物群落图、湿度图和温度图的字典
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 生物群落生成器未启用")
|
||
return {
|
||
'biome_map': np.zeros_like(heightmap, dtype=np.int32),
|
||
'moisture_map': np.zeros_like(heightmap, dtype=np.float32),
|
||
'temperature_map': np.zeros_like(heightmap, dtype=np.float32)
|
||
}
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成受侵蚀影响的生物群落...")
|
||
|
||
# 生成基础气候数据
|
||
climate_data = self._generate_climate_data(heightmap)
|
||
moisture_map = climate_data['moisture']
|
||
temperature_map = climate_data['temperature']
|
||
|
||
# 创建生物群落图
|
||
height, width = heightmap.shape
|
||
biome_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 考虑侵蚀和沉积影响生成生物群落
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
moisture_value = moisture_map[y, x]
|
||
temperature_value = temperature_map[y, x]
|
||
erosion_value = erosion_map[y, x] if y < erosion_map.shape[0] and x < erosion_map.shape[1] else 0
|
||
sediment_value = sediment_map[y, x] if y < sediment_map.shape[0] and x < sediment_map.shape[1] else 0
|
||
|
||
# 调整湿度和温度以考虑侵蚀影响
|
||
adjusted_moisture = moisture_value + sediment_value * 0.2 - erosion_value * 0.1
|
||
adjusted_temperature = temperature_value - erosion_value * 0.1
|
||
|
||
# 确保调整后的值在有效范围内
|
||
adjusted_moisture = max(0.0, min(1.0, adjusted_moisture))
|
||
adjusted_temperature = max(0.0, min(1.0, adjusted_temperature))
|
||
|
||
# 确定生物群落
|
||
biome_map[y, x] = self._determine_biome(height_value, adjusted_moisture, adjusted_temperature)
|
||
|
||
# 应用边缘混合
|
||
biome_map = self._apply_edge_blending(biome_map, heightmap, moisture_map, temperature_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['biomes_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['biomes_generated']
|
||
|
||
# 更新生物群落分布统计
|
||
self._update_biome_distribution_stats(biome_map)
|
||
|
||
print(f"✓ 受侵蚀影响的生物群落生成完成,耗时: {generation_time:.2f}秒")
|
||
return {
|
||
'biome_map': biome_map,
|
||
'moisture_map': moisture_map,
|
||
'temperature_map': temperature_map
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 受侵蚀影响的生物群落生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
height, width = heightmap.shape
|
||
return {
|
||
'biome_map': np.zeros((height, width), dtype=np.int32),
|
||
'moisture_map': np.zeros((height, width), dtype=np.float32),
|
||
'temperature_map': np.zeros((height, width), dtype=np.float32)
|
||
}
|
||
|
||
def blend_biome_maps(self, biome_maps: List[np.ndarray], weights: List[float] = None) -> np.ndarray:
|
||
"""
|
||
混合多个生物群落图
|
||
|
||
Args:
|
||
biome_maps: 生物群落图列表
|
||
weights: 权重列表(如果为None,则平均分配权重)
|
||
|
||
Returns:
|
||
混合后的生物群落图
|
||
"""
|
||
try:
|
||
if not biome_maps:
|
||
return np.array([])
|
||
|
||
if len(biome_maps) == 1:
|
||
return biome_maps[0].copy()
|
||
|
||
# 确保所有生物群落图尺寸相同
|
||
base_shape = biome_maps[0].shape
|
||
for bm in biome_maps:
|
||
if bm.shape != base_shape:
|
||
raise ValueError("所有生物群落图必须具有相同的尺寸")
|
||
|
||
# 处理权重
|
||
if weights is None:
|
||
weights = [1.0 / len(biome_maps)] * len(biome_maps)
|
||
elif len(weights) != len(biome_maps):
|
||
raise ValueError("权重数量必须与生物群落图数量相同")
|
||
|
||
# 归一化权重
|
||
total_weight = sum(weights)
|
||
if total_weight > 0:
|
||
weights = [w / total_weight for w in weights]
|
||
|
||
# 混合生物群落图
|
||
height, width = base_shape
|
||
blended = np.zeros(base_shape, dtype=np.int32)
|
||
|
||
# 为每个像素计算加权生物群落类型
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 计算每个生物群落类型的加权投票
|
||
biome_votes = {}
|
||
for i, (bm, weight) in enumerate(zip(biome_maps, weights)):
|
||
biome_type = bm[y, x]
|
||
biome_votes[biome_type] = biome_votes.get(biome_type, 0.0) + weight
|
||
|
||
# 选择得票最多的生物群落类型
|
||
blended[y, x] = max(biome_votes, key=biome_votes.get)
|
||
|
||
return blended
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落图混合失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
# 返回第一个生物群落图的副本
|
||
return biome_maps[0].copy() if biome_maps else np.array([])
|
||
|
||
def smooth_biome_map(self, biome_map: np.ndarray, iterations: int = 1) -> np.ndarray:
|
||
"""
|
||
平滑生物群落图
|
||
|
||
Args:
|
||
biome_map: 原始生物群落图
|
||
iterations: 平滑迭代次数
|
||
|
||
Returns:
|
||
平滑后的生物群落图
|
||
"""
|
||
try:
|
||
smoothed = biome_map.copy()
|
||
height, width = smoothed.shape
|
||
|
||
for _ in range(iterations):
|
||
new_map = smoothed.copy()
|
||
|
||
# 对每个像素进行平滑处理
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 统计邻居的生物群落类型
|
||
neighbor_types = []
|
||
|
||
# 检查8个邻居
|
||
for dy in [-1, 0, 1]:
|
||
for dx in [-1, 0, 1]:
|
||
if dy == 0 and dx == 0:
|
||
continue
|
||
|
||
ny, nx = y + dy, x + dx
|
||
if 0 <= ny < height and 0 <= nx < width:
|
||
neighbor_types.append(smoothed[ny, nx])
|
||
|
||
# 如果大多数邻居是相同类型,则改变当前像素
|
||
if neighbor_types:
|
||
unique, counts = np.unique(neighbor_types, return_counts=True)
|
||
most_common_type = unique[np.argmax(counts)]
|
||
most_common_count = np.max(counts)
|
||
|
||
# 如果超过一半的邻居是相同类型,则采用该类型
|
||
if most_common_count > len(neighbor_types) / 2:
|
||
new_map[y, x] = most_common_type
|
||
|
||
smoothed = new_map
|
||
|
||
return smoothed
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落图平滑失败: {e}")
|
||
return biome_map
|
||
|
||
def generate_biome_colors(self, biome_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
为生物群落图生成颜色表示
|
||
|
||
Args:
|
||
biome_map: 生物群落图
|
||
|
||
Returns:
|
||
颜色图 (height, width, 3)
|
||
"""
|
||
try:
|
||
height, width = biome_map.shape
|
||
color_map = np.zeros((height, width, 3), dtype=np.uint8)
|
||
|
||
# 为每个生物群落类型分配颜色
|
||
for y in range(height):
|
||
for x in range(width):
|
||
biome_id = biome_map[y, x]
|
||
biome_info = self.biome_types.get(biome_id, {'color': (128, 128, 128)})
|
||
color_map[y, x] = biome_info['color']
|
||
|
||
return color_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落颜色图生成失败: {e}")
|
||
height, width = biome_map.shape
|
||
return np.zeros((height, width, 3), dtype=np.uint8)
|
||
|
||
def export_biome_legend(self, filename: str) -> bool:
|
||
"""
|
||
导出生物群落图例
|
||
|
||
Args:
|
||
filename: 文件名
|
||
|
||
Returns:
|
||
是否导出成功
|
||
"""
|
||
try:
|
||
import json
|
||
|
||
legend_data = {}
|
||
for biome_id, biome_info in self.biome_types.items():
|
||
legend_data[biome_id] = {
|
||
'name': biome_info['name'],
|
||
'color': biome_info['color'],
|
||
'humidity': biome_info['humidity'],
|
||
'temperature': biome_info['temperature']
|
||
}
|
||
|
||
with open(filename, 'w', encoding='utf-8') as f:
|
||
json.dump(legend_data, f, ensure_ascii=False, indent=2)
|
||
|
||
print(f"✓ 生物群落图例已导出到: {filename}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落图例导出失败: {e}")
|
||
return False
|
||
|
||
def import_biome_legend(self, filename: str) -> bool:
|
||
"""
|
||
导入生物群落图例
|
||
|
||
Args:
|
||
filename: 文件名
|
||
|
||
Returns:
|
||
是否导入成功
|
||
"""
|
||
try:
|
||
import json
|
||
|
||
with open(filename, 'r', encoding='utf-8') as f:
|
||
legend_data = json.load(f)
|
||
|
||
# 更新生物群落类型
|
||
self.biome_types = {}
|
||
for biome_id, biome_info in legend_data.items():
|
||
self.biome_types[int(biome_id)] = {
|
||
'name': biome_info['name'],
|
||
'color': tuple(biome_info['color']),
|
||
'humidity': biome_info['humidity'],
|
||
'temperature': biome_info['temperature']
|
||
}
|
||
|
||
print(f"✓ 生物群落图例已从 {filename} 导入")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生物群落图例导入失败: {e}")
|
||
return False
|
||
|
||
def get_biome_distribution_stats(self) -> Dict[str, int]:
|
||
"""
|
||
获取生物群落分布统计
|
||
|
||
Returns:
|
||
生物群落分布统计字典
|
||
"""
|
||
return self.stats['biome_distribution'].copy()
|
||
|
||
def reset_stats(self):
|
||
"""重置统计信息"""
|
||
self.stats = {
|
||
'biomes_generated': 0,
|
||
'total_generation_time': 0.0,
|
||
'average_generation_time': 0.0,
|
||
'biome_distribution': {}
|
||
}
|
||
print("✓ 生物群落生成器统计信息已重置")
|
||
|
||
def set_biome_types(self, biome_types: Dict[int, Dict[str, Any]]):
|
||
"""
|
||
设置生物群落类型
|
||
|
||
Args:
|
||
biome_types: 生物群落类型字典
|
||
"""
|
||
self.biome_types = biome_types
|
||
print(f"✓ 生物群落类型已更新,共 {len(biome_types)} 种生物群落")
|
||
|
||
def add_biome_type(self, biome_id: int, biome_info: Dict[str, Any]):
|
||
"""
|
||
添加生物群落类型
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
biome_info: 生物群落信息
|
||
"""
|
||
self.biome_types[biome_id] = biome_info
|
||
print(f"✓ 生物群落类型 {biome_info['name']} (ID: {biome_id}) 已添加")
|
||
|
||
def remove_biome_type(self, biome_id: int):
|
||
"""
|
||
移除生物群落类型
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
"""
|
||
if biome_id in self.biome_types:
|
||
biome_name = self.biome_types[biome_id]['name']
|
||
del self.biome_types[biome_id]
|
||
print(f"✓ 生物群落类型 {biome_name} (ID: {biome_id}) 已移除")
|
||
else:
|
||
print(f"✗ 无效的生物群落ID: {biome_id}")
|
||
|
||
def modify_biome_type(self, biome_id: int, modifications: Dict[str, Any]):
|
||
"""
|
||
修改生物群落类型
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
modifications: 修改内容
|
||
"""
|
||
if biome_id in self.biome_types:
|
||
self.biome_types[biome_id].update(modifications)
|
||
print(f"✓ 生物群落类型 (ID: {biome_id}) 已修改")
|
||
else:
|
||
print(f"✗ 无效的生物群落ID: {biome_id}") |