EG/plugins/user/procedural_terrain_generation/biomes/biome_generator.py
2025-12-12 16:16:15 +08:00

1275 lines
51 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
生物群落生成器
负责生成程序化地形的生物群落分布
"""
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}")