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

This commit is contained in:
陈横 2025-08-18 15:10:57 +08:00
commit eb3ce79c17
9 changed files with 74 additions and 120 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1187,44 +1187,32 @@ class SelectionSystem:
# 检查目标节点是否有父节点
parent_node = self.gizmoTarget.getParent()
# 确定轴向量的变换上下文
if parent_node and parent_node != self.world.render:
# 子节点:使用父节点的局部坐标系
print(f"子节点拖拽 - 父节点: {parent_node.getName()}, 父节点旋转: {parent_node.getHpr()}")
transform_context = parent_node
else:
# 顶级模型:使用世界坐标系
print(f"顶级模型拖拽 - 使用世界坐标系")
transform_context = self.world.render
# 计算轴向量在正确坐标系中的方向
if self.dragGizmoAxis == "x":
# 在变换上下文中的X轴方向
# 在局部坐标系中的X轴方向
local_axis_vector = Vec3(1, 0, 0)
elif self.dragGizmoAxis == "y":
# 在变换上下文中的Y轴方向
# 在局部坐标系中的Y轴方向
local_axis_vector = Vec3(0, 1, 0)
elif self.dragGizmoAxis == "z":
# 在变换上下文中的Z轴方向
# 在局部坐标系中的Z轴方向
local_axis_vector = Vec3(0, 0, 1)
else:
print(f"拖拽更新失败: 未知轴类型 {self.dragGizmoAxis}")
return
# 将局部轴向量转换到世界坐标系(用于屏幕投影)
if transform_context != self.world.render:
# 获取变换矩阵并应用到轴向量上
transform_mat = transform_context.getMat(self.world.render)
# 只旋转向量,不平移
# 确定轴向量的变换上下文
if parent_node and parent_node != self.world.render:
# 子节点:使用父节点的局部坐标系
# print(f"子节点拖拽 - 父节点: {parent_node.getName()}, 父节点旋转: {parent_node.getHpr()}")
# transform_context = parent_node
transform_mat = parent_node.getMat(self.world.render)
world_axis_vector = transform_mat.xformVec(local_axis_vector)
world_axis_vector.normalize() # 归一化
print(f"转换后的轴向量: {local_axis_vector} -> {world_axis_vector}")
else:
# 顶级节点,直接使用世界轴向量
world_axis_vector = local_axis_vector
print(f"世界轴向量: {world_axis_vector}")
# 计算轴的端点位置(用于屏幕投影)
# 顶级模型:使用世界坐标系
# print(f"顶级模型拖拽 - 使用世界坐标系")
# transform_context = self.world.render
axis_end = gizmo_world_pos + world_axis_vector
# 投影到屏幕空间
@ -1279,83 +1267,62 @@ class SelectionSystem:
projected_distance = (mouseDeltaX * screen_axis_dir[0] +
mouseDeltaY * screen_axis_dir[1])
# 计算动态比例因子,基于相机距离和视野角度
cam_pos = self.world.cam.getPos()
distance_to_object = (cam_pos - gizmo_world_pos).length()
scale_adjustment = 1.0
if parent_node and parent_node!= self.world.render:
current_node = parent_node
total_scale = 1.0
while current_node and current_node != self.world.render:
node_scale = current_node.getScale()
avg_scale = (node_scale.x+node_scale.y + node_scale.z)/3.0
total_scale *= avg_scale
current_node = current_node.getParent()
if total_scale>0:
scale_adjustment = 1.0 / total_scale
# parent_scale = parent_node.getScale()
# avg_scale = (parent_scale.x+parent_scale.y+parent_scale.z)/3.0
# if avg_scale>0:
# scale_adjustment = 1.0 / avg_scale
# 获取相机的视野角度
fov = self.world.cam.node().getLens().getFov()[0] # 水平视野角度
fov_radians = math.radians(fov)
# 获取窗口尺寸
winWidth, winHeight = self.world.getWindowSize()
fixed_pixel_to_world_ratio = 0.01 # 1像素 = 0.01世界单位
scale_factor = fixed_pixel_to_world_ratio * scale_adjustment
# 计算一个像素在世界坐标系中的大小(在目标物体的距离处)
# 使用透视投影公式world_size = screen_size * distance * tan(fov/2) / (screen_width/2)
pixel_to_world_ratio = distance_to_object * math.tan(fov_radians / 2) / (winWidth / 2)
# 【改进修复】:智能缩放补偿,区分继承缩放和本体缩放
# 计算父节点链的累积缩放(不包括目标节点本身)
parent_cumulative_scale = 1.0
current_node = self.gizmoTarget.getParent()
while current_node and current_node != self.world.render:
node_scale = current_node.getScale()
# 使用缩放的几何平均值作为累积因子
scale_magnitude = (abs(node_scale.x) * abs(node_scale.y) * abs(node_scale.z)) ** (1.0/3.0)
parent_cumulative_scale *= scale_magnitude
current_node = current_node.getParent()
# 获取目标节点自身的缩放
target_scale = self.gizmoTarget.getScale()
target_scale_magnitude = (abs(target_scale.x) * abs(target_scale.y) * abs(target_scale.z)) ** (1.0/3.0)
# 智能补偿策略:
# 1. 只对父节点链的小缩放进行完全补偿(这通常是单位转换导致的)
# 2. 对目标节点自身的缩放进行部分补偿(避免大模型缩小后移动过快)
parent_compensation = 1.0 / parent_cumulative_scale if parent_cumulative_scale > 0 else 1.0
# 对目标节点自身的缩放使用平方根补偿,减少过度补偿
target_compensation = 1.0 / math.sqrt(target_scale_magnitude) if target_scale_magnitude > 0 else 1.0
# 限制目标补偿的最大值,避免移动过快
target_compensation = min(target_compensation, 10.0) # 最大10倍补偿
# 综合补偿因子
total_compensation = parent_compensation * target_compensation
scale_factor = pixel_to_world_ratio * 0.5*total_compensation
# 【关键修复】:在正确的坐标系中计算移动向量
# 计算移动距离(标量)
movement_distance = projected_distance * scale_factor
# 在正确的坐标系中计算移动向量
if transform_context != self.world.render:
# 子节点:在父节点的局部坐标系中移动
if self.dragGizmoAxis == "x":
movement_local = Vec3(movement_distance, 0, 0)
elif self.dragGizmoAxis == "y":
movement_local = Vec3(0, movement_distance, 0)
elif self.dragGizmoAxis == "z":
movement_local = Vec3(0, 0, movement_distance)
# 将局部移动向量转换到父节点的坐标系中
# 由于我们要应用到目标节点上,而目标节点相对于父节点,我们直接使用局部移动
movement = movement_local
print(f"子节点移动向量(局部): {movement}")
# 获取当前位置并只修改选中轴的坐标
currentPos = self.gizmoTargetStartPos
# 根据拖拽的轴,只修改对应的坐标分量
if self.dragGizmoAxis == "x":
newPos = Vec3(currentPos.x + movement_distance, currentPos.y, currentPos.z)
print(f"X轴移动{currentPos.x} -> {newPos.x}")
elif self.dragGizmoAxis == "y":
newPos = Vec3(currentPos.x, currentPos.y + movement_distance, currentPos.z)
print(f"Y轴移动{currentPos.y} -> {newPos.y}")
elif self.dragGizmoAxis == "z":
newPos = Vec3(currentPos.x, currentPos.y, currentPos.z + movement_distance)
print(f"Z轴移动{currentPos.z} -> {newPos.z}")
else:
# 顶级模型:在世界坐标系中移动
if self.dragGizmoAxis == "x":
movement = Vec3(movement_distance, 0, 0)
elif self.dragGizmoAxis == "y":
movement = Vec3(0, movement_distance, 0)
elif self.dragGizmoAxis == "z":
movement = Vec3(0, 0, movement_distance)
print(f"顶级模型移动向量(世界): {movement}")
# 应用移动到目标节点
newPos = self.gizmoTargetStartPos + movement
self.gizmoTarget.setPos(newPos)
print(f"未知轴: {self.dragGizmoAxis}")
return
# 应用新位置到目标节点
light_object = self.gizmoTarget.getPythonTag("rp_light_object")
if light_object:
light_object.pos = newPos
self.gizmoTarget.setPos(newPos)
else:
self.gizmoTarget.setPos(newPos)
# 更新坐标轴位置 - 计算新的中心位置
minPoint = Point3()
maxPoint = Point3()
if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
center = Point3((minPoint.x + maxPoint.x) * 0.5,
(minPoint.y + maxPoint.y) * 0.5,
(minPoint.z + maxPoint.z) * 0.5)
self.gizmo.setPos(center)
# 实时更新属性面板
self.world.property_panel.refreshModelValues(self.gizmoTarget)
# 每次拖拽都输出调试信息(但限制频率)
@ -1365,18 +1332,9 @@ class SelectionSystem:
import time
current_time = time.time()
if current_time - self._last_drag_debug_time > 0.1: # 每0.1秒最多输出一次
print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 距离:{distance_to_object:.2f}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}")
print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}")
self._last_drag_debug_time = current_time
newPos = self.gizmoTargetStartPos + movement
light_object = self.gizmoTarget.getPythonTag("rp_light_object")
if light_object:
light_object.pos = newPos
self.gizmoTarget.setPos(newPos)
else:
self.gizmoTarget.setPos(newPos)
self.gizmo.setPos(newPos)
except Exception as e:
print(f"更新坐标轴拖拽失败: {str(e)}")
import traceback

