EG/plugins/user/procedural_terrain_generation/erosion/erosion_processor.py
2025-10-30 11:46:41 +08:00

1201 lines
46 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
import time
class ErosionProcessor:
"""
侵蚀处理器
负责模拟地形的侵蚀过程,包括水力侵蚀、热力侵蚀、风力侵蚀等
"""
def __init__(self, plugin):
"""
初始化侵蚀处理器
Args:
plugin: 程序化地形生成插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 侵蚀配置
self.seed = plugin.config.get('seed', 12345)
# 侵蚀参数
self.erosion_params = {
'water_erosion': {
'enabled': True,
'rate': 0.1,
'solubility': 0.8,
'evaporation': 0.01,
'iterations': 50,
'inertia': 0.05,
'sediment_capacity': 0.5,
'deposition_rate': 0.1
},
'thermal_erosion': {
'enabled': True,
'rate': 0.05,
'angle_of_repose': 30.0, # 度
'iterations': 30
},
'wind_erosion': {
'enabled': True,
'rate': 0.02,
'direction': 0.0, # 弧度
'iterations': 20
},
'chemical_erosion': {
'enabled': False,
'rate': 0.01,
'iterations': 10
},
'glacial_erosion': {
'enabled': False,
'rate': 0.03,
'iterations': 15
}
}
# 侵蚀图层
self.erosion_map = None
self.sediment_map = None
self.water_map = None
self.flow_map = None
# 统计信息
self.stats = {
'erosion_processed': 0,
'total_processing_time': 0.0,
'average_processing_time': 0.0,
'total_erosion_amount': 0.0,
'total_sediment_amount': 0.0
}
print("✓ 侵蚀处理器已创建")
def initialize(self) -> bool:
"""
初始化侵蚀处理器
Returns:
是否初始化成功
"""
try:
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 apply_erosion(self, heightmap: np.ndarray, seed: int = None) -> Dict[str, np.ndarray]:
"""
应用侵蚀效果到高度图
Args:
heightmap: 高度图数据
seed: 随机种子
Returns:
包含处理后高度图和侵蚀相关数据的字典
"""
try:
processing_start_time = time.time()
if not self.enabled:
print("✗ 侵蚀处理器未启用")
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
# 更新种子
if seed is not None:
self.seed = seed
print("✓ 开始应用侵蚀效果...")
# 初始化侵蚀图层
self._initialize_erosion_layers(heightmap.shape)
# 创建高度图副本进行处理
processed_heightmap = heightmap.copy()
# 应用各种侵蚀类型
if self.erosion_params['water_erosion']['enabled']:
print(" → 应用水力侵蚀...")
processed_heightmap = self._apply_water_erosion(processed_heightmap)
if self.erosion_params['thermal_erosion']['enabled']:
print(" → 应用热力侵蚀...")
processed_heightmap = self._apply_thermal_erosion(processed_heightmap)
if self.erosion_params['wind_erosion']['enabled']:
print(" → 应用风力侵蚀...")
processed_heightmap = self._apply_wind_erosion(processed_heightmap)
if self.erosion_params['chemical_erosion']['enabled']:
print(" → 应用化学侵蚀...")
processed_heightmap = self._apply_chemical_erosion(processed_heightmap)
if self.erosion_params['glacial_erosion']['enabled']:
print(" → 应用冰川侵蚀...")
processed_heightmap = self._apply_glacial_erosion(processed_heightmap)
# 更新统计信息
processing_time = time.time() - processing_start_time
self.stats['erosion_processed'] += 1
self.stats['total_processing_time'] += processing_time
self.stats['average_processing_time'] = self.stats['total_processing_time'] / self.stats['erosion_processed']
print(f"✓ 侵蚀处理完成,耗时: {processing_time:.2f}")
return {
'heightmap': processed_heightmap,
'erosion_map': self.erosion_map,
'sediment_map': self.sediment_map,
'water_map': self.water_map
}
except Exception as e:
print(f"✗ 侵蚀处理失败: {e}")
import traceback
traceback.print_exc()
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
def _initialize_erosion_layers(self, shape: tuple):
"""初始化侵蚀相关图层"""
try:
height, width = shape
self.erosion_map = np.zeros((height, width), dtype=np.float32)
self.sediment_map = np.zeros((height, width), dtype=np.float32)
self.water_map = np.zeros((height, width), dtype=np.float32)
self.flow_map = np.zeros((height, width, 2), dtype=np.float32) # x, y 流向
except Exception as e:
print(f"✗ 侵蚀图层初始化失败: {e}")
def _apply_water_erosion(self, heightmap: np.ndarray) -> np.ndarray:
"""
应用水力侵蚀
Args:
heightmap: 高度图数据
Returns:
处理后的高度图
"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取水力侵蚀参数
params = self.erosion_params['water_erosion']
iterations = params['iterations']
erosion_rate = params['rate']
solubility = params['solubility']
evaporation = params['evaporation']
inertia = params['inertia']
sediment_capacity = params['sediment_capacity']
deposition_rate = params['deposition_rate']
# 模拟多个迭代步骤
for iteration in range(iterations):
# 计算水流方向
self._calculate_water_flow(processed)
# 模拟侵蚀和沉积
for y in range(height):
for x in range(width):
# 计算当前点的侵蚀量
erosion_amount = erosion_rate * solubility * self.water_map[y, x]
# 应用侵蚀
if erosion_amount > 0:
processed[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
self.sediment_map[y, x] += erosion_amount
# 检查沉积(如果水流速度慢且携带沉积物过多)
sediment_ratio = self.sediment_map[y, x] / max(0.001, self.water_map[y, x])
if sediment_ratio > sediment_capacity:
deposition_amount = (sediment_ratio - sediment_capacity) * deposition_rate
processed[y, x] += deposition_amount
self.sediment_map[y, x] -= deposition_amount
# 应用蒸发
self.water_map *= (1.0 - evaporation)
# 确保高度值合理
processed = np.clip(processed, 0.0, 1.0)
# 更新统计信息
self.stats['total_erosion_amount'] += np.sum(self.erosion_map)
self.stats['total_sediment_amount'] += np.sum(self.sediment_map)
return processed
except Exception as e:
print(f"✗ 水力侵蚀应用失败: {e}")
return heightmap
def _calculate_water_flow(self, heightmap: np.ndarray):
"""
计算水流方向和累积
Args:
heightmap: 高度图数据
"""
try:
height, width = heightmap.shape
# 简化的水流计算
for y in range(height):
for x in range(width):
# 增加降雨量
self.water_map[y, x] += 0.01
# 计算流向(流向最低的邻居)
lowest_height = heightmap[y, x]
flow_x, flow_y = 0, 0
# 检查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_height = heightmap[ny, nx] + self.water_map[ny, nx]
if neighbor_height < lowest_height:
lowest_height = neighbor_height
flow_x, flow_y = dx, dy
# 设置流向
self.flow_map[y, x, 0] = flow_x
self.flow_map[y, x, 1] = flow_y
# 将水流移动到下一个位置
if flow_x != 0 or flow_y != 0:
ny, nx = y + int(flow_y), x + int(flow_x)
if 0 <= ny < height and 0 <= nx < width:
self.water_map[ny, nx] += self.water_map[y, x] * 0.8
self.water_map[y, x] *= 0.2
except Exception as e:
print(f"✗ 水流计算失败: {e}")
def _apply_thermal_erosion(self, heightmap: np.ndarray) -> np.ndarray:
"""
应用热力侵蚀(重力侵蚀)
Args:
heightmap: 高度图数据
Returns:
处理后的高度图
"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取热力侵蚀参数
params = self.erosion_params['thermal_erosion']
iterations = params['iterations']
erosion_rate = params['rate']
angle_of_repose = math.radians(params['angle_of_repose'])
tan_repose = math.tan(angle_of_repose)
# 模拟多个迭代步骤
for iteration in range(iterations):
new_heightmap = processed.copy()
# 对每个点应用热力侵蚀
for y in range(height):
for x in range(width):
current_height = processed[y, x]
# 检查4个直接邻居
for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
ny, nx = y + dy, x + dx
if 0 <= ny < height and 0 <= nx < width:
neighbor_height = processed[ny, nx]
# 计算高度差
height_diff = current_height - neighbor_height
# 如果高度差超过角度限制
if height_diff > 0:
# 计算最大稳定高度差
max_diff = tan_repose * 1.0 # 假设网格间距为1
# 如果超过最大稳定差值,则发生侵蚀
if height_diff > max_diff:
# 移动材料到较低位置
move_amount = (height_diff - max_diff) * erosion_rate
new_heightmap[y, x] -= move_amount
new_heightmap[ny, nx] += move_amount
# 更新侵蚀图
self.erosion_map[y, x] += move_amount
processed = new_heightmap
return processed
except Exception as e:
print(f"✗ 热力侵蚀应用失败: {e}")
return heightmap
def _apply_wind_erosion(self, heightmap: np.ndarray) -> np.ndarray:
"""
应用风力侵蚀
Args:
heightmap: 高度图数据
Returns:
处理后的高度图
"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取风力侵蚀参数
params = self.erosion_params['wind_erosion']
iterations = params['iterations']
erosion_rate = params['rate']
wind_direction = params['direction']
# 计算风向向量
wind_dx = math.cos(wind_direction)
wind_dy = math.sin(wind_direction)
# 模拟多个迭代步骤
for iteration in range(iterations):
new_heightmap = processed.copy()
# 对每个点应用风力侵蚀
for y in range(height):
for x in range(width):
# 风从特定方向吹来
# 计算风的来源位置
source_x = int(x - wind_dx * 2) # 2个单位距离
source_y = int(y - wind_dy * 2)
# 如果来源位置有效
if 0 <= source_y < height and 0 <= source_x < width:
# 如果当前点比来源点高,则可能发生风蚀
height_diff = processed[y, x] - processed[source_y, source_x]
if height_diff > 0.01: # 阈值
# 应用风力侵蚀
erosion_amount = erosion_rate * height_diff
new_heightmap[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
processed = new_heightmap
return processed
except Exception as e:
print(f"✗ 风力侵蚀应用失败: {e}")
return heightmap
def _apply_chemical_erosion(self, heightmap: np.ndarray) -> np.ndarray:
"""
应用化学侵蚀
Args:
heightmap: 高度图数据
Returns:
处理后的高度图
"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取化学侵蚀参数
params = self.erosion_params['chemical_erosion']
iterations = params['iterations']
erosion_rate = params['rate']
# 模拟多个迭代步骤
for iteration in range(iterations):
# 简化的化学侵蚀:基于高度和随机因素
for y in range(height):
for x in range(width):
# 化学侵蚀通常影响较低的区域(如河流、湖泊)
if processed[y, x] < 0.3: # 假设低地更容易发生化学侵蚀
# 添加随机因素
chemical_factor = np.random.random() * 0.5 + 0.5
erosion_amount = erosion_rate * chemical_factor * (0.3 - processed[y, x])
processed[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
return processed
except Exception as e:
print(f"✗ 化学侵蚀应用失败: {e}")
return heightmap
def _apply_glacial_erosion(self, heightmap: np.ndarray) -> np.ndarray:
"""
应用冰川侵蚀
Args:
heightmap: 高度图数据
Returns:
处理后的高度图
"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取冰川侵蚀参数
params = self.erosion_params['glacial_erosion']
iterations = params['iterations']
erosion_rate = params['rate']
# 模拟多个迭代步骤
for iteration in range(iterations):
# 冰川侵蚀平滑地形并创建U型谷
new_heightmap = processed.copy()
for y in range(height):
for x in range(width):
# 冰川通常在高海拔地区形成
if processed[y, x] > 0.7: # 高海拔
# 平滑周围区域
total_height = 0.0
count = 0
# 检查3x3邻域
for dy in [-1, 0, 1]:
for dx in [-1, 0, 1]:
ny, nx = y + dy, x + dx
if 0 <= ny < height and 0 <= nx < width:
total_height += processed[ny, nx]
count += 1
if count > 0:
average_height = total_height / count
# 应用冰川侵蚀(平滑化)
glacial_effect = erosion_rate * (processed[y, x] - average_height)
new_heightmap[y, x] -= glacial_effect
self.erosion_map[y, x] += abs(glacial_effect)
processed = new_heightmap
return processed
except Exception as e:
print(f"✗ 冰川侵蚀应用失败: {e}")
return heightmap
def set_seed(self, seed: int):
"""
设置随机种子
Args:
seed: 随机种子
"""
self.seed = seed
print(f"✓ 侵蚀处理器随机种子设置为: {seed}")
def set_erosion_parameters(self, erosion_type: str, params: Dict[str, Any]):
"""
设置侵蚀参数
Args:
erosion_type: 侵蚀类型
params: 参数字典
"""
if erosion_type in self.erosion_params:
self.erosion_params[erosion_type].update(params)
print(f"{erosion_type} 侵蚀参数已更新: {self.erosion_params[erosion_type]}")
else:
print(f"✗ 无效的侵蚀类型: {erosion_type}")
def enable_erosion_type(self, erosion_type: str, enabled: bool):
"""
启用/禁用侵蚀类型
Args:
erosion_type: 侵蚀类型
enabled: 是否启用
"""
if erosion_type in self.erosion_params:
self.erosion_params[erosion_type]['enabled'] = enabled
state = "启用" if enabled else "禁用"
print(f"{erosion_type} 侵蚀已{state}")
else:
print(f"✗ 无效的侵蚀类型: {erosion_type}")
def get_stats(self) -> Dict[str, Any]:
"""
获取统计信息
Returns:
统计信息字典
"""
# 更新平均处理时间
if self.stats['erosion_processed'] > 0:
self.stats['average_processing_time'] = self.stats['total_processing_time'] / self.stats['erosion_processed']
return self.stats.copy()
def reset_stats(self):
"""重置统计信息"""
self.stats = {
'erosion_processed': 0,
'total_processing_time': 0.0,
'average_processing_time': 0.0,
'total_erosion_amount': 0.0,
'total_sediment_amount': 0.0
}
print("✓ 侵蚀处理器统计信息已重置")
def get_erosion_map(self) -> np.ndarray:
"""
获取侵蚀图
Returns:
侵蚀图数据
"""
return self.erosion_map if self.erosion_map is not None else np.array([])
def get_sediment_map(self) -> np.ndarray:
"""
获取沉积物图
Returns:
沉积物图数据
"""
return self.sediment_map if self.sediment_map is not None else np.array([])
def get_water_map(self) -> np.ndarray:
"""
获取水图
Returns:
水图数据
"""
return self.water_map if self.water_map is not None else np.array([])
def set_water_erosion_parameters(self, rate: float = None, solubility: float = None,
evaporation: float = None, inertia: float = None,
sediment_capacity: float = None, deposition_rate: float = None):
"""
设置水力侵蚀参数
Args:
rate: 侵蚀速率
solubility: 溶解度
evaporation: 蒸发率
inertia: 惯性
sediment_capacity: 沉积物容量
deposition_rate: 沉积率
"""
params = self.erosion_params['water_erosion']
if rate is not None:
params['rate'] = max(0.0, rate)
if solubility is not None:
params['solubility'] = max(0.0, min(1.0, solubility))
if evaporation is not None:
params['evaporation'] = max(0.0, min(1.0, evaporation))
if inertia is not None:
params['inertia'] = max(0.0, min(1.0, inertia))
if sediment_capacity is not None:
params['sediment_capacity'] = max(0.0, sediment_capacity)
if deposition_rate is not None:
params['deposition_rate'] = max(0.0, deposition_rate)
print(f"✓ 水力侵蚀参数已更新: {params}")
def set_thermal_erosion_parameters(self, rate: float = None, angle_of_repose: float = None):
"""
设置热力侵蚀参数
Args:
rate: 侵蚀速率
angle_of_repose: 休止角(度)
"""
params = self.erosion_params['thermal_erosion']
if rate is not None:
params['rate'] = max(0.0, rate)
if angle_of_repose is not None:
params['angle_of_repose'] = max(0.0, angle_of_repose)
print(f"✓ 热力侵蚀参数已更新: {params}")
def set_wind_erosion_parameters(self, rate: float = None, direction: float = None):
"""
设置风力侵蚀参数
Args:
rate: 侵蚀速率
direction: 风向(弧度)
"""
params = self.erosion_params['wind_erosion']
if rate is not None:
params['rate'] = max(0.0, rate)
if direction is not None:
params['direction'] = direction
print(f"✓ 风力侵蚀参数已更新: {params}")
def apply_selective_erosion(self, heightmap: np.ndarray, mask: np.ndarray,
seed: int = None) -> Dict[str, np.ndarray]:
"""
在指定区域应用侵蚀
Args:
heightmap: 高度图数据
mask: 掩码0-1范围1表示完全应用侵蚀
seed: 随机种子
Returns:
包含处理后高度图和侵蚀相关数据的字典
"""
try:
processing_start_time = time.time()
if not self.enabled:
print("✗ 侵蚀处理器未启用")
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
# 更新种子
if seed is not None:
self.seed = seed
print("✓ 开始应用选择性侵蚀...")
# 确保掩码尺寸匹配
if mask.shape != heightmap.shape:
print("✗ 掩码尺寸与高度图不匹配")
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
# 初始化侵蚀图层
self._initialize_erosion_layers(heightmap.shape)
# 创建高度图副本进行处理
processed_heightmap = heightmap.copy()
# 在掩码区域应用侵蚀
mask_indices = np.where(mask > 0.5)
# 创建临时高度图只包含掩码区域
temp_heightmap = heightmap.copy()
# 应用各种侵蚀类型(只在掩码区域)
if self.erosion_params['water_erosion']['enabled']:
print(" → 应用水力侵蚀(选择性)...")
temp_heightmap = self._apply_water_erosion_selective(temp_heightmap, mask)
if self.erosion_params['thermal_erosion']['enabled']:
print(" → 应用热力侵蚀(选择性)...")
temp_heightmap = self._apply_thermal_erosion_selective(temp_heightmap, mask)
if self.erosion_params['wind_erosion']['enabled']:
print(" → 应用风力侵蚀(选择性)...")
temp_heightmap = self._apply_wind_erosion_selective(temp_heightmap, mask)
# 只更新掩码区域的值
processed_heightmap[mask_indices] = temp_heightmap[mask_indices]
# 更新统计信息
processing_time = time.time() - processing_start_time
self.stats['erosion_processed'] += 1
self.stats['total_processing_time'] += processing_time
self.stats['average_processing_time'] = self.stats['total_processing_time'] / self.stats['erosion_processed']
print(f"✓ 选择性侵蚀处理完成,耗时: {processing_time:.2f}")
return {
'heightmap': processed_heightmap,
'erosion_map': self.erosion_map,
'sediment_map': self.sediment_map,
'water_map': self.water_map
}
except Exception as e:
print(f"✗ 选择性侵蚀处理失败: {e}")
import traceback
traceback.print_exc()
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
def _apply_water_erosion_selective(self, heightmap: np.ndarray, mask: np.ndarray) -> np.ndarray:
"""在选择区域应用水力侵蚀"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取水力侵蚀参数
params = self.erosion_params['water_erosion']
iterations = params['iterations']
erosion_rate = params['rate'] * 0.5 # 降低速率以适应选择性应用
# 只在掩码区域应用侵蚀
mask_indices = np.where(mask > 0.5)
# 简化的选择性水力侵蚀
for iteration in range(iterations):
for y, x in zip(mask_indices[0], mask_indices[1]):
# 简单的侵蚀:降低高点,填充低点
if processed[y, x] > 0.8: # 高点
erosion_amount = erosion_rate * mask[y, x]
processed[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
elif processed[y, x] < 0.2: # 低点
deposition_amount = erosion_rate * 0.5 * mask[y, x]
processed[y, x] += deposition_amount
self.sediment_map[y, x] += deposition_amount
return processed
except Exception as e:
print(f"✗ 选择性水力侵蚀应用失败: {e}")
return heightmap
def _apply_thermal_erosion_selective(self, heightmap: np.ndarray, mask: np.ndarray) -> np.ndarray:
"""在选择区域应用热力侵蚀"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取热力侵蚀参数
params = self.erosion_params['thermal_erosion']
iterations = params['iterations']
erosion_rate = params['rate'] * 0.5 # 降低速率以适应选择性应用
# 只在掩码区域应用侵蚀
mask_indices = np.where(mask > 0.5)
# 简化的选择性热力侵蚀
for iteration in range(iterations):
for y, x in zip(mask_indices[0], mask_indices[1]):
# 检查直接邻居
for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
ny, nx = y + dy, x + dx
if 0 <= ny < height and 0 <= nx < width:
height_diff = processed[y, x] - processed[ny, nx]
if height_diff > 0.1: # 显著高度差
move_amount = erosion_rate * height_diff * mask[y, x]
processed[y, x] -= move_amount
processed[ny, nx] += move_amount
self.erosion_map[y, x] += move_amount
return processed
except Exception as e:
print(f"✗ 选择性热力侵蚀应用失败: {e}")
return heightmap
def _apply_wind_erosion_selective(self, heightmap: np.ndarray, mask: np.ndarray) -> np.ndarray:
"""在选择区域应用风力侵蚀"""
try:
processed = heightmap.copy()
height, width = processed.shape
# 获取风力侵蚀参数
params = self.erosion_params['wind_erosion']
erosion_rate = params['rate'] * 0.5 # 降低速率以适应选择性应用
wind_direction = params['direction']
# 计算风向向量
wind_dx = math.cos(wind_direction)
wind_dy = math.sin(wind_direction)
# 只在掩码区域应用侵蚀
mask_indices = np.where(mask > 0.5)
# 简化的选择性风力侵蚀
for y, x in zip(mask_indices[0], mask_indices[1]):
# 风从特定方向吹来
source_x = int(x - wind_dx * 1) # 1个单位距离
source_y = int(y - wind_dy * 1)
# 如果来源位置有效且也在掩码区域内
if (0 <= source_y < height and 0 <= source_x < width and
mask[source_y, source_x] > 0.5):
# 应用风力侵蚀
height_diff = processed[y, x] - processed[source_y, source_x]
if height_diff > 0.01:
erosion_amount = erosion_rate * height_diff * mask[y, x]
processed[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
return processed
except Exception as e:
print(f"✗ 选择性风力侵蚀应用失败: {e}")
return heightmap
def blend_erosion_maps(self, erosion_maps: List[np.ndarray], weights: List[float] = None) -> np.ndarray:
"""
混合多个侵蚀图
Args:
erosion_maps: 侵蚀图列表
weights: 权重列表如果为None则平均分配权重
Returns:
混合后的侵蚀图
"""
try:
if not erosion_maps:
return np.array([])
if len(erosion_maps) == 1:
return erosion_maps[0].copy()
# 确保所有侵蚀图尺寸相同
base_shape = erosion_maps[0].shape
for em in erosion_maps:
if em.shape != base_shape:
raise ValueError("所有侵蚀图必须具有相同的尺寸")
# 处理权重
if weights is None:
weights = [1.0 / len(erosion_maps)] * len(erosion_maps)
elif len(weights) != len(erosion_maps):
raise ValueError("权重数量必须与侵蚀图数量相同")
# 归一化权重
total_weight = sum(weights)
if total_weight > 0:
weights = [w / total_weight for w in weights]
# 混合侵蚀图
blended = np.zeros(base_shape, dtype=np.float32)
for i, (em, weight) in enumerate(zip(erosion_maps, weights)):
blended += em * weight
return blended
except Exception as e:
print(f"✗ 侵蚀图混合失败: {e}")
import traceback
traceback.print_exc()
# 返回第一个侵蚀图的副本
return erosion_maps[0].copy() if erosion_maps else np.array([])
def smooth_erosion_map(self, erosion_map: np.ndarray, kernel_size: int = 3) -> np.ndarray:
"""
平滑侵蚀图
Args:
erosion_map: 原始侵蚀图
kernel_size: 平滑核大小
Returns:
平滑后的侵蚀图
"""
try:
if kernel_size < 1:
return erosion_map
smoothed = erosion_map.copy()
height, width = smoothed.shape
# 简单的均值滤波
half_kernel = kernel_size // 2
for y in range(height):
for x in range(width):
total = 0.0
count = 0
# 应用平滑核
for dy in range(-half_kernel, half_kernel + 1):
for dx in range(-half_kernel, half_kernel + 1):
ny, nx = y + dy, x + dx
if 0 <= ny < height and 0 <= nx < width:
total += erosion_map[ny, nx]
count += 1
if count > 0:
smoothed[y, x] = total / count
return smoothed
except Exception as e:
print(f"✗ 侵蚀图平滑失败: {e}")
return erosion_map
def apply_erosion_intensity(self, heightmap: np.ndarray, intensity_map: np.ndarray,
seed: int = None) -> Dict[str, np.ndarray]:
"""
根据强度图应用侵蚀
Args:
heightmap: 高度图数据
intensity_map: 侵蚀强度图0-1范围
seed: 随机种子
Returns:
包含处理后高度图和侵蚀相关数据的字典
"""
try:
processing_start_time = time.time()
if not self.enabled:
print("✗ 侵蚀处理器未启用")
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
# 更新种子
if seed is not None:
self.seed = seed
# 确保强度图尺寸匹配
if intensity_map.shape != heightmap.shape:
print("✗ 强度图尺寸与高度图不匹配")
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
print("✓ 开始应用强度控制的侵蚀...")
# 初始化侵蚀图层
self._initialize_erosion_layers(heightmap.shape)
# 创建高度图副本进行处理
processed_heightmap = heightmap.copy()
# 根据强度图调整侵蚀参数
base_water_params = self.erosion_params['water_erosion'].copy()
base_thermal_params = self.erosion_params['thermal_erosion'].copy()
base_wind_params = self.erosion_params['wind_erosion'].copy()
# 对每个点根据强度应用侵蚀
height, width = processed_heightmap.shape
for y in range(height):
for x in range(width):
intensity = intensity_map[y, x]
if intensity > 0.1: # 只在强度大于阈值时应用侵蚀
# 调整侵蚀参数
self.erosion_params['water_erosion']['rate'] = base_water_params['rate'] * intensity
self.erosion_params['thermal_erosion']['rate'] = base_thermal_params['rate'] * intensity
self.erosion_params['wind_erosion']['rate'] = base_wind_params['rate'] * intensity
# 应用局部侵蚀(简化版本)
if self.erosion_params['water_erosion']['enabled']:
# 简化的水力侵蚀
if processed_heightmap[y, x] > 0.8:
erosion_amount = self.erosion_params['water_erosion']['rate'] * 0.1
processed_heightmap[y, x] -= erosion_amount
self.erosion_map[y, x] += erosion_amount
# 恢复原始侵蚀参数
self.erosion_params['water_erosion'] = base_water_params
self.erosion_params['thermal_erosion'] = base_thermal_params
self.erosion_params['wind_erosion'] = base_wind_params
# 更新统计信息
processing_time = time.time() - processing_start_time
self.stats['erosion_processed'] += 1
self.stats['total_processing_time'] += processing_time
self.stats['average_processing_time'] = self.stats['total_processing_time'] / self.stats['erosion_processed']
print(f"✓ 强度控制的侵蚀处理完成,耗时: {processing_time:.2f}")
return {
'heightmap': processed_heightmap,
'erosion_map': self.erosion_map,
'sediment_map': self.sediment_map,
'water_map': self.water_map
}
except Exception as e:
print(f"✗ 强度控制的侵蚀处理失败: {e}")
import traceback
traceback.print_exc()
return {
'heightmap': heightmap,
'erosion_map': np.zeros_like(heightmap, dtype=np.float32),
'sediment_map': np.zeros_like(heightmap, dtype=np.float32),
'water_map': np.zeros_like(heightmap, dtype=np.float32)
}
def export_erosion_data(self, filename: str) -> bool:
"""
导出侵蚀数据
Args:
filename: 文件名
Returns:
是否导出成功
"""
try:
import json
erosion_data = {
'parameters': self.erosion_params,
'stats': self.stats,
'timestamp': time.time()
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(erosion_data, f, ensure_ascii=False, indent=2)
print(f"✓ 侵蚀数据已导出到: {filename}")
return True
except Exception as e:
print(f"✗ 侵蚀数据导出失败: {e}")
return False
def import_erosion_data(self, filename: str) -> bool:
"""
导入侵蚀数据
Args:
filename: 文件名
Returns:
是否导入成功
"""
try:
import json
with open(filename, 'r', encoding='utf-8') as f:
erosion_data = json.load(f)
# 更新参数
if 'parameters' in erosion_data:
self.erosion_params.update(erosion_data['parameters'])
# 更新统计信息
if 'stats' in erosion_data:
self.stats.update(erosion_data['stats'])
print(f"✓ 侵蚀数据已从 {filename} 导入")
return True
except Exception as e:
print(f"✗ 侵蚀数据导入失败: {e}")
return False
def reset_erosion_maps(self):
"""重置侵蚀图层"""
self.erosion_map = None
self.sediment_map = None
self.water_map = None
self.flow_map = None
print("✓ 侵蚀图层已重置")
def get_erosion_types(self) -> List[str]:
"""
获取可用的侵蚀类型
Returns:
侵蚀类型列表
"""
return list(self.erosion_params.keys())
def get_erosion_parameters(self, erosion_type: str) -> Dict[str, Any]:
"""
获取指定侵蚀类型的参数
Args:
erosion_type: 侵蚀类型
Returns:
参数字典
"""
return self.erosion_params.get(erosion_type, {}).copy()