""" 水体表面模块 负责水体表面的几何生成和动态更新 """ import time from typing import Dict, Any, List, Optional, Tuple import math import numpy as np class WaterSurface: """ 水体表面管理器 负责水体表面的几何生成、动态更新和渲染准备 """ def __init__(self, plugin): """ 初始化水体表面管理器 Args: plugin: 水体和流体模拟插件实例 """ self.plugin = plugin self.enabled = False self.initialized = False # 网格配置 self.mesh_config = { 'resolution_x': 128, 'resolution_z': 128, 'tessellation_level': 1, 'patch_size': 10.0, 'lod_enabled': True, 'lod_distance': 50.0 } # 表面顶点数据 self.vertices = [] self.normals = [] self.uvs = [] self.indices = [] # 表面高度场 self.height_field = None self.normal_field = None # 动态更新配置 self.update_config = { 'update_frequency': 60.0, # Hz 'last_update_time': 0.0, 'needs_update': True } # 纹理坐标配置 self.uv_config = { 'scale_u': 1.0, 'scale_v': 1.0, 'scroll_u': 0.0, 'scroll_v': 0.0, 'scroll_speed_u': 0.1, 'scroll_speed_v': 0.05 } # 几何统计 self.geometry_stats = { 'vertex_count': 0, 'triangle_count': 0, 'patch_count': 0, 'updates_performed': 0 } # 性能优化 self.optimization_settings = { 'frustum_culling': True, 'occlusion_culling': False, 'dynamic_lod': True, 'vertex_cache': True } print("✓ 水体表面管理器已创建") def initialize(self) -> bool: """ 初始化水体表面管理器 Returns: 是否初始化成功 """ try: # 初始化高度场 self._initialize_height_field() # 生成初始网格 self._generate_initial_mesh() 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._clear_mesh_data() self.initialized = False print("✓ 水体表面管理器资源已清理") except Exception as e: print(f"✗ 水体表面管理器资源清理失败: {e}") import traceback traceback.print_exc() def update(self, dt: float): """ 更新水体表面状态 Args: dt: 时间增量 """ try: if not self.enabled: return # 更新时间 self.update_config['last_update_time'] += dt # 检查是否需要更新 update_interval = 1.0 / self.update_config['update_frequency'] if self.update_config['last_update_time'] >= update_interval: # 更新表面几何 self._update_surface_geometry(dt) # 更新纹理坐标 self._update_texture_coordinates(dt) # 重置更新计时器 self.update_config['last_update_time'] = 0.0 self.update_config['needs_update'] = False # 更新统计信息 self.geometry_stats['updates_performed'] += 1 except Exception as e: print(f"✗ 水体表面管理器更新失败: {e}") import traceback traceback.print_exc() def _initialize_height_field(self): """初始化高度场""" try: res_x = self.mesh_config['resolution_x'] res_z = self.mesh_config['resolution_z'] # 初始化高度场 self.height_field = np.zeros((res_x, res_z)) # 初始化法线场 self.normal_field = np.zeros((res_x, res_z, 3)) print("✓ 高度场初始化完成") except Exception as e: print(f"✗ 高度场初始化失败: {e}") raise def _generate_initial_mesh(self): """生成初始网格""" try: self._clear_mesh_data() res_x = self.mesh_config['resolution_x'] res_z = self.mesh_config['resolution_z'] patch_size = self.mesh_config['patch_size'] # 生成顶点 for i in range(res_x): for j in range(res_z): x = (i - res_x / 2) * patch_size / res_x z = (j - res_z / 2) * patch_size / res_z y = self.height_field[i, j] if self.height_field is not None else 0.0 self.vertices.append((x, y, z)) # 生成UV坐标 u = i / (res_x - 1) * self.uv_config['scale_u'] v = j / (res_z - 1) * self.uv_config['scale_v'] self.uvs.append((u, v)) # 生成索引(三角形列表) for i in range(res_x - 1): for j in range(res_z - 1): # 第一个三角形 self.indices.append(i * res_z + j) self.indices.append((i + 1) * res_z + j) self.indices.append(i * res_z + (j + 1)) # 第二个三角形 self.indices.append((i + 1) * res_z + j) self.indices.append((i + 1) * res_z + (j + 1)) self.indices.append(i * res_z + (j + 1)) # 计算法线 self._calculate_normals() # 更新统计信息 self.geometry_stats['vertex_count'] = len(self.vertices) self.geometry_stats['triangle_count'] = len(self.indices) // 3 self.geometry_stats['patch_count'] = (res_x - 1) * (res_z - 1) print("✓ 初始网格生成完成") except Exception as e: print(f"✗ 初始网格生成失败: {e}") raise def _clear_mesh_data(self): """清理网格数据""" try: self.vertices.clear() self.normals.clear() self.uvs.clear() self.indices.clear() if self.height_field is not None: self.height_field.fill(0) if self.normal_field is not None: self.normal_field.fill(0) except Exception as e: print(f"✗ 网格数据清理失败: {e}") def _update_surface_geometry(self, dt: float): """ 更新表面几何 Args: dt: 时间增量 """ try: if not self.vertices or self.height_field is None: return res_x = self.mesh_config['resolution_x'] res_z = self.mesh_config['resolution_z'] # 更新顶点高度 for i in range(res_x): for j in range(res_z): vertex_index = i * res_z + j if vertex_index < len(self.vertices): x, y, z = self.vertices[vertex_index] # 从高度场获取新高度 new_height = self.height_field[i, j] self.vertices[vertex_index] = (x, new_height, z) # 重新计算法线 self._calculate_normals() except Exception as e: print(f"✗ 表面几何更新失败: {e}") def _calculate_normals(self): """计算顶点法线""" try: if not self.vertices or not self.indices: return # 清除旧法线 self.normals.clear() # 初始化法线数组 vertex_normals = [np.array([0.0, 0.0, 0.0]) for _ in self.vertices] # 计算每个三角形的法线并累加到顶点 for i in range(0, len(self.indices), 3): i0 = self.indices[i] i1 = self.indices[i + 1] i2 = self.indices[i + 2] # 获取三角形顶点 v0 = np.array(self.vertices[i0]) v1 = np.array(self.vertices[i1]) v2 = np.array(self.vertices[i2]) # 计算边向量 edge1 = v1 - v0 edge2 = v2 - v0 # 计算法线(叉积) normal = np.cross(edge1, edge2) # 归一化 normal_length = np.linalg.norm(normal) if normal_length > 0: normal = normal / normal_length # 累加到顶点法线 vertex_normals[i0] += normal vertex_normals[i1] += normal vertex_normals[i2] += normal # 归一化顶点法线并存储 for normal in vertex_normals: normal_length = np.linalg.norm(normal) if normal_length > 0: normal = normal / normal_length self.normals.append(tuple(normal)) except Exception as e: print(f"✗ 法线计算失败: {e}") def _update_texture_coordinates(self, dt: float): """ 更新纹理坐标 Args: dt: 时间增量 """ try: # 更新滚动偏移 self.uv_config['scroll_u'] += self.uv_config['scroll_speed_u'] * dt self.uv_config['scroll_v'] += self.uv_config['scroll_speed_v'] * dt # 保持在[0,1)范围内 self.uv_config['scroll_u'] %= 1.0 self.uv_config['scroll_v'] %= 1.0 # 更新UV坐标 if self.uvs: res_x = self.mesh_config['resolution_x'] res_z = self.mesh_config['resolution_z'] for i in range(res_x): for j in range(res_z): vertex_index = i * res_z + j if vertex_index < len(self.uvs): u = (i / (res_x - 1) + self.uv_config['scroll_u']) * self.uv_config['scale_u'] v = (j / (res_z - 1) + self.uv_config['scroll_v']) * self.uv_config['scale_v'] self.uvs[vertex_index] = (u % 1.0, v % 1.0) except Exception as e: print(f"✗ 纹理坐标更新失败: {e}") def update_height_field(self, new_height_field: np.ndarray): """ 更新高度场 Args: new_height_field: 新的高度场数据 """ try: if self.height_field is None: self._initialize_height_field() # 复制新高度场数据 if new_height_field.shape == self.height_field.shape: self.height_field[:, :] = new_height_field[:, :] else: # 如果尺寸不匹配,进行插值 from scipy.interpolate import interp2d old_x = np.linspace(0, 1, self.height_field.shape[0]) old_y = np.linspace(0, 1, self.height_field.shape[1]) new_x = np.linspace(0, 1, new_height_field.shape[0]) new_y = np.linspace(0, 1, new_height_field.shape[1]) interpolator = interp2d(old_x, old_y, new_height_field, kind='linear') self.height_field[:, :] = interpolator(old_x, old_y) # 标记需要更新 self.update_config['needs_update'] = True except Exception as e: print(f"✗ 高度场更新失败: {e}") def generate_wave_pattern(self, wave_parameters: Dict[str, float], time_value: float) -> np.ndarray: """ 生成波浪模式 Args: wave_parameters: 波浪参数字典 time_value: 时间值 Returns: 高度场数组 """ try: if self.height_field is None: self._initialize_height_field() res_x, res_z = self.height_field.shape patch_size = self.mesh_config['patch_size'] # 获取波浪参数 amplitude = wave_parameters.get('amplitude', 1.0) wavelength = wave_parameters.get('wavelength', 10.0) speed = wave_parameters.get('speed', 1.0) direction = wave_parameters.get('direction', 0.0) # 弧度 # 计算波向量 wave_vector_x = 2 * math.pi / wavelength * math.cos(direction) wave_vector_z = 2 * math.pi / wavelength * math.sin(direction) # 生成波浪 for i in range(res_x): for j in range(res_z): # 计算世界坐标 x = (i - res_x / 2) * patch_size / res_x z = (j - res_z / 2) * patch_size / res_z # 计算相位 phase = wave_vector_x * x + wave_vector_z * z - speed * time_value # 计算波浪高度 height = amplitude * math.sin(phase) self.height_field[i, j] = height return self.height_field.copy() except Exception as e: print(f"✗ 波浪模式生成失败: {e}") # 返回零高度场 return np.zeros((res_x, res_z)) if self.height_field is not None else np.zeros((1, 1)) def generate_gerstner_waves(self, waves: List[Dict[str, float]], time_value: float) -> np.ndarray: """ 生成Gerstner波浪(更真实的波浪模型) Args: waves: 波浪参数列表,每个包含'amplitude', 'wavelength', 'speed', 'direction' time_value: 时间值 Returns: 高度场数组 """ try: if self.height_field is None: self._initialize_height_field() res_x, res_z = self.height_field.shape patch_size = self.mesh_config['patch_size'] # 清零高度场 self.height_field.fill(0) # 为每个波浪累加贡献 for wave in waves: amplitude = wave.get('amplitude', 1.0) wavelength = wave.get('wavelength', 10.0) speed = wave.get('speed', 1.0) direction = wave.get('direction', 0.0) # 计算波数和频率 wavenumber = 2 * math.pi / wavelength frequency = speed * wavenumber # 计算波向量 kx = wavenumber * math.cos(direction) kz = wavenumber * math.sin(direction) # 为每个点计算波浪贡献 for i in range(res_x): for j in range(res_z): # 计算世界坐标 x = (i - res_x / 2) * patch_size / res_x z = (j - res_z / 2) * patch_size / res_z # 计算相位 phase = kx * x + kz * z - frequency * time_value # Gerstner波浪公式 height_contribution = amplitude * math.sin(phase) self.height_field[i, j] += height_contribution return self.height_field.copy() except Exception as e: print(f"✗ Gerstner波浪生成失败: {e}") return np.zeros((res_x, res_z)) if self.height_field is not None else np.zeros((1, 1)) def get_mesh_data(self) -> Dict[str, List[Tuple[float, float, float]]]: """ 获取网格数据 Returns: 包含顶点、法线、UV坐标和索引的字典 """ return { 'vertices': self.vertices.copy(), 'normals': self.normals.copy(), 'uvs': self.uvs.copy(), 'indices': self.indices.copy() } def get_height_at(self, x: float, z: float) -> float: """ 获取指定位置的高度 Args: x, z: 世界坐标 Returns: 高度值 """ try: if self.height_field is None: return 0.0 res_x, res_z = self.height_field.shape patch_size = self.mesh_config['patch_size'] # 转换到网格坐标 grid_x = (x / patch_size + 0.5) * res_x grid_z = (z / patch_size + 0.5) * res_z # 边界检查 if grid_x < 0 or grid_x >= res_x or grid_z < 0 or grid_z >= res_z: return 0.0 # 简单的双线性插值 i = int(grid_x) j = int(grid_z) fx = grid_x - i fz = grid_z - j # 边界处理 i1 = min(i + 1, res_x - 1) j1 = min(j + 1, res_z - 1) # 双线性插值 h00 = self.height_field[i, j] h10 = self.height_field[i1, j] h01 = self.height_field[i, j1] h11 = self.height_field[i1, j1] h0 = h00 * (1 - fx) + h10 * fx h1 = h01 * (1 - fx) + h11 * fx return h0 * (1 - fz) + h1 * fz except Exception as e: print(f"✗ 高度获取失败: {e}") return 0.0 def set_mesh_config(self, config: Dict[str, Any]): """ 设置网格配置 Args: config: 网格配置字典 """ try: old_res_x = self.mesh_config['resolution_x'] old_res_z = self.mesh_config['resolution_z'] self.mesh_config.update(config) new_res_x = self.mesh_config['resolution_x'] new_res_z = self.mesh_config['resolution_z'] # 如果分辨率改变,重新生成网格 if old_res_x != new_res_x or old_res_z != new_res_z: self._generate_initial_mesh() print(f"✓ 网格配置已更新: {self.mesh_config}") except Exception as e: print(f"✗ 网格配置设置失败: {e}") def get_mesh_config(self) -> Dict[str, Any]: """ 获取网格配置 Returns: 网格配置字典 """ return self.mesh_config.copy() def set_uv_config(self, config: Dict[str, float]): """ 设置UV配置 Args: config: UV配置字典 """ try: self.uv_config.update(config) print(f"✓ UV配置已更新: {self.uv_config}") except Exception as e: print(f"✗ UV配置设置失败: {e}") def get_uv_config(self) -> Dict[str, float]: """ 获取UV配置 Returns: UV配置字典 """ return self.uv_config.copy() def get_geometry_stats(self) -> Dict[str, int]: """ 获取几何统计信息 Returns: 几何统计字典 """ return self.geometry_stats.copy() def reset_geometry_stats(self): """重置几何统计信息""" try: self.geometry_stats = { 'vertex_count': len(self.vertices), 'triangle_count': len(self.indices) // 3, 'patch_count': (self.mesh_config['resolution_x'] - 1) * (self.mesh_config['resolution_z'] - 1), 'updates_performed': 0 } print("✓ 几何统计信息已重置") except Exception as e: print(f"✗ 几何统计信息重置失败: {e}") def apply_displacement_map(self, displacement_map: np.ndarray): """ 应用位移贴图 Args: displacement_map: 位移贴图数据 """ try: self.update_height_field(displacement_map) print("✓ 位移贴图已应用") except Exception as e: print(f"✗ 位移贴图应用失败: {e}")