456 lines
16 KiB
Python
456 lines
16 KiB
Python
"""
|
||
导航网格优化器
|
||
提供自动优化导航网格的功能,包括多边形合并、简化和重构
|
||
"""
|
||
|
||
import math
|
||
from typing import List, Dict, Set, Tuple, Optional
|
||
from panda3d.core import Point3
|
||
|
||
class NavMeshOptimizer:
|
||
"""
|
||
导航网格优化器
|
||
提供自动优化导航网格的功能
|
||
"""
|
||
|
||
def __init__(self, navmesh_manager):
|
||
"""
|
||
初始化导航网格优化器
|
||
|
||
Args:
|
||
navmesh_manager: 导航网格管理器实例
|
||
"""
|
||
self.navmesh_manager = navmesh_manager
|
||
self.optimization_settings = {
|
||
'merge_threshold': 0.5, # 合并阈值
|
||
'simplify_threshold': 0.1, # 简化阈值
|
||
'min_polygon_area': 0.01, # 最小多边形面积
|
||
'max_vertices_per_polygon': 8, # 每个多边形最大顶点数
|
||
'preserve_boundaries': True, # 是否保留边界
|
||
'preserve_portals': True # 是否保留门户点
|
||
}
|
||
|
||
def set_optimization_settings(self, settings: Dict):
|
||
"""
|
||
设置优化参数
|
||
|
||
Args:
|
||
settings: 优化参数字典
|
||
"""
|
||
self.optimization_settings.update(settings)
|
||
|
||
def get_optimization_settings(self) -> Dict:
|
||
"""
|
||
获取优化参数
|
||
|
||
Returns:
|
||
优化参数字典
|
||
"""
|
||
return self.optimization_settings.copy()
|
||
|
||
def merge_adjacent_polygons(self) -> int:
|
||
"""
|
||
合并相邻的多边形
|
||
|
||
Returns:
|
||
合并的多边形数量
|
||
"""
|
||
if not self.navmesh_manager or not self.navmesh_manager.polygons:
|
||
return 0
|
||
|
||
merged_count = 0
|
||
processed_polygons = set()
|
||
|
||
# 创建多边形ID列表的副本,避免在迭代时修改字典
|
||
polygon_ids = list(self.navmesh_manager.polygons.keys())
|
||
|
||
for polygon_id in polygon_ids:
|
||
if polygon_id in processed_polygons:
|
||
continue
|
||
|
||
if polygon_id not in self.navmesh_manager.polygons:
|
||
continue
|
||
|
||
polygon = self.navmesh_manager.polygons[polygon_id]
|
||
|
||
# 检查是否可以与其他多边形合并
|
||
for neighbor_id in polygon.neighbors[:]: # 使用副本避免修改时的问题
|
||
if neighbor_id in processed_polygons:
|
||
continue
|
||
|
||
if neighbor_id not in self.navmesh_manager.polygons:
|
||
continue
|
||
|
||
neighbor = self.navmesh_manager.polygons[neighbor_id]
|
||
|
||
# 检查合并条件
|
||
if self._can_merge_polygons(polygon, neighbor):
|
||
# 执行合并
|
||
if self._merge_two_polygons(polygon_id, neighbor_id):
|
||
merged_count += 1
|
||
processed_polygons.add(polygon_id)
|
||
processed_polygons.add(neighbor_id)
|
||
break
|
||
|
||
# 重新计算连接关系
|
||
if merged_count > 0:
|
||
self.navmesh_manager._calculate_polygon_connections()
|
||
self.navmesh_manager._calculate_portal_points()
|
||
self.navmesh_manager._update_navmesh_visualization()
|
||
|
||
return merged_count
|
||
|
||
def _can_merge_polygons(self, polygon1, polygon2) -> bool:
|
||
"""
|
||
检查两个多边形是否可以合并
|
||
|
||
Args:
|
||
polygon1: 第一个多边形
|
||
polygon2: 第二个多边形
|
||
|
||
Returns:
|
||
是否可以合并
|
||
"""
|
||
# 检查是否有共享边
|
||
if not self._polygons_share_full_edge(polygon1, polygon2):
|
||
return False
|
||
|
||
# 检查合并后的多边形是否过于复杂
|
||
merged_vertices = self._get_merged_vertices(polygon1, polygon2)
|
||
if len(merged_vertices) > self.optimization_settings['max_vertices_per_polygon']:
|
||
return False
|
||
|
||
# 检查面积
|
||
merged_area = self._calculate_polygon_area(merged_vertices)
|
||
if merged_area < self.optimization_settings['min_polygon_area']:
|
||
return False
|
||
|
||
return True
|
||
|
||
def _polygons_share_full_edge(self, polygon1, polygon2) -> bool:
|
||
"""
|
||
检查两个多边形是否共享完整的一条边
|
||
|
||
Args:
|
||
polygon1: 第一个多边形
|
||
polygon2: 第二个多边形
|
||
|
||
Returns:
|
||
是否共享完整边
|
||
"""
|
||
threshold = 0.01 # 点重合阈值
|
||
|
||
# 检查polygon1的每条边
|
||
for i in range(len(polygon1.vertices)):
|
||
v1_start = polygon1.vertices[i]
|
||
v1_end = polygon1.vertices[(i + 1) % len(polygon1.vertices)]
|
||
|
||
# 检查polygon2的每条边
|
||
for j in range(len(polygon2.vertices)):
|
||
v2_start = polygon2.vertices[j]
|
||
v2_end = polygon2.vertices[(j + 1) % len(polygon2.vertices)]
|
||
|
||
# 检查边是否重合(方向相同)
|
||
if (self._points_equal(v1_start, v2_start, threshold) and
|
||
self._points_equal(v1_end, v2_end, threshold)):
|
||
return True
|
||
|
||
# 检查边是否重合(方向相反)
|
||
if (self._points_equal(v1_start, v2_end, threshold) and
|
||
self._points_equal(v1_end, v2_start, threshold)):
|
||
return True
|
||
|
||
return False
|
||
|
||
def _points_equal(self, p1: Point3, p2: Point3, threshold: float) -> bool:
|
||
"""检查两点是否相等(在阈值范围内)"""
|
||
return (p1 - p2).length() < threshold
|
||
|
||
def _get_merged_vertices(self, polygon1, polygon2) -> List[Point3]:
|
||
"""
|
||
获取两个多边形合并后的顶点
|
||
|
||
Args:
|
||
polygon1: 第一个多边形
|
||
polygon2: 第二个多边形
|
||
|
||
Returns:
|
||
合并后的顶点列表
|
||
"""
|
||
# 这是一个简化的实现
|
||
# 实际应用中需要正确处理多边形合并的几何运算
|
||
merged_vertices = []
|
||
merged_vertices.extend(polygon1.vertices)
|
||
merged_vertices.extend(polygon2.vertices)
|
||
return merged_vertices
|
||
|
||
def _calculate_polygon_area(self, vertices: List[Point3]) -> float:
|
||
"""计算多边形面积(使用鞋带公式)"""
|
||
if len(vertices) < 3:
|
||
return 0.0
|
||
|
||
area = 0.0
|
||
n = len(vertices)
|
||
for i in range(n):
|
||
j = (i + 1) % n
|
||
area += vertices[i].x * vertices[j].z
|
||
area -= vertices[j].x * vertices[i].z
|
||
return abs(area) / 2.0
|
||
|
||
def _merge_two_polygons(self, polygon1_id: int, polygon2_id: int) -> bool:
|
||
"""
|
||
合并两个多边形
|
||
|
||
Args:
|
||
polygon1_id: 第一个多边形ID
|
||
polygon2_id: 第二个多边形ID
|
||
|
||
Returns:
|
||
是否合并成功
|
||
"""
|
||
try:
|
||
if (polygon1_id not in self.navmesh_manager.polygons or
|
||
polygon2_id not in self.navmesh_manager.polygons):
|
||
return False
|
||
|
||
polygon1 = self.navmesh_manager.polygons[polygon1_id]
|
||
polygon2 = self.navmesh_manager.polygons[polygon2_id]
|
||
|
||
# 创建合并后的新多边形
|
||
merged_vertices = self._merge_polygon_vertices(polygon1, polygon2)
|
||
if not merged_vertices:
|
||
return False
|
||
|
||
# 移除旧的多边形
|
||
self.navmesh_manager.remove_polygon(polygon2_id)
|
||
|
||
# 更新第一个多边形
|
||
polygon1.vertices = merged_vertices
|
||
polygon1.center = polygon1._calculate_center()
|
||
polygon1.area = polygon1._calculate_area()
|
||
polygon1.bounding_box = polygon1._calculate_bounding_box()
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"合并多边形失败: {e}")
|
||
return False
|
||
|
||
def _merge_polygon_vertices(self, polygon1, polygon2) -> List[Point3]:
|
||
"""
|
||
合并两个多边形的顶点
|
||
|
||
Args:
|
||
polygon1: 第一个多边形
|
||
polygon2: 第二个多边形
|
||
|
||
Returns:
|
||
合并后的顶点列表
|
||
"""
|
||
# 这是一个简化的实现
|
||
# 实际应用中需要正确处理多边形合并的几何运算
|
||
# 包括移除共享边,正确排序顶点等
|
||
|
||
# 简单合并所有顶点
|
||
merged_vertices = []
|
||
merged_vertices.extend(polygon1.vertices)
|
||
merged_vertices.extend(polygon2.vertices)
|
||
|
||
# 去除重复顶点(简化处理)
|
||
unique_vertices = []
|
||
for vertex in merged_vertices:
|
||
is_duplicate = False
|
||
for existing_vertex in unique_vertices:
|
||
if self._points_equal(vertex, existing_vertex, 0.01):
|
||
is_duplicate = True
|
||
break
|
||
if not is_duplicate:
|
||
unique_vertices.append(vertex)
|
||
|
||
return unique_vertices[:self.optimization_settings['max_vertices_per_polygon']]
|
||
|
||
def simplify_polygons(self) -> int:
|
||
"""
|
||
简化多边形(移除不必要的顶点)
|
||
|
||
Returns:
|
||
简化的顶点数量
|
||
"""
|
||
if not self.navmesh_manager or not self.navmesh_manager.polygons:
|
||
return 0
|
||
|
||
simplified_count = 0
|
||
|
||
for polygon in self.navmesh_manager.polygons.values():
|
||
original_vertex_count = len(polygon.vertices)
|
||
simplified_vertices = self._simplify_polygon(polygon.vertices)
|
||
|
||
if len(simplified_vertices) < original_vertex_count:
|
||
polygon.vertices = simplified_vertices
|
||
polygon.center = polygon._calculate_center()
|
||
polygon.area = polygon._calculate_area()
|
||
polygon.bounding_box = polygon._calculate_bounding_box()
|
||
simplified_count += original_vertex_count - len(simplified_vertices)
|
||
|
||
# 重新计算连接关系
|
||
if simplified_count > 0:
|
||
self.navmesh_manager._calculate_polygon_connections()
|
||
self.navmesh_manager._calculate_portal_points()
|
||
self.navmesh_manager._update_navmesh_visualization()
|
||
|
||
return simplified_count
|
||
|
||
def _simplify_polygon(self, vertices: List[Point3]) -> List[Point3]:
|
||
"""
|
||
简化单个多边形
|
||
|
||
Args:
|
||
vertices: 多边形顶点列表
|
||
|
||
Returns:
|
||
简化后的顶点列表
|
||
"""
|
||
if len(vertices) <= 3:
|
||
return vertices
|
||
|
||
simplified = [vertices[0]] # 保留第一个顶点
|
||
i = 0
|
||
|
||
while i < len(vertices):
|
||
# 找到下一个需要保留的顶点
|
||
next_i = self._find_next_significant_vertex(vertices, i)
|
||
if next_i >= len(vertices):
|
||
break
|
||
|
||
simplified.append(vertices[next_i])
|
||
i = next_i
|
||
|
||
# 确保至少有3个顶点
|
||
if len(simplified) < 3:
|
||
return vertices[:3]
|
||
|
||
return simplified
|
||
|
||
def _find_next_significant_vertex(self, vertices: List[Point3], start_index: int) -> int:
|
||
"""
|
||
找到下一个重要的顶点(需要保留的顶点)
|
||
|
||
Args:
|
||
vertices: 顶点列表
|
||
start_index: 起始索引
|
||
|
||
Returns:
|
||
下一个重要顶点的索引
|
||
"""
|
||
threshold = self.optimization_settings['simplify_threshold']
|
||
|
||
# 简化处理:每隔几个顶点保留一个
|
||
step = max(1, int(len(vertices) / 8)) # 保持最多8个顶点
|
||
next_index = min(start_index + step, len(vertices) - 1)
|
||
|
||
return next_index
|
||
|
||
def remove_small_polygons(self) -> int:
|
||
"""
|
||
移除极小的多边形
|
||
|
||
Returns:
|
||
移除的多边形数量
|
||
"""
|
||
if not self.navmesh_manager or not self.navmesh_manager.polygons:
|
||
return 0
|
||
|
||
min_area = self.optimization_settings['min_polygon_area']
|
||
removed_count = 0
|
||
|
||
# 创建需要移除的多边形ID列表
|
||
to_remove = []
|
||
for polygon_id, polygon in self.navmesh_manager.polygons.items():
|
||
if polygon.area < min_area:
|
||
to_remove.append(polygon_id)
|
||
|
||
# 移除多边形
|
||
for polygon_id in to_remove:
|
||
self.navmesh_manager.remove_polygon(polygon_id)
|
||
removed_count += 1
|
||
|
||
# 更新连接关系
|
||
if removed_count > 0:
|
||
self.navmesh_manager._calculate_polygon_connections()
|
||
self.navmesh_manager._calculate_portal_points()
|
||
self.navmesh_manager._update_navmesh_visualization()
|
||
|
||
return removed_count
|
||
|
||
def optimize_mesh(self, optimization_level: str = 'medium') -> Dict:
|
||
"""
|
||
执行完整的网格优化
|
||
|
||
Args:
|
||
optimization_level: 优化级别 ('light', 'medium', 'heavy')
|
||
|
||
Returns:
|
||
优化结果统计
|
||
"""
|
||
results = {
|
||
'merged_polygons': 0,
|
||
'simplified_vertices': 0,
|
||
'removed_polygons': 0,
|
||
'total_optimizations': 0
|
||
}
|
||
|
||
# 根据优化级别设置参数
|
||
if optimization_level == 'heavy':
|
||
self.optimization_settings['merge_threshold'] = 0.3
|
||
self.optimization_settings['simplify_threshold'] = 0.2
|
||
self.optimization_settings['min_polygon_area'] = 0.05
|
||
elif optimization_level == 'light':
|
||
self.optimization_settings['merge_threshold'] = 0.7
|
||
self.optimization_settings['simplify_threshold'] = 0.05
|
||
self.optimization_settings['min_polygon_area'] = 0.005
|
||
|
||
# 执行优化步骤
|
||
if optimization_level in ['medium', 'heavy']:
|
||
results['merged_polygons'] = self.merge_adjacent_polygons()
|
||
|
||
results['simplified_vertices'] = self.simplify_polygons()
|
||
results['removed_polygons'] = self.remove_small_polygons()
|
||
|
||
results['total_optimizations'] = (results['merged_polygons'] +
|
||
results['simplified_vertices'] +
|
||
results['removed_polygons'])
|
||
|
||
return results
|
||
|
||
def fill_gaps(self) -> int:
|
||
"""
|
||
填补导航网格中的空隙
|
||
|
||
Returns:
|
||
填补的多边形数量
|
||
"""
|
||
# 这是一个复杂的功能,需要分析未覆盖的区域并生成新的多边形
|
||
# 简化实现:返回0
|
||
return 0
|
||
|
||
def smooth_boundaries(self) -> int:
|
||
"""
|
||
平滑多边形边界
|
||
|
||
Returns:
|
||
平滑处理的边数
|
||
"""
|
||
# 这需要复杂的几何运算来平滑多边形边界
|
||
# 简化实现:返回0
|
||
return 0
|
||
|
||
def balance_polygon_sizes(self) -> int:
|
||
"""
|
||
平衡多边形大小
|
||
|
||
Returns:
|
||
调整的多边形数量
|
||
"""
|
||
# 这需要分析多边形大小分布并调整过大或过小的多边形
|
||
# 简化实现:返回0
|
||
return 0 |