forked from Rowland/EG
Merge remote-tracking branch 'origin/main_ch_eg' into addRender
# Conflicts: # ui/property_panel.py
This commit is contained in:
commit
e62ae89289
@ -309,51 +309,81 @@ class CollisionManager:
|
|||||||
CollisionPlane, CollisionPolygon, Plane, Vec3
|
CollisionPlane, CollisionPolygon, Plane, Vec3
|
||||||
)
|
)
|
||||||
|
|
||||||
bounds = model.getBounds()
|
# 获取考虑变换后的实际尺寸和中心
|
||||||
if bounds.isEmpty():
|
transformed_info = self._getTransformedModelInfo(model)
|
||||||
|
if not transformed_info:
|
||||||
# 默认小球体
|
# 默认小球体
|
||||||
return CollisionSphere(Point3(0, 0, 0), 1.0)
|
return CollisionSphere(Point3(0, 0, 0), 1.0)
|
||||||
|
|
||||||
center = bounds.getCenter()
|
center = transformed_info['center']
|
||||||
radius = bounds.getRadius()
|
radius = transformed_info['radius']
|
||||||
|
actual_size = transformed_info['size']
|
||||||
|
scale_factor = transformed_info['scale_factor']
|
||||||
|
|
||||||
# 自动选择最适合的形状
|
# 自动选择最适合的形状
|
||||||
if shape_type == 'auto':
|
if shape_type == 'auto':
|
||||||
shape_type = self._determineOptimalShape(model, bounds)
|
shape_type = self._determineOptimalShape(model, transformed_info)
|
||||||
|
|
||||||
if shape_type == 'sphere':
|
if shape_type == 'sphere':
|
||||||
return CollisionSphere(center, kwargs.get('radius', radius))
|
# 优化球形碰撞体
|
||||||
|
sphere_radius = kwargs.get('radius', radius)
|
||||||
|
|
||||||
|
# 支持位置偏移
|
||||||
|
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
|
||||||
|
sphere_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
|
||||||
|
|
||||||
|
return CollisionSphere(sphere_center, sphere_radius)
|
||||||
|
|
||||||
elif shape_type == 'box':
|
elif shape_type == 'box':
|
||||||
# 创建自定义尺寸的包围盒
|
# 优化盒型碰撞体 - 更精确的尺寸和位置控制(考虑缩放)
|
||||||
width = kwargs.get('width', bounds.getMax().x - bounds.getMin().x)
|
# 获取自定义尺寸,如果没有提供则使用变换后的实际尺寸
|
||||||
length = kwargs.get('length', bounds.getMax().y - bounds.getMin().y)
|
width = kwargs.get('width', actual_size.x)
|
||||||
height = kwargs.get('height', bounds.getMax().z - bounds.getMin().z)
|
length = kwargs.get('length', actual_size.y)
|
||||||
|
height = kwargs.get('height', actual_size.z)
|
||||||
|
|
||||||
# 计算盒子的最小和最大点
|
# 支持位置偏移
|
||||||
|
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
|
||||||
|
box_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
|
||||||
|
|
||||||
|
# 计算盒子的最小和最大点(基于偏移后的中心)
|
||||||
half_width = width / 2
|
half_width = width / 2
|
||||||
half_length = length / 2
|
half_length = length / 2
|
||||||
half_height = height / 2
|
half_height = height / 2
|
||||||
|
|
||||||
min_point = Point3(center.x - half_width, center.y - half_length, center.z - half_height)
|
min_point = Point3(box_center.x - half_width, box_center.y - half_length, box_center.z - half_height)
|
||||||
max_point = Point3(center.x + half_width, center.y + half_length, center.z + half_height)
|
max_point = Point3(box_center.x + half_width, box_center.y + half_length, box_center.z + half_height)
|
||||||
return CollisionBox(min_point, max_point)
|
return CollisionBox(min_point, max_point)
|
||||||
|
|
||||||
elif shape_type == 'capsule':
|
elif shape_type == 'capsule':
|
||||||
# 创建自定义参数的胶囊体
|
# 优化胶囊体碰撞 - 更合理的比例和位置控制(考虑缩放)
|
||||||
custom_height = kwargs.get('height', (bounds.getMax().z - bounds.getMin().z))
|
# 使用变换后的实际高度,或自定义高度
|
||||||
custom_radius = kwargs.get('radius', min(bounds.getRadius() * 0.5, custom_height * 0.3))
|
custom_height = kwargs.get('height', actual_size.z)
|
||||||
|
|
||||||
# 计算胶囊体的两个端点
|
# 更合理的半径计算:基于变换后模型宽度的平均值
|
||||||
point_a = Point3(center.x, center.y, center.z - custom_height/2 + custom_radius)
|
default_radius = min(actual_size.x, actual_size.y) / 2.5 # 稍微小于模型的宽度
|
||||||
point_b = Point3(center.x, center.y, center.z + custom_height/2 - custom_radius)
|
custom_radius = kwargs.get('radius', min(default_radius, custom_height * 0.4))
|
||||||
|
|
||||||
|
# 支持位置偏移
|
||||||
|
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
|
||||||
|
capsule_center = Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z)
|
||||||
|
|
||||||
|
# 计算胶囊体的两个端点(确保半径不会超出高度)
|
||||||
|
effective_height = max(custom_height, custom_radius * 2.1) # 确保高度至少是半径的2倍多一点
|
||||||
|
point_a = Point3(capsule_center.x, capsule_center.y, capsule_center.z - effective_height/2 + custom_radius)
|
||||||
|
point_b = Point3(capsule_center.x, capsule_center.y, capsule_center.z + effective_height/2 - custom_radius)
|
||||||
return CollisionCapsule(point_a, point_b, custom_radius)
|
return CollisionCapsule(point_a, point_b, custom_radius)
|
||||||
|
|
||||||
elif shape_type == 'plane':
|
elif shape_type == 'plane':
|
||||||
# 创建平面(适合地面、墙面)
|
# 优化平面碰撞 - 支持位置偏移和更灵活的法向量
|
||||||
normal = kwargs.get('normal', Vec3(0, 0, 1))
|
normal = kwargs.get('normal', Vec3(0, 0, 1))
|
||||||
point = kwargs.get('point', center)
|
|
||||||
plane = Plane(normal, point)
|
# 支持位置偏移
|
||||||
|
pos_offset = kwargs.get('position_offset', Vec3(0, 0, 0))
|
||||||
|
plane_point = kwargs.get('point', Point3(center.x + pos_offset.x, center.y + pos_offset.y, center.z + pos_offset.z))
|
||||||
|
|
||||||
|
# 标准化法向量
|
||||||
|
normal.normalize()
|
||||||
|
plane = Plane(normal, plane_point)
|
||||||
return CollisionPlane(plane)
|
return CollisionPlane(plane)
|
||||||
|
|
||||||
elif shape_type == 'polygon':
|
elif shape_type == 'polygon':
|
||||||
@ -378,10 +408,10 @@ class CollisionManager:
|
|||||||
#print("⚠️ 多边形至少需要3个顶点,回退到球体")
|
#print("⚠️ 多边形至少需要3个顶点,回退到球体")
|
||||||
return CollisionSphere(center, radius)
|
return CollisionSphere(center, radius)
|
||||||
|
|
||||||
def _determineOptimalShape(self, model, bounds):
|
def _determineOptimalShape(self, model, transformed_info):
|
||||||
"""根据模型特征自动确定最适合的碰撞体形状"""
|
"""根据模型特征自动确定最适合的碰撞体形状"""
|
||||||
# 获取模型尺寸比例
|
# 获取变换后的模型尺寸比例
|
||||||
size = bounds.getMax() - bounds.getMin()
|
size = transformed_info['size']
|
||||||
max_dim = max(size.x, size.y, size.z)
|
max_dim = max(size.x, size.y, size.z)
|
||||||
min_dim = min(size.x, size.y, size.z)
|
min_dim = min(size.x, size.y, size.z)
|
||||||
|
|
||||||
@ -410,6 +440,87 @@ class CollisionManager:
|
|||||||
else: # 其他用包围盒
|
else: # 其他用包围盒
|
||||||
return 'box'
|
return 'box'
|
||||||
|
|
||||||
|
def _getTransformedModelInfo(self, model):
|
||||||
|
"""获取考虑变换后的模型信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model: 模型节点
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含变换后的尺寸、中心、半径等信息
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取原始包围盒
|
||||||
|
bounds = model.getBounds()
|
||||||
|
if bounds.isEmpty():
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取模型的变换矩阵
|
||||||
|
transform = model.getTransform()
|
||||||
|
scale = model.getScale()
|
||||||
|
|
||||||
|
# 计算缩放因子(取三轴缩放的平均值)
|
||||||
|
scale_factor = (abs(scale.x) + abs(scale.y) + abs(scale.z)) / 3.0
|
||||||
|
|
||||||
|
# 获取原始尺寸
|
||||||
|
original_size = bounds.getMax() - bounds.getMin()
|
||||||
|
|
||||||
|
# 应用缩放到尺寸
|
||||||
|
actual_size = Vec3(
|
||||||
|
original_size.x * abs(scale.x),
|
||||||
|
original_size.y * abs(scale.y),
|
||||||
|
original_size.z * abs(scale.z)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取变换后的中心点(在世界坐标系中)
|
||||||
|
original_center = bounds.getCenter()
|
||||||
|
if hasattr(model, 'getPos'):
|
||||||
|
# 模型在世界坐标系中的位置
|
||||||
|
world_center = model.getPos(model.getParent() if model.getParent() else model)
|
||||||
|
center = Point3(world_center.x, world_center.y, world_center.z)
|
||||||
|
else:
|
||||||
|
# 如果无法获取世界位置,使用原始中心
|
||||||
|
center = original_center
|
||||||
|
|
||||||
|
# 计算变换后的半径(考虑缩放)
|
||||||
|
original_radius = bounds.getRadius()
|
||||||
|
transformed_radius = original_radius * scale_factor
|
||||||
|
|
||||||
|
# 调试信息
|
||||||
|
print(f"🔍 模型 {model.getName()} 变换信息:")
|
||||||
|
print(f" 原始尺寸: {original_size}")
|
||||||
|
print(f" 缩放因子: {scale}")
|
||||||
|
print(f" 变换后尺寸: {actual_size}")
|
||||||
|
print(f" 变换后半径: {transformed_radius:.2f}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'center': center,
|
||||||
|
'radius': transformed_radius,
|
||||||
|
'size': actual_size,
|
||||||
|
'scale_factor': scale_factor,
|
||||||
|
'original_size': original_size,
|
||||||
|
'scale': scale,
|
||||||
|
'transform': transform
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 获取模型变换信息失败: {e}")
|
||||||
|
# 回退到原始包围盒
|
||||||
|
bounds = model.getBounds()
|
||||||
|
if bounds.isEmpty():
|
||||||
|
return None
|
||||||
|
|
||||||
|
original_size = bounds.getMax() - bounds.getMin()
|
||||||
|
return {
|
||||||
|
'center': bounds.getCenter(),
|
||||||
|
'radius': bounds.getRadius(),
|
||||||
|
'size': original_size,
|
||||||
|
'scale_factor': 1.0,
|
||||||
|
'original_size': original_size,
|
||||||
|
'scale': Vec3(1, 1, 1),
|
||||||
|
'transform': None
|
||||||
|
}
|
||||||
|
|
||||||
def createMouseRay(self, screen_x, screen_y, mask_types=['SELECTABLE']):
|
def createMouseRay(self, screen_x, screen_y, mask_types=['SELECTABLE']):
|
||||||
"""创建鼠标射线"""
|
"""创建鼠标射线"""
|
||||||
# 组合掩码
|
# 组合掩码
|
||||||
|
|||||||
@ -95,11 +95,11 @@ class PropertyPanelManager:
|
|||||||
# 当节点被拖拽后,需要根据新父节点的状态来更新可见性
|
# 当节点被拖拽后,需要根据新父节点的状态来更新可见性
|
||||||
self._syncEffectiveVisibility(node)
|
self._syncEffectiveVisibility(node)
|
||||||
self._syncSceneVisibility()
|
self._syncSceneVisibility()
|
||||||
|
|
||||||
def _syncSceneVisibility(self):
|
def _syncSceneVisibility(self):
|
||||||
scene_root = self.world.render
|
scene_root = self.world.render
|
||||||
self._syncEffectiveVisibility(scene_root)
|
self._syncEffectiveVisibility(scene_root)
|
||||||
|
|
||||||
|
|
||||||
def updatePropertyPanel(self, item):
|
def updatePropertyPanel(self, item):
|
||||||
"""更新属性面板显示"""
|
"""更新属性面板显示"""
|
||||||
if not self._propertyLayout or not self._propertyLayout.parent():
|
if not self._propertyLayout or not self._propertyLayout.parent():
|
||||||
@ -292,16 +292,19 @@ class PropertyPanelManager:
|
|||||||
def updateXPosition(value):
|
def updateXPosition(value):
|
||||||
terrain_node.setX(value)
|
terrain_node.setX(value)
|
||||||
self.refreshModelValues(terrain_node)
|
self.refreshModelValues(terrain_node)
|
||||||
|
|
||||||
self.pos_x.valueChanged.connect(updateXPosition)
|
self.pos_x.valueChanged.connect(updateXPosition)
|
||||||
|
|
||||||
def updateYPosition(value):
|
def updateYPosition(value):
|
||||||
terrain_node.setY(value)
|
terrain_node.setY(value)
|
||||||
self.refreshModelValues(terrain_node)
|
self.refreshModelValues(terrain_node)
|
||||||
|
|
||||||
self.pos_y.valueChanged.connect(updateYPosition)
|
self.pos_y.valueChanged.connect(updateYPosition)
|
||||||
|
|
||||||
def updateZPosition(value):
|
def updateZPosition(value):
|
||||||
terrain_node.setZ(value)
|
terrain_node.setZ(value)
|
||||||
self.refreshModelValues(terrain_node)
|
self.refreshModelValues(terrain_node)
|
||||||
|
|
||||||
self.pos_z.valueChanged.connect(updateZPosition)
|
self.pos_z.valueChanged.connect(updateZPosition)
|
||||||
|
|
||||||
transform_layout.addWidget(self.pos_x, 1, 1)
|
transform_layout.addWidget(self.pos_x, 1, 1)
|
||||||
@ -563,6 +566,7 @@ class PropertyPanelManager:
|
|||||||
for name in other_controls:
|
for name in other_controls:
|
||||||
if hasattr(self, name):
|
if hasattr(self, name):
|
||||||
setattr(self, name, None)
|
setattr(self, name, None)
|
||||||
|
|
||||||
def _setUserVisible(self, node, visible):
|
def _setUserVisible(self, node, visible):
|
||||||
node.setPythonTag("user_visible", visible)
|
node.setPythonTag("user_visible", visible)
|
||||||
self._syncEffectiveVisibility(node)
|
self._syncEffectiveVisibility(node)
|
||||||
@ -830,6 +834,7 @@ class PropertyPanelManager:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
setattr(self, name, None)
|
setattr(self, name, None)
|
||||||
|
|
||||||
def _refreshWorldPos(self, model):
|
def _refreshWorldPos(self, model):
|
||||||
if not hasattr(self, 'worldXSpin'):
|
if not hasattr(self, 'worldXSpin'):
|
||||||
return
|
return
|
||||||
@ -1090,16 +1095,19 @@ class PropertyPanelManager:
|
|||||||
def updateXPosition(value):
|
def updateXPosition(value):
|
||||||
model.setX(value)
|
model.setX(value)
|
||||||
self.refreshModelValues(model)
|
self.refreshModelValues(model)
|
||||||
|
|
||||||
self.pos_x.valueChanged.connect(updateXPosition)
|
self.pos_x.valueChanged.connect(updateXPosition)
|
||||||
|
|
||||||
def updateYPosition(value):
|
def updateYPosition(value):
|
||||||
model.setY(value)
|
model.setY(value)
|
||||||
self.refreshModelValues(model)
|
self.refreshModelValues(model)
|
||||||
|
|
||||||
self.pos_y.valueChanged.connect(updateYPosition)
|
self.pos_y.valueChanged.connect(updateYPosition)
|
||||||
|
|
||||||
def updateZPosition(value):
|
def updateZPosition(value):
|
||||||
model.setZ(value)
|
model.setZ(value)
|
||||||
self.refreshModelValues(model)
|
self.refreshModelValues(model)
|
||||||
|
|
||||||
self.pos_z.valueChanged.connect(updateZPosition)
|
self.pos_z.valueChanged.connect(updateZPosition)
|
||||||
|
|
||||||
# 创建并设置 X, Y, Z 标签居中
|
# 创建并设置 X, Y, Z 标签居中
|
||||||
@ -1423,8 +1431,11 @@ class PropertyPanelManager:
|
|||||||
|
|
||||||
if gui_type in ["2d_image", "2d_video_screen"]:
|
if gui_type in ["2d_image", "2d_video_screen"]:
|
||||||
scale = gui_element.getScale()
|
scale = gui_element.getScale()
|
||||||
width = scale.getX() if hasattr(scale, 'getX') else scale[0] if isinstance(scale,(tuple, list)) else scale
|
width = scale.getX() if hasattr(scale, 'getX') else scale[0] if isinstance(scale,
|
||||||
height = scale.getZ() if hasattr(scale, 'getZ') else scale[1] if isinstance(scale,(tuple, list)) and len(scale) > 1 else scale
|
(tuple, list)) else scale
|
||||||
|
height = scale.getZ() if hasattr(scale, 'getZ') else scale[1] if isinstance(scale,
|
||||||
|
(tuple, list)) and len(
|
||||||
|
scale) > 1 else scale
|
||||||
|
|
||||||
transform_layout.addWidget(QLabel("宽度"), 4, 0)
|
transform_layout.addWidget(QLabel("宽度"), 4, 0)
|
||||||
self.scale_x = QDoubleSpinBox()
|
self.scale_x = QDoubleSpinBox()
|
||||||
@ -1532,7 +1543,6 @@ class PropertyPanelManager:
|
|||||||
self.scale_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "z", v))
|
self.scale_z.valueChanged.connect(lambda v: self.world.gui_manager.editGUIScale(gui_element, "z", v))
|
||||||
transform_layout.addWidget(self.scale_z, 3, 3)
|
transform_layout.addWidget(self.scale_z, 3, 3)
|
||||||
|
|
||||||
|
|
||||||
transform_group.setLayout(transform_layout)
|
transform_group.setLayout(transform_layout)
|
||||||
self._propertyLayout.addWidget(transform_group)
|
self._propertyLayout.addWidget(transform_group)
|
||||||
|
|
||||||
@ -1604,7 +1614,8 @@ class PropertyPanelManager:
|
|||||||
# 显示当前贴图路径(简化显示)
|
# 显示当前贴图路径(简化显示)
|
||||||
current_texture_path = gui_element.getTag("texture_path") or "未设置"
|
current_texture_path = gui_element.getTag("texture_path") or "未设置"
|
||||||
if current_texture_path != "未设置":
|
if current_texture_path != "未设置":
|
||||||
display_path = current_texture_path if len(current_texture_path) <= 10 else current_texture_path[:7] + "..."
|
display_path = current_texture_path if len(current_texture_path) <= 10 else current_texture_path[
|
||||||
|
:7] + "..."
|
||||||
else:
|
else:
|
||||||
display_path = current_texture_path
|
display_path = current_texture_path
|
||||||
texture_label = QLabel(display_path)
|
texture_label = QLabel(display_path)
|
||||||
@ -1653,7 +1664,8 @@ class PropertyPanelManager:
|
|||||||
# 显示当前贴图路径(简化显示)
|
# 显示当前贴图路径(简化显示)
|
||||||
current_texture_path = gui_element.getTag("texture_path") or gui_element.getTag("image_path") or "未设置"
|
current_texture_path = gui_element.getTag("texture_path") or gui_element.getTag("image_path") or "未设置"
|
||||||
if current_texture_path != "未设置":
|
if current_texture_path != "未设置":
|
||||||
display_path = current_texture_path if len(current_texture_path) <= 10 else current_texture_path[:7] + "..."
|
display_path = current_texture_path if len(current_texture_path) <= 10 else current_texture_path[
|
||||||
|
:7] + "..."
|
||||||
else:
|
else:
|
||||||
display_path = current_texture_path
|
display_path = current_texture_path
|
||||||
texture_label = QLabel(display_path)
|
texture_label = QLabel(display_path)
|
||||||
@ -2811,6 +2823,7 @@ class PropertyPanelManager:
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _selectInfoPanelBackgroundColor(self, info_panel, r_spin, g_spin, b_spin, a_spin):
|
def _selectInfoPanelBackgroundColor(self, info_panel, r_spin, g_spin, b_spin, a_spin):
|
||||||
"""选择信息面板背景颜色"""
|
"""选择信息面板背景颜色"""
|
||||||
try:
|
try:
|
||||||
@ -3376,7 +3389,8 @@ class PropertyPanelManager:
|
|||||||
load_url_btn = QPushButton("加载URL")
|
load_url_btn = QPushButton("加载URL")
|
||||||
self._current_load_url_btn_3d = load_url_btn
|
self._current_load_url_btn_3d = load_url_btn
|
||||||
|
|
||||||
load_url_btn.clicked.connect(lambda: self._loadVideoFromURLWithOpenCV_3D(video_screen, self.url_input.text().strip()))
|
load_url_btn.clicked.connect(
|
||||||
|
lambda: self._loadVideoFromURLWithOpenCV_3D(video_screen, self.url_input.text().strip()))
|
||||||
url_layout.addWidget(load_url_btn)
|
url_layout.addWidget(load_url_btn)
|
||||||
|
|
||||||
url_group.setLayout(url_layout)
|
url_group.setLayout(url_layout)
|
||||||
@ -3470,7 +3484,8 @@ class PropertyPanelManager:
|
|||||||
load_url_btn = QPushButton("加载URL")
|
load_url_btn = QPushButton("加载URL")
|
||||||
self._current_load_url_btn_2d = load_url_btn
|
self._current_load_url_btn_2d = load_url_btn
|
||||||
|
|
||||||
load_url_btn.clicked.connect(lambda: self._loadVideoFromURLWithOpenCV(video_screen, self.url_input.text().strip()))
|
load_url_btn.clicked.connect(
|
||||||
|
lambda: self._loadVideoFromURLWithOpenCV(video_screen, self.url_input.text().strip()))
|
||||||
url_layout.addWidget(load_url_btn)
|
url_layout.addWidget(load_url_btn)
|
||||||
|
|
||||||
url_group.setLayout(url_layout)
|
url_group.setLayout(url_layout)
|
||||||
@ -3754,6 +3769,7 @@ class PropertyPanelManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 停止视频失败: {e}")
|
print(f"❌ 停止视频失败: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _loadNew2DVideo(self, video_screen, item):
|
def _loadNew2DVideo(self, video_screen, item):
|
||||||
"""为2D视频屏幕加载新视频文件"""
|
"""为2D视频屏幕加载新视频文件"""
|
||||||
try:
|
try:
|
||||||
@ -3941,7 +3957,8 @@ class PropertyPanelManager:
|
|||||||
print("检测到首次加载,再次调用_loadVideoFromURLWithOpenCV_3D以确保正确显示")
|
print("检测到首次加载,再次调用_loadVideoFromURLWithOpenCV_3D以确保正确显示")
|
||||||
from PyQt5.QtCore import QTimer
|
from PyQt5.QtCore import QTimer
|
||||||
QTimer.singleShot(100, lambda: self._loadVideoFromURLWithOpenCV_3D(video_screen, url))
|
QTimer.singleShot(100, lambda: self._loadVideoFromURLWithOpenCV_3D(video_screen, url))
|
||||||
QTimer.singleShot(200,lambda :setattr(self,'_first_load_processed',False)if hasattr(self,'_first_load_processed')else None)
|
QTimer.singleShot(200, lambda: setattr(self, '_first_load_processed', False) if hasattr(self,
|
||||||
|
'_first_load_processed') else None)
|
||||||
|
|
||||||
# 恢复按钮状态
|
# 恢复按钮状态
|
||||||
update_button_text("加载URL")
|
update_button_text("加载URL")
|
||||||
@ -4031,7 +4048,6 @@ class PropertyPanelManager:
|
|||||||
print(f"❌ 停止3D视频失败: {e}")
|
print(f"❌ 停止3D视频失败: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _loadNewVideo(self, video_screen, item):
|
def _loadNewVideo(self, video_screen, item):
|
||||||
try:
|
try:
|
||||||
file_path, _ = QFileDialog.getOpenFileName(
|
file_path, _ = QFileDialog.getOpenFileName(
|
||||||
@ -4355,7 +4371,6 @@ class PropertyPanelManager:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _updateScriptPropertyPanel(self, game_object):
|
def _updateScriptPropertyPanel(self, game_object):
|
||||||
"""更新脚本属性面板"""
|
"""更新脚本属性面板"""
|
||||||
# 获取对象上的脚本
|
# 获取对象上的脚本
|
||||||
@ -4683,7 +4698,6 @@ class PropertyPanelManager:
|
|||||||
|
|
||||||
return unique_names
|
return unique_names
|
||||||
|
|
||||||
|
|
||||||
def _updateModelMaterialPanel(self, model):
|
def _updateModelMaterialPanel(self, model):
|
||||||
"""模型材质属性"""
|
"""模型材质属性"""
|
||||||
if model.is_empty():
|
if model.is_empty():
|
||||||
@ -4728,7 +4742,7 @@ class PropertyPanelManager:
|
|||||||
material_name = material.get_name() if hasattr(material,
|
material_name = material.get_name() if hasattr(material,
|
||||||
'get_name') and material.get_name() else f"材质{i + 1}"
|
'get_name') and material.get_name() else f"材质{i + 1}"
|
||||||
unique_name = f"{material_name}({model_name})"
|
unique_name = f"{material_name}({model_name})"
|
||||||
#print(f"材质 {i}: 未找到几何节点,使用材质名称 '{material_name}'")
|
print(f"材质 {i}: 未找到几何节点,使用材质名称 '{material_name}'")
|
||||||
|
|
||||||
# 处理重复名称
|
# 处理重复名称
|
||||||
if unique_name in name_counter:
|
if unique_name in name_counter:
|
||||||
@ -4767,7 +4781,7 @@ class PropertyPanelManager:
|
|||||||
# 基础颜色编辑
|
# 基础颜色编辑
|
||||||
base_color = self._getOrCreateMaterialBaseColor(material)
|
base_color = self._getOrCreateMaterialBaseColor(material)
|
||||||
if base_color is not None:
|
if base_color is not None:
|
||||||
#print(f"材质基础颜色: {base_color}")
|
print(f"材质基础颜色: {base_color}")
|
||||||
|
|
||||||
# 基础颜色标题
|
# 基础颜色标题
|
||||||
color_row = 2 if material_status != "标准PBR材质" else 1
|
color_row = 2 if material_status != "标准PBR材质" else 1
|
||||||
@ -4950,8 +4964,6 @@ class PropertyPanelManager:
|
|||||||
# gloss_button.clicked.connect(lambda checked, mat=material: self._selectGlossTexture(mat))
|
# gloss_button.clicked.connect(lambda checked, mat=material: self._selectGlossTexture(mat))
|
||||||
# self._propertyLayout.addRow("光泽贴图:", gloss_button)
|
# self._propertyLayout.addRow("光泽贴图:", gloss_button)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 在纹理按钮后添加当前贴图信息显示
|
# 在纹理按钮后添加当前贴图信息显示
|
||||||
current_row = self._displayCurrentTextures(material, material_layout, current_row)
|
current_row = self._displayCurrentTextures(material, material_layout, current_row)
|
||||||
|
|
||||||
@ -5101,7 +5113,6 @@ class PropertyPanelManager:
|
|||||||
print(f"检查材质状态时出错: {e}")
|
print(f"检查材质状态时出错: {e}")
|
||||||
return "未知材质类型(可尝试编辑)"
|
return "未知材质类型(可尝试编辑)"
|
||||||
|
|
||||||
|
|
||||||
def _getTextureModeString(self, mode):
|
def _getTextureModeString(self, mode):
|
||||||
"""获取纹理模式的字符串表示"""
|
"""获取纹理模式的字符串表示"""
|
||||||
from panda3d.core import TextureStage
|
from panda3d.core import TextureStage
|
||||||
@ -5156,7 +5167,7 @@ class PropertyPanelManager:
|
|||||||
try:
|
try:
|
||||||
# 方法1: 尝试获取base_color属性
|
# 方法1: 尝试获取base_color属性
|
||||||
if hasattr(material, 'base_color') and material.base_color is not None:
|
if hasattr(material, 'base_color') and material.base_color is not None:
|
||||||
#print(f"✓ 找到base_color属性: {material.base_color}")
|
print(f"✓ 找到base_color属性: {material.base_color}")
|
||||||
return material.base_color
|
return material.base_color
|
||||||
|
|
||||||
# 方法2: 尝试调用get_base_color方法
|
# 方法2: 尝试调用get_base_color方法
|
||||||
@ -5369,8 +5380,6 @@ class PropertyPanelManager:
|
|||||||
self._applyGlossTexture(material, normalized_path)
|
self._applyGlossTexture(material, normalized_path)
|
||||||
print(f"已选择光泽贴图:{filename} -> 标准化路径:{normalized_path}")
|
print(f"已选择光泽贴图:{filename} -> 标准化路径:{normalized_path}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def _applyDiffuseTexture(self, texture_path):
|
# def _applyDiffuseTexture(self, texture_path):
|
||||||
# from panda3d.core import TextureStage
|
# from panda3d.core import TextureStage
|
||||||
# try:
|
# try:
|
||||||
@ -5472,7 +5481,8 @@ class PropertyPanelManager:
|
|||||||
for i, stage in enumerate(all_stages):
|
for i, stage in enumerate(all_stages):
|
||||||
tex = node.getTexture(stage)
|
tex = node.getTexture(stage)
|
||||||
mode_name = self._getTextureModeString(stage.getMode())
|
mode_name = self._getTextureModeString(stage.getMode())
|
||||||
print(f"阶段 {i}: {stage.getName()}, Sort: {stage.getSort()}, 模式: {mode_name}, 纹理: {tex.getName() if tex else 'None'}")
|
print(
|
||||||
|
f"阶段 {i}: {stage.getName()}, Sort: {stage.getSort()}, 模式: {mode_name}, 纹理: {tex.getName() if tex else 'None'}")
|
||||||
print("==========================================")
|
print("==========================================")
|
||||||
|
|
||||||
print(f"漫反射贴图已成功应用:{texture_path}")
|
print(f"漫反射贴图已成功应用:{texture_path}")
|
||||||
@ -5602,7 +5612,6 @@ class PropertyPanelManager:
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
def _applyRoughnessTexture_FINAL(self, material, texture_path):
|
def _applyRoughnessTexture_FINAL(self, material, texture_path):
|
||||||
"""应用粗糙度贴图 - 先编译后绑定策略"""
|
"""应用粗糙度贴图 - 先编译后绑定策略"""
|
||||||
try:
|
try:
|
||||||
@ -5642,8 +5651,6 @@ class PropertyPanelManager:
|
|||||||
else:
|
else:
|
||||||
print("✅ 检测到材质已有法线贴图")
|
print("✅ 检测到材质已有法线贴图")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 5. 检查是否有金属性贴图和透明漫反射贴图,选择合适的PBR效果
|
# 5. 检查是否有金属性贴图和透明漫反射贴图,选择合适的PBR效果
|
||||||
print("🔧 步骤2:检查金属性贴图和透明度设置...")
|
print("🔧 步骤2:检查金属性贴图和透明度设置...")
|
||||||
has_metallic = self._hasMetallicTexture(node)
|
has_metallic = self._hasMetallicTexture(node)
|
||||||
@ -5701,7 +5708,6 @@ class PropertyPanelManager:
|
|||||||
roughness_stage.setMode(TextureStage.MModulate)
|
roughness_stage.setMode(TextureStage.MModulate)
|
||||||
node.setTexture(roughness_stage, texture)
|
node.setTexture(roughness_stage, texture)
|
||||||
|
|
||||||
|
|
||||||
# 7. 验证效果
|
# 7. 验证效果
|
||||||
applied_texture = node.getTexture(roughness_stage)
|
applied_texture = node.getTexture(roughness_stage)
|
||||||
if applied_texture:
|
if applied_texture:
|
||||||
@ -6439,8 +6445,6 @@ class PropertyPanelManager:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _applyEmissionTexture(self, material, texture_path):
|
def _applyEmissionTexture(self, material, texture_path):
|
||||||
"""应用自发光贴图"""
|
"""应用自发光贴图"""
|
||||||
try:
|
try:
|
||||||
@ -6789,7 +6793,7 @@ class PropertyPanelManager:
|
|||||||
# print(f"找到匹配的几何节点: {geom_np.get_name()}")
|
# print(f"找到匹配的几何节点: {geom_np.get_name()}")
|
||||||
return geom_np
|
return geom_np
|
||||||
|
|
||||||
#print("未找到匹配的几何节点")
|
print("未找到匹配的几何节点")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _findSpecificGeomNodeForMaterial(self, target_material):
|
def _findSpecificGeomNodeForMaterial(self, target_material):
|
||||||
@ -7004,7 +7008,8 @@ class PropertyPanelManager:
|
|||||||
if model_index in [1, 3]: # 自发光或透明模式
|
if model_index in [1, 3]: # 自发光或透明模式
|
||||||
self._refreshMaterialUI()
|
self._refreshMaterialUI()
|
||||||
|
|
||||||
print(f"着色模型已更新为: {model_index} ({'自发光' if model_index == 1 else '透明' if model_index == 3 else '默认'})")
|
print(
|
||||||
|
f"着色模型已更新为: {model_index} ({'自发光' if model_index == 1 else '透明' if model_index == 3 else '默认'})")
|
||||||
|
|
||||||
def _addTransparencyPanel(self, material, material_layout, current_row):
|
def _addTransparencyPanel(self, material, material_layout, current_row):
|
||||||
"""添加透明度控制面板"""
|
"""添加透明度控制面板"""
|
||||||
@ -7089,7 +7094,6 @@ class PropertyPanelManager:
|
|||||||
self.world.render_pipeline.prepare_scene(model)
|
self.world.render_pipeline.prepare_scene(model)
|
||||||
print(f"[透明] 不透明度={opacity_slider:.2f} 已同步")
|
print(f"[透明] 不透明度={opacity_slider:.2f} 已同步")
|
||||||
|
|
||||||
|
|
||||||
def _applyTransparentRenderingEffect(self):
|
def _applyTransparentRenderingEffect(self):
|
||||||
from panda3d.core import TransparencyAttrib
|
from panda3d.core import TransparencyAttrib
|
||||||
"""为当前选中的模型应用透明渲染效果(简化版本)"""
|
"""为当前选中的模型应用透明渲染效果(简化版本)"""
|
||||||
@ -7113,7 +7117,6 @@ class PropertyPanelManager:
|
|||||||
sort=100
|
sort=100
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# 让RenderPipeline自动处理透明材质
|
# 让RenderPipeline自动处理透明材质
|
||||||
# 当emission.x=3时,RenderPipeline会自动设置正确的渲染参数
|
# 当emission.x=3时,RenderPipeline会自动设置正确的渲染参数
|
||||||
self.world.render_pipeline.prepare_scene(model)
|
self.world.render_pipeline.prepare_scene(model)
|
||||||
@ -7281,7 +7284,8 @@ class PropertyPanelManager:
|
|||||||
presets = {
|
presets = {
|
||||||
"塑料": {"base_color": Vec4(0.8, 0.8, 0.8, 1.0), "roughness": 0.7, "metallic": 0.0, "ior": 1.4},
|
"塑料": {"base_color": Vec4(0.8, 0.8, 0.8, 1.0), "roughness": 0.7, "metallic": 0.0, "ior": 1.4},
|
||||||
"金属": {"base_color": Vec4(0.7, 0.7, 0.7, 1.0), "roughness": 0.1, "metallic": 1.0, "ior": 1.5},
|
"金属": {"base_color": Vec4(0.7, 0.7, 0.7, 1.0), "roughness": 0.1, "metallic": 1.0, "ior": 1.5},
|
||||||
"玻璃": {"base_color": Vec4(0.9, 0.9, 1.0, 0.2), "roughness": 0.0, "metallic": 0.0, "ior": 1.5,"shading_model":3,"transparency":0.2},
|
"玻璃": {"base_color": Vec4(0.9, 0.9, 1.0, 0.2), "roughness": 0.0, "metallic": 0.0, "ior": 1.5,
|
||||||
|
"shading_model": 3, "transparency": 0.2},
|
||||||
"橡胶": {"base_color": Vec4(0.2, 0.2, 0.2, 1.0), "roughness": 0.9, "metallic": 0.0, "ior": 1.3},
|
"橡胶": {"base_color": Vec4(0.2, 0.2, 0.2, 1.0), "roughness": 0.9, "metallic": 0.0, "ior": 1.3},
|
||||||
"木材": {"base_color": Vec4(0.6, 0.4, 0.2, 1.0), "roughness": 0.8, "metallic": 0.0, "ior": 1.3},
|
"木材": {"base_color": Vec4(0.6, 0.4, 0.2, 1.0), "roughness": 0.8, "metallic": 0.0, "ior": 1.3},
|
||||||
"陶瓷": {"base_color": Vec4(0.9, 0.9, 0.85, 1.0), "roughness": 0.1, "metallic": 0.0, "ior": 1.6},
|
"陶瓷": {"base_color": Vec4(0.9, 0.9, 0.85, 1.0), "roughness": 0.1, "metallic": 0.0, "ior": 1.6},
|
||||||
@ -7649,7 +7653,6 @@ class PropertyPanelManager:
|
|||||||
sun_group.setLayout(sun_layout)
|
sun_group.setLayout(sun_layout)
|
||||||
self._propertyLayout.addWidget(sun_group)
|
self._propertyLayout.addWidget(sun_group)
|
||||||
|
|
||||||
|
|
||||||
def _onSunAzimuthSliderChanged(self, value):
|
def _onSunAzimuthSliderChanged(self, value):
|
||||||
"""滑块值改变时的回调"""
|
"""滑块值改变时的回调"""
|
||||||
try:
|
try:
|
||||||
@ -7758,19 +7761,19 @@ class PropertyPanelManager:
|
|||||||
# 方法1: 直接控制Day Time Editor中的sun_azimuth节点
|
# 方法1: 直接控制Day Time Editor中的sun_azimuth节点
|
||||||
success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth_value)
|
success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth_value)
|
||||||
if success:
|
if success:
|
||||||
#print(f"✅ 通过Day Time Editor设置方位角: {azimuth_value}°")
|
print(f"✅ 通过Day Time Editor设置方位角: {azimuth_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 方法2: 使用我们的太阳控制器作为备用
|
# 方法2: 使用我们的太阳控制器作为备用
|
||||||
if hasattr(self.world, 'sun_controller'):
|
if hasattr(self.world, 'sun_controller'):
|
||||||
self.world.sun_controller.set_sun_azimuth(azimuth_value)
|
self.world.sun_controller.set_sun_azimuth(azimuth_value)
|
||||||
#print(f"✅ 通过太阳控制器设置方位角: {azimuth_value}°")
|
print(f"✅ 通过太阳控制器设置方位角: {azimuth_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 方法3: 直接调用主程序的太阳控制方法
|
# 方法3: 直接调用主程序的太阳控制方法
|
||||||
if hasattr(self.world, 'setSunAzimuth'):
|
if hasattr(self.world, 'setSunAzimuth'):
|
||||||
self.world.setSunAzimuth(azimuth_value)
|
self.world.setSunAzimuth(azimuth_value)
|
||||||
#print(f"✅ 通过主程序方法设置方位角: {azimuth_value}°")
|
print(f"✅ 通过主程序方法设置方位角: {azimuth_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("⚠️ 所有方位角设置方法都不可用")
|
print("⚠️ 所有方位角设置方法都不可用")
|
||||||
@ -7786,19 +7789,19 @@ class PropertyPanelManager:
|
|||||||
# 方法1: 直接控制Day Time Editor中的sun_altitude节点
|
# 方法1: 直接控制Day Time Editor中的sun_altitude节点
|
||||||
success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude_value)
|
success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude_value)
|
||||||
if success:
|
if success:
|
||||||
#print(f"✅ 通过Day Time Editor设置高度角: {altitude_value}°")
|
print(f"✅ 通过Day Time Editor设置高度角: {altitude_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 方法2: 使用我们的太阳控制器作为备用
|
# 方法2: 使用我们的太阳控制器作为备用
|
||||||
if hasattr(self.world, 'sun_controller'):
|
if hasattr(self.world, 'sun_controller'):
|
||||||
self.world.sun_controller.set_sun_altitude(altitude_value)
|
self.world.sun_controller.set_sun_altitude(altitude_value)
|
||||||
#print(f"✅ 通过太阳控制器设置高度角: {altitude_value}°")
|
print(f"✅ 通过太阳控制器设置高度角: {altitude_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 方法3: 直接调用主程序的太阳控制方法
|
# 方法3: 直接调用主程序的太阳控制方法
|
||||||
if hasattr(self.world, 'setSunAltitude'):
|
if hasattr(self.world, 'setSunAltitude'):
|
||||||
self.world.setSunAltitude(altitude_value)
|
self.world.setSunAltitude(altitude_value)
|
||||||
#print(f"✅ 通过主程序方法设置高度角: {altitude_value}°")
|
print(f"✅ 通过主程序方法设置高度角: {altitude_value}°")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("⚠️ 所有高度角设置方法都不可用")
|
print("⚠️ 所有高度角设置方法都不可用")
|
||||||
@ -8075,7 +8078,7 @@ class PropertyPanelManager:
|
|||||||
format_info = self._getModelFormat(origin_model)
|
format_info = self._getModelFormat(origin_model)
|
||||||
processed = []
|
processed = []
|
||||||
|
|
||||||
#print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||||
|
|
||||||
for name in anim_names:
|
for name in anim_names:
|
||||||
display_name = name
|
display_name = name
|
||||||
@ -8109,7 +8112,7 @@ class PropertyPanelManager:
|
|||||||
display_name = name
|
display_name = name
|
||||||
|
|
||||||
processed.append((display_name, original_name))
|
processed.append((display_name, original_name))
|
||||||
#print(f"[动画分析] {original_name} → {display_name}")
|
print(f"[动画分析] {original_name} → {display_name}")
|
||||||
|
|
||||||
return processed
|
return processed
|
||||||
|
|
||||||
@ -8143,7 +8146,7 @@ class PropertyPanelManager:
|
|||||||
if frames > 1:
|
if frames > 1:
|
||||||
valid_anims += 1
|
valid_anims += 1
|
||||||
total_frames += frames
|
total_frames += frames
|
||||||
#print(f"[动画分析] '{anim_name}': {frames} 帧")
|
print(f"[动画分析] '{anim_name}': {frames} 帧")
|
||||||
else:
|
else:
|
||||||
print(f"[动画分析] '{anim_name}': 无有效帧数 ({frames})")
|
print(f"[动画分析] '{anim_name}': 无有效帧数 ({frames})")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -8168,7 +8171,7 @@ class PropertyPanelManager:
|
|||||||
if not filepath:
|
if not filepath:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
#print(f"[Actor加载] 尝试加载: {filepath}")
|
print(f"[Actor加载] 尝试加载: {filepath}")
|
||||||
|
|
||||||
# 检查是否是 FBX 文件,如果是,使用专门的 FBX 动画加载器
|
# 检查是否是 FBX 文件,如果是,使用专门的 FBX 动画加载器
|
||||||
if filepath.lower().endswith('.fbx'):
|
if filepath.lower().endswith('.fbx'):
|
||||||
@ -8177,13 +8180,13 @@ class PropertyPanelManager:
|
|||||||
# 其他格式使用标准 Actor 加载
|
# 其他格式使用标准 Actor 加载
|
||||||
try:
|
try:
|
||||||
import gltf
|
import gltf
|
||||||
#print(f"[GLTF加载] 尝试加载: {filepath}")
|
print(f"[GLTF加载] 尝试加载: {filepath}")
|
||||||
# test_actor=Actor(NodePath(gltf._loader.GltfLoader.load_file(filepath,None)))
|
# test_actor=Actor(NodePath(gltf._loader.GltfLoader.load_file(filepath,None)))
|
||||||
test_actor = Actor(NodePath(gltf.load_model(filepath, None)))
|
test_actor = Actor(NodePath(gltf.load_model(filepath, None)))
|
||||||
anims = test_actor.getAnimNames()
|
anims = test_actor.getAnimNames()
|
||||||
test_actor.reparentTo(self.world.render)
|
test_actor.reparentTo(self.world.render)
|
||||||
self._actor_cache[origin_model] = test_actor
|
self._actor_cache[origin_model] = test_actor
|
||||||
#print(f"[Actor加载] 标准加载检测到动画: {anims}")
|
print(f"[Actor加载] 标准加载检测到动画: {anims}")
|
||||||
if not anims:
|
if not anims:
|
||||||
test_actor.cleanup()
|
test_actor.cleanup()
|
||||||
test_actor.removeNode()
|
test_actor.removeNode()
|
||||||
@ -8196,14 +8199,14 @@ class PropertyPanelManager:
|
|||||||
def _createFBXActor(self, origin_model, filepath):
|
def _createFBXActor(self, origin_model, filepath):
|
||||||
"""专门为 FBX 文件创建 Actor,使用转换方式获取真实动画"""
|
"""专门为 FBX 文件创建 Actor,使用转换方式获取真实动画"""
|
||||||
try:
|
try:
|
||||||
#print(f"[FBX动画] 开始处理 FBX 动画: {filepath}")
|
print(f"[FBX动画] 开始处理 FBX 动画: {filepath}")
|
||||||
|
|
||||||
# 方法1: 尝试转换 FBX 为包含动画的格式
|
# 方法1: 尝试转换 FBX 为包含动画的格式
|
||||||
converted_actor = self._convertFBXToActor(filepath)
|
converted_actor = self._convertFBXToActor(filepath)
|
||||||
if converted_actor:
|
if converted_actor:
|
||||||
converted_actor.reparentTo(self.world.render)
|
converted_actor.reparentTo(self.world.render)
|
||||||
self._actor_cache[origin_model] = converted_actor
|
self._actor_cache[origin_model] = converted_actor
|
||||||
#print(f"[FBX动画] 转换成功,动画: {converted_actor.getAnimNames()}")
|
print(f"[FBX动画] 转换成功,动画: {converted_actor.getAnimNames()}")
|
||||||
return converted_actor
|
return converted_actor
|
||||||
|
|
||||||
# 方法2: 直接加载但进行动画数据修复
|
# 方法2: 直接加载但进行动画数据修复
|
||||||
@ -8213,7 +8216,7 @@ class PropertyPanelManager:
|
|||||||
if fixed_actor and fixed_actor.getAnimNames():
|
if fixed_actor and fixed_actor.getAnimNames():
|
||||||
fixed_actor.reparentTo(self.world.render)
|
fixed_actor.reparentTo(self.world.render)
|
||||||
self._actor_cache[origin_model] = fixed_actor
|
self._actor_cache[origin_model] = fixed_actor
|
||||||
#print(f"[FBX动画] 修复成功,动画: {fixed_actor.getAnimNames()}")
|
print(f"[FBX动画] 修复成功,动画: {fixed_actor.getAnimNames()}")
|
||||||
return fixed_actor
|
return fixed_actor
|
||||||
|
|
||||||
print(f"[FBX动画] 无法获取有效动画数据")
|
print(f"[FBX动画] 无法获取有效动画数据")
|
||||||
@ -8544,8 +8547,6 @@ except Exception as e:
|
|||||||
print(f"[系统转换] 系统转换失败: {e}")
|
print(f"[系统转换] 系统转换失败: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _playAnimation(self, origin_model):
|
def _playAnimation(self, origin_model):
|
||||||
actor = self._getActor(origin_model)
|
actor = self._getActor(origin_model)
|
||||||
if not actor:
|
if not actor:
|
||||||
@ -8570,7 +8571,6 @@ except Exception as e:
|
|||||||
actor.play(display_name)
|
actor.play(display_name)
|
||||||
print(f"『动画播放』:{display_name}")
|
print(f"『动画播放』:{display_name}")
|
||||||
|
|
||||||
|
|
||||||
def _pauseAnimation(self, origin_model):
|
def _pauseAnimation(self, origin_model):
|
||||||
actor = self._getActor(origin_model)
|
actor = self._getActor(origin_model)
|
||||||
if not actor:
|
if not actor:
|
||||||
@ -8603,7 +8603,6 @@ except Exception as e:
|
|||||||
origin_model.show()
|
origin_model.show()
|
||||||
print("『动画』停止切换至原始模型")
|
print("『动画』停止切换至原始模型")
|
||||||
|
|
||||||
|
|
||||||
def _loopAnimation(self, origin_model):
|
def _loopAnimation(self, origin_model):
|
||||||
actor = self._getActor(origin_model)
|
actor = self._getActor(origin_model)
|
||||||
if not actor:
|
if not actor:
|
||||||
@ -8672,7 +8671,8 @@ except Exception as e:
|
|||||||
actor.stop()
|
actor.stop()
|
||||||
if anim_name and actor.getAnimControl(anim_name):
|
if anim_name and actor.getAnimControl(anim_name):
|
||||||
actor.getAnimControl(anim_name).pose(0)
|
actor.getAnimControl(anim_name).pose(0)
|
||||||
actor.hide();origin_model.show()
|
actor.hide();
|
||||||
|
origin_model.show()
|
||||||
elif cmd == "loop":
|
elif cmd == "loop":
|
||||||
origin_model.hide()
|
origin_model.hide()
|
||||||
actor.show()
|
actor.show()
|
||||||
@ -8772,6 +8772,7 @@ except Exception as e:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"创建碰撞面板失败: {e}")
|
print(f"创建碰撞面板失败: {e}")
|
||||||
|
|
||||||
def _addCollisionParameterControls(self, model, layout, start_row, shape_type):
|
def _addCollisionParameterControls(self, model, layout, start_row, shape_type):
|
||||||
"""添加碰撞参数调整控件"""
|
"""添加碰撞参数调整控件"""
|
||||||
try:
|
try:
|
||||||
@ -8840,6 +8841,20 @@ except Exception as e:
|
|||||||
layout.addWidget(radius_label, current_row, 0)
|
layout.addWidget(radius_label, current_row, 0)
|
||||||
|
|
||||||
self.collision_radius = self._createCollisionSpinBox(0.1, 100, 2)
|
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)
|
||||||
|
|
||||||
self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v))
|
self.collision_radius.valueChanged.connect(lambda v: self._updateSphereRadius(model, v))
|
||||||
layout.addWidget(self.collision_radius, current_row, 1)
|
layout.addWidget(self.collision_radius, current_row, 1)
|
||||||
current_row += 1
|
current_row += 1
|
||||||
@ -8859,6 +8874,23 @@ except Exception as e:
|
|||||||
self.collision_length = self._createCollisionSpinBox(0.1, 100, 2)
|
self.collision_length = self._createCollisionSpinBox(0.1, 100, 2)
|
||||||
self.collision_height = 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)
|
||||||
|
|
||||||
layout.addWidget(QLabel("宽度:"), current_row, 0)
|
layout.addWidget(QLabel("宽度:"), current_row, 0)
|
||||||
layout.addWidget(self.collision_width, current_row, 1)
|
layout.addWidget(self.collision_width, current_row, 1)
|
||||||
current_row += 1
|
current_row += 1
|
||||||
@ -8887,6 +8919,24 @@ except Exception as e:
|
|||||||
layout.addWidget(radius_label, current_row, 0)
|
layout.addWidget(radius_label, current_row, 0)
|
||||||
|
|
||||||
self.collision_capsule_radius = self._createCollisionSpinBox(0.1, 100, 2)
|
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)
|
||||||
|
|
||||||
self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v))
|
self.collision_capsule_radius.valueChanged.connect(lambda v: self._updateCapsuleRadius(model, v))
|
||||||
layout.addWidget(self.collision_capsule_radius, current_row, 1)
|
layout.addWidget(self.collision_capsule_radius, current_row, 1)
|
||||||
current_row += 1
|
current_row += 1
|
||||||
@ -8895,6 +8945,20 @@ except Exception as e:
|
|||||||
layout.addWidget(height_label, current_row, 0)
|
layout.addWidget(height_label, current_row, 0)
|
||||||
|
|
||||||
self.collision_capsule_height = self._createCollisionSpinBox(0.1, 100, 2)
|
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)
|
||||||
|
|
||||||
self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v))
|
self.collision_capsule_height.valueChanged.connect(lambda v: self._updateCapsuleHeight(model, v))
|
||||||
layout.addWidget(self.collision_capsule_height, current_row, 1)
|
layout.addWidget(self.collision_capsule_height, current_row, 1)
|
||||||
current_row += 1
|
current_row += 1
|
||||||
@ -8932,20 +8996,18 @@ except Exception as e:
|
|||||||
self.collision_normal_z.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'z', v))
|
self.collision_normal_z.valueChanged.connect(lambda v: self._updatePlaneNormal(model, 'z', v))
|
||||||
|
|
||||||
return current_row
|
return current_row
|
||||||
|
|
||||||
def _hasCollision(self, model):
|
def _hasCollision(self, model):
|
||||||
"""检查模型是否已有碰撞体"""
|
"""检查模型是否已有碰撞体"""
|
||||||
try:
|
try:
|
||||||
from panda3d.core import CollisionNode
|
from panda3d.core import CollisionNode
|
||||||
|
|
||||||
# if model.hasTag("has_collision") and model.getTag("has_collision") == "true":
|
|
||||||
# return True
|
|
||||||
|
|
||||||
# 检查模型及其子节点是否有碰撞节点
|
# 检查模型及其子节点是否有碰撞节点
|
||||||
collision_nodes = model.findAllMatches("**/+CollisionNode")
|
collision_nodes = model.findAllMatches("**/+CollisionNode")
|
||||||
has_collision = collision_nodes.getNumPaths() > 0
|
has_collision = collision_nodes.getNumPaths() > 0
|
||||||
|
|
||||||
if has_collision:
|
print(
|
||||||
print(f"检测到模型{model.getName()}已有{collision_nodes.getNumPaths()}个碰撞节点")
|
f"碰撞检查:模型 {model.getName()} - {'有' if has_collision else '无'}碰撞 (找到{collision_nodes.getNumPaths()}个碰撞节点)")
|
||||||
|
|
||||||
return has_collision
|
return has_collision
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -9118,21 +9180,11 @@ except Exception as e:
|
|||||||
|
|
||||||
print(f"✅ 为模型 {model.getName()} 添加了 {shape_type} 碰撞体")
|
print(f"✅ 为模型 {model.getName()} 添加了 {shape_type} 碰撞体")
|
||||||
|
|
||||||
# 简单更新按钮状态,不调用完整的状态更新
|
# 重置更新标志,确保状态能够更新
|
||||||
if hasattr(self, 'collision_button'):
|
self._updating_collision_panel = False
|
||||||
self.collision_button.setText("移除碰撞")
|
|
||||||
try:
|
|
||||||
self.collision_button.clicked.disconnect()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.collision_button.clicked.connect(lambda: self._removeCollisionAndUpdate(model))
|
|
||||||
|
|
||||||
if hasattr(self, 'collision_status_text'):
|
# 更新面板状态 - 添加参数控件和显示/隐藏按钮
|
||||||
self.collision_status_text.setText("已启用")
|
self._updateCollisionPanelState(model)
|
||||||
self.collision_status_text.setStyleSheet("color: green;")
|
|
||||||
|
|
||||||
if hasattr(self, 'collision_shape_combo'):
|
|
||||||
self.collision_shape_combo.setEnabled(False)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("场景管理器未初始化")
|
print("场景管理器未初始化")
|
||||||
@ -9157,6 +9209,7 @@ except Exception as e:
|
|||||||
|
|
||||||
# 重置状态并更新界面
|
# 重置状态并更新界面
|
||||||
self._previous_collision_state = True # 强制刷新
|
self._previous_collision_state = True # 强制刷新
|
||||||
|
self._updating_collision_panel = False # 确保状态能够更新
|
||||||
self._updateCollisionPanelState(model)
|
self._updateCollisionPanelState(model)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -9171,7 +9224,8 @@ except Exception as e:
|
|||||||
|
|
||||||
self._updating_collision_panel = True
|
self._updating_collision_panel = True
|
||||||
|
|
||||||
if hasattr(self, 'collision_button') and hasattr(self, 'collision_status_text') and hasattr(self, 'collision_shape_combo'):
|
if hasattr(self, 'collision_button') and hasattr(self, 'collision_status_text') and hasattr(self,
|
||||||
|
'collision_shape_combo'):
|
||||||
has_collision = self._hasCollision(model)
|
has_collision = self._hasCollision(model)
|
||||||
|
|
||||||
# 更新状态文本和颜色
|
# 更新状态文本和颜色
|
||||||
@ -9327,73 +9381,176 @@ except Exception as e:
|
|||||||
print(f"加载平面参数失败: {e}")
|
print(f"加载平面参数失败: {e}")
|
||||||
|
|
||||||
def _updateCollisionPosition(self, model, axis, value):
|
def _updateCollisionPosition(self, model, axis, value):
|
||||||
"""更新碰撞位置"""
|
"""更新碰撞位置偏移"""
|
||||||
try:
|
try:
|
||||||
collision_nodes = model.findAllMatches("**/+CollisionNode")
|
# 防止重复调用导致无限循环
|
||||||
for collision_np in collision_nodes:
|
if getattr(self, '_updating_collision_position', False):
|
||||||
current_pos = collision_np.getPos()
|
return
|
||||||
if axis == 'x':
|
self._updating_collision_position = True
|
||||||
collision_np.setPos(value, current_pos.y, current_pos.z)
|
|
||||||
elif axis == 'y':
|
# 获取当前所有位置偏移值
|
||||||
collision_np.setPos(current_pos.x, value, current_pos.z)
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
elif axis == 'z':
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
collision_np.setPos(current_pos.x, current_pos.y, value)
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
print(f"更新碰撞位置 {axis}: {value}")
|
|
||||||
break
|
# 获取当前碰撞形状类型
|
||||||
|
current_shape = self._getCurrentCollisionShape(model)
|
||||||
|
if not current_shape:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 重新创建碰撞体,传入位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
# 根据形状类型收集其他参数
|
||||||
|
if current_shape == 'sphere':
|
||||||
|
radius = self.collision_radius.value() if hasattr(self, 'collision_radius') else 1.0
|
||||||
|
self._recreateCollisionShape(model, current_shape, radius=radius, position_offset=position_offset)
|
||||||
|
elif current_shape == 'box':
|
||||||
|
width = self.collision_width.value() if hasattr(self, 'collision_width') else 2.0
|
||||||
|
length = self.collision_length.value() if hasattr(self, 'collision_length') else 2.0
|
||||||
|
height = self.collision_height.value() if hasattr(self, 'collision_height') else 2.0
|
||||||
|
self._recreateCollisionShape(model, current_shape, width=width, length=length, height=height, position_offset=position_offset)
|
||||||
|
elif current_shape == 'capsule':
|
||||||
|
cap_radius = self.collision_capsule_radius.value() if hasattr(self, 'collision_capsule_radius') else 0.5
|
||||||
|
cap_height = self.collision_capsule_height.value() if hasattr(self, 'collision_capsule_height') else 2.0
|
||||||
|
self._recreateCollisionShape(model, current_shape, radius=cap_radius, height=cap_height, position_offset=position_offset)
|
||||||
|
elif current_shape == 'plane':
|
||||||
|
normal_x = self.collision_normal_x.value() if hasattr(self, 'collision_normal_x') else 0
|
||||||
|
normal_y = self.collision_normal_y.value() if hasattr(self, 'collision_normal_y') else 0
|
||||||
|
normal_z = self.collision_normal_z.value() if hasattr(self, 'collision_normal_z') else 1
|
||||||
|
self._recreateCollisionShape(model, current_shape, normal=(normal_x, normal_y, normal_z), position_offset=position_offset)
|
||||||
|
|
||||||
|
print(f"更新碰撞位置偏移 {axis}: {value}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新碰撞位置失败: {e}")
|
print(f"更新碰撞位置失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_collision_position = False
|
||||||
|
|
||||||
def _updateSphereRadius(self, model, radius):
|
def _updateSphereRadius(self, model, radius):
|
||||||
"""更新球形半径"""
|
"""更新球形半径"""
|
||||||
try:
|
try:
|
||||||
self._recreateCollisionShape(model, 'sphere', radius=radius)
|
# 防止重复调用导致无限循环
|
||||||
|
if getattr(self, '_updating_sphere_radius', False):
|
||||||
|
return
|
||||||
|
self._updating_sphere_radius = True
|
||||||
|
|
||||||
|
# 获取当前位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
self._recreateCollisionShape(model, 'sphere', radius=radius, position_offset=position_offset)
|
||||||
print(f"更新球形半径: {radius}")
|
print(f"更新球形半径: {radius}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新球形半径失败: {e}")
|
print(f"更新球形半径失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_sphere_radius = False
|
||||||
|
|
||||||
def _updateBoxSize(self, model, dimension, value):
|
def _updateBoxSize(self, model, dimension, value):
|
||||||
"""更新盒型尺寸"""
|
"""更新盒型尺寸"""
|
||||||
try:
|
try:
|
||||||
|
# 防止重复调用导致无限循环
|
||||||
|
if getattr(self, '_updating_box_size', False):
|
||||||
|
return
|
||||||
|
self._updating_box_size = True
|
||||||
|
|
||||||
# 获取当前所有尺寸
|
# 获取当前所有尺寸
|
||||||
width = self.collision_width.value() if hasattr(self, 'collision_width') else value
|
width = self.collision_width.value() if hasattr(self, 'collision_width') else value
|
||||||
length = self.collision_length.value() if hasattr(self, 'collision_length') else value
|
length = self.collision_length.value() if hasattr(self, 'collision_length') else value
|
||||||
height = self.collision_height.value() if hasattr(self, 'collision_height') else value
|
height = self.collision_height.value() if hasattr(self, 'collision_height') else value
|
||||||
|
|
||||||
self._recreateCollisionShape(model, 'box', width=width, length=length, height=height)
|
# 获取当前位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
self._recreateCollisionShape(model, 'box', width=width, length=length, height=height, position_offset=position_offset)
|
||||||
print(f"更新盒型{dimension}: {value}")
|
print(f"更新盒型{dimension}: {value}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新盒型尺寸失败: {e}")
|
print(f"更新盒型尺寸失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_box_size = False
|
||||||
|
|
||||||
def _updateCapsuleRadius(self, model, radius):
|
def _updateCapsuleRadius(self, model, radius):
|
||||||
"""更新胶囊体半径"""
|
"""更新胶囊体半径"""
|
||||||
try:
|
try:
|
||||||
|
# 防止重复调用导致无限循环
|
||||||
|
if getattr(self, '_updating_capsule_radius', False):
|
||||||
|
return
|
||||||
|
self._updating_capsule_radius = True
|
||||||
|
|
||||||
height = self.collision_capsule_height.value() if hasattr(self, 'collision_capsule_height') else 2.0
|
height = self.collision_capsule_height.value() if hasattr(self, 'collision_capsule_height') else 2.0
|
||||||
self._recreateCollisionShape(model, 'capsule', radius=radius, height=height)
|
|
||||||
|
# 获取当前位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
self._recreateCollisionShape(model, 'capsule', radius=radius, height=height, position_offset=position_offset)
|
||||||
print(f"更新胶囊体半径: {radius}")
|
print(f"更新胶囊体半径: {radius}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新胶囊体半径失败: {e}")
|
print(f"更新胶囊体半径失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_capsule_radius = False
|
||||||
|
|
||||||
def _updateCapsuleHeight(self, model, height):
|
def _updateCapsuleHeight(self, model, height):
|
||||||
"""更新胶囊体高度"""
|
"""更新胶囊体高度"""
|
||||||
try:
|
try:
|
||||||
|
# 防止重复调用导致无限循环
|
||||||
|
if getattr(self, '_updating_capsule_height', False):
|
||||||
|
return
|
||||||
|
self._updating_capsule_height = True
|
||||||
|
|
||||||
radius = self.collision_capsule_radius.value() if hasattr(self, 'collision_capsule_radius') else 0.5
|
radius = self.collision_capsule_radius.value() if hasattr(self, 'collision_capsule_radius') else 0.5
|
||||||
self._recreateCollisionShape(model, 'capsule', radius=radius, height=height)
|
|
||||||
|
# 获取当前位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
self._recreateCollisionShape(model, 'capsule', radius=radius, height=height, position_offset=position_offset)
|
||||||
print(f"更新胶囊体高度: {height}")
|
print(f"更新胶囊体高度: {height}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新胶囊体高度失败: {e}")
|
print(f"更新胶囊体高度失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_capsule_height = False
|
||||||
|
|
||||||
def _updatePlaneNormal(self, model, axis, value):
|
def _updatePlaneNormal(self, model, axis, value):
|
||||||
"""更新平面法向量"""
|
"""更新平面法向量"""
|
||||||
try:
|
try:
|
||||||
|
# 防止重复调用导致无限循环
|
||||||
|
if getattr(self, '_updating_plane_normal', False):
|
||||||
|
return
|
||||||
|
self._updating_plane_normal = True
|
||||||
|
|
||||||
# 获取当前法向量
|
# 获取当前法向量
|
||||||
normal_x = self.collision_normal_x.value() if hasattr(self, 'collision_normal_x') else 0
|
normal_x = self.collision_normal_x.value() if hasattr(self, 'collision_normal_x') else 0
|
||||||
normal_y = self.collision_normal_y.value() if hasattr(self, 'collision_normal_y') else 0
|
normal_y = self.collision_normal_y.value() if hasattr(self, 'collision_normal_y') else 0
|
||||||
normal_z = self.collision_normal_z.value() if hasattr(self, 'collision_normal_z') else 1
|
normal_z = self.collision_normal_z.value() if hasattr(self, 'collision_normal_z') else 1
|
||||||
|
|
||||||
self._recreateCollisionShape(model, 'plane', normal=(normal_x, normal_y, normal_z))
|
# 获取当前位置偏移
|
||||||
|
from panda3d.core import Vec3
|
||||||
|
pos_x = self.collision_pos_x.value() if hasattr(self, 'collision_pos_x') else 0.0
|
||||||
|
pos_y = self.collision_pos_y.value() if hasattr(self, 'collision_pos_y') else 0.0
|
||||||
|
pos_z = self.collision_pos_z.value() if hasattr(self, 'collision_pos_z') else 0.0
|
||||||
|
position_offset = Vec3(pos_x, pos_y, pos_z)
|
||||||
|
|
||||||
|
self._recreateCollisionShape(model, 'plane', normal=(normal_x, normal_y, normal_z), position_offset=position_offset)
|
||||||
print(f"更新平面法向量 {axis}: {value}")
|
print(f"更新平面法向量 {axis}: {value}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"更新平面法向量失败: {e}")
|
print(f"更新平面法向量失败: {e}")
|
||||||
|
finally:
|
||||||
|
self._updating_plane_normal = False
|
||||||
|
|
||||||
def _recreateCollisionShape(self, model, shape_type, **kwargs):
|
def _recreateCollisionShape(self, model, shape_type, **kwargs):
|
||||||
"""重新创建碰撞形状(保持位置和可见性)"""
|
"""重新创建碰撞形状(保持位置和可见性)"""
|
||||||
@ -9757,7 +9914,9 @@ except Exception as e:
|
|||||||
if widget and hasattr(widget, 'text'):
|
if widget and hasattr(widget, 'text'):
|
||||||
# 检查是否是参数相关的标签
|
# 检查是否是参数相关的标签
|
||||||
text = widget.text()
|
text = widget.text()
|
||||||
if any(keyword in text for keyword in ['位置偏移', 'X:', 'Y:', 'Z:', '半径:', '尺寸:', '宽度:', '长度:', '高度:', '法向量:', 'Nx:', 'Ny:', 'Nz:']):
|
if any(keyword in text for keyword in
|
||||||
|
['位置偏移', 'X:', 'Y:', 'Z:', '半径:', '尺寸:', '宽度:', '长度:', '高度:',
|
||||||
|
'法向量:', 'Nx:', 'Ny:', 'Nz:']):
|
||||||
widgets_to_remove.append(widget)
|
widgets_to_remove.append(widget)
|
||||||
|
|
||||||
# 移除参数标签
|
# 移除参数标签
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user