1370 lines
53 KiB
Python
1370 lines
53 KiB
Python
"""
|
||
植被生成器
|
||
负责生成程序化地形的植被分布
|
||
"""
|
||
|
||
import numpy as np
|
||
import math
|
||
from typing import Dict, Any, List, Tuple
|
||
import random
|
||
|
||
class VegetationGenerator:
|
||
"""
|
||
植被生成器
|
||
负责生成程序化地形的植被分布,基于生物群落、湿度、温度等因素
|
||
"""
|
||
|
||
def __init__(self, plugin):
|
||
"""
|
||
初始化植被生成器
|
||
|
||
Args:
|
||
plugin: 程序化地形生成插件实例
|
||
"""
|
||
self.plugin = plugin
|
||
self.enabled = False
|
||
self.initialized = False
|
||
|
||
# 植被配置
|
||
self.seed = plugin.config.get('seed', 12345)
|
||
|
||
# 植被类型定义
|
||
self.vegetation_types = {
|
||
0: {'name': 'none', 'density': 0.0, 'color': (0, 0, 0)},
|
||
1: {'name': 'grass', 'density': 0.3, 'color': (144, 238, 144)},
|
||
2: {'name': 'shrub', 'density': 0.2, 'color': (107, 142, 35)},
|
||
3: {'name': 'flower', 'density': 0.1, 'color': (255, 105, 180)},
|
||
4: {'name': 'tree', 'density': 0.15, 'color': (34, 139, 34)},
|
||
5: {'name': 'pine_tree', 'density': 0.12, 'color': (0, 128, 0)},
|
||
6: {'name': 'palm_tree', 'density': 0.08, 'color': (0, 100, 0)},
|
||
7: {'name': 'cactus', 'density': 0.05, 'color': (0, 100, 0)},
|
||
8: {'name': 'bush', 'density': 0.25, 'color': (107, 142, 35)},
|
||
9: {'name': 'fern', 'density': 0.2, 'color': (34, 139, 34)},
|
||
10: {'name': 'moss', 'density': 0.15, 'color': (144, 238, 144)},
|
||
11: {'name': 'vine', 'density': 0.1, 'color': (0, 128, 0)},
|
||
12: {'name': 'seaweed', 'density': 0.4, 'color': (0, 128, 0)},
|
||
13: {'name': 'algae', 'density': 0.35, 'color': (0, 100, 0)},
|
||
14: {'name': 'kelp', 'density': 0.3, 'color': (0, 128, 0)},
|
||
15: {'name': 'coral', 'density': 0.25, 'color': (255, 99, 71)}
|
||
}
|
||
|
||
# 生物群落植被分布
|
||
self.biome_vegetation = {
|
||
0: [0, 12, 13, 14, 15], # ocean
|
||
1: [0, 1, 2], # beach
|
||
2: [0, 1, 2, 8], # plains
|
||
3: [0, 1, 2, 4, 9], # forest
|
||
4: [0, 1, 2, 4, 6, 9, 11], # jungle
|
||
5: [0, 1, 2, 7], # desert
|
||
6: [0, 1, 2, 5], # mountain
|
||
7: [0, 1, 2], # snow
|
||
8: [0, 1, 2, 5, 10], # taiga
|
||
9: [0, 1, 2], # tundra
|
||
10: [0, 1, 2, 3, 9, 10], # swamp
|
||
11: [0, 1, 2, 8] # savanna
|
||
}
|
||
|
||
# 植被生成参数
|
||
self.vegetation_params = {
|
||
'density_scale': 10.0,
|
||
'clumping_factor': 0.7,
|
||
'slope_tolerance': 0.3,
|
||
'height_min': 0.0,
|
||
'height_max': 1.0,
|
||
'humidity_min': 0.0,
|
||
'humidity_max': 1.0,
|
||
'temperature_min': 0.0,
|
||
'temperature_max': 1.0,
|
||
'edge_blending': 0.05
|
||
}
|
||
|
||
# 噪声生成器引用
|
||
self.noise_generator = None
|
||
|
||
# 植被分布图
|
||
self.vegetation_map = None
|
||
|
||
# 统计信息
|
||
self.stats = {
|
||
'vegetation_generated': 0,
|
||
'total_generation_time': 0.0,
|
||
'average_generation_time': 0.0,
|
||
'vegetation_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_vegetation(self, heightmap: np.ndarray, biome_map: np.ndarray, seed: int = None) -> np.ndarray:
|
||
"""
|
||
生成植被分布
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成植被分布...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 生成基础植被噪声
|
||
vegetation_noise = self._generate_vegetation_noise(width, height)
|
||
|
||
# 为每个像素生成植被
|
||
print(" → 生成植被类型...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 获取当前位置的属性
|
||
height_value = heightmap[y, x]
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
|
||
# 生成植被
|
||
vegetation_type = self._generate_vegetation_at_position(
|
||
x, y, height_value, biome_type, vegetation_noise[y, x]
|
||
)
|
||
self.vegetation_map[y, x] = vegetation_type
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def _generate_vegetation_noise(self, width: int, height: int) -> np.ndarray:
|
||
"""
|
||
生成植被噪声图
|
||
|
||
Args:
|
||
width: 宽度
|
||
height: 高度
|
||
|
||
Returns:
|
||
植被噪声图
|
||
"""
|
||
try:
|
||
noise_map = np.zeros((height, width), dtype=np.float32)
|
||
|
||
if self.noise_generator:
|
||
# 生成分形噪声
|
||
for y in range(height):
|
||
for x in range(width):
|
||
noise_value = self.noise_generator.generate_fractal_noise(
|
||
x / self.vegetation_params['density_scale'],
|
||
y / self.vegetation_params['density_scale'],
|
||
self.seed,
|
||
octaves=3,
|
||
persistence=0.7,
|
||
lacunarity=2.0
|
||
)
|
||
noise_map[y, x] = (noise_value + 1.0) / 2.0 # 转换到0-1范围
|
||
else:
|
||
# 如果没有噪声生成器,使用随机噪声
|
||
noise_map = np.random.rand(height, width).astype(np.float32)
|
||
|
||
return noise_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被噪声生成失败: {e}")
|
||
return np.random.rand(height, width).astype(np.float32)
|
||
|
||
def _generate_vegetation_at_position(self, x: int, y: int, height: float, biome: int, noise: float) -> int:
|
||
"""
|
||
在指定位置生成植被
|
||
|
||
Args:
|
||
x: X坐标
|
||
y: Y坐标
|
||
height: 高度值
|
||
biome: 生物群落类型
|
||
noise: 噪声值
|
||
|
||
Returns:
|
||
植被类型ID
|
||
"""
|
||
try:
|
||
# 检查高度限制
|
||
if height < self.vegetation_params['height_min'] or height > self.vegetation_params['height_max']:
|
||
return 0 # 无植被
|
||
|
||
# 获取生物群落的植被类型
|
||
biome_veg_types = self.biome_vegetation.get(biome, [0])
|
||
|
||
# 根据噪声值选择植被类型
|
||
if noise > 0.7 and len(biome_veg_types) > 1:
|
||
# 高噪声值,选择较稀有的植被
|
||
weights = [self.vegetation_types.get(v, {'density': 0.1})['density'] for v in biome_veg_types]
|
||
# 反转权重以使稀有植被更容易被选中
|
||
weights = [1.0 / (w + 0.01) for w in weights]
|
||
vegetation_type = np.random.choice(biome_veg_types, p=np.array(weights) / sum(weights))
|
||
elif noise > 0.4 and len(biome_veg_types) > 1:
|
||
# 中等噪声值,随机选择
|
||
vegetation_type = np.random.choice(biome_veg_types)
|
||
elif len(biome_veg_types) > 0:
|
||
# 低噪声值,选择常见的植被
|
||
weights = [self.vegetation_types.get(v, {'density': 0.1})['density'] for v in biome_veg_types]
|
||
vegetation_type = np.random.choice(biome_veg_types, p=np.array(weights) / sum(weights))
|
||
else:
|
||
vegetation_type = 0 # 无植被
|
||
|
||
return vegetation_type
|
||
|
||
except Exception as e:
|
||
print(f"✗ 位置植被生成失败: {e}")
|
||
return 0
|
||
|
||
def _post_process_vegetation(self, vegetation_map: np.ndarray, heightmap: np.ndarray, biome_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
后处理植被图
|
||
|
||
Args:
|
||
vegetation_map: 原始植被图
|
||
heightmap: 高度图
|
||
biome_map: 生物群落图
|
||
|
||
Returns:
|
||
处理后的植被图
|
||
"""
|
||
try:
|
||
processed = vegetation_map.copy()
|
||
height, width = processed.shape
|
||
|
||
# 应用聚类效果
|
||
processed = self._apply_clumping(processed, heightmap)
|
||
|
||
# 应用坡度限制
|
||
processed = self._apply_slope_limit(processed, heightmap)
|
||
|
||
# 应用边缘混合
|
||
processed = self._apply_edge_blending(processed)
|
||
|
||
return processed
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被后处理失败: {e}")
|
||
return vegetation_map
|
||
|
||
def _apply_clumping(self, vegetation_map: np.ndarray, heightmap: np.ndarray) -> np.ndarray:
|
||
"""
|
||
应用植被聚类效果
|
||
|
||
Args:
|
||
vegetation_map: 植被图
|
||
heightmap: 高度图
|
||
|
||
Returns:
|
||
处理后的植被图
|
||
"""
|
||
try:
|
||
processed = vegetation_map.copy()
|
||
height, width = processed.shape
|
||
clumping_factor = self.vegetation_params['clumping_factor']
|
||
|
||
# 对每个植被类型应用聚类
|
||
for y in range(height):
|
||
for x in range(width):
|
||
if vegetation_map[y, x] != 0: # 如果有植被
|
||
# 检查邻居是否有相同类型的植被
|
||
same_type_neighbors = 0
|
||
total_neighbors = 0
|
||
|
||
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:
|
||
total_neighbors += 1
|
||
if vegetation_map[ny, nx] == vegetation_map[y, x]:
|
||
same_type_neighbors += 1
|
||
|
||
# 根据邻居情况调整植被类型
|
||
if total_neighbors > 0:
|
||
similarity = same_type_neighbors / total_neighbors
|
||
if similarity < clumping_factor and np.random.random() > 0.5:
|
||
# 减少孤立的植被
|
||
processed[y, x] = 0
|
||
|
||
return processed
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被聚类应用失败: {e}")
|
||
return vegetation_map
|
||
|
||
def _apply_slope_limit(self, vegetation_map: np.ndarray, heightmap: np.ndarray) -> np.ndarray:
|
||
"""
|
||
应用坡度限制
|
||
|
||
Args:
|
||
vegetation_map: 植被图
|
||
heightmap: 高度图
|
||
|
||
Returns:
|
||
处理后的植被图
|
||
"""
|
||
try:
|
||
processed = vegetation_map.copy()
|
||
height, width = processed.shape
|
||
slope_tolerance = self.vegetation_params['slope_tolerance']
|
||
|
||
# 计算坡度并应用限制
|
||
for y in range(height):
|
||
for x in range(width):
|
||
if vegetation_map[y, x] != 0: # 如果有植被
|
||
# 计算当前位置的坡度
|
||
slope = self._calculate_slope(heightmap, x, y)
|
||
|
||
# 如果坡度太大,移除植被
|
||
if slope > slope_tolerance:
|
||
processed[y, x] = 0
|
||
|
||
return processed
|
||
|
||
except Exception as e:
|
||
print(f"✗ 坡度限制应用失败: {e}")
|
||
return vegetation_map
|
||
|
||
def _calculate_slope(self, heightmap: np.ndarray, x: int, y: int) -> float:
|
||
"""
|
||
计算指定位置的坡度
|
||
|
||
Args:
|
||
heightmap: 高度图
|
||
x: X坐标
|
||
y: Y坐标
|
||
|
||
Returns:
|
||
坡度值
|
||
"""
|
||
try:
|
||
height, width = heightmap.shape
|
||
|
||
# 计算梯度
|
||
dx = 0.0
|
||
dy = 0.0
|
||
|
||
# X方向梯度
|
||
if x > 0 and x < width - 1:
|
||
dx = (heightmap[y, x + 1] - heightmap[y, x - 1]) / 2.0
|
||
elif x == 0:
|
||
dx = heightmap[y, x + 1] - heightmap[y, x]
|
||
else:
|
||
dx = heightmap[y, x] - heightmap[y, x - 1]
|
||
|
||
# Y方向梯度
|
||
if y > 0 and y < height - 1:
|
||
dy = (heightmap[y + 1, x] - heightmap[y - 1, x]) / 2.0
|
||
elif y == 0:
|
||
dy = heightmap[y + 1, x] - heightmap[y, x]
|
||
else:
|
||
dy = heightmap[y, x] - heightmap[y - 1, x]
|
||
|
||
# 计算坡度(梯度的模)
|
||
slope = math.sqrt(dx * dx + dy * dy)
|
||
return slope
|
||
|
||
except Exception as e:
|
||
print(f"✗ 坡度计算失败: {e}")
|
||
return 0.0
|
||
|
||
def _apply_edge_blending(self, vegetation_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
应用边缘混合
|
||
|
||
Args:
|
||
vegetation_map: 植被图
|
||
|
||
Returns:
|
||
处理后的植被图
|
||
"""
|
||
try:
|
||
processed = vegetation_map.copy()
|
||
height, width = processed.shape
|
||
blend_width = max(1, int(min(height, width) * self.vegetation_params['edge_blending']))
|
||
|
||
# 简化的边缘混合
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 在边缘区域随机减少植被
|
||
if (x < blend_width or x > width - blend_width or
|
||
y < blend_width or y > height - blend_width):
|
||
if np.random.random() < 0.3: # 30%概率移除边缘植被
|
||
processed[y, x] = 0
|
||
|
||
return processed
|
||
|
||
except Exception as e:
|
||
print(f"✗ 边缘混合应用失败: {e}")
|
||
return vegetation_map
|
||
|
||
def _update_vegetation_distribution_stats(self, vegetation_map: np.ndarray):
|
||
"""更新植被分布统计"""
|
||
try:
|
||
unique, counts = np.unique(vegetation_map, return_counts=True)
|
||
for veg_id, count in zip(unique, counts):
|
||
veg_name = self.vegetation_types.get(veg_id, {}).get('name', f'vegetation_{veg_id}')
|
||
self.stats['vegetation_distribution'][veg_name] = self.stats['vegetation_distribution'].get(veg_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_vegetation_parameters(self, params: Dict[str, Any]):
|
||
"""
|
||
设置植被生成参数
|
||
|
||
Args:
|
||
params: 参数字典
|
||
"""
|
||
self.vegetation_params.update(params)
|
||
print(f"✓ 植被生成参数已更新: {self.vegetation_params}")
|
||
|
||
def get_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取统计信息
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
# 更新平均生成时间
|
||
if self.stats['vegetation_generated'] > 0:
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
return self.stats.copy()
|
||
|
||
def get_vegetation_info(self, vegetation_id: int) -> Dict[str, Any]:
|
||
"""
|
||
获取植被信息
|
||
|
||
Args:
|
||
vegetation_id: 植被ID
|
||
|
||
Returns:
|
||
植被信息字典
|
||
"""
|
||
return self.vegetation_types.get(vegetation_id, {
|
||
'name': f'unknown_{vegetation_id}',
|
||
'density': 0.1,
|
||
'color': (128, 128, 128)
|
||
})
|
||
|
||
def get_available_vegetation(self) -> List[Dict[str, Any]]:
|
||
"""
|
||
获取可用的植被类型列表
|
||
|
||
Returns:
|
||
植被信息列表
|
||
"""
|
||
return [self.get_vegetation_info(veg_id) for veg_id in sorted(self.vegetation_types.keys())]
|
||
|
||
def generate_vegetation_with_density(self, heightmap: np.ndarray, biome_map: np.ndarray,
|
||
density_map: np.ndarray, seed: int = None) -> np.ndarray:
|
||
"""
|
||
根据密度图生成植被
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
density_map: 植被密度图数据
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成基于密度的植被分布...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 确保密度图尺寸匹配
|
||
if density_map.shape != heightmap.shape:
|
||
print("✗ 密度图尺寸与高度图不匹配")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 为每个像素生成植被
|
||
print(" → 生成密度控制的植被...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
# 获取当前位置的属性
|
||
height_value = heightmap[y, x]
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
density_value = density_map[y, x]
|
||
|
||
# 根据密度值决定是否生成植被
|
||
if np.random.random() < density_value:
|
||
# 生成植被
|
||
vegetation_type = self._generate_vegetation_at_position(
|
||
x, y, height_value, biome_type, np.random.random()
|
||
)
|
||
self.vegetation_map[y, x] = vegetation_type
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 基于密度的植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 基于密度的植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def generate_forest_vegetation(self, heightmap: np.ndarray, biome_map: np.ndarray,
|
||
tree_density: float = 0.15, seed: int = None) -> np.ndarray:
|
||
"""
|
||
生成森林植被(专门用于森林区域)
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
tree_density: 树木密度
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成森林植被...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 专门处理森林生物群落(ID 3 和 4)
|
||
forest_biomes = [3, 4] # forest 和 jungle
|
||
|
||
# 为每个像素生成森林植被
|
||
print(" → 生成森林植被...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
|
||
# 只在森林生物群落中生成植被
|
||
if biome_type in forest_biomes:
|
||
# 根据树木密度决定是否生成树
|
||
if np.random.random() < tree_density:
|
||
# 选择合适的树种
|
||
if biome_type == 4: # jungle
|
||
tree_type = 6 if np.random.random() < 0.3 else 4 # 30%概率生成棕榈树
|
||
else: # forest
|
||
tree_type = 5 if height_value > 0.8 else 4 # 高海拔生成松树
|
||
|
||
self.vegetation_map[y, x] = tree_type
|
||
else:
|
||
# 生成地面植被
|
||
ground_vegetation = [1, 2, 8, 9] # grass, shrub, bush, fern
|
||
self.vegetation_map[y, x] = np.random.choice(ground_vegetation)
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 森林植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 森林植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def generate_desert_vegetation(self, heightmap: np.ndarray, biome_map: np.ndarray,
|
||
cactus_density: float = 0.05, seed: int = None) -> np.ndarray:
|
||
"""
|
||
生成沙漠植被(专门用于沙漠区域)
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
cactus_density: 仙人掌密度
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成沙漠植被...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 专门处理沙漠生物群落(ID 5)
|
||
desert_biome = 5
|
||
|
||
# 为每个像素生成沙漠植被
|
||
print(" → 生成沙漠植被...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
|
||
# 只在沙漠生物群落中生成植被
|
||
if biome_type == desert_biome:
|
||
# 根据仙人掌密度决定是否生成仙人掌
|
||
if np.random.random() < cactus_density:
|
||
self.vegetation_map[y, x] = 7 # cactus
|
||
else:
|
||
# 生成稀疏的地面植被
|
||
if np.random.random() < 0.1: # 10%概率
|
||
ground_vegetation = [1, 2, 8] # grass, shrub, bush
|
||
self.vegetation_map[y, x] = np.random.choice(ground_vegetation)
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 沙漠植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 沙漠植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def generate_water_vegetation(self, heightmap: np.ndarray, biome_map: np.ndarray,
|
||
water_level: float = 0.2, seed: int = None) -> np.ndarray:
|
||
"""
|
||
生成水下植被(专门用于水域)
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
water_level: 水位
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成水下植被...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 专门处理水域生物群落(ID 0)
|
||
water_biome = 0
|
||
|
||
# 为每个像素生成水下植被
|
||
print(" → 生成水下植被...")
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightmap[y, x]
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
|
||
# 只在水域中且低于水位的地方生成植被
|
||
if biome_type == water_biome and height_value < water_level:
|
||
# 生成水下植被
|
||
water_vegetation = [12, 13, 14, 15] # seaweed, algae, kelp, coral
|
||
self.vegetation_map[y, x] = np.random.choice(water_vegetation)
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 水下植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 水下植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
def blend_vegetation_maps(self, vegetation_maps: List[np.ndarray], weights: List[float] = None) -> np.ndarray:
|
||
"""
|
||
混合多个植被图
|
||
|
||
Args:
|
||
vegetation_maps: 植被图列表
|
||
weights: 权重列表(如果为None,则平均分配权重)
|
||
|
||
Returns:
|
||
混合后的植被图
|
||
"""
|
||
try:
|
||
if not vegetation_maps:
|
||
return np.array([])
|
||
|
||
if len(vegetation_maps) == 1:
|
||
return vegetation_maps[0].copy()
|
||
|
||
# 确保所有植被图尺寸相同
|
||
base_shape = vegetation_maps[0].shape
|
||
for vm in vegetation_maps:
|
||
if vm.shape != base_shape:
|
||
raise ValueError("所有植被图必须具有相同的尺寸")
|
||
|
||
# 处理权重
|
||
if weights is None:
|
||
weights = [1.0 / len(vegetation_maps)] * len(vegetation_maps)
|
||
elif len(weights) != len(vegetation_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):
|
||
# 计算每个植被类型的加权投票
|
||
veg_votes = {}
|
||
for i, (vm, weight) in enumerate(zip(vegetation_maps, weights)):
|
||
veg_type = vm[y, x]
|
||
veg_votes[veg_type] = veg_votes.get(veg_type, 0.0) + weight
|
||
|
||
# 选择得票最多的植被类型
|
||
blended[y, x] = max(veg_votes, key=veg_votes.get)
|
||
|
||
return blended
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被图混合失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
# 返回第一个植被图的副本
|
||
return vegetation_maps[0].copy() if vegetation_maps else np.array([])
|
||
|
||
def smooth_vegetation_map(self, vegetation_map: np.ndarray, iterations: int = 1) -> np.ndarray:
|
||
"""
|
||
平滑植被图
|
||
|
||
Args:
|
||
vegetation_map: 原始植被图
|
||
iterations: 平滑迭代次数
|
||
|
||
Returns:
|
||
平滑后的植被图
|
||
"""
|
||
try:
|
||
smoothed = vegetation_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 vegetation_map
|
||
|
||
def generate_vegetation_colors(self, vegetation_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
为植被图生成颜色表示
|
||
|
||
Args:
|
||
vegetation_map: 植被图
|
||
|
||
Returns:
|
||
颜色图 (height, width, 3)
|
||
"""
|
||
try:
|
||
height, width = vegetation_map.shape
|
||
color_map = np.zeros((height, width, 3), dtype=np.uint8)
|
||
|
||
# 为每个植被类型分配颜色
|
||
for y in range(height):
|
||
for x in range(width):
|
||
veg_id = vegetation_map[y, x]
|
||
veg_info = self.vegetation_types.get(veg_id, {'color': (128, 128, 128)})
|
||
color_map[y, x] = veg_info['color']
|
||
|
||
return color_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被颜色图生成失败: {e}")
|
||
height, width = vegetation_map.shape
|
||
return np.zeros((height, width, 3), dtype=np.uint8)
|
||
|
||
def export_vegetation_legend(self, filename: str) -> bool:
|
||
"""
|
||
导出植被图例
|
||
|
||
Args:
|
||
filename: 文件名
|
||
|
||
Returns:
|
||
是否导出成功
|
||
"""
|
||
try:
|
||
import json
|
||
|
||
legend_data = {}
|
||
for veg_id, veg_info in self.vegetation_types.items():
|
||
legend_data[veg_id] = {
|
||
'name': veg_info['name'],
|
||
'color': veg_info['color'],
|
||
'density': veg_info['density']
|
||
}
|
||
|
||
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_vegetation_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.vegetation_types = {}
|
||
for veg_id, veg_info in legend_data.items():
|
||
self.vegetation_types[int(veg_id)] = {
|
||
'name': veg_info['name'],
|
||
'color': tuple(veg_info['color']),
|
||
'density': veg_info['density']
|
||
}
|
||
|
||
print(f"✓ 植被图例已从 {filename} 导入")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被图例导入失败: {e}")
|
||
return False
|
||
|
||
def get_vegetation_distribution_stats(self) -> Dict[str, int]:
|
||
"""
|
||
获取植被分布统计
|
||
|
||
Returns:
|
||
植被分布统计字典
|
||
"""
|
||
return self.stats['vegetation_distribution'].copy()
|
||
|
||
def reset_stats(self):
|
||
"""重置统计信息"""
|
||
self.stats = {
|
||
'vegetation_generated': 0,
|
||
'total_generation_time': 0.0,
|
||
'average_generation_time': 0.0,
|
||
'vegetation_distribution': {}
|
||
}
|
||
print("✓ 植被生成器统计信息已重置")
|
||
|
||
def set_vegetation_types(self, vegetation_types: Dict[int, Dict[str, Any]]):
|
||
"""
|
||
设置植被类型
|
||
|
||
Args:
|
||
vegetation_types: 植被类型字典
|
||
"""
|
||
self.vegetation_types = vegetation_types
|
||
print(f"✓ 植被类型已更新,共 {len(vegetation_types)} 种植被")
|
||
|
||
def add_vegetation_type(self, vegetation_id: int, vegetation_info: Dict[str, Any]):
|
||
"""
|
||
添加植被类型
|
||
|
||
Args:
|
||
vegetation_id: 植被ID
|
||
vegetation_info: 植被信息
|
||
"""
|
||
self.vegetation_types[vegetation_id] = vegetation_info
|
||
print(f"✓ 植被类型 {vegetation_info['name']} (ID: {vegetation_id}) 已添加")
|
||
|
||
def remove_vegetation_type(self, vegetation_id: int):
|
||
"""
|
||
移除植被类型
|
||
|
||
Args:
|
||
vegetation_id: 植被ID
|
||
"""
|
||
if vegetation_id in self.vegetation_types:
|
||
veg_name = self.vegetation_types[vegetation_id]['name']
|
||
del self.vegetation_types[vegetation_id]
|
||
print(f"✓ 植被类型 {veg_name} (ID: {vegetation_id}) 已移除")
|
||
else:
|
||
print(f"✗ 无效的植被ID: {vegetation_id}")
|
||
|
||
def modify_vegetation_type(self, vegetation_id: int, modifications: Dict[str, Any]):
|
||
"""
|
||
修改植被类型
|
||
|
||
Args:
|
||
vegetation_id: 植被ID
|
||
modifications: 修改内容
|
||
"""
|
||
if vegetation_id in self.vegetation_types:
|
||
self.vegetation_types[vegetation_id].update(modifications)
|
||
print(f"✓ 植被类型 (ID: {vegetation_id}) 已修改")
|
||
else:
|
||
print(f"✗ 无效的植被ID: {vegetation_id}")
|
||
|
||
def set_biome_vegetation(self, biome_id: int, vegetation_types: List[int]):
|
||
"""
|
||
设置生物群落的植被类型
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
vegetation_types: 植被类型列表
|
||
"""
|
||
self.biome_vegetation[biome_id] = vegetation_types
|
||
print(f"✓ 生物群落 {biome_id} 的植被类型已设置为: {vegetation_types}")
|
||
|
||
def get_biome_vegetation(self, biome_id: int) -> List[int]:
|
||
"""
|
||
获取生物群落的植被类型
|
||
|
||
Args:
|
||
biome_id: 生物群落ID
|
||
|
||
Returns:
|
||
植被类型列表
|
||
"""
|
||
return self.biome_vegetation.get(biome_id, []).copy()
|
||
|
||
def generate_vegetation_density_map(self, heightmap: np.ndarray, biome_map: np.ndarray) -> np.ndarray:
|
||
"""
|
||
生成植被密度图
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
|
||
Returns:
|
||
植被密度图
|
||
"""
|
||
try:
|
||
height, width = heightmap.shape
|
||
density_map = np.zeros((height, width), dtype=np.float32)
|
||
|
||
# 为每个生物群落生成不同的密度模式
|
||
for y in range(height):
|
||
for x in range(width):
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
height_value = heightmap[y, x]
|
||
|
||
# 根据生物群落类型确定基础密度
|
||
if biome_type == 0: # ocean
|
||
density_map[y, x] = 0.0
|
||
elif biome_type == 1: # beach
|
||
density_map[y, x] = 0.1
|
||
elif biome_type == 2: # plains
|
||
density_map[y, x] = 0.4
|
||
elif biome_type in [3, 4]: # forest, jungle
|
||
density_map[y, x] = 0.8
|
||
elif biome_type == 5: # desert
|
||
density_map[y, x] = 0.05
|
||
elif biome_type == 6: # mountain
|
||
# 山地密度随高度变化
|
||
density_map[y, x] = max(0.0, 0.6 - height_value)
|
||
elif biome_type in [7, 8, 9]: # snow, taiga, tundra
|
||
density_map[y, x] = 0.3
|
||
elif biome_type == 10: # swamp
|
||
density_map[y, x] = 0.6
|
||
elif biome_type == 11: # savanna
|
||
density_map[y, x] = 0.3
|
||
else:
|
||
density_map[y, x] = 0.2
|
||
|
||
# 应用噪声以增加自然变化
|
||
if self.noise_generator:
|
||
for y in range(height):
|
||
for x in range(width):
|
||
noise_value = self.noise_generator.generate_noise(
|
||
x / 20.0, y / 20.0, self.seed + 5000
|
||
)
|
||
density_map[y, x] = max(0.0, min(1.0, density_map[y, x] + noise_value * 0.1))
|
||
|
||
return density_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被密度图生成失败: {e}")
|
||
return np.zeros_like(heightmap, dtype=np.float32)
|
||
|
||
def apply_vegetation_mask(self, vegetation_map: np.ndarray, mask: np.ndarray) -> np.ndarray:
|
||
"""
|
||
应用掩码到植被图
|
||
|
||
Args:
|
||
vegetation_map: 原始植被图
|
||
mask: 掩码(0-1范围,0表示移除植被)
|
||
|
||
Returns:
|
||
处理后的植被图
|
||
"""
|
||
try:
|
||
if mask.shape != vegetation_map.shape:
|
||
print("✗ 掩码尺寸与植被图不匹配")
|
||
return vegetation_map
|
||
|
||
masked = vegetation_map.copy()
|
||
|
||
# 根据掩码值调整植被
|
||
for y in range(masked.shape[0]):
|
||
for x in range(masked.shape[1]):
|
||
# 掩码值越小,移除植被的概率越大
|
||
if np.random.random() > mask[y, x]:
|
||
masked[y, x] = 0 # 移除植被
|
||
|
||
return masked
|
||
|
||
except Exception as e:
|
||
print(f"✗ 植被掩码应用失败: {e}")
|
||
return vegetation_map
|
||
|
||
def generate_clustered_vegetation(self, heightmap: np.ndarray, biome_map: np.ndarray,
|
||
cluster_density: float = 0.2, seed: int = None) -> np.ndarray:
|
||
"""
|
||
生成聚类植被
|
||
|
||
Args:
|
||
heightmap: 高度图数据
|
||
biome_map: 生物群落图数据
|
||
cluster_density: 聚类密度
|
||
seed: 随机种子
|
||
|
||
Returns:
|
||
植被分布图
|
||
"""
|
||
try:
|
||
import time
|
||
generation_start_time = time.time()
|
||
|
||
if not self.enabled:
|
||
print("✗ 植被生成器未启用")
|
||
return np.zeros_like(heightmap, dtype=np.int32)
|
||
|
||
# 更新种子
|
||
if seed is not None:
|
||
self.seed = seed
|
||
|
||
print("✓ 开始生成聚类植被...")
|
||
|
||
# 初始化随机数生成器
|
||
random.seed(self.seed)
|
||
np.random.seed(self.seed)
|
||
|
||
# 创建植被图
|
||
height, width = heightmap.shape
|
||
self.vegetation_map = np.zeros((height, width), dtype=np.int32)
|
||
|
||
# 生成聚类中心点
|
||
num_clusters = int(width * height * cluster_density / 100)
|
||
clusters = []
|
||
for _ in range(num_clusters):
|
||
x = np.random.randint(0, width)
|
||
y = np.random.randint(0, height)
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
# 获取该生物群落的植被类型
|
||
veg_types = self.biome_vegetation.get(biome_type, [1, 2]) # 默认为草和灌木
|
||
veg_type = np.random.choice(veg_types) if veg_types else 1
|
||
cluster_size = np.random.randint(5, 20) # 聚类大小
|
||
clusters.append((x, y, veg_type, cluster_size))
|
||
|
||
# 在聚类中心点周围生成植被
|
||
print(" → 生成植被聚类...")
|
||
for cx, cy, veg_type, cluster_size in clusters:
|
||
for _ in range(cluster_size):
|
||
# 在聚类中心附近随机生成植被
|
||
angle = np.random.random() * 2 * np.pi
|
||
distance = np.random.random() * cluster_size / 2
|
||
x = int(cx + np.cos(angle) * distance)
|
||
y = int(cy + np.sin(angle) * distance)
|
||
|
||
# 确保坐标在有效范围内
|
||
if 0 <= x < width and 0 <= y < height:
|
||
# 检查该位置是否适合该植被类型
|
||
height_value = heightmap[y, x]
|
||
biome_type = biome_map[y, x] if y < biome_map.shape[0] and x < biome_map.shape[1] else 0
|
||
|
||
# 简单的适宜性检查
|
||
if height_value > 0.1 and biome_type != 0: # 不在水中
|
||
self.vegetation_map[y, x] = veg_type
|
||
|
||
# 应用后处理
|
||
print(" → 应用后处理...")
|
||
self.vegetation_map = self._post_process_vegetation(self.vegetation_map, heightmap, biome_map)
|
||
|
||
# 更新统计信息
|
||
generation_time = time.time() - generation_start_time
|
||
self.stats['vegetation_generated'] += 1
|
||
self.stats['total_generation_time'] += generation_time
|
||
self.stats['average_generation_time'] = self.stats['total_generation_time'] / self.stats['vegetation_generated']
|
||
|
||
# 更新植被分布统计
|
||
self._update_vegetation_distribution_stats(self.vegetation_map)
|
||
|
||
print(f"✓ 聚类植被生成完成,耗时: {generation_time:.2f}秒")
|
||
return self.vegetation_map
|
||
|
||
except Exception as e:
|
||
print(f"✗ 聚类植被生成失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros_like(heightmap, dtype=np.int32) |