From 4f70cc113b8703542064fca2b425a1aa10e5b64a Mon Sep 17 00:00:00 2001 From: ayuan9957 <107920784+ayuan9957@users.noreply.github.com> Date: Sun, 1 Mar 2026 12:16:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8B=86=E5=88=86updateGizmoDrag?= =?UTF-8?q?=E5=B9=B6=E5=90=8C=E6=AD=A5=E4=BB=BB=E5=8A=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/selection.py | 511 +++++++++++++++++++++++----------------------- 1 file changed, 251 insertions(+), 260 deletions(-) diff --git a/core/selection.py b/core/selection.py index 280f1ac5..cef4b4a0 100644 --- a/core/selection.py +++ b/core/selection.py @@ -1639,279 +1639,270 @@ class SelectionSystem: import traceback traceback.print_exc() + def _validate_gizmo_drag_state(self): + if not self.isDraggingGizmo: + print("拖拽更新失败: 不在拖拽状态") + return False + if not self.gizmoTarget: + print("拖拽更新失败: 没有拖拽目标") + return False + if not hasattr(self, 'dragStartMousePos') or not self.dragStartMousePos: + print("拖拽更新失败: 没有拖拽起始位置") + return False + if not hasattr(self, 'gizmoTargetStartPos') or not self.gizmoTargetStartPos: + print("拖拽更新失败: 没有目标起始位置") + return False + if not hasattr(self, 'gizmoStartPos') or not self.gizmoStartPos: + print("拖拽更新失败: 没有坐标轴起始位置") + return False + return True + + def _refresh_gizmo_target_panel(self): + if hasattr(self.world, 'property_panel') and self.world.property_panel: + self.world.property_panel.refreshModelValues(self.gizmoTarget) + + def _apply_scale_drag(self, mouse_delta_x, mouse_delta_y, is_gui_element): + scale_factor = 1.0 + (mouse_delta_x + mouse_delta_y) * 0.01 + scale_factor = max(0.001, scale_factor) + start_scale = getattr(self, 'gizmoTargetStartScale', Vec3(1, 1, 1)) + + if is_gui_element: + if self.dragGizmoAxis == "x": + new_scale = Vec3(start_scale.x * scale_factor, start_scale.y, start_scale.z) + elif self.dragGizmoAxis == "y": + new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z) + elif self.dragGizmoAxis == "z": + new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * scale_factor) + else: + new_scale = Vec3( + start_scale.x * scale_factor, + start_scale.y * scale_factor, + start_scale.z * scale_factor, + ) + else: + if self.dragGizmoAxis == "x": + new_scale = Vec3(start_scale.x * scale_factor, start_scale.y, start_scale.z) + elif self.dragGizmoAxis == "y": + new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z) + elif self.dragGizmoAxis == "z": + z_scale_factor = 1.0 - (mouse_delta_x + mouse_delta_y) * 0.01 + new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * z_scale_factor) + else: + new_scale = Vec3( + start_scale.x * scale_factor, + start_scale.y * scale_factor, + start_scale.z * scale_factor, + ) + + new_scale = Vec3( + max(0.001, new_scale.x), + max(0.001, new_scale.y), + max(0.001, new_scale.z), + ) + self.gizmoTarget.setScale(new_scale) + self._refresh_gizmo_target_panel() + + def _apply_rotate_drag(self, mouse_delta_x, mouse_delta_y): + rotation_speed = 0.5 + rotation_amount = (mouse_delta_x + mouse_delta_y) * rotation_speed + start_hpr = getattr(self, 'gizmoTargetStartHpr', self.gizmoTarget.getHpr()) + + if self.dragGizmoAxis == "x": + new_hpr = Vec3(start_hpr.x + rotation_amount, start_hpr.y, start_hpr.z) + elif self.dragGizmoAxis == "y": + new_hpr = Vec3(start_hpr.x, start_hpr.y - rotation_amount, start_hpr.z) + elif self.dragGizmoAxis == "z": + new_hpr = Vec3(start_hpr.x, start_hpr.y, start_hpr.z + rotation_amount) + else: + new_hpr = Vec3( + start_hpr.x + rotation_amount, + start_hpr.y + rotation_amount, + start_hpr.z + rotation_amount, + ) + + self.gizmoTarget.setHpr(new_hpr) + self._refresh_gizmo_target_panel() + + def _get_local_axis_vector(self): + if self.dragGizmoAxis == "x": + return Vec3(1, 0, 0) + if self.dragGizmoAxis == "y": + return Vec3(0, 1, 0) + if self.dragGizmoAxis == "z": + return Vec3(0, 0, 1) + print(f"拖拽更新失败: 未知轴类型 {self.dragGizmoAxis}") + return None + + def _compute_world_axis_vector(self, local_axis_vector): + world_axis_vector = local_axis_vector + parent_node = self.gizmoTarget.getParent() + + if parent_node and parent_node != self.world.render: + try: + transfrom_mat = parent_node.getMat(self.world.render) + if transfrom_mat.is_identity() or self._isMatrixValid(transfrom_mat): + world_axis_vector = transfrom_mat.xformVec(local_axis_vector) + else: + print("警告: 检测到无效变换矩阵,使用默认轴向量") + except Exception as e: + print(f"变换计算出错: {e},使用默认轴向量") + + return world_axis_vector + + def _world_to_screen(self, world_pos): + try: + cam_pos = self.world.cam.getRelativePoint(self.world.render, world_pos) + if cam_pos.getY() <= 0: + return None + + screen_pos = Point2() + if self.world.cam.node().getLens().project(cam_pos, screen_pos): + win_width, win_height = self.world.getWindowSize() + win_x = (screen_pos.x + 1) * 0.5 * win_width + win_y = (1 - screen_pos.y) * 0.5 * win_height + return win_x, win_y + return None + except Exception as e: + print(f"世界坐标转屏幕坐标失败: {e}") + return None + + def _compute_parent_scale_factor(self): + total_scale_factor = 1.0 + current_node = self.gizmoTarget.getParent() + + while current_node and current_node != self.world.render: + try: + if not current_node.isEmpty(): + node_scale = current_node.getScale() + if node_scale.x > 0 and node_scale.y > 0 and node_scale.z > 0: + avg_scale = (node_scale.x + node_scale.y + node_scale.z) / 3.0 + total_scale_factor *= avg_scale + current_node = current_node.getParent() + else: + break + except Exception: + break + return total_scale_factor + + def _compute_axis_movement_distance(self, mouse_delta_x, mouse_delta_y): + gizmo_world_pos = self.gizmoStartPos + local_axis_vector = self._get_local_axis_vector() + if local_axis_vector is None: + return None + + world_axis_vector = self._compute_world_axis_vector(local_axis_vector) + axis_start_screen = self._world_to_screen(gizmo_world_pos) + axis_end_world = gizmo_world_pos + world_axis_vector + axis_end_screen = self._world_to_screen(axis_end_world) + + if not axis_start_screen or not axis_end_screen: + print("拖拽更新失败: 无法获取轴线屏幕坐标") + return None + + screen_axis_dir = ( + axis_end_screen[0] - axis_start_screen[0], + axis_end_screen[1] - axis_start_screen[1], + ) + length = math.sqrt(screen_axis_dir[0] ** 2 + screen_axis_dir[1] ** 2) + if length <= 0: + print("拖拽更新失败: 屏幕轴方向长度为0") + return None + + screen_axis_dir = ( + screen_axis_dir[0] / length, + screen_axis_dir[1] / length, + ) + projected_distance = ( + mouse_delta_x * screen_axis_dir[0] + + mouse_delta_y * screen_axis_dir[1] + ) + + cam_pos = self.world.cam.getPos(self.world.render) + distance_to_object = (cam_pos - gizmo_world_pos).length() + lens = self.world.cam.node().getLens() + fov = lens.getFov()[0] + win_width, _ = self.world.getWindowSize() + + pixels_to_world_units = (2 * distance_to_object * math.tan(math.radians(fov / 2))) / win_width + movement_distance = projected_distance * pixels_to_world_units + + total_scale_factor = self._compute_parent_scale_factor() + if total_scale_factor > 0: + movement_distance = movement_distance / total_scale_factor + + return movement_distance + + def _apply_translate_drag(self, movement_distance): + current_pos = self.gizmoTargetStartPos + + if self.dragGizmoAxis == "x": + new_pos = Vec3(current_pos.x + movement_distance, current_pos.y, current_pos.z) + elif self.dragGizmoAxis == "y": + new_pos = Vec3(current_pos.x, current_pos.y + movement_distance, current_pos.z) + elif self.dragGizmoAxis == "z": + new_pos = Vec3(current_pos.x, current_pos.y, current_pos.z + movement_distance) + else: + print(f"未知轴: {self.dragGizmoAxis}") + return + + light_object = self.gizmoTarget.getPythonTag("rp_light_object") + if light_object: + self.gizmoTarget.setPos(new_pos) + self._sync_rp_light_position(self.gizmoTarget, light_object) + print(f"🔄 光源拖拽移动: {current_pos} -> {new_pos}") + else: + self.gizmoTarget.setPos(new_pos) + print(f"🔄 节点拖拽移动: {current_pos} -> {new_pos} (轴: {self.dragGizmoAxis}, 距离: {movement_distance:.3f})") + + self._refresh_gizmo_target_panel() + + min_point = Point3() + max_point = Point3() + if self.gizmoTarget.calcTightBounds(min_point, max_point, self.world.render): + center = Point3( + (min_point.x + max_point.x) * 0.5, + (min_point.y + max_point.y) * 0.5, + (min_point.z + max_point.z) * 0.5, + ) + self.gizmo.setPos(center) + + self._refresh_gizmo_target_panel() + + if not hasattr(self, '_last_drag_debug_time'): + self._last_drag_debug_time = 0 + + import time + current_time = time.time() + if current_time - self._last_drag_debug_time > 0.1: + self._last_drag_debug_time = current_time + def updateGizmoDrag(self, mouseX, mouseY): """更新坐标轴拖拽 - 使用正确的坐标系变换,支持旋转后的子节点拖拽""" try: - # 添加详细的状态检查和调试信息 - if not self.isDraggingGizmo: - print("拖拽更新失败: 不在拖拽状态") - return - if not self.gizmoTarget: - print("拖拽更新失败: 没有拖拽目标") - return - if not hasattr(self, 'dragStartMousePos') or not self.dragStartMousePos: - print("拖拽更新失败: 没有拖拽起始位置") - return - if not hasattr(self, 'gizmoTargetStartPos') or not self.gizmoTargetStartPos: - print("拖拽更新失败: 没有目标起始位置") - return - if not hasattr(self, 'gizmoStartPos') or not self.gizmoStartPos: - print("拖拽更新失败: 没有坐标轴起始位置") + if not self._validate_gizmo_drag_state(): return is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False is_rotate_tool = self.world.tool_manager.isRotateTool() if self.world.tool_manager else False - - is_gui_element = (hasattr(self.gizmoTarget,'getTag') and - self.gizmoTarget.getTag("is_gui_element") == "1") - - - # 计算鼠标移动距离(屏幕像素) - mouseDeltaX = mouseX - self.dragStartMousePos[0] - mouseDeltaY = mouseY - self.dragStartMousePos[1] - - if is_scale_tool: - scale_factor = 1.0 + (mouseDeltaX + mouseDeltaY) * 0.01 - - scale_factor = max(0.001, scale_factor) - - start_scale = getattr(self,'gizmoTargetStartScale',Vec3(1,1,1)) - - if is_gui_element: - if self.dragGizmoAxis == "x": - new_scale = Vec3(start_scale.x * scale_factor,start_scale.y,start_scale.z) - elif self.dragGizmoAxis == "y": - new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z) - elif self.dragGizmoAxis == "z": - new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * scale_factor) - else: - new_scale = Vec3(start_scale.x * scale_factor, - start_scale.y * scale_factor, - start_scale.z * scale_factor) - else: - # 普通3D模型的缩放处理 - if self.dragGizmoAxis == "x": - new_scale = Vec3(start_scale.x * scale_factor, start_scale.y, start_scale.z) - elif self.dragGizmoAxis == "y": - new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z) - elif self.dragGizmoAxis == "z": - z_scale_factor = 1.0 - (mouseDeltaX + mouseDeltaY) * 0.01 - new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * z_scale_factor) - else: - new_scale = Vec3(start_scale.x * scale_factor, - start_scale.y * scale_factor, - start_scale.z * scale_factor) - - new_scale = Vec3( - max(0.001,new_scale.x), - max(0.001,new_scale.y), - max(0.001,new_scale.z) - ) - - # 应用新缩放值 - self.gizmoTarget.setScale(new_scale) - if hasattr(self.world, 'property_panel') and self.world.property_panel: - self.world.property_panel.refreshModelValues(self.gizmoTarget) - return - elif is_rotate_tool: - rotation_speed = 0.5 - rotation_amount = (mouseDeltaX + mouseDeltaY) * rotation_speed - start_hpr = getattr(self,'gizmoTargetStartHpr',self.gizmoTarget.getHpr()) - - if self.dragGizmoAxis == "x": - new_hpr = Vec3(start_hpr.x+rotation_amount,start_hpr.y,start_hpr.z) - elif self.dragGizmoAxis == "y": - new_hpr = Vec3(start_hpr.x,start_hpr.y-rotation_amount,start_hpr.z) - elif self.dragGizmoAxis == "z": - new_hpr = Vec3(start_hpr.x,start_hpr.y,start_hpr.z+rotation_amount) - else: - # 默认绕所有轴旋转 - new_hpr = Vec3(start_hpr.x + rotation_amount, - start_hpr.y + rotation_amount, - start_hpr.z + rotation_amount) - self.gizmoTarget.setHpr(new_hpr) - if hasattr(self.world, 'property_panel') and self.world.property_panel: - self.world.property_panel.refreshModelValues(self.gizmoTarget) - return - - # 使用坐标轴的实际位置而不是目标节点位置来计算屏幕投影 - gizmo_world_pos = self.gizmoStartPos - - # 【关键修复】:获取正确的轴向量,考虑父节点的旋转 - # 检查目标节点是否有父节点 - parent_node = self.gizmoTarget.getParent() - - # 计算轴向量在正确坐标系中的方向 - if self.dragGizmoAxis == "x": - # 在局部坐标系中的X轴方向 - local_axis_vector = Vec3(1, 0, 0) - elif self.dragGizmoAxis == "y": - # 在局部坐标系中的Y轴方向 - local_axis_vector = Vec3(0, 1, 0) - elif self.dragGizmoAxis == "z": - # 在局部坐标系中的Z轴方向 - local_axis_vector = Vec3(0, 0, 1) - else: - print(f"拖拽更新失败: 未知轴类型 {self.dragGizmoAxis}") - return - - world_axis_vector = local_axis_vector - - if parent_node and parent_node != self.world.render: - try: - #获取变换矩阵 - transfrom_mat = parent_node.getMat(self.world.render) - if transfrom_mat.is_identity() or self._isMatrixValid(transfrom_mat): - world_axis_vector = transfrom_mat.xformVec(local_axis_vector) - else: - print("警告: 检测到无效变换矩阵,使用默认轴向量") - except Exception as e: - print(f"变换计算出错: {e},使用默认轴向量") - else: - world_axis_vector = local_axis_vector - - # 确定轴向量的变换上下文 - # if parent_node and parent_node != self.world.render: - # transform_mat = parent_node.getMat(self.world.render) - # world_axis_vector = transform_mat.xformVec(local_axis_vector) - # else: - # world_axis_vector = local_axis_vector - - #axis_end = gizmo_world_pos + world_axis_vector - - # 投影到屏幕空间 - def worldToScreen(worldPos): - try: - camPos = self.world.cam.getRelativePoint(self.world.render, worldPos) - if camPos.getY() <= 0: - return None - - screenPos = Point2() - if self.world.cam.node().getLens().project(camPos, screenPos): - winWidth, winHeight = self.world.getWindowSize() - winX = (screenPos.x + 1) * 0.5 * winWidth - winY = (1 - screenPos.y) * 0.5 * winHeight - return (winX, winY) - return None - except Exception as e: - print(f"世界坐标转屏幕坐标失败: {e}") - return None - axis_start_screen = worldToScreen(gizmo_world_pos) - axis_end_world = gizmo_world_pos + world_axis_vector - axis_end_screen = worldToScreen(axis_end_world) - - if not axis_start_screen or not axis_end_screen: - print("拖拽更新失败: 无法获取轴线屏幕坐标") - return - - screen_axis_dir = ( - axis_end_screen[0] - axis_start_screen[0], - axis_end_screen[1] - axis_start_screen[1] + is_gui_element = ( + hasattr(self.gizmoTarget, 'getTag') and + self.gizmoTarget.getTag("is_gui_element") == "1" ) - # 归一化屏幕轴方向 - import math - length = math.sqrt(screen_axis_dir[0]**2 + screen_axis_dir[1]**2) - if length > 0: - #screen_axis_dir = (screen_axis_dir[0] / length, screen_axis_dir[1] / length) - screen_axis_dir = ( - screen_axis_dir[0] / length, - screen_axis_dir[1] / length - ) + mouse_delta_x = mouseX - self.dragStartMousePos[0] + mouse_delta_y = mouseY - self.dragStartMousePos[1] - else: - print("拖拽更新失败: 屏幕轴方向长度为0") + if is_scale_tool: + self._apply_scale_drag(mouse_delta_x, mouse_delta_y, is_gui_element) + return + if is_rotate_tool: + self._apply_rotate_drag(mouse_delta_x, mouse_delta_y) return - # 将鼠标移动投影到轴方向上 - projected_distance = (mouseDeltaX * screen_axis_dir[0] + - mouseDeltaY * screen_axis_dir[1]) - - cam_pos = self.world.cam.getPos(self.world.render) - distance_to_object = (cam_pos - gizmo_world_pos).length() - - lens = self.world.cam.node().getLens() - fov = lens.getFov()[0] - winWidth,winHeight = self.world.getWindowSize() - - pixels_to_world_units = (2*distance_to_object*math.tan(math.radians(fov/2)))/winWidth - - movement_distance = projected_distance * pixels_to_world_units - - total_scale_factor = 1.0 - current_node = self.gizmoTarget.getParent() - - while current_node and current_node != self.world.render: - try: - if not current_node.isEmpty(): - node_scale = current_node.getScale() - if node_scale.x > 0 and node_scale.y >0 and node_scale.z >0 : - avg_scale = (node_scale.x + node_scale.y + node_scale.z)/3.0 - total_scale_factor *= avg_scale - #avg_scale = (node_scale.x+node_scale.y + node_scale.z) / 3.0 - #total_scale_factor *= avg_scale - current_node = current_node.getParent() - else: - break - except: - break - - - if total_scale_factor > 0: - movement_distance = movement_distance / total_scale_factor - - 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: - print(f"未知轴: {self.dragGizmoAxis}") + movement_distance = self._compute_axis_movement_distance(mouse_delta_x, mouse_delta_y) + if movement_distance is None: return - - # 应用新位置到目标节点 - light_object = self.gizmoTarget.getPythonTag("rp_light_object") - if light_object: - self.gizmoTarget.setPos(newPos) - self._sync_rp_light_position(self.gizmoTarget, light_object) - print(f"🔄 光源拖拽移动: {currentPos} -> {newPos}") - else: - self.gizmoTarget.setPos(newPos) - print(f"🔄 节点拖拽移动: {currentPos} -> {newPos} (轴: {self.dragGizmoAxis}, 距离: {movement_distance:.3f})") - - # 更新属性面板 - if hasattr(self.world, 'property_panel') and self.world.property_panel: - self.world.property_panel.refreshModelValues(self.gizmoTarget) - - # 更新坐标轴位置 - 计算新的中心位置 - 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) - - # 实时更新属性面板 - if hasattr(self.world, 'property_panel') and self.world.property_panel: - self.world.property_panel.refreshModelValues(self.gizmoTarget) - - # 每次拖拽都输出调试信息(但限制频率) - if not hasattr(self, '_last_drag_debug_time'): - self._last_drag_debug_time = 0 - - import time - current_time = time.time() - if current_time - self._last_drag_debug_time > 0.1: # 每0.1秒最多输出一次 - #print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}") - self._last_drag_debug_time = current_time + self._apply_translate_drag(movement_distance) except Exception as e: print(f"更新坐标轴拖拽失败: {str(e)}")