1
0
forked from Rowland/EG

1.优化碰撞面板保存修改参数

This commit is contained in:
陈横 2025-09-18 09:49:59 +08:00
parent f9bd83c876
commit a1380f9a46

View File

@ -37,6 +37,9 @@ class PropertyPanelManager:
self.world.terrain_edit_strength = 0.3
if not hasattr(self.world, 'terrain_edit_operation'): # 这里原来是 terrain_edit_opertaion
self.world.terrain_edit_operation = "add"
# 初始化碰撞参数加载标志位
self._loading_collision_params = False
# 定义紧凑样式
self.compact_style = """
@ -8788,6 +8791,13 @@ except Exception as e:
self.collision_pos_y = self._createCollisionSpinBox(-100, 100, 2)
self.collision_pos_z = self._createCollisionSpinBox(-100, 100, 2)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置默认位置偏移(无偏移)
self.collision_pos_x.setValue(0.0)
self.collision_pos_y.setValue(0.0)
self.collision_pos_z.setValue(0.0)
layout.addWidget(QLabel("X:"), current_row, 0)
layout.addWidget(self.collision_pos_x, current_row, 1)
current_row += 1
@ -8842,18 +8852,20 @@ except Exception as e:
self.collision_radius = self._createCollisionSpinBox(0.1, 100, 2)
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
default_radius = transformed_info['radius']
self.collision_radius.setValue(default_radius)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
default_radius = bounds.getRadius()
self.collision_radius.setValue(default_radius)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
default_radius = transformed_info['radius']
self.collision_radius.setValue(default_radius)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
default_radius = bounds.getRadius()
self.collision_radius.setValue(default_radius)
self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v))
layout.addWidget(self.collision_radius, current_row, 1)
@ -8874,22 +8886,24 @@ except Exception as e:
self.collision_length = self._createCollisionSpinBox(0.1, 100, 2)
self.collision_height = self._createCollisionSpinBox(0.1, 100, 2)
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
self.collision_width.setValue(actual_size.x)
self.collision_length.setValue(actual_size.y)
self.collision_height.setValue(actual_size.z)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
self.collision_width.setValue(model_size.x)
self.collision_length.setValue(model_size.y)
self.collision_height.setValue(model_size.z)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
self.collision_width.setValue(actual_size.x)
self.collision_length.setValue(actual_size.y)
self.collision_height.setValue(actual_size.z)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
self.collision_width.setValue(model_size.x)
self.collision_length.setValue(model_size.y)
self.collision_height.setValue(model_size.z)
layout.addWidget(QLabel("宽度:"), current_row, 0)
layout.addWidget(self.collision_width, current_row, 1)
@ -8920,22 +8934,24 @@ except Exception as e:
self.collision_capsule_radius = self._createCollisionSpinBox(0.1, 100, 2)
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
# 更合理的默认半径:基于变换后模型宽度的平均值
default_radius = min(actual_size.x, actual_size.y) / 2.5
self.collision_capsule_radius.setValue(default_radius)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
# 更合理的默认半径:基于模型宽度的平均值
default_radius = min(model_size.x, model_size.y) / 2.5
self.collision_capsule_radius.setValue(default_radius)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置基于模型变换后尺寸的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
# 更合理的默认半径:基于变换后模型宽度的平均值
default_radius = min(actual_size.x, actual_size.y) / 2.5
self.collision_capsule_radius.setValue(default_radius)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
# 更合理的默认半径:基于模型宽度的平均值
default_radius = min(model_size.x, model_size.y) / 2.5
self.collision_capsule_radius.setValue(default_radius)
self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v))
layout.addWidget(self.collision_capsule_radius, current_row, 1)
@ -8946,18 +8962,20 @@ except Exception as e:
self.collision_capsule_height = self._createCollisionSpinBox(0.1, 100, 2)
# 设置基于模型变换后高度的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
self.collision_capsule_height.setValue(actual_size.z)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
self.collision_capsule_height.setValue(model_size.z)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置基于模型变换后高度的默认值
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
actual_size = transformed_info['size']
self.collision_capsule_height.setValue(actual_size.z)
else:
# 回退到原始包围盒
bounds = model.getBounds()
if not bounds.isEmpty():
model_size = bounds.getMax() - bounds.getMin()
self.collision_capsule_height.setValue(model_size.z)
self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v))
layout.addWidget(self.collision_capsule_height, current_row, 1)
@ -8978,6 +8996,13 @@ except Exception as e:
self.collision_normal_y = self._createCollisionSpinBox(-1, 1, 2)
self.collision_normal_z = self._createCollisionSpinBox(-1, 1, 2)
# 只在没有现有碰撞时设置默认值否则由_loadCurrentCollisionParameters加载实际值
if not self._hasCollision(model):
# 设置默认法向量(向上)
self.collision_normal_x.setValue(0.0)
self.collision_normal_y.setValue(0.0)
self.collision_normal_z.setValue(1.0)
layout.addWidget(QLabel("Nx:"), current_row, 0)
layout.addWidget(self.collision_normal_x, current_row, 1)
current_row += 1
@ -9123,6 +9148,10 @@ except Exception as e:
return
self._adding_collision = True
# 初始化加载参数标志位
if not hasattr(self, '_loading_collision_params'):
self._loading_collision_params = False
if hasattr(self.world, 'scene_manager'):
# 获取选中的碰撞形状
@ -9299,17 +9328,44 @@ except Exception as e:
def _loadCurrentCollisionParameters(self, model, shape_type):
"""加载当前碰撞参数到界面"""
try:
# 设置标志位,防止在加载参数时触发更新
self._loading_collision_params = True
collision_nodes = model.findAllMatches("**/+CollisionNode")
for collision_np in collision_nodes:
collision_node = collision_np.node()
if collision_node.getNumSolids() > 0:
solid = collision_node.getSolid(0)
# 获取碰撞节点的位置
pos = collision_np.getPos()
self.collision_pos_x.setValue(pos.x)
self.collision_pos_y.setValue(pos.y)
self.collision_pos_z.setValue(pos.z)
# 从碰撞体形状中提取位置偏移
if hasattr(self, 'collision_pos_x'):
# 获取模型的实际中心(考虑变换)
if hasattr(self.world, 'collision_manager'):
transformed_info = self.world.collision_manager._getTransformedModelInfo(model)
if transformed_info:
model_center = transformed_info['center']
else:
model_center = model.getBounds().getCenter() if not model.getBounds().isEmpty() else Point3(0, 0, 0)
else:
model_center = model.getBounds().getCenter() if not model.getBounds().isEmpty() else Point3(0, 0, 0)
# 获取碰撞体的中心
collision_center = self._getCollisionShapeCenter(solid)
if collision_center:
# 计算偏移:碰撞体中心 - 模型中心
offset_x = collision_center.x - model_center.x
offset_y = collision_center.y - model_center.y
offset_z = collision_center.z - model_center.z
self.collision_pos_x.setValue(offset_x)
self.collision_pos_y.setValue(offset_y)
self.collision_pos_z.setValue(offset_z)
print(f"加载位置偏移: ({offset_x:.2f}, {offset_y:.2f}, {offset_z:.2f})")
else:
# 如果无法计算偏移设置为0
self.collision_pos_x.setValue(0.0)
self.collision_pos_y.setValue(0.0)
self.collision_pos_z.setValue(0.0)
print("无法计算位置偏移设置为0")
if shape_type == 'sphere':
self._loadSphereParameters(solid)
@ -9323,6 +9379,40 @@ except Exception as e:
except Exception as e:
print(f"加载碰撞参数失败: {e}")
finally:
# 重置标志位
self._loading_collision_params = False
def _getCollisionShapeCenter(self, solid):
"""从碰撞体形状中获取中心点"""
try:
from panda3d.core import CollisionSphere, CollisionBox, CollisionCapsule, CollisionPlane, Point3
if isinstance(solid, CollisionSphere):
return solid.getCenter()
elif isinstance(solid, CollisionBox):
# 盒子的中心是最小点和最大点的中点
min_pt = solid.getMin()
max_pt = solid.getMax()
return Point3((min_pt.x + max_pt.x) * 0.5,
(min_pt.y + max_pt.y) * 0.5,
(min_pt.z + max_pt.z) * 0.5)
elif isinstance(solid, CollisionCapsule):
# 胶囊体的中心是两个端点的中点
point_a = solid.getPointA()
point_b = solid.getPointB()
return Point3((point_a.x + point_b.x) * 0.5,
(point_a.y + point_b.y) * 0.5,
(point_a.z + point_b.z) * 0.5)
elif isinstance(solid, CollisionPlane):
# 平面没有明确的中心,返回平面上的一个点
plane = solid.getPlane()
return plane.getPoint()
else:
return None
except Exception as e:
print(f"获取碰撞体中心失败: {e}")
return None
def _loadSphereParameters(self, solid):
"""加载球形参数"""
@ -9383,8 +9473,8 @@ except Exception as e:
def _updateCollisionPosition(self, model, axis, value):
"""更新碰撞位置偏移"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_collision_position', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_collision_position', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_collision_position = True
@ -9431,8 +9521,8 @@ except Exception as e:
def _updateSphereRadius(self, model, radius):
"""更新球形半径"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_sphere_radius', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_sphere_radius', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_sphere_radius = True
@ -9453,8 +9543,8 @@ except Exception as e:
def _updateBoxSize(self, model, dimension, value):
"""更新盒型尺寸"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_box_size', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_box_size', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_box_size = True
@ -9480,8 +9570,8 @@ except Exception as e:
def _updateCapsuleRadius(self, model, radius):
"""更新胶囊体半径"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_capsule_radius', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_capsule_radius', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_capsule_radius = True
@ -9504,8 +9594,8 @@ except Exception as e:
def _updateCapsuleHeight(self, model, height):
"""更新胶囊体高度"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_capsule_height', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_capsule_height', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_capsule_height = True
@ -9528,8 +9618,8 @@ except Exception as e:
def _updatePlaneNormal(self, model, axis, value):
"""更新平面法向量"""
try:
# 防止重复调用导致无限循环
if getattr(self, '_updating_plane_normal', False):
# 防止重复调用导致无限循环,以及在加载参数时防止更新
if getattr(self, '_updating_plane_normal', False) or getattr(self, '_loading_collision_params', False):
return
self._updating_plane_normal = True
@ -9553,7 +9643,7 @@ except Exception as e:
self._updating_plane_normal = False
def _recreateCollisionShape(self, model, shape_type, **kwargs):
"""重新创建碰撞形状(保持位置和可见性)"""
"""重新创建碰撞形状(保持可见性)"""
try:
# 保存当前状态
collision_nodes = model.findAllMatches("**/+CollisionNode")
@ -9561,7 +9651,6 @@ except Exception as e:
return
collision_np = collision_nodes[0]
current_pos = collision_np.getPos()
is_visible = not collision_np.isHidden()
# 移除旧的碰撞体
@ -9572,7 +9661,7 @@ except Exception as e:
cNode = CollisionNode(f'modelCollision_{model.getName()}')
cNode.setIntoCollideMask(BitMask32.bit(2))
# 创建新形状
# 创建新形状(位置偏移已经烘焙在形状中)
if hasattr(self.world, 'collision_manager'):
collision_shape = self.world.collision_manager.createCollisionShape(model, shape_type, **kwargs)
else:
@ -9582,9 +9671,10 @@ except Exception as e:
cNode.addSolid(collision_shape)
# 重新附加并恢复状态
# 重新附加(不设置额外位置,因为位置偏移已经在形状中)
new_collision_np = model.attachNewNode(cNode)
new_collision_np.setPos(current_pos)
# 碰撞节点默认位置为 (0, 0, 0),位置偏移通过形状几何体处理
new_collision_np.setPos(0, 0, 0)
if is_visible:
new_collision_np.show()