From e4084c5bf64323350bc5e018926ca6db3557a6ca Mon Sep 17 00:00:00 2001 From: Hector <2055590199@qq.com> Date: Thu, 21 Aug 2025 10:44:59 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=9D=90=E6=A0=87=E8=BD=B4=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=BE=85=E6=9B=B4=E6=94=B9=202.=E6=8B=96=E5=8A=A8=E5=90=8E?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=8F=AF=E8=A7=81=E6=80=A7=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RenderPipelineFile/config/daytime.yaml | 2 +- core/selection.py | 346 +++++++++++++------------ main.py | 6 +- ui/property_panel.py | 24 +- ui/widgets.py | 9 +- 5 files changed, 217 insertions(+), 170 deletions(-) diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index e67993cc..199088f8 100644 --- a/RenderPipelineFile/config/daytime.yaml +++ b/RenderPipelineFile/config/daytime.yaml @@ -17,7 +17,7 @@ control_points: scattering: sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5089595376,0.9690650222],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] sun_color: [[[0.5010435645,0.5818710306],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] - sun_azimuth: [[[0.5000000000,0.4416666667]]] + sun_azimuth: [[[0.5000000000,0.0000000000]]] sun_altitude: [[[0.5000000000,1.0000000000]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: diff --git a/core/selection.py b/core/selection.py index ecbbe5c4..22ff8ef5 100644 --- a/core/selection.py +++ b/core/selection.py @@ -308,6 +308,13 @@ class SelectionSystem: self.setGizmoAxisColor("y", self.gizmo_colors["y"]) self.setGizmoAxisColor("z", self.gizmo_colors["z"]) + self._updateGizmoScreenSize() + + self._setupGizmoRendering() + + # 现在才显示坐标轴 + self.gizmo.show() + # 只启动一次更新任务 taskMgr.add(self.updateGizmoTask, "updateGizmo") @@ -363,38 +370,59 @@ class SelectionSystem: self.setGizmoAxisColor("y", self.gizmo_colors["y"]) self.setGizmoAxisColor("z", self.gizmo_colors["z"]) - # 使用最强的渲染设置,确保坐标轴绝对不会被遮挡 - # self.gizmo.setBin("default", 0) # 使用最高的GUI渲染层 - # self.gizmo.setDepthTest(True) # 完全禁用深度测试 - # self.gizmo.setDepthWrite(True) # 禁用深度写入 - # self.gizmo.setTwoSided(True) # 双面渲染 - - + #设置渲染属性,解决模型遮挡和阴影问题 + self._setupGizmoRendering() except Exception as e: print(f"创建坐标轴几何体失败: {str(e)}") - def _applyAxisMaterial(self, node, color): - from panda3d.core import Material, Vec4, ColorWriteAttrib, DepthWriteAttrib - # 构造一个“自发光”的材质,让颜色不依赖灯光/法线 - m = Material() - col = Vec4(*color) - m.setBaseColor(col) # 记录用 - m.setDiffuse(col) # 记录用(RP标准材质可能不读取) - m.setEmission(Vec4(col.x, col.y, col.z, 1.0)) # 关键:用发光通道显示颜色 - node.setMaterial(m, 1) + def _setupGizmoRendering(self): + try: + axis_nodes = [self.gizmoXAxis,self.gizmoYAxis,self.gizmoZAxis] - # 走能直接写到最终画面的阶段(两种方式二选一): - # 方式1:完全绕过管线(最稳) - node.setTag("pipeline-disable", "1") - # 方式2:留在管线内但走最终阶段(若不想禁用管线,可改用下面这一行,并去掉上一行) - # node.setTag("pipeline-stage", "final") + for axis_node in axis_nodes: + if axis_node: + #禁用光照和阴影 + axis_node.setLightOff() + axis_node.setShaderOff() + axis_node.setFogOff() + #设置渲染层级,确保大多数对象之前渲染 + axis_node.setBin("fixed",30) + axis_node.setDepthWrite(False) + axis_node.setDepthTest(False) + arrow_nodes = [] + if self.gizmoXAxis: + x_arrow = self.gizmoXAxis.find("x_arrow") + if x_arrow: + arrow_nodes.append(x_arrow) + if self.gizmoYAxis: + y_arrow = self.gizmoYAxis.find("y_arrow") + if y_arrow: + arrow_nodes.append(y_arrow) + if self.gizmoZAxis: + z_arrow = self.gizmoZAxis.find("z_arrow") + if z_arrow: + arrow_nodes.append(z_arrow) - # 可见性状态:允许颜色写入,不写深度,双面可见,禁用雾 - node.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.C_all)) - node.setAttrib(DepthWriteAttrib.make(False)) - node.setTwoSided(True) - node.setFogOff() + for arrow_node in arrow_nodes: + if arrow_node: + arrow_node.setLightOff() + arrow_node.setShaderOff() + arrow_node.setFogOff() + arrow_node.setBin("fixed",31) + arrow_node.setDepthWrite(False) + arrow_node.setDepthTest(False) + #启用透明度S + arrow_node.setTransparency(TransparencyAttrib.MAlpha) + if self.gizmo: + self.gizmo.setLightOff() + self.gizmo.setShaderOff() + self.gizmo.setFogOff() + self.gizmo.setBin("fixed",29) + self.gizmo.setDepthWrite(False) + self.gizmo.setDepthTest(False) + except Exception as e: + print(f"设置坐标轴渲染属性失败: {str(e)}") def updateGizmoTask(self, task): """坐标轴更新任务 - 包含固定大小功能""" @@ -429,25 +457,6 @@ class SelectionSystem: else: # 只在必要时更新位置和朝向 self._updateGizmoPositionAndOrientation() - # # 更新坐标轴位置,始终在目标节点中心 - # 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) - # - # # 【关键修复】:更新坐标轴朝向以跟踪父节点的变化 - # parent_node = self.gizmoTarget.getParent() - # if parent_node and parent_node != self.world.render: - # # 子节点:坐标轴朝向跟随父节点 - # parent_hpr = parent_node.getHpr() - # self.gizmo.setHpr(parent_hpr) - # else: - # # 顶级模型:使用世界坐标系朝向 - # self.gizmo.setHpr(0, 0, 0) # 【新功能】:动态调整坐标轴大小,保持固定的屏幕大小 self._updateGizmoScreenSize() @@ -517,8 +526,8 @@ class SelectionSystem: self.gizmo.setScale(scale_factor) # 限制缩放范围,避免过大或过小 - min_scale = 0.1 - max_scale = 10.0 + min_scale = 0.08 + max_scale = 100.0 final_scale = max(min_scale, min(max_scale, scale_factor)) if final_scale != scale_factor: @@ -630,19 +639,13 @@ class SelectionSystem: else: arrow_node.setTransparency(TransparencyAttrib.MNone) - # 创建自定义渲染状态 - 确保始终在最前方 - # state = RenderState.make( - # ColorWriteAttrib.make(ColorWriteAttrib.CAll), # 允许颜色写入 - # DepthTestAttrib.make(DepthTestAttrib.MAlways), # 始终通过深度测试 - # DepthWriteAttrib.make(DepthWriteAttrib.MOff) # 不写入深度缓冲区 - # ) - # # 应用渲染状态 - # arrow_node.set_state(state) - arrow_node.setLightOff() # 禁用光照影响 arrow_node.setShaderOff() # 禁用着色器 arrow_node.setFogOff() # 禁用雾效果 - arrow_node.setBin("fixed", 10) # 使用固定渲染顺序,确保在最前 + + arrow_node.setBin("fixed",31) + #arrow_node.setDepthWrite(False) + #arrow_node.setDepthTest(True) # 保存材质引用以便后续修改 if axis == "x": @@ -655,8 +658,9 @@ class SelectionSystem: axis_node.setLightOff() axis_node.setShaderOff() axis_node.setFogOff() - #axis_node.set_state(state) - axis_node.setBin("fixed", 9) # 确保轴节点在箭头模型之前渲染 + axis_node.setBin("fixed", 30) + #axis_node.setDepthWrite(False) + #axis_node.setDepthTest(True) except Exception as e: print(f"设置坐标轴颜色失败: {str(e)}") @@ -907,38 +911,10 @@ class SelectionSystem: # ==================== 高亮和交互 ==================== - def updateGizmoHighlight(self, mouseX, mouseY): - """更新坐标轴高亮状态""" - if not self.gizmo or self.isDraggingGizmo: - return - - import time - current_time = time.time() - if not hasattr(self,'_last_highlight_time'): - self._last_highlight_time = 0 - - if current_time - self._last_highlight_time<0.05: - return - self._last_highlight_time = current_time - - hoveredAxis = self._detectHoveredAxis(mouseX, mouseY) - - if not hasattr(self,'_hover_stability_counter'): - self._hover_stability_counter = {} - self._last_detected_axis = None - - if hoveredAxis !=self._last_detected_axis: - self._hover_stability_counter[hoveredAxis]=1 - self._last_detected_axis = hoveredAxis - else: - self._hover_stability_counter[hoveredAxis] = self._hover_stability_counter.get(hoveredAxis,0)+1 - - if self._hover_stability_counter.get(hoveredAxis,0)>=2: - if hoveredAxis != self.gizmoHighlightAxis: - self._updateAxisHighlight(hoveredAxis) - - # 检测鼠标悬停的轴(使用相同的检测逻辑但不输出调试信息) - hoveredAxis = None + def detectGizmoAxisAtMouse(self, mouseX, mouseY): + """统一的坐标轴检测方法 - 同时用于高亮和点击检测""" + if not self.gizmo or not self.gizmoTarget: + return None try: # 获取坐标轴中心的世界坐标 @@ -951,29 +927,29 @@ class SelectionSystem: # 将3D坐标投影到屏幕坐标 def worldToScreen(worldPos): - try: - # 转换为相机坐标系 - camPos = self.world.cam.getRelativePoint(self.world.render, worldPos) + try: + # 转换为相机坐标系 + camPos = self.world.cam.getRelativePoint(self.world.render, worldPos) - # 检查点是否在相机前方 - if camPos.getY() <= 0: - return None + # 检查点是否在相机前方 + if camPos.getY() <= 0: + return None - # 使用相机lens进行投影 - screenPos = Point2() - lens = self.world.cam.node().getLens() + # 使用相机lens进行投影 + screenPos = Point2() + lens = self.world.cam.node().getLens() - if lens.project(camPos, screenPos): - # 获取准确的窗口尺寸 - winWidth, winHeight = self.world.getWindowSize() + if lens.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: - return None + # 转换为像素坐标 + winX = (screenPos.x + 1) * 0.5 * winWidth + winY = (1 - screenPos.y) * 0.5 * winHeight + return (winX, winY) + return None + except: + return None # 获取各坐标轴的屏幕投影 gizmo_screen = worldToScreen(gizmo_world_pos) @@ -981,70 +957,90 @@ class SelectionSystem: y_screen = worldToScreen(y_end) z_screen = worldToScreen(z_end) - # 只要坐标轴中心在屏幕内,就进行检测 - if gizmo_screen: - click_threshold = 25 + # 如果坐标轴中心不在屏幕内,返回None + if not gizmo_screen: + return None - def isNearLine(mousePos, start, end, threshold): - import math - A = mousePos[1] - start[1] - B = start[0] - mousePos[0] - C = (end[1] - start[1]) * mousePos[0] + (start[0] - end[0]) * mousePos[1] + end[0] * start[1] - start[0] * end[1] + # 设置检测阈值 + click_threshold = 35 # 统一使用25像素的检测阈值 - length = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2) - if length == 0: - return False + # 更准确的点到线段距离计算方法 + def distanceToLineSegment(mousePos, start, end): + import math + mx, my = mousePos + x1, y1 = start + x2, y2 = end - distance = abs(C) / length - t = ((mousePos[0] - start[0]) * (end[0] - start[0]) + - (mousePos[1] - start[1]) * (end[1] - start[1])) / (length * length) + # 线段向量 + dx = x2 - x1 + dy = y2 - y1 - return distance < threshold and 0 <= t <= 1 + # 线段长度平方 + length_sq = dx * dx + dy * dy - mouse_pos = (mouseX, mouseY) + if length_sq == 0: + # 线段退化为点 + return math.sqrt((mx - x1) ** 2 + (my - y1) ** 2) - # 分别检测每个轴,为在屏幕外的轴端点提供替代方案 - # 按优先级检测轴(Z > X > Y) + # 投影参数 + t = max(0, min(1, ((mx - x1) * dx + (my - y1) * dy) / length_sq)) - # 对于轴端点在屏幕外的情况,使用较短的轴段进行检测 - def getAxisScreenPoint(axis_name, axis_screen_end): - if axis_screen_end: - return axis_screen_end - # 如果端点在屏幕外,使用轴长度的一半作为检测点 - if axis_name == "x": - half_end = gizmo_world_pos + Vec3(self.axis_length * 0.5, 0, 0) - elif axis_name == "y": - half_end = gizmo_world_pos + Vec3(0, self.axis_length * 0.5, 0) - elif axis_name == "z": - half_end = gizmo_world_pos + Vec3(0, 0, self.axis_length * 0.5) - return worldToScreen(half_end) + # 投影点坐标 + proj_x = x1 + t * dx + proj_y = y1 + t * dy - # 获取有效的检测点(优先使用完整轴,备用使用半轴) - z_detect_point = getAxisScreenPoint("z", z_screen) - x_detect_point = getAxisScreenPoint("x", x_screen) - y_detect_point = getAxisScreenPoint("y", y_screen) + # 返回点到投影点的距离 + return math.sqrt((mx - proj_x) ** 2 + (my - proj_y) ** 2) - if z_detect_point and isNearLine(mouse_pos, gizmo_screen, z_detect_point, click_threshold): - hoveredAxis = "z" - elif x_detect_point and isNearLine(mouse_pos, gizmo_screen, x_detect_point, click_threshold): - hoveredAxis = "x" - elif y_detect_point and isNearLine(mouse_pos, gizmo_screen, y_detect_point, click_threshold): - hoveredAxis = "y" + mouse_pos = (mouseX, mouseY) + + # 检测各个轴 - 按优先级检测(Z > X > Y) + axes_to_check = [ + ("z", z_screen), + ("x", x_screen), + ("y", y_screen) + ] + + for axis_name, axis_end in axes_to_check: + if axis_end: + distance = distanceToLineSegment(mouse_pos, gizmo_screen, axis_end) + if distance < click_threshold: + return axis_name + + # 如果没有检测到,返回None + return None except Exception as e: - pass # 静默处理错误,避免频繁输出 + # 静默处理错误,避免频繁输出 + return None - # 如果高亮状态发生变化 - if hoveredAxis != self.gizmoHighlightAxis: - # 恢复之前高亮的轴 - if self.gizmoHighlightAxis: - self.setGizmoAxisColor(self.gizmoHighlightAxis, self.gizmo_colors[self.gizmoHighlightAxis]) + def updateGizmoHighlight(self, mouseX, mouseY): + """更新坐标轴高亮状态""" + if not self.gizmo or self.isDraggingGizmo: + return - # 高亮新的轴 - if hoveredAxis: - self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis]) + # 使用统一的检测方法 + hoveredAxis = self.detectGizmoAxisAtMouse(mouseX, mouseY) - self.gizmoHighlightAxis = hoveredAxis + # 简化稳定性检测逻辑 + if not hasattr(self, '_last_detected_axis'): + self._last_detected_axis = None + + # 如果检测结果发生变化,立即更新高亮 + if hoveredAxis != self._last_detected_axis: + # 更新轴的高亮状态 + if hoveredAxis != self.gizmoHighlightAxis: + # 恢复之前高亮的轴 + if self.gizmoHighlightAxis: + self.setGizmoAxisColor(self.gizmoHighlightAxis, self.gizmo_colors[self.gizmoHighlightAxis]) + + # 高亮新的轴 + if hoveredAxis: + self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis]) + + self.gizmoHighlightAxis = hoveredAxis + + self._last_detected_axis = hoveredAxis def _detectHoveredAxis(self, mouseX, mouseY): """检测鼠标悬停的轴 - 提取为独立方法""" @@ -1078,14 +1074,36 @@ class SelectionSystem: return self.isDraggingGizmo = True - self.dragGizmoAxis = axis + # 使用当前高亮的轴,如果有的话 + if self.gizmoHighlightAxis: + self.dragGizmoAxis = self.gizmoHighlightAxis + else: + self.dragGizmoAxis = axis self.dragStartMousePos = (mouseX, mouseY) # 保存开始拖拽时目标节点的位置和坐标轴的位置 self.gizmoTargetStartPos = self.gizmoTarget.getPos() self.gizmoStartPos = self.gizmo.getPos(self.world.render) # 坐标轴的世界位置 - print(f"开始拖拽 {axis} 轴 - 目标起始位置: {self.gizmoTargetStartPos}, 坐标轴位置: {self.gizmoStartPos}, 鼠标: ({mouseX}, {mouseY})") + # 确保正在拖动的轴保持高亮状态 + if self.dragGizmoAxis and self.dragGizmoAxis in self.gizmo_colors: + # 先将所有轴恢复为正常颜色 + for axis_name in self.gizmo_colors.keys(): + if axis_name != self.dragGizmoAxis: + self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name]) + + # 然后将当前拖动的轴设置为高亮颜色 + self.setGizmoAxisColor(self.dragGizmoAxis, self.gizmo_highlight_colors[self.dragGizmoAxis]) + self.gizmoHighlightAxis = self.dragGizmoAxis + elif axis and axis in self.gizmo_colors: + for axis_name in self.gizmo_colors.keys(): + self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name]) + + self.setGizmoAxisColor(axis, self.gizmo_highlight_colors[axis]) + self.gizmoHighlightAxis = axis + + print( + f"开始拖拽 {self.dragGizmoAxis} 轴 - 目标起始位置: {self.gizmoTargetStartPos}, 坐标轴位置: {self.gizmoStartPos}, 鼠标: ({mouseX}, {mouseY})") except Exception as e: print(f"开始坐标轴拖拽失败: {str(e)}") @@ -1311,6 +1329,10 @@ class SelectionSystem: def stopGizmoDrag(self): """停止坐标轴拖拽""" print(f"停止坐标轴拖拽 - 轴: {self.dragGizmoAxis}") + if self.dragGizmoAxis and self.dragGizmoAxis in self.gizmo_colors: + self.setGizmoAxisColor(self.dragGizmoAxis, self.gizmo_colors[self.dragGizmoAxis]) + # 不要将 gizmoHighlightAxis 设置为 None,保持当前高亮轴的状态 + # self.gizmoHighlightAxis = None self.isDraggingGizmo = False self.dragGizmoAxis = None diff --git a/main.py b/main.py index 3329c223..065ef800 100644 --- a/main.py +++ b/main.py @@ -369,9 +369,6 @@ class MyWorld(CoreWorld): def updatePropertyPanel(self, item): """更新属性面板显示 - 代理到property_panel""" return self.property_panel.updatePropertyPanel(item) - - # def addAnimationPanel(self,originmodel,filepath): - # return self.property_panel._addAnimationPanel(originmodel,filepath) def updateGUIPropertyPanel(self, gui_element): """更新GUI元素属性面板 - 代理到property_panel""" @@ -380,6 +377,9 @@ class MyWorld(CoreWorld): def removeActorForModel(self,model): return self.property_panel.removeActorForModel( model) + def updateNodeVisibilityAfterDrag(self,item): + return self.property_panel.updateNodeVisibilityAfterDrag(item) + # ==================== 工具管理代理 ==================== def setCurrentTool(self, tool): diff --git a/ui/property_panel.py b/ui/property_panel.py index 8b6382a7..eb13630f 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -51,6 +51,16 @@ class PropertyPanelManager: if item.widget(): item.widget().deleteLater() + def updateNodeVisibilityAfterDrag(self, item): + """拖拽结束后更新节点的可见性状态""" + node = item.data(0, Qt.UserRole) + if not node: + return + + # 当节点被拖拽后,需要根据新父节点的状态来更新可见性 + self._syncEffectiveVisibility(node) + + def updatePropertyPanel(self, item): """更新属性面板显示""" if not self._propertyLayout or not self._propertyLayout.parent(): @@ -133,7 +143,17 @@ class PropertyPanelManager: def _syncEffectiveVisibility(self, start_node): """广度优先,确保父隐藏则子一定隐藏""" - q = deque([(start_node, True)]) # (node, parent_effective_visible) + # 获取起始节点的父节点 + parent_node = start_node.getParent() + + # 确定父节点的有效可见性 + parent_effective_visible = True + if parent_node: + parent_effective_visible = parent_node.getPythonTag("effective_visible") + if parent_effective_visible is None: + parent_effective_visible = True + + q = deque([(start_node, parent_effective_visible)]) # (node, parent_effective_visible) while q: node, parent_eff = q.popleft() @@ -143,7 +163,7 @@ class PropertyPanelManager: eff = parent_eff and user node.setPythonTag("effective_visible", eff) - #特殊处理:检查是否为碰撞体节点 + # 特殊处理:检查是否为碰撞体节点 if node.getName().startswith("modelCollision_"): node.hide() diff --git a/ui/widgets.py b/ui/widgets.py index dcd1cc76..625c4b66 100644 --- a/ui/widgets.py +++ b/ui/widgets.py @@ -339,6 +339,7 @@ class CustomTreeWidget(QTreeWidget): # 获取节点引用 dragged_node = dragged_item.data(0, Qt.UserRole) + amtarget_node = target_item.data(0, Qt.UserRole) # 如果目标是模型根节点,使用 render 作为新父节点 if target_item.text(0) == "模型": @@ -360,13 +361,17 @@ class CustomTreeWidget(QTreeWidget): # 接受拖放事件,更新树形控件 super().dropEvent(event) - + + #self.world.property_panel.updateNodeVisibilityAfterDrag(dragged_item) # 更新属性面板 self.world.updatePropertyPanel(dragged_item) self.world.property_panel._syncEffectiveVisibility(dragged_node) + + + else: event.ignore() - + def isValidParentChild(self, dragged_item, target_item): """检查是否是有效的父子关系""" # 不能拖放到自己上