531 lines
16 KiB
Python
531 lines
16 KiB
Python
"""
|
|
导航网格工具集
|
|
整合导航网格的各种工具功能
|
|
"""
|
|
|
|
from typing import Dict, Any, List, Optional
|
|
from panda3d.core import Point3
|
|
|
|
class NavMeshTools:
|
|
"""
|
|
导航网格工具集
|
|
整合导航网格的各种工具功能
|
|
"""
|
|
|
|
def __init__(self, plugin):
|
|
"""
|
|
初始化工具集
|
|
|
|
Args:
|
|
plugin: 导航网格插件实例
|
|
"""
|
|
self.plugin = plugin
|
|
self.tools = {}
|
|
|
|
# 初始化各个工具
|
|
self._initialize_tools()
|
|
|
|
def _initialize_tools(self):
|
|
"""初始化各个工具"""
|
|
try:
|
|
from .navmesh_analyzer import NavMeshAnalyzer
|
|
from .navmesh_optimizer import NavMeshOptimizer
|
|
from ..utils.navmesh_serializer import NavMeshSerializer
|
|
|
|
# 初始化分析器
|
|
self.tools['analyzer'] = NavMeshAnalyzer(self.plugin.navmesh_manager)
|
|
|
|
# 初始化优化器
|
|
self.tools['optimizer'] = NavMeshOptimizer(self.plugin.navmesh_manager)
|
|
|
|
# 初始化序列化器
|
|
self.tools['serializer'] = NavMeshSerializer()
|
|
|
|
except Exception as e:
|
|
print(f"初始化工具失败: {e}")
|
|
|
|
def analyze_mesh_quality(self) -> Dict:
|
|
"""
|
|
分析导航网格质量
|
|
|
|
Returns:
|
|
质量分析结果
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].analyze_mesh_quality()
|
|
return {}
|
|
|
|
def suggest_optimizations(self) -> List[Dict]:
|
|
"""
|
|
提供优化建议
|
|
|
|
Returns:
|
|
优化建议列表
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].suggest_optimizations()
|
|
return []
|
|
|
|
def analyze_pathfinding_performance(self, sample_count: int = 100) -> Dict:
|
|
"""
|
|
分析寻路性能
|
|
|
|
Args:
|
|
sample_count: 采样次数
|
|
|
|
Returns:
|
|
性能分析结果
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].analyze_pathfinding_performance(sample_count)
|
|
return {}
|
|
|
|
def detect_bottlenecks(self) -> List[Dict]:
|
|
"""
|
|
检测导航网格中的瓶颈区域
|
|
|
|
Returns:
|
|
瓶颈区域列表
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].detect_bottlenecks()
|
|
return []
|
|
|
|
def analyze_coverage(self) -> Dict:
|
|
"""
|
|
分析导航网格覆盖情况
|
|
|
|
Returns:
|
|
覆盖情况分析结果
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].analyze_coverage()
|
|
return {}
|
|
|
|
def generate_report(self) -> Dict:
|
|
"""
|
|
生成完整的分析报告
|
|
|
|
Returns:
|
|
分析报告
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].generate_report()
|
|
return {}
|
|
|
|
def export_report(self, filepath: str) -> bool:
|
|
"""
|
|
导出分析报告到文件
|
|
|
|
Args:
|
|
filepath: 导出文件路径
|
|
|
|
Returns:
|
|
是否导出成功
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].export_report(filepath)
|
|
return False
|
|
|
|
def get_complexity_score(self) -> float:
|
|
"""
|
|
计算导航网格复杂度评分
|
|
|
|
Returns:
|
|
复杂度评分 (0-100)
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].get_complexity_score()
|
|
return 0.0
|
|
|
|
def recommend_simplification(self) -> Dict:
|
|
"""
|
|
推荐网格简化策略
|
|
|
|
Returns:
|
|
简化策略建议
|
|
"""
|
|
if 'analyzer' in self.tools:
|
|
return self.tools['analyzer'].recommend_simplification()
|
|
return {}
|
|
|
|
def merge_adjacent_polygons(self) -> int:
|
|
"""
|
|
合并相邻的多边形
|
|
|
|
Returns:
|
|
合并的多边形数量
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].merge_adjacent_polygons()
|
|
return 0
|
|
|
|
def simplify_polygons(self) -> int:
|
|
"""
|
|
简化多边形(移除不必要的顶点)
|
|
|
|
Returns:
|
|
简化的顶点数量
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].simplify_polygons()
|
|
return 0
|
|
|
|
def remove_small_polygons(self) -> int:
|
|
"""
|
|
移除极小的多边形
|
|
|
|
Returns:
|
|
移除的多边形数量
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].remove_small_polygons()
|
|
return 0
|
|
|
|
def optimize_mesh(self, optimization_level: str = 'medium') -> Dict:
|
|
"""
|
|
执行完整的网格优化
|
|
|
|
Args:
|
|
optimization_level: 优化级别 ('light', 'medium', 'heavy')
|
|
|
|
Returns:
|
|
优化结果统计
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].optimize_mesh(optimization_level)
|
|
return {}
|
|
|
|
def set_optimization_settings(self, settings: Dict):
|
|
"""
|
|
设置优化参数
|
|
|
|
Args:
|
|
settings: 优化参数字典
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
self.tools['optimizer'].set_optimization_settings(settings)
|
|
|
|
def get_optimization_settings(self) -> Dict:
|
|
"""
|
|
获取优化参数
|
|
|
|
Returns:
|
|
优化参数字典
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].get_optimization_settings()
|
|
return {}
|
|
|
|
def fill_gaps(self) -> int:
|
|
"""
|
|
填补导航网格中的空隙
|
|
|
|
Returns:
|
|
填补的多边形数量
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].fill_gaps()
|
|
return 0
|
|
|
|
def smooth_boundaries(self) -> int:
|
|
"""
|
|
平滑多边形边界
|
|
|
|
Returns:
|
|
平滑处理的边数
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].smooth_boundaries()
|
|
return 0
|
|
|
|
def balance_polygon_sizes(self) -> int:
|
|
"""
|
|
平衡多边形大小
|
|
|
|
Returns:
|
|
调整的多边形数量
|
|
"""
|
|
if 'optimizer' in self.tools:
|
|
return self.tools['optimizer'].balance_polygon_sizes()
|
|
return 0
|
|
|
|
def serialize_navmesh(self) -> str:
|
|
"""
|
|
序列化导航网格为字符串
|
|
|
|
Returns:
|
|
序列化后的字符串
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].serialize_navmesh(self.plugin.navmesh_manager)
|
|
return ""
|
|
|
|
def deserialize_navmesh(self, data_str: str) -> bool:
|
|
"""
|
|
从字符串反序列化导航网格
|
|
|
|
Args:
|
|
data_str: 序列化数据字符串
|
|
|
|
Returns:
|
|
是否反序列化成功
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].deserialize_navmesh(data_str, self.plugin.navmesh_manager)
|
|
return False
|
|
|
|
def save_navmesh_to_file(self, filepath: str,
|
|
compress: bool = False, pretty_print: bool = False) -> bool:
|
|
"""
|
|
保存导航网格到文件
|
|
|
|
Args:
|
|
filepath: 文件路径
|
|
compress: 是否压缩
|
|
pretty_print: 是否美化输出
|
|
|
|
Returns:
|
|
是否保存成功
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].save_navmesh_to_file(
|
|
self.plugin.navmesh_manager, filepath, compress, pretty_print)
|
|
return False
|
|
|
|
def load_navmesh_from_file(self, filepath: str) -> bool:
|
|
"""
|
|
从文件加载导航网格
|
|
|
|
Args:
|
|
filepath: 文件路径
|
|
|
|
Returns:
|
|
是否加载成功
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].load_navmesh_from_file(filepath, self.plugin.navmesh_manager)
|
|
return False
|
|
|
|
def export_to_standard_format(self, filepath: str,
|
|
format_type: str = 'obj') -> bool:
|
|
"""
|
|
导出为标准格式
|
|
|
|
Args:
|
|
filepath: 导出文件路径
|
|
format_type: 导出格式 ('obj', 'ply')
|
|
|
|
Returns:
|
|
是否导出成功
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].export_to_standard_format(
|
|
self.plugin.navmesh_manager, filepath, format_type)
|
|
return False
|
|
|
|
def import_from_standard_format(self, filepath: str,
|
|
format_type: str = 'obj') -> bool:
|
|
"""
|
|
从标准格式导入
|
|
|
|
Args:
|
|
filepath: 导入文件路径
|
|
format_type: 导入格式 ('obj', 'ply')
|
|
|
|
Returns:
|
|
是否导入成功
|
|
"""
|
|
if 'serializer' in self.tools and self.plugin.navmesh_manager:
|
|
return self.tools['serializer'].import_from_standard_format(
|
|
filepath, self.plugin.navmesh_manager, format_type)
|
|
return False
|
|
|
|
def get_file_info(self, filepath: str) -> Dict[str, Any]:
|
|
"""
|
|
获取导航网格文件信息
|
|
|
|
Args:
|
|
filepath: 文件路径
|
|
|
|
Returns:
|
|
文件信息字典
|
|
"""
|
|
if 'serializer' in self.tools:
|
|
return self.tools['serializer'].get_file_info(filepath)
|
|
return {}
|
|
|
|
def batch_analyze(self, filepaths: List[str]) -> Dict[str, Dict]:
|
|
"""
|
|
批量分析多个导航网格文件
|
|
|
|
Args:
|
|
filepaths: 文件路径列表
|
|
|
|
Returns:
|
|
各文件的分析结果
|
|
"""
|
|
results = {}
|
|
|
|
for filepath in filepaths:
|
|
try:
|
|
info = self.get_file_info(filepath)
|
|
results[filepath] = info
|
|
except Exception as e:
|
|
results[filepath] = {'error': str(e)}
|
|
|
|
return results
|
|
|
|
def compare_navmeshes(self, navmesh1_path: str, navmesh2_path: str) -> Dict:
|
|
"""
|
|
比较两个导航网格
|
|
|
|
Args:
|
|
navmesh1_path: 第一个导航网格文件路径
|
|
navmesh2_path: 第二个导航网格文件路径
|
|
|
|
Returns:
|
|
比较结果
|
|
"""
|
|
try:
|
|
info1 = self.get_file_info(navmesh1_path)
|
|
info2 = self.get_file_info(navmesh2_path)
|
|
|
|
comparison = {
|
|
'file1': info1,
|
|
'file2': info2,
|
|
'differences': {}
|
|
}
|
|
|
|
# 比较关键指标
|
|
for key in ['polygon_count', 'obstacle_count', 'file_size']:
|
|
val1 = info1.get(key, 0)
|
|
val2 = info2.get(key, 0)
|
|
if val1 != val2:
|
|
comparison['differences'][key] = {
|
|
'file1': val1,
|
|
'file2': val2,
|
|
'difference': val2 - val1,
|
|
'percentage': ((val2 - val1) / val1 * 100) if val1 != 0 else float('inf')
|
|
}
|
|
|
|
return comparison
|
|
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def validate_navmesh_file(self, filepath: str) -> Dict:
|
|
"""
|
|
验证导航网格文件的有效性
|
|
|
|
Args:
|
|
filepath: 文件路径
|
|
|
|
Returns:
|
|
验证结果
|
|
"""
|
|
try:
|
|
info = self.get_file_info(filepath)
|
|
|
|
validation = {
|
|
'filepath': filepath,
|
|
'valid': True,
|
|
'issues': [],
|
|
'info': info
|
|
}
|
|
|
|
# 检查基本信息
|
|
if info.get('file_size', 0) == 0:
|
|
validation['valid'] = False
|
|
validation['issues'].append('文件为空')
|
|
|
|
if info.get('version') == 'unknown':
|
|
validation['issues'].append('无法识别文件版本')
|
|
|
|
# 检查多边形数量
|
|
polygon_count = info.get('polygon_count', 0)
|
|
if polygon_count == 0:
|
|
validation['issues'].append('导航网格不包含多边形')
|
|
elif polygon_count > 100000:
|
|
validation['issues'].append('多边形数量过多,可能影响性能')
|
|
|
|
return validation
|
|
|
|
except Exception as e:
|
|
return {
|
|
'filepath': filepath,
|
|
'valid': False,
|
|
'issues': [str(e)],
|
|
'error': True
|
|
}
|
|
|
|
def optimize_and_save(self, input_filepath: str, output_filepath: str,
|
|
optimization_level: str = 'medium') -> Dict:
|
|
"""
|
|
优化并保存导航网格
|
|
|
|
Args:
|
|
input_filepath: 输入文件路径
|
|
output_filepath: 输出文件路径
|
|
optimization_level: 优化级别
|
|
|
|
Returns:
|
|
操作结果
|
|
"""
|
|
try:
|
|
# 加载导航网格
|
|
if not self.load_navmesh_from_file(input_filepath):
|
|
return {'success': False, 'error': '无法加载输入文件'}
|
|
|
|
# 执行优化
|
|
optimization_result = self.optimize_mesh(optimization_level)
|
|
|
|
# 保存优化后的网格
|
|
if not self.save_navmesh_to_file(output_filepath):
|
|
return {'success': False, 'error': '无法保存优化后的文件'}
|
|
|
|
return {
|
|
'success': True,
|
|
'optimization_result': optimization_result,
|
|
'input_file': input_filepath,
|
|
'output_file': output_filepath
|
|
}
|
|
|
|
except Exception as e:
|
|
return {'success': False, 'error': str(e)}
|
|
|
|
def create_optimization_pipeline(self, steps: List[str]) -> Dict:
|
|
"""
|
|
创建优化流水线
|
|
|
|
Args:
|
|
steps: 优化步骤列表
|
|
|
|
Returns:
|
|
流水线执行结果
|
|
"""
|
|
results = {
|
|
'steps': steps,
|
|
'results': [],
|
|
'total_improvement': 0
|
|
}
|
|
|
|
for step in steps:
|
|
try:
|
|
if step == 'merge':
|
|
result = {'step': 'merge', 'polygons_merged': self.merge_adjacent_polygons()}
|
|
elif step == 'simplify':
|
|
result = {'step': 'simplify', 'vertices_simplified': self.simplify_polygons()}
|
|
elif step == 'remove_small':
|
|
result = {'step': 'remove_small', 'polygons_removed': self.remove_small_polygons()}
|
|
elif step == 'optimize':
|
|
result = {'step': 'optimize', 'optimization_result': self.optimize_mesh()}
|
|
else:
|
|
result = {'step': step, 'error': '未知的优化步骤'}
|
|
|
|
results['results'].append(result)
|
|
|
|
except Exception as e:
|
|
results['results'].append({'step': step, 'error': str(e)})
|
|
|
|
return results |