From 8e8564048ef871044f74a5d57d9ecc2a025112ee Mon Sep 17 00:00:00 2001 From: Hector <2055590199@qq.com> Date: Fri, 22 Aug 2025 10:09:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9D=90=E6=A0=87=E8=BD=B4=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E6=97=8B=E8=BD=AC=EF=BC=8C=E7=BC=A9=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RenderPipelineFile/config/daytime.yaml | 2 +- core/RotationHandleFull.fbx | Bin 0 -> 56028 bytes core/RotationHandleQuarter.fbx | Bin 0 -> 41244 bytes core/UniformScaleHandle.fbx | Bin 0 -> 25504 bytes core/selection.py | 691 ++++++++++++++++++++----- core/tool_manager.py | 46 -- ui/property_panel.py | 94 +++- 7 files changed, 657 insertions(+), 176 deletions(-) create mode 100755 core/RotationHandleFull.fbx create mode 100755 core/RotationHandleQuarter.fbx create mode 100755 core/UniformScaleHandle.fbx diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index af9861ed..98f9815c 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.5000000000]]] + sun_azimuth: [[[0.5000000000,0.9555555556]]] sun_altitude: [[[0.5000000000,1.0000000000]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: diff --git a/core/RotationHandleFull.fbx b/core/RotationHandleFull.fbx new file mode 100755 index 0000000000000000000000000000000000000000..8c42b1b9ef7c216ffba940ca703e1e2f7787d121 GIT binary patch literal 56028 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 Z|LV$r!{&~;_x5Jeivo*>$_1Tj{STND;F16U literal 0 HcmV?d00001 diff --git a/core/UniformScaleHandle.fbx b/core/UniformScaleHandle.fbx new file mode 100755 index 0000000000000000000000000000000000000000..dc0bfb89d913f167483cab3f8572398f04d4f09b GIT binary patch literal 25504 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 literal 0 HcmV?d00001 diff --git a/core/selection.py b/core/selection.py index 22ff8ef5..8cd4e826 100644 --- a/core/selection.py +++ b/core/selection.py @@ -11,7 +11,7 @@ from PIL.ImageChops import lighter from panda3d.core import (Vec3, Point3, Point2, LineSegs, ColorAttrib, RenderState, DepthTestAttrib, CollisionTraverser, CollisionHandlerQueue, CollisionNode, CollisionRay, GeomNode, BitMask32, Material, LColor, DepthWriteAttrib, - TransparencyAttrib, Vec4) + TransparencyAttrib, Vec4, CollisionCapsule) from direct.task.TaskManagerGlobal import taskMgr import math @@ -38,6 +38,9 @@ class SelectionSystem: self.gizmoXAxis = None # X轴 self.gizmoYAxis = None # Y轴 self.gizmoZAxis = None # Z轴 + self.gizmoRotXAxis = None + self.gizmoRotYAxis = None + self.gizmoRotZAxis = None self.axis_length = 5.0 # 坐标轴长度(增加到5.0) # 拖拽相关状态 @@ -312,6 +315,8 @@ class SelectionSystem: self._setupGizmoRendering() + self.setupGizmoCollision() + # 现在才显示坐标轴 self.gizmo.show() @@ -329,41 +334,120 @@ class SelectionSystem: if not self.gizmo: return - model_paths = [ - "core/TranslateArrowHandle.fbx", - "EG/core/TranslateArrowHandle.fbx", - ] - arrow_model = None + 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 + + if is_scale_tool: + model_paths = [ + "core/UniformScaleHandle.fbx", + ] + elif is_rotate_tool: + model_paths = [ + "core/RotationHandleQuarter.fbx", + ] + else: + model_paths = [ + "core/TranslateArrowHandle.fbx", + ] + + # model_paths = [ + # "core/TranslateArrowHandle.fbx", + # "EG/core/TranslateArrowHandle.fbx", + # ] + gizmo_model = None + gizmoRot_model = None for path in model_paths: try: - arrow_model = self.world.loader.loadModel(path) - if arrow_model: - print(f"成功加载模型: {path}") - break + if is_rotate_tool: + gizmo_model = self.world.loader.loadModel("core/TranslateArrowHandle.fbx") + gizmoRot_model = self.world.loader.loadModel(path) + else: + gizmo_model = self.world.loader.loadModel(path) + if gizmo_model: + print(f"成功加载模型: {path}") + break except: continue + + x_rHandle = None + y_rHandle = None + z_rHandle = None + + if is_rotate_tool: + self.gizmoRotXAxis = self.gizmo.attachNewNode("gizmo_rot_x_axis") + x_rHandle = gizmoRot_model.copyTo(self.gizmoRotXAxis) + x_rHandle.setName("x_handle") + self.gizmoRotYAxis = self.gizmo.attachNewNode("gizmo_rot_y_axis") + y_rHandle = gizmoRot_model.copyTo(self.gizmoRotYAxis) + y_rHandle.setName("y_handle") + self.gizmoRotZAxis = self.gizmo.attachNewNode("gizmo_rot_z_axis") + z_rHandle = gizmoRot_model.copyTo(self.gizmoRotZAxis) + z_rHandle.setName("z_handle") + self.gizmoXAxis = self.gizmo.attachNewNode("gizmo_x_axis") - x_arrow = arrow_model.copyTo(self.gizmoXAxis) - x_arrow.setName("x_arrow") - x_arrow.setHpr(0,-90,0) - x_arrow.setScale(0.1,0.05,0.05) - x_arrow.setPos(0,0,0) + x_handle = gizmo_model.copyTo(self.gizmoXAxis) + x_handle.setName("x_handle") self.gizmoYAxis = self.gizmo.attachNewNode("gizmo_y_axis") - y_arrow = arrow_model.copyTo(self.gizmoYAxis) - y_arrow.setName("y_arrow") - y_arrow.setHpr(90,0,0) - y_arrow.setScale(0.1,0.05,0.05) - y_arrow.setPos(0,0,0) + y_handle = gizmo_model.copyTo(self.gizmoYAxis) + y_handle.setName("y_handle") - # 创建Z轴(蓝色) self.gizmoZAxis = self.gizmo.attachNewNode("gizmo_z_axis") - z_arrow = arrow_model.copyTo(self.gizmoZAxis) - z_arrow.setName("z_arrow") - # 旋转箭头使其指向Z轴正方向 - z_arrow.setHpr(0, 0, -90) # 根据需要调整旋转 - z_arrow.setScale(0.1,0.05,0.05) - z_arrow.setPos(0, 0, 0) + z_handle = gizmo_model.copyTo(self.gizmoZAxis) + z_handle.setName("z_handle") + + if is_scale_tool: + x_handle.setHpr(0,-90,0) + x_handle.setScale(0.6,0.03,0.03) + x_handle.setPos(2.2,0,0) + + y_handle.setHpr(90,0,0) + y_handle.setScale(0.6,0.03,0.03) + y_handle.setPos(0,2.2,0) + + z_handle.setHpr(0,0,-90) + z_handle.setScale(0.6,0.03,0.03) + z_handle.setPos(0,0,2.2) + elif is_rotate_tool: + x_rHandle.setHpr(0,0,90) + x_rHandle.setScale(0.025,0.0125,0.0125) + x_rHandle.setPos(0,0,0) + + y_rHandle.setHpr(0,0,0) + y_rHandle.setScale(0.025,0.0125,0.0125) + y_rHandle.setPos(0,0,0) + + z_rHandle.setHpr(-90,0,0) + z_rHandle.setScale(0.025,0.0125,0.0125) + z_rHandle.setPos(0,0,0) + + x_handle.setHpr(0, -90, 0) + x_handle.setScale(0.1, 0.05, 0.05) + x_handle.setPos(0, 0, 0) + + y_handle.setHpr(90, 0, 0) + y_handle.setScale(0.1, 0.05, 0.05) + y_handle.setPos(0, 0, 0) + + z_handle.setHpr(0, 0, -90) + z_handle.setScale(0.1, 0.05, 0.05) + z_handle.setPos(0, 0, 0) + + self.setGizmoRotAxisColor("x", self.gizmo_colors["x"]) + self.setGizmoRotAxisColor("y", self.gizmo_colors["y"]) + self.setGizmoRotAxisColor("z", self.gizmo_colors["z"]) + else: + x_handle.setHpr(0,-90,0) + x_handle.setScale(0.1,0.05,0.05) + x_handle.setPos(0,0,0) + + y_handle.setHpr(90,0,0) + y_handle.setScale(0.1,0.05,0.05) + y_handle.setPos(0,0,0) + + z_handle.setHpr(0,0,-90) + z_handle.setScale(0.1,0.05,0.05) + z_handle.setPos(0,0,0) # 设置初始颜色 self.setGizmoAxisColor("x", self.gizmo_colors["x"]) @@ -379,6 +463,7 @@ class SelectionSystem: def _setupGizmoRendering(self): try: axis_nodes = [self.gizmoXAxis,self.gizmoYAxis,self.gizmoZAxis] + axis_Rotnodes = [self.gizmoRotXAxis, self.gizmoRotYAxis, self.gizmoRotZAxis] for axis_node in axis_nodes: if axis_node: @@ -388,21 +473,45 @@ class SelectionSystem: axis_node.setFogOff() #设置渲染层级,确保大多数对象之前渲染 axis_node.setBin("fixed",30) - axis_node.setDepthWrite(False) - axis_node.setDepthTest(False) + #axis_node.setDepthWrite(False) + #axis_node.setDepthTest(True) + + for axis_rotnode in axis_Rotnodes: + if axis_rotnode: + axis_rotnode.setLightOff() + axis_rotnode.setShaderOff() + axis_rotnode.setFogOff() + axis_rotnode.setBin("fixed",30) + #axis_rotnode.setDepthWrite(False) + #axis_rotnode.setDepthTest(True) + arrow_nodes = [] if self.gizmoXAxis: - x_arrow = self.gizmoXAxis.find("x_arrow") - if x_arrow: - arrow_nodes.append(x_arrow) + x_handle = self.gizmoXAxis.find("x_handle") + if x_handle: + arrow_nodes.append(x_handle) if self.gizmoYAxis: - y_arrow = self.gizmoYAxis.find("y_arrow") - if y_arrow: - arrow_nodes.append(y_arrow) + y_handle = self.gizmoYAxis.find("y_handle") + if y_handle: + arrow_nodes.append(y_handle) if self.gizmoZAxis: - z_arrow = self.gizmoZAxis.find("z_arrow") - if z_arrow: - arrow_nodes.append(z_arrow) + z_handle = self.gizmoZAxis.find("z_handle") + if z_handle: + arrow_nodes.append(z_handle) + + rot_nodes = [] + if self.gizmoRotXAxis: + x_rHandle = self.gizmoRotXAxis.find("x_handle") + if x_rHandle: + rot_nodes.append(x_rHandle) + if self.gizmoRotYAxis: + y_rHandle = self.gizmoRotYAxis.find("y_handle") + if y_rHandle: + rot_nodes.append(y_rHandle) + if self.gizmoRotZAxis: + z_rHandle = self.gizmoRotZAxis.find("z_handle") + if z_rHandle: + rot_nodes.append(z_rHandle) for arrow_node in arrow_nodes: if arrow_node: @@ -410,17 +519,30 @@ class SelectionSystem: arrow_node.setShaderOff() arrow_node.setFogOff() arrow_node.setBin("fixed",31) - arrow_node.setDepthWrite(False) - arrow_node.setDepthTest(False) + #arrow_node.setDepthWrite(False) + #arrow_node.setDepthTest(False) #启用透明度S arrow_node.setTransparency(TransparencyAttrib.MAlpha) + + for rot_node in rot_nodes: + if rot_node: + rot_node.setLightOff() + rot_node.setShaderOff() + rot_node.setFogOff() + rot_node.setBin("fixed",31) + #rot_node.setDepthWrite(False) + #rot_node.setDepthTest(False) + #启用透明度S + rot_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) + # self.gizmo.setDepthWrite(False) + #self.gizmo.setDepthTest(False) + except Exception as e: print(f"设置坐标轴渲染属性失败: {str(e)}") @@ -448,12 +570,49 @@ class SelectionSystem: self.clearGizmo() return task.done + 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 + was_scale_tool = getattr(self,'_last_tool_scale_state',False) + was_rotate_tool =getattr(self,'_last_tool_rotate_state',False) + + tool_changed = (is_scale_tool!=was_scale_tool) or (is_rotate_tool != was_rotate_tool) + + if tool_changed: + self._last_tool_scale_state = is_scale_tool + self._last_tool_rotate_state = is_rotate_tool + + if self.gizmoXAxis: + self.gizmoXAxis.removeNode() + self.gizmoXAxis = None + if self.gizmoYAxis: + self.gizmoYAxis.removeNode() + self.gizmoYAxis = None + if self.gizmoZAxis: + self.gizmoZAxis.removeNode() + self.gizmoZAxis = None + if self.gizmoRotXAxis: + self.gizmoRotXAxis.removeNode() + self.gizmoRotXAxis = None + if self.gizmoRotYAxis: + self.gizmoRotYAxis.removeNode() + self.gizmoRotYAxis = None + if self.gizmoRotZAxis: + self.gizmoRotZAxis.removeNode() + self.gizmoRotZAxis = None + + self.createGizmoGeometry() + + self.setGizmoAxisColor("x",self.gizmo_colors["x"]) + self.setGizmoAxisColor("y",self.gizmo_colors["y"]) + self.setGizmoAxisColor("z",self.gizmo_colors["z"]) + + self.setupGizmoCollision() + light_object = self.gizmoTarget.getPythonTag("rp_light_object") if light_object: light_pos = light_object.pos self.gizmo.setPos(light_object.pos) self.gizmoTarget.setPos(light_pos) - else: # 只在必要时更新位置和朝向 self._updateGizmoPositionAndOrientation() @@ -486,13 +645,25 @@ class SelectionSystem: self.gizmo.setPos(center) self._last_gizmo_bounds_update = current_time - # 更新朝向 - parent_node = self.gizmoTarget.getParent() - if parent_node and parent_node != self.world.render: - parent_hpr = parent_node.getHpr() - self.gizmo.setHpr(parent_hpr) + is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False + + if is_scale_tool: + self.gizmo.setHpr(self.gizmoTarget.getHpr()) else: - self.gizmo.setHpr(0, 0, 0) + 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) + + # 更新朝向 + # 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) def _updateGizmoScreenSize(self): """动态调整坐标轴大小,保持固定的屏幕大小""" @@ -589,6 +760,97 @@ class SelectionSystem: # except: # pass + + def setGizmoRotAxisColor(self, axis, color): + """使用材质设置坐标轴颜色 - RenderPipeline兼容版本""" + try: + from panda3d.core import Material, Vec4,ColorWriteAttrib,DepthWriteAttrib,DepthTestAttrib,TransparencyAttrib + + # 获取对应的轴节点 + axis_nodes = { + "x": self.gizmoRotXAxis, + "y": self.gizmoRotYAxis, + "z": self.gizmoRotZAxis + } + + if axis not in axis_nodes or not axis_nodes[axis]: + return + + axis_node = axis_nodes[axis] + + handle_node = None + handle_node = axis_node.find("x_handle") if axis == "x" else handle_node + handle_node = axis_node.find("y_handle") if axis == "y" else handle_node + handle_node = axis_node.find("z_handle") if axis == "z" else handle_node + + #如果找不到特定名称的节点,尝试查找任何子节点 + if not handle_node: + children = axis_node.getChildren() + if children.getNumPath()>0: + handle_node = children[0] + + if not handle_node: + print(f"未找到{axis}轴的处理模型") + return + + # 创建或获取材质 + mat = Material() + + # 设置材质属性 - 使用自发光确保在RenderPipeline下可见 + mat.setBaseColor(Vec4(color[0], color[1], color[2], color[3])) + mat.setDiffuse(Vec4(0, 0, 0, 1)) + #mat.setEmission(Vec4(color[0], color[1], color[2], 1.0)) # 自发光 + mat.setEmission(Vec4(1,1,1,1.0)) # 自发光 + mat.set_roughness(1) + + # 应用材质 + handle_node.setMaterial(mat, 1) + + + # 设置透明度 + if color[3] < 1.0: + handle_node.setTransparency(TransparencyAttrib.MAlpha) + else: + handle_node.setTransparency(TransparencyAttrib.MNone) + + handle_node.setLightOff() # 禁用光照影响 + handle_node.setShaderOff() # 禁用着色器 + handle_node.setFogOff() # 禁用雾效果 + + handle_node.setBin("fixed",31) + #arrow_node.setDepthWrite(False) + #arrow_node.setDepthTest(True) + + # 保存材质引用以便后续修改 + if axis == "x": + self.xMat = mat + elif axis == "y": + self.yMat = mat + elif axis == "z": + self.zMat = mat + + axis_node.setLightOff() + axis_node.setShaderOff() + axis_node.setFogOff() + axis_node.setBin("fixed", 30) + axis_node.setDepthWrite(False) + axis_node.setDepthTest(True) + + except Exception as e: + print(f"设置坐标轴颜色失败: {str(e)}") + # 回退到简单颜色设置 + try: + axis_nodes = { + "x": self.gizmoXAxis, + "y": self.gizmoYAxis, + "z": self.gizmoZAxis + } + + if axis in axis_nodes and axis_nodes[axis]: + axis_nodes[axis].setColor(color[0], color[1], color[2], color[3]) + except: + pass + def setGizmoAxisColor(self, axis, color): """使用材质设置坐标轴颜色 - RenderPipeline兼容版本""" try: @@ -606,17 +868,19 @@ class SelectionSystem: axis_node = axis_nodes[axis] - # 查找箭头模型节点 - arrow_node = None - if axis == "x": - arrow_node = axis_node.find("x_arrow") - elif axis == "y": - arrow_node = axis_node.find("y_arrow") - elif axis == "z": - arrow_node = axis_node.find("z_arrow") + handle_node = None + handle_node = axis_node.find("x_handle") if axis == "x" else handle_node + handle_node = axis_node.find("y_handle") if axis == "y" else handle_node + handle_node = axis_node.find("z_handle") if axis == "z" else handle_node - if not arrow_node: - print(f"未找到{axis}轴的箭头模型") + #如果找不到特定名称的节点,尝试查找任何子节点 + if not handle_node: + children = axis_node.getChildren() + if children.getNumPath()>0: + handle_node = children[0] + + if not handle_node: + print(f"未找到{axis}轴的处理模型") return # 创建或获取材质 @@ -630,20 +894,20 @@ class SelectionSystem: mat.set_roughness(1) # 应用材质 - arrow_node.setMaterial(mat, 1) + handle_node.setMaterial(mat, 1) # 设置透明度 if color[3] < 1.0: - arrow_node.setTransparency(TransparencyAttrib.MAlpha) + handle_node.setTransparency(TransparencyAttrib.MAlpha) else: - arrow_node.setTransparency(TransparencyAttrib.MNone) + handle_node.setTransparency(TransparencyAttrib.MNone) - arrow_node.setLightOff() # 禁用光照影响 - arrow_node.setShaderOff() # 禁用着色器 - arrow_node.setFogOff() # 禁用雾效果 + handle_node.setLightOff() # 禁用光照影响 + handle_node.setShaderOff() # 禁用着色器 + handle_node.setFogOff() # 禁用雾效果 - arrow_node.setBin("fixed",31) + handle_node.setBin("fixed",31) #arrow_node.setDepthWrite(False) #arrow_node.setDepthTest(True) @@ -1019,8 +1283,9 @@ class SelectionSystem: if not self.gizmo or self.isDraggingGizmo: return - # 使用统一的检测方法 - hoveredAxis = self.detectGizmoAxisAtMouse(mouseX, mouseY) + # 使用碰撞检测方法 + hoveredAxis = self.detectGizmoAxisWithCollision(mouseX, mouseY) + #hoveredAxis = self.detectGizmoAxisAtMouse(mouseX, mouseY) # 简化稳定性检测逻辑 if not hasattr(self, '_last_detected_axis'): @@ -1037,6 +1302,11 @@ class SelectionSystem: # 高亮新的轴 if hoveredAxis: self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis]) + else: + # 如果没有悬停在任何轴上,确保所有轴都恢复原始颜色 + for axis_name in ["x", "y", "z"]: + if axis_name != self.dragGizmoAxis: # 不要改变正在拖拽的轴的颜色 + self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name]) self.gizmoHighlightAxis = hoveredAxis @@ -1074,17 +1344,25 @@ class SelectionSystem: return self.isDraggingGizmo = True - # 使用当前高亮的轴,如果有的话 + + # 使用当前高亮的轴,如果有的话;否则使用传入的轴 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) # 坐标轴的世界位置 + # 添加对缩放的支持:保存初始缩放值 + if self.world.tool_manager.isScaleTool(): + self.gizmoTargetStartScale = self.gizmoTarget.getScale() + elif self.world.tool_manager.isRotateTool(): + self.gizmoTargetStartHpr = self.gizmoTarget.getHpr() + # 确保正在拖动的轴保持高亮状态 if self.dragGizmoAxis and self.dragGizmoAxis in self.gizmo_colors: # 先将所有轴恢复为正常颜色 @@ -1094,13 +1372,15 @@ class SelectionSystem: # 然后将当前拖动的轴设置为高亮颜色 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]) + if axis_name != axis: + self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name]) self.setGizmoAxisColor(axis, self.gizmo_highlight_colors[axis]) - self.gizmoHighlightAxis = axis + self.dragGizmoAxis = axis + + self.gizmoHighlightAxis = self.dragGizmoAxis print( f"开始拖拽 {self.dragGizmoAxis} 轴 - 目标起始位置: {self.gizmoTargetStartPos}, 坐标轴位置: {self.gizmoStartPos}, 鼠标: ({mouseX}, {mouseY})") @@ -1130,10 +1410,55 @@ class SelectionSystem: print("拖拽更新失败: 没有坐标轴起始位置") return + is_scale_tool = self.world.tool_manager.isScaleTool() + is_rotate_tool = self.world.tool_manager.isRotateTool() + # 计算鼠标移动距离(屏幕像素) mouseDeltaX = mouseX - self.dragStartMousePos[0] mouseDeltaY = mouseY - self.dragStartMousePos[1] + if is_scale_tool: + scale_factor = 1.0 + (mouseDeltaX + mouseDeltaY) * 0.01 + start_scale = getattr(self,'gizmoTargetStartScale',Vec3(1,1,1)) + + target_hpr = self.gizmoTarget.getHpr() + + 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 * scale_factor, + start_scale.z * scale_factor) + #应用新缩放值 + self.gizmoTarget.setScale(new_scale) + #实时更新属性面板 + 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) + self.world.property_panel.refreshModelValues(self.gizmoTarget) + return + # 使用坐标轴的实际位置而不是目标节点位置来计算屏幕投影 gizmo_world_pos = self.gizmoStartPos @@ -1184,21 +1509,6 @@ class SelectionSystem: axis_start_screen = worldToScreen(gizmo_world_pos) axis_end_world = gizmo_world_pos + world_axis_vector axis_end_screen = worldToScreen(axis_end_world) - #gizmo_screen = worldToScreen(gizmo_world_pos) - #axis_screen = worldToScreen(axis_end) - - # if not gizmo_screen: - # print("拖拽更新失败: 坐标轴中心不在屏幕内") - # return - # if not axis_screen: - # print("拖拽更新失败: 坐标轴端点不在屏幕内") - # return - # - # # 计算轴在屏幕空间的方向向量 - # screen_axis_dir = ( - # axis_screen[0] - gizmo_screen[0], - # axis_screen[1] - gizmo_screen[1] - # ) if not axis_start_screen or not axis_end_screen: print("拖拽更新失败: 无法获取轴线屏幕坐标") @@ -1209,7 +1519,6 @@ class SelectionSystem: axis_end_screen[1] - axis_start_screen[1] ) - # 归一化屏幕轴方向 import math length = math.sqrt(screen_axis_dir[0]**2 + screen_axis_dir[1]**2) @@ -1253,40 +1562,16 @@ class SelectionSystem: currentPos = self.gizmoTargetStartPos - # 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 - # - # - # fixed_pixel_to_world_ratio = 0.01 # 1像素 = 0.01世界单位 - # scale_factor = fixed_pixel_to_world_ratio * scale_adjustment - # - # movement_distance = projected_distance * 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}") + #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}") + #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}") + #print(f"Z轴移动:{currentPos.z} -> {newPos.z}") else: print(f"未知轴: {self.dragGizmoAxis}") return @@ -1329,10 +1614,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 + + # 恢复所有轴的颜色 + for axis_name in ["x", "y", "z"]: + self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name]) self.isDraggingGizmo = False self.dragGizmoAxis = None @@ -1341,6 +1626,13 @@ class SelectionSystem: self.gizmoTargetStartPos = None self.gizmoStartPos = None + if hasattr(self, 'gizmoTargetStartScale'): + delattr(self, 'gizmoTargetStartScale') + if hasattr(self, 'gizmoTargetStartHpr'): + delattr(self, 'gizmoTargetStartHpr') + + # 重置高亮轴 + self.gizmoHighlightAxis = None # ==================== 选择管理 ==================== def updateSelection(self, nodePath): @@ -1393,3 +1685,164 @@ class SelectionSystem: if self.selectionBoxTarget and self.selectionBoxTarget.isEmpty(): self.clearSelectionBox() + + def setupGizmoCollision(self): + if not self.gizmo or not self.gizmoXAxis or not self.gizmoYAxis or not self.gizmoZAxis: + return + + # 清除现有的碰撞节点 + for axis_name in ["x", "y", "z"]: + axis_node = getattr(self, f"gizmo{axis_name.upper()}Axis") + if axis_node: + # 查找并移除所有现有的碰撞节点 + collision_nodes = axis_node.findAllMatches("**/gizmo_collision_*") + for collision_node in collision_nodes: + collision_node.removeNode() + + # 为每个轴创建碰撞体 + self.createAxisCollision("x", self.gizmoXAxis) + self.createAxisCollision("y", self.gizmoYAxis) + self.createAxisCollision("z", self.gizmoZAxis) + + def createAxisCollision(self, axis_name, axis_node): + # 为单个轴创建碰撞体 + try: + handle_node = axis_node.find(f"{axis_name}_handle") + if not handle_node or handle_node.isEmpty(): + children = axis_node.getChildren() + if children.getNumPaths() > 0: + handle_node = children[0] + else: + print(f"警告: 未找到 {axis_name} 轴的 handle 节点") + return + + collision_node = CollisionNode(f"gizmo_collision_{axis_name}") + collision_node.setIntoCollideMask(BitMask32.bit(1)) # 设置为into对象 + collision_node.setFromCollideMask(BitMask32.allOff()) # 不作为from对象 + + # 调整碰撞尺寸以匹配实际的轴长度和坐标轴缩放 + scale_factor = self.gizmo.getScale().x if self.gizmo else 1.0 + axis_length = 2.0 * scale_factor + radius = 0.3 * scale_factor + + # 根据轴的类型创建合适的碰撞体 + if axis_name == "x": + capsule = CollisionCapsule( + Point3(0, 0, 0), + Point3(axis_length, 0, 0), + radius + ) + collision_node.addSolid(capsule) + elif axis_name == "y": + capsule = CollisionCapsule( + Point3(0, 0, 0), + Point3(0, axis_length, 0), + radius + ) + collision_node.addSolid(capsule) + elif axis_name == "z": + capsule = CollisionCapsule( + Point3(0, 0, 0), + Point3(0, 0, axis_length), + radius + ) + collision_node.addSolid(capsule) + + # 将碰撞节点附加到handle节点,使其与可视化几何体保持一致 + collision_np = handle_node.attachNewNode(collision_node) + + # 设置标签以便识别 + collision_np.setTag("gizmo_axis", axis_name) + collision_np.setTag("pickable", "1") + + collision_np.hide() # 隐藏碰撞体,只用于检测 + + print(f"✓ 成功创建 {axis_name} 轴碰撞体") + + except Exception as e: + print(f"创建{axis_name}轴碰撞体失败: {e}") + import traceback + traceback.print_exc() + + def detectGizmoAxisWithCollision(self, mouseX, mouseY): + # 使用碰撞体检测鼠标是否悬停在坐标轴上 + if not self.gizmo: + return None + + try: + ray = CollisionRay() + + win_width, win_height = self.world.getWindowSize() + + mouse_x_ndc = (mouseX / win_width) * 2.0 - 1.0 + mouse_y_ndc = 1.0 - (mouseY / win_height) * 2.0 + + ray.setFromLens(self.world.cam.node(), mouse_x_ndc, mouse_y_ndc) + + traverser = CollisionTraverser("gizmo_traverser") + handler = CollisionHandlerQueue() + + # 创建射线节点 + ray_node = CollisionNode('mouseRay') + ray_node.addSolid(ray) + ray_node.setFromCollideMask(BitMask32.bit(1)) # 射线作为from对象 + ray_node.setIntoCollideMask(BitMask32.allOff()) # 射线不作为into对象 + ray_np = self.world.render.attachNewNode(ray_node) + + # 为所有轴的碰撞体设置正确的掩码并添加到遍历器 + collision_found = False + for axis_name in ["x", "y", "z"]: + axis_node = getattr(self, f"gizmo{axis_name.upper()}Axis") + if axis_node: + collision_node_path = axis_node.find("**/gizmo_collision_*") + if not collision_node_path.isEmpty(): + collision_node = collision_node_path.node() + collision_node.setFromCollideMask(BitMask32.allOff()) # 碰撞体不作为from对象 + collision_node.setIntoCollideMask(BitMask32.bit(1)) # 碰撞体作为into对象 + collision_found = True + + if not collision_found: + ray_np.removeNode() + return None + + # 执行碰撞检测 - 这里是关键修复点 + traverser.addCollider(ray_np, handler) + traverser.traverse(self.world.render) + + ray_np.removeNode() + + # 检查是否有碰撞 + if handler.getNumEntries() > 0: + handler.sortEntries() + closest_entry = handler.getEntry(0) + + # 获取碰撞的对象 + collided_object = closest_entry.getIntoNodePath() + axis_tag = collided_object.getTag("gizmo_axis") + + if axis_tag in ["x", "y", "z"]: + return axis_tag + + return None + + except Exception as e: + print(f"使用碰撞体检测坐标轴失败: {e}") + import traceback + traceback.print_exc() + return None + + def debugGizmoCollision(self): + print("===碰撞体调试信息===") + for axis_name in ["x","y","z"]: + axis_node = getattr(self,f"gizmo{axis_name.upper()}Axis") + if axis_node: + handle_node = axis_node.find(f"{axis_name}_handle") + collision_node = axis_node.find("**/gizmo_collision_*") + print(f"{axis_name.upper()}轴:") + print(f" - 轴节点: {axis_node}") + print(f" - Handle节点: {handle_node}") + print(f" - 碰撞节点: {collision_node}") + if not collision_node.isEmpty(): + print(f" - 碰撞体标签: {collision_node.getTag('gizmo_axis')}") + else: + print(f"{axis_name.upper()}轴节点不存在") \ No newline at end of file diff --git a/core/tool_manager.py b/core/tool_manager.py index c9344f69..4f5f4cdf 100644 --- a/core/tool_manager.py +++ b/core/tool_manager.py @@ -150,49 +150,3 @@ class ToolManager: except Exception as e: print(f"❌ 启动插件配置器失败: {e}") return False - - def cleanup_processes(self): - """清理所有启动的进程""" - try: - # 清理插件配置器进程 - if hasattr(self, '_plugin_configurator_process') and self._plugin_configurator_process: - if self._plugin_configurator_process.poll() is None: - print("🔄 正在关闭插件配置器...") - self._plugin_configurator_process.terminate() - try: - # 等待进程结束,最多等待5秒 - self._plugin_configurator_process.wait(timeout=5) - print("✅ 插件配置器已正常关闭") - except subprocess.TimeoutExpired: - print("⚠️ 插件配置器未响应,强制关闭...") - self._plugin_configurator_process.kill() - self._plugin_configurator_process.wait() - print("✅ 插件配置器已强制关闭") - self._plugin_configurator_process = None - - # 清理材质编辑器进程(如果存在) - if hasattr(self, '_material_editor_process') and self._material_editor_process: - if self._material_editor_process.poll() is None: - print("🔄 正在关闭材质编辑器...") - self._material_editor_process.terminate() - try: - self._material_editor_process.wait(timeout=5) - print("✅ 材质编辑器已正常关闭") - except subprocess.TimeoutExpired: - print("⚠️ 材质编辑器未响应,强制关闭...") - self._material_editor_process.kill() - self._material_editor_process.wait() - print("✅ 材质编辑器已强制关闭") - self._material_editor_process = None - - except Exception as e: - print(f"⚠️ 清理进程时出错: {e}") - - def get_plugin_configurator_status(self): - """获取插件配置器的运行状态""" - if hasattr(self, '_plugin_configurator_process') and self._plugin_configurator_process: - if self._plugin_configurator_process.poll() is None: - return "运行中" - else: - return "已停止" - return "未启动" diff --git a/ui/property_panel.py b/ui/property_panel.py index d2c51790..0421e5b3 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -395,19 +395,27 @@ class PropertyPanelManager: self.scale_y = QDoubleSpinBox() self.scale_z = QDoubleSpinBox() - # 设置缩放控件属性 - for scale_widget in [self.scale_x, self.scale_y, self.scale_z]: - scale_widget.setRange(0.01, 100) - scale_widget.setSingleStep(0.1) + current_scale = model.getScale() - self.scale_x.setValue(model.getScale().getX()) - self.scale_y.setValue(model.getScale().getY()) - self.scale_z.setValue(model.getScale().getZ()) + # 设置缩放控件属性 + for i, (scale_widget, scale_value) in enumerate(zip([self.scale_x, self.scale_y, self.scale_z], + [current_scale.getX(), current_scale.getY(), + current_scale.getZ()])): + scale_widget.setRange(-1000, 1000) + scale_widget.setSingleStep(0.1) + # 如果缩放值为0,设置为一个很小的非零值 + if scale_value == 0: + scale_value = 0.01 if scale_value >= 0 else -0.01 + scale_widget.setValue(scale_value) + + self.scale_x.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_x, value)) + self.scale_y.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_y, value)) + self.scale_z.valueChanged.connect(lambda value: self._onScaleValueChanged(self.scale_z, value)) # 连接缩放变化事件 - self.scale_x.valueChanged.connect(lambda v: model.setScale(v, model.getScale().getY(), model.getScale().getZ())) - self.scale_y.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), v, model.getScale().getZ())) - self.scale_z.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), model.getScale().getY(), v)) + self.scale_x.valueChanged.connect(lambda value: self._updateXScale(model, value)) + self.scale_y.valueChanged.connect(lambda value: self._updateYScale(model, value)) + self.scale_z.valueChanged.connect(lambda value: self._updateZScale(model, value)) # 创建并设置 X, Y, Z 标签居中 x_label3 = QLabel("X") @@ -434,6 +442,72 @@ class PropertyPanelManager: # 材质属性组 self._updateModelMaterialPanel(model) + def _onScaleValueChanged(self, scale_widget, value): + """确保缩放值不为0""" + if value == 0: + # 设置为一个很小的非零值,保持原有符号 + if hasattr(scale_widget, 'value') and scale_widget.value() > 0: + scale_widget.setValue(0.01) + else: + scale_widget.setValue(-0.01) + + def _updateXScale(self, model, value): + """更新X轴缩放值""" + # 确保值不为0 + if value == 0: + sender = None + # 通过遍历找到发出信号的控件 + for widget in [self.scale_x, self.scale_y, self.scale_z]: + if widget.value() == value: + sender = widget + break + if sender: + self._onScaleValueChanged(sender, value) + return + + # 更新模型的X轴缩放 + current_scale = model.getScale() + model.setScale(value, current_scale.getY(), current_scale.getZ()) + self.refreshModelValues(model) + + def _updateYScale(self, model, value): + """更新Y轴缩放值""" + # 确保值不为0 + if value == 0: + sender = None + # 通过遍历找到发出信号的控件 + for widget in [self.scale_x, self.scale_y, self.scale_z]: + if widget.value() == value: + sender = widget + break + if sender: + self._onScaleValueChanged(sender, value) + return + + # 更新模型的Y轴缩放 + current_scale = model.getScale() + model.setScale(current_scale.getX(), value, current_scale.getZ()) + self.refreshModelValues(model) + + def _updateZScale(self, model, value): + """更新Z轴缩放值""" + # 确保值不为0 + if value == 0: + sender = None + # 通过遍历找到发出信号的控件 + for widget in [self.scale_x, self.scale_y, self.scale_z]: + if widget.value() == value: + sender = widget + break + if sender: + self._onScaleValueChanged(sender, value) + return + + # 更新模型的Z轴缩放 + current_scale = model.getScale() + model.setScale(current_scale.getX(), current_scale.getY(), value) + self.refreshModelValues(model) + def refreshModelValues(self,nodePath): if not nodePath or self._propertyLayout is None: return