""" 导航网格插件主文件 """ import time import json from plugins.plugin_manager import BasePlugin from .core.navmesh_manager import NavMeshManager from .editor.navmesh_editor import NavMeshEditor from .tools.pathfinder import Pathfinder from .tools.navmesh_tools import NavMeshTools class Plugin(BasePlugin): """ 导航网格插件 提供基于导航网格的寻路和路径规划功能 """ def __init__(self, plugin_manager, name): super().__init__(plugin_manager, name) self.config = { "version": "1.0.0", "author": "EG Team", "description": "基于导航网格的寻路系统,支持复杂地形的路径规划" } # 核心组件 self.navmesh_manager = None self.navmesh_editor = None self.pathfinder = None self.navmesh_tools = None # GUI元素 self.gui_elements = [] # 事件处理器 self.event_handlers = {} # 插件状态 self.is_initialized = False self.is_enabled = False # 性能监控 self.performance_stats = { 'initialization_time': 0.0, 'enable_time': 0.0, 'last_update_time': 0.0, 'total_updates': 0 } def initialize(self) -> bool: """ 初始化插件 """ try: if self.is_initialized: return True init_start_time = time.time() print(f"初始化导航网格插件: {self.name}") # 初始化导航网格管理器 self.navmesh_manager = NavMeshManager(self.plugin_manager.world) # 初始化路径查找器 self.pathfinder = Pathfinder(self.navmesh_manager) # 初始化导航网格编辑器 self.navmesh_editor = NavMeshEditor( self.plugin_manager.world, self.navmesh_manager ) # 初始化导航网格工具集 self.navmesh_tools = NavMeshTools(self) self.is_initialized = True self.performance_stats['initialization_time'] = time.time() - init_start_time print("✓ 导航网格插件初始化完成") return True except Exception as e: print(f"✗ 导航网格插件初始化失败: {e}") import traceback traceback.print_exc() return False def enable(self) -> bool: """ 启用插件 """ if not super().enable(): return False if self.is_enabled: return True try: enable_start_time = time.time() print(f"启用导航网格插件: {self.name}") # 启用导航网格管理器 if self.navmesh_manager: self.navmesh_manager.enable() # 注册事件处理器 self._register_event_handlers() # 创建GUI界面 self._create_gui() self.is_enabled = True self.performance_stats['enable_time'] = time.time() - enable_start_time print("✓ 导航网格插件启用成功") return True except Exception as e: print(f"✗ 导航网格插件启用失败: {e}") import traceback traceback.print_exc() return False def disable(self) -> bool: """ 禁用插件 """ if not super().disable(): return False if not self.is_enabled: return True try: print(f"禁用导航网格插件: {self.name}") # 清理GUI界面 self._cleanup_gui() # 移除事件处理器 self._unregister_event_handlers() # 禁用导航网格管理器 if self.navmesh_manager: self.navmesh_manager.disable() self.is_enabled = False print("✓ 导航网格插件禁用成功") return True except Exception as e: print(f"✗ 导航网格插件禁用失败: {e}") import traceback traceback.print_exc() return False def finalize(self): """ 清理插件资源 """ print(f"清理导航网格插件资源: {self.name}") # 清理所有组件 if self.navmesh_editor: self.navmesh_editor.cleanup() self.navmesh_editor = None if self.pathfinder: self.pathfinder = None if self.navmesh_tools: self.navmesh_tools = None if self.navmesh_manager: self.navmesh_manager.cleanup() self.navmesh_manager = None self.is_initialized = False self.is_enabled = False def _register_event_handlers(self): """ 注册事件处理器 """ world = self.plugin_manager.world # 导航网格系统控制快捷键 self.event_handlers['f1'] = self.toggle_navmesh_editor self.event_handlers['f2'] = self.toggle_navmesh_visibility self.event_handlers['f3'] = self.find_random_path self.event_handlers['f4'] = self.clear_paths self.event_handlers['f5'] = self.generate_navmesh self.event_handlers['f6'] = self.validate_navmesh self.event_handlers['f7'] = self.optimize_navmesh self.event_handlers['f8'] = self.show_stats self.event_handlers['f9'] = self.analyze_navmesh self.event_handlers['f10'] = self.export_report self.event_handlers['f11'] = self.simplify_navmesh self.event_handlers['f12'] = self.detect_bottlenecks # 注册事件 for event, handler in self.event_handlers.items(): world.accept(event, handler) world.accept(event.upper(), handler) # 同时注册大写事件 def _unregister_event_handlers(self): """ 移除事件处理器 """ world = self.plugin_manager.world for event in self.event_handlers.keys(): world.ignore(event) world.ignore(event.upper()) self.event_handlers.clear() def _create_gui(self): """ 创建GUI界面 """ try: world = self.plugin_manager.world # 检查是否有GUI管理器 if not hasattr(world, 'gui_manager') or not world.gui_manager: print("⚠ GUI管理器不可用,跳过GUI创建") return gui_manager = world.gui_manager # 创建导航网格控制面板 panel_button = gui_manager.createGUIButton( pos=(0.02, 0, 0.75), text="导航网格", size=0.06 ) self.gui_elements.append(panel_button) # 创建功能按钮 functions = [ ("编辑器 [F1]", self.toggle_navmesh_editor), ("显示/隐藏 [F2]", self.toggle_navmesh_visibility), ("随机路径 [F3]", self.find_random_path), ("清除路径 [F4]", self.clear_paths), ("生成网格 [F5]", self.generate_navmesh), ("验证网格 [F6]", self.validate_navmesh), ("优化网格 [F7]", self.optimize_navmesh), ("显示统计 [F8]", self.show_stats), ("分析网格 [F9]", self.analyze_navmesh), ("导出报告 [F10]", self.export_report), ("简化网格 [F11]", self.simplify_navmesh), ("检测瓶颈 [F12]", self.detect_bottlenecks) ] for i, (name, callback) in enumerate(functions): button = gui_manager.createGUIButton( pos=(0.02, 0, 0.68 - i * 0.055), text=name, size=0.045 ) self.gui_elements.append(button) world.accept(f"navmesh_function_{name}", callback) except Exception as e: print(f"⚠ GUI创建失败: {e}") def _cleanup_gui(self): """ 清理GUI界面 """ try: world = self.plugin_manager.world if hasattr(world, 'gui_manager') and world.gui_manager: gui_manager = world.gui_manager # 删除所有GUI元素 for element in self.gui_elements: gui_manager.deleteGUIElement(element) self.gui_elements.clear() except Exception as e: print(f"⚠ GUI清理失败: {e}") # 事件处理方法 def toggle_navmesh_editor(self): """切换导航网格编辑器显示""" if self.navmesh_editor: self.navmesh_editor.toggle_visibility() def toggle_navmesh_visibility(self): """切换导航网格可见性""" if self.navmesh_manager: self.navmesh_manager.toggle_visibility() def find_random_path(self): """查找随机路径""" if self.pathfinder and self.navmesh_manager: # 获取随机起点和终点 start = self.navmesh_manager.get_random_point() end = self.navmesh_manager.get_random_point() if start and end: path = self.pathfinder.find_path(start, end) if path: print(f"✓ 找到路径,包含 {len(path)} 个节点") # 可视化路径 self.navmesh_manager.visualize_path(path) else: print("⚠ 未找到路径") def clear_paths(self): """清除所有路径可视化""" if self.navmesh_manager: self.navmesh_manager.clear_path_visualization() def generate_navmesh(self): """生成导航网格""" if self.navmesh_manager: success = self.navmesh_manager.generate_from_scene() if success: print("✓ 导航网格生成完成") else: print("✗ 导航网格生成失败") def validate_navmesh(self): """验证导航网格""" if self.navmesh_manager: is_valid = self.navmesh_manager.validate_navmesh() if is_valid: print("✓ 导航网格验证通过") else: print("✗ 导航网格验证失败") def optimize_navmesh(self): """优化导航网格""" if self.navmesh_manager and self.navmesh_tools: print("✓ 开始优化导航网格...") optimization_result = self.navmesh_tools.optimize_mesh('medium') print(f"✓ 导航网格优化完成: {optimization_result}") def show_stats(self): """显示统计信息""" if self.navmesh_manager: stats = self.navmesh_manager.get_stats() print("=== 导航网格统计信息 ===") for key, value in stats.items(): print(f" {key}: {value}") if self.pathfinder: path_stats = self.pathfinder.get_pathfinding_stats() print("=== 寻路统计信息 ===") for key, value in path_stats.items(): print(f" {key}: {value}") if self.navmesh_editor: editor_stats = self.navmesh_editor.get_stats() print("=== 编辑器统计信息 ===") for key, value in editor_stats.items(): print(f" {key}: {value}") def analyze_navmesh(self): """分析导航网格""" if self.navmesh_tools: print("✓ 开始分析导航网格...") quality_report = self.navmesh_tools.analyze_mesh_quality() suggestions = self.navmesh_tools.suggest_optimizations() print("=== 导航网格质量分析 ===") print(f" 多边形数量: {quality_report.get('polygon_count', 0)}") print(f" 顶点数量: {quality_report.get('vertex_count', 0)}") print(f" 孤立多边形: {quality_report.get('isolated_polygons', 0)}") print(f" 小多边形: {quality_report.get('small_polygons', 0)}") print(f" 不规则多边形: {quality_report.get('irregular_polygons', 0)}") print("=== 优化建议 ===") for i, suggestion in enumerate(suggestions[:5]): # 显示前5个建议 print(f" {i+1}. {suggestion.get('description', '无描述')}") print(f" 建议: {suggestion.get('recommendation', '无建议')}") def export_report(self): """导出分析报告""" if self.navmesh_tools: print("✓ 开始导出分析报告...") timestamp = int(time.time()) filename = f"navmesh_analysis_report_{timestamp}.json" success = self.navmesh_tools.export_report(filename) if success: print(f"✓ 分析报告已导出到: {filename}") else: print("✗ 导出分析报告失败") def simplify_navmesh(self): """简化导航网格""" if self.navmesh_tools: print("✓ 开始简化导航网格...") simplification_result = self.navmesh_tools.optimize_mesh('light') print(f"✓ 导航网格简化完成: {simplification_result}") def detect_bottlenecks(self): """检测瓶颈区域""" if self.navmesh_tools: print("✓ 开始检测瓶颈区域...") bottlenecks = self.navmesh_tools.detect_bottlenecks() print("=== 瓶颈区域检测结果 ===") if bottlenecks: for i, bottleneck in enumerate(bottlenecks[:5]): # 显示前5个瓶颈 position = bottleneck.get('position', '未知') compactness = bottleneck.get('compactness', 0) print(f" {i+1}. 位置: {position}, 紧凑度: {compactness:.4f}") else: print(" 未检测到明显的瓶颈区域") def get_info(self) -> dict: """ 获取插件信息 """ info = super().get_info() info.update({ "version": self.config.get("version", "1.0.0"), "author": self.config.get("author", "EG Team"), "description": self.config.get("description", "导航网格插件"), "navmesh_loaded": self.navmesh_manager.is_navmesh_loaded() if self.navmesh_manager else False, "navmesh_polygons": self.navmesh_manager.get_polygon_count() if self.navmesh_manager else 0, "is_enabled": self.is_enabled, "is_initialized": self.is_initialized }) return info # 公共接口方法 def load_navmesh_from_file(self, filepath: str) -> bool: """从文件加载导航网格""" if self.navmesh_tools: return self.navmesh_tools.load_navmesh_from_file(filepath) return False def save_navmesh_to_file(self, filepath: str) -> bool: """保存导航网格到文件""" if self.navmesh_tools: return self.navmesh_tools.save_navmesh_to_file(filepath) return False def generate_navmesh_from_scene(self) -> bool: """从当前场景生成导航网格""" if self.navmesh_manager: return self.navmesh_manager.generate_from_scene() return False def find_path(self, start_point, end_point): """查找两点之间的路径""" if self.pathfinder: return self.pathfinder.find_path(start_point, end_point) return None def is_point_walkable(self, point) -> bool: """检查点是否可行走""" if self.navmesh_manager: return self.navmesh_manager.is_point_walkable(point) return False def get_closest_point(self, point): """获取导航网格上最近的点""" if self.navmesh_manager: return self.navmesh_manager.get_closest_point(point) return None def add_dynamic_obstacle(self, position, radius, height): """添加动态障碍物""" if self.navmesh_manager: obstacle_id = self.navmesh_manager.add_obstacle(position, radius, height) # 通知寻路器 if self.pathfinder: self.pathfinder.add_dynamic_obstacle(obstacle_id, []) return obstacle_id return -1 def remove_dynamic_obstacle(self, obstacle_id): """移除动态障碍物""" if self.navmesh_manager: success = self.navmesh_manager.remove_obstacle(obstacle_id) # 通知寻路器 if self.pathfinder and success: self.pathfinder.remove_dynamic_obstacle(obstacle_id) return success return False def set_navmesh_settings(self, settings: dict): """设置导航网格参数""" if self.navmesh_manager: if 'cell_size' in settings: self.navmesh_manager.set_cell_size(settings['cell_size']) if 'agent_height' in settings: self.navmesh_manager.set_agent_height(settings['agent_height']) if 'agent_radius' in settings: self.navmesh_manager.set_agent_radius(settings['agent_radius']) if 'max_slope' in settings: self.navmesh_manager.set_max_slope(settings['max_slope']) if 'max_climb' in settings: self.navmesh_manager.set_max_climb(settings['max_climb']) if 'min_region_area' in settings: self.navmesh_manager.set_min_region_area(settings['min_region_area']) if 'merge_region_area' in settings: self.navmesh_manager.set_merge_region_area(settings['merge_region_area']) if 'edge_max_len' in settings: self.navmesh_manager.set_edge_max_len(settings['edge_max_len']) if 'edge_max_error' in settings: self.navmesh_manager.set_edge_max_error(settings['edge_max_error']) if 'verts_per_poly' in settings: self.navmesh_manager.set_verts_per_poly(settings['verts_per_poly']) def get_navmesh_settings(self) -> dict: """获取导航网格参数""" if self.navmesh_manager: return { 'cell_size': self.navmesh_manager.cell_size, 'agent_height': self.navmesh_manager.agent_height, 'agent_radius': self.navmesh_manager.agent_radius, 'max_slope': self.navmesh_manager.max_slope, 'max_climb': self.navmesh_manager.max_climb, 'min_region_area': self.navmesh_manager.min_region_area, 'merge_region_area': self.navmesh_manager.merge_region_area, 'edge_max_len': self.navmesh_manager.edge_max_len, 'edge_max_error': self.navmesh_manager.edge_max_error, 'verts_per_poly': self.navmesh_manager.verts_per_poly } return {} def set_pathfinder_algorithm(self, algorithm: str): """设置寻路算法""" if self.pathfinder: self.pathfinder.set_algorithm(algorithm) def get_available_algorithms(self) -> list: """获取可用的寻路算法""" if self.pathfinder: return self.pathfinder.get_available_algorithms() return [] def set_pathfinder_settings(self, settings: dict): """设置寻路参数""" if self.pathfinder: if 'heuristic_weight' in settings: self.pathfinder.set_heuristic_weight(settings['heuristic_weight']) if 'max_path_nodes' in settings: self.pathfinder.set_max_path_nodes(settings['max_path_nodes']) if 'use_funnel_pruning' in settings: self.pathfinder.enable_funnel_pruning(settings['use_funnel_pruning']) if 'smooth_paths' in settings: self.pathfinder.enable_path_smoothing(settings['smooth_paths']) def get_pathfinder_settings(self) -> dict: """获取寻路参数""" if self.pathfinder: return { 'heuristic_weight': self.pathfinder.heuristic_weight, 'max_path_nodes': self.pathfinder.max_path_nodes, 'use_funnel_pruning': self.pathfinder.use_funnel_pruning, 'smooth_paths': self.pathfinder.smooth_paths, 'current_algorithm': self.pathfinder.current_algorithm } return {} def find_path_with_waypoints(self, waypoints: list): """查找经过多个路点的路径""" if self.pathfinder: return self.pathfinder.find_path_with_multiple_waypoints(waypoints) return None def estimate_path_cost(self, start_point, end_point): """估算路径代价""" if self.pathfinder: return self.pathfinder.estimate_path_cost(start_point, end_point) return None def validate_path(self, path: list) -> bool: """验证路径有效性""" if self.pathfinder: return self.pathfinder.validate_path(path) return False def get_path_complexity(self, path: list) -> dict: """分析路径复杂度""" if self.pathfinder: return self.pathfinder.get_path_complexity(path) return {} def find_alternative_paths(self, start_point, end_point, count: int = 3): """查找备选路径""" if self.pathfinder: return self.pathfinder.find_alternative_paths(start_point, end_point, count) return [] def clear_path_cache(self): """清空路径缓存""" if self.pathfinder: self.pathfinder.clear_cache() def rebuild_navmesh(self): """重新构建导航网格""" if self.navmesh_manager: return self.navmesh_manager.rebuild_navmesh() return False def enable_spatial_hashing(self, enabled: bool): """启用/禁用空间哈希""" if self.navmesh_manager: self.navmesh_manager.enable_spatial_hashing(enabled) def set_spatial_hash_cell_size(self, size: float): """设置空间哈希网格单元大小""" if self.navmesh_manager: self.navmesh_manager.set_spatial_hash_cell_size(size) def export_statistics(self, filepath: str) -> bool: """导出统计信息""" try: stats_data = { 'plugin_info': self.get_info(), 'navmesh_settings': self.get_navmesh_settings(), 'pathfinder_settings': self.get_pathfinder_settings(), 'performance_stats': self.performance_stats, 'timestamp': time.strftime("%Y-%m-%d %H:%M:%S") } if self.navmesh_manager: stats_data['navmesh_stats'] = self.navmesh_manager.get_stats() if self.pathfinder: stats_data['pathfinder_stats'] = self.pathfinder.get_pathfinding_stats() if self.navmesh_editor: stats_data['editor_stats'] = self.navmesh_editor.get_stats() with open(filepath, 'w', encoding='utf-8') as f: json.dump(stats_data, f, indent=2, ensure_ascii=False) print(f"✓ 统计信息已导出到: {filepath}") return True except Exception as e: print(f"✗ 导出统计信息失败: {e}") return False def import_settings(self, filepath: str) -> bool: """导入设置""" try: with open(filepath, 'r', encoding='utf-8') as f: settings = json.load(f) # 应用导航网格设置 if 'navmesh_settings' in settings: self.set_navmesh_settings(settings['navmesh_settings']) # 应用寻路设置 if 'pathfinder_settings' in settings: pf_settings = settings['pathfinder_settings'] self.set_pathfinder_settings(pf_settings) print(f"✓ 设置已从 {filepath} 导入") return True except Exception as e: print(f"✗ 导入设置失败: {e}") return False def export_settings(self, filepath: str) -> bool: """导出设置""" try: settings_data = { 'navmesh_settings': self.get_navmesh_settings(), 'pathfinder_settings': self.get_pathfinder_settings() } with open(filepath, 'w', encoding='utf-8') as f: json.dump(settings_data, f, indent=2, ensure_ascii=False) print(f"✓ 设置已导出到: {filepath}") return True except Exception as e: print(f"✗ 导出设置失败: {e}") return False # 新增的工具方法 def analyze_mesh_quality(self) -> dict: """分析导航网格质量""" if self.navmesh_tools: return self.navmesh_tools.analyze_mesh_quality() return {} def suggest_optimizations(self) -> list: """提供优化建议""" if self.navmesh_tools: return self.navmesh_tools.suggest_optimizations() return [] def analyze_pathfinding_performance(self, sample_count: int = 100) -> dict: """分析寻路性能""" if self.navmesh_tools: return self.navmesh_tools.analyze_pathfinding_performance(sample_count) return {} def detect_navigation_bottlenecks(self) -> list: """检测导航网格中的瓶颈区域""" if self.navmesh_tools: return self.navmesh_tools.detect_bottlenecks() return [] def analyze_coverage(self) -> dict: """分析导航网格覆盖情况""" if self.navmesh_tools: return self.navmesh_tools.analyze_coverage() return {} def generate_analysis_report(self) -> dict: """生成完整的分析报告""" if self.navmesh_tools: return self.navmesh_tools.generate_report() return {} def export_analysis_report(self, filepath: str) -> bool: """导出分析报告到文件""" if self.navmesh_tools: return self.navmesh_tools.export_report(filepath) return False def get_mesh_complexity_score(self) -> float: """计算导航网格复杂度评分""" if self.navmesh_tools: return self.navmesh_tools.get_complexity_score() return 0.0 def recommend_mesh_simplification(self) -> dict: """推荐网格简化策略""" if self.navmesh_tools: return self.navmesh_tools.recommend_simplification() return {} def merge_adjacent_polygons(self) -> int: """合并相邻的多边形""" if self.navmesh_tools: return self.navmesh_tools.merge_adjacent_polygons() return 0 def simplify_mesh_polygons(self) -> int: """简化多边形(移除不必要的顶点)""" if self.navmesh_tools: return self.navmesh_tools.simplify_polygons() return 0 def remove_small_mesh_polygons(self) -> int: """移除极小的多边形""" if self.navmesh_tools: return self.navmesh_tools.remove_small_polygons() return 0 def optimize_navigation_mesh(self, optimization_level: str = 'medium') -> dict: """执行完整的网格优化""" if self.navmesh_tools: return self.navmesh_tools.optimize_mesh(optimization_level) return {} def set_optimization_settings(self, settings: dict): """设置优化参数""" if self.navmesh_tools: self.navmesh_tools.set_optimization_settings(settings) def get_optimization_settings(self) -> dict: """获取优化参数""" if self.navmesh_tools: return self.navmesh_tools.get_optimization_settings() return {} def fill_mesh_gaps(self) -> int: """填补导航网格中的空隙""" if self.navmesh_tools: return self.navmesh_tools.fill_gaps() return 0 def smooth_mesh_boundaries(self) -> int: """平滑多边形边界""" if self.navmesh_tools: return self.navmesh_tools.smooth_boundaries() return 0 def balance_polygon_sizes(self) -> int: """平衡多边形大小""" if self.navmesh_tools: return self.navmesh_tools.balance_polygon_sizes() return 0 def serialize_navigation_mesh(self) -> str: """序列化导航网格为字符串""" if self.navmesh_tools: return self.navmesh_tools.serialize_navmesh() return "" def deserialize_navigation_mesh(self, data_str: str) -> bool: """从字符串反序列化导航网格""" if self.navmesh_tools: return self.navmesh_tools.deserialize_navmesh(data_str) return False def save_navmesh_to_file_advanced(self, filepath: str, compress: bool = False, pretty_print: bool = False) -> bool: """保存导航网格到文件(高级选项)""" if self.navmesh_tools: return self.navmesh_tools.save_navmesh_to_file(filepath, compress, pretty_print) return False def load_navmesh_from_file_advanced(self, filepath: str) -> bool: """从文件加载导航网格(高级选项)""" if self.navmesh_tools: return self.navmesh_tools.load_navmesh_from_file(filepath) return False def export_to_standard_format(self, filepath: str, format_type: str = 'obj') -> bool: """导出为标准格式""" if self.navmesh_tools: return self.navmesh_tools.export_to_standard_format(filepath, format_type) return False def import_from_standard_format(self, filepath: str, format_type: str = 'obj') -> bool: """从标准格式导入""" if self.navmesh_tools: return self.navmesh_tools.import_from_standard_format(filepath, format_type) return False def get_navmesh_file_info(self, filepath: str) -> dict: """获取导航网格文件信息""" if self.navmesh_tools: return self.navmesh_tools.get_file_info(filepath) return {} def batch_analyze_navmeshes(self, filepaths: list) -> dict: """批量分析多个导航网格文件""" if self.navmesh_tools: return self.navmesh_tools.batch_analyze(filepaths) return {} def compare_navigation_meshes(self, navmesh1_path: str, navmesh2_path: str) -> dict: """比较两个导航网格""" if self.navmesh_tools: return self.navmesh_tools.compare_navmeshes(navmesh1_path, navmesh2_path) return {} def validate_navmesh_file(self, filepath: str) -> dict: """验证导航网格文件的有效性""" if self.navmesh_tools: return self.navmesh_tools.validate_navmesh_file(filepath) return {} def optimize_and_save_navmesh(self, input_filepath: str, output_filepath: str, optimization_level: str = 'medium') -> dict: """优化并保存导航网格""" if self.navmesh_tools: return self.navmesh_tools.optimize_and_save(input_filepath, output_filepath, optimization_level) return {} def create_optimization_pipeline(self, steps: list) -> dict: """创建优化流水线""" if self.navmesh_tools: return self.navmesh_tools.create_optimization_pipeline(steps) return {}