1201 lines
46 KiB
Python
1201 lines
46 KiB
Python
"""
|
||
侵蚀处理器
|
||
负责模拟地形的侵蚀过程
|
||
"""
|
||
|
||
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() |