View File

@ -405,7 +405,8 @@ class MainWindow(QMainWindow):
self.createPointLight.clicked.connect(lambda :self.world.createPointLight())
# 连接树节点点击信号
self.treeWidget.itemClicked.connect(self.world.onTreeItemClicked)
# self.treeWidget.itemClicked.connect(self.world.onTreeItemClicked)
self.treeWidget.itemSelectionChanged.connect(lambda :self.world.onTreeItemClicked(self.treeWidget.currentItem(), 0))
print("已连接点击信号")
# 连接工具切换信号

View File

@ -877,7 +877,7 @@ class PropertyPanelManager:
print(f"材质基础颜色: {base_color}")
# 基础颜色标题
color_row = 2 if material_status != "标准PBR材质" else 0
color_row = 2 if material_status != "标准PBR材质" else 1
material_layout.addWidget(QLabel("基础颜色"), color_row, 0)
# R, G, B 标签
@ -3870,20 +3870,15 @@ class PropertyPanelManager:
azimuth, altitude = presets[preset_name]
# 更新滑块和数值框
self.sun_azimuth_slider.blockSignals(True)
self.sun_azimuth_spinbox.blockSignals(True)
self.sun_altitude_slider.blockSignals(True)
self.sun_altitude_spinbox.blockSignals(True)
# 更新滑块和数值框
self.azimuthSpinBox.blockSignals(True)
self.altitudeSpinBox.blockSignals(True)
self.sun_azimuth_slider.setValue(azimuth)
self.sun_azimuth_spinbox.setValue(azimuth)
self.sun_altitude_slider.setValue(altitude)
self.sun_altitude_spinbox.setValue(altitude)
self.azimuthSpinBox.setValue(azimuth)
self.altitudeSpinBox.setValue(altitude)
self.sun_azimuth_slider.blockSignals(False)
self.sun_azimuth_spinbox.blockSignals(False)
self.sun_altitude_slider.blockSignals(False)
self.sun_altitude_spinbox.blockSignals(False)
self.azimuthSpinBox.blockSignals(False)
self.altitudeSpinBox.blockSignals(False)
# 应用设置 - 优先使用Day Time Editor
azimuth_success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth)