1
0
forked from Rowland/EG

Merge remote-tracking branch 'origin/main_ch_eg' into addRender

# Conflicts:
#	ui/property_panel.py
This commit is contained in:
Hector 2025-09-17 17:56:31 +08:00
commit e62ae89289
2 changed files with 869 additions and 599 deletions

View File

@ -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']):
"""创建鼠标射线""" """创建鼠标射线"""
# 组合掩码 # 组合掩码

View File

@ -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)
# 移除参数标签 # 移除参数标签