diff --git a/core/TranslateArrowHandle.fbx b/core/TranslateArrowHandle.fbx new file mode 100755 index 00000000..29725650 Binary files /dev/null and b/core/TranslateArrowHandle.fbx differ diff --git a/core/selection.py b/core/selection.py index 5d28830b..ecbbe5c4 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) + TransparencyAttrib, Vec4) from direct.task.TaskManagerGlobal import taskMgr import math @@ -50,14 +50,14 @@ class SelectionSystem: # 高亮相关 self.gizmoHighlightAxis = None self.gizmo_colors = { - "x": (1*10, 0, 0, 1), # 红色 - "y": (0, 1*10, 0, 1), # 绿色 - "z": (0, 0, 1*10, 1) # 蓝色 + "x": (1, 0, 0, 0), # 红色 + "y": (0, 1, 0, 0), # 绿色 + "z": (0, 0, 1, 0) # 蓝色 } self.gizmo_highlight_colors = { - "x": (1.5*20, 1.5*20, 0, 1), # 黄色高亮 - "y": (1.5*20, 1.5*20, 0, 1), # 黄色高亮 - "z": (1.5*20, 1.5*20, 0, 1) # 黄色高亮 + "x": (1, 1, 0, 0), # 黄色高亮 + "y": (1, 1, 0, 0), # 黄色高亮 + "z": (1, 1, 0, 0) # 黄色高亮 } print("✓ 选择和变换系统初始化完成") @@ -303,7 +303,7 @@ class SelectionSystem: # 只调用一次几何体创建 self.createGizmoGeometry() - # 只调用一次颜色设置 + #只调用一次颜色设置 self.setGizmoAxisColor("x", self.gizmo_colors["x"]) self.setGizmoAxisColor("y", self.gizmo_colors["y"]) self.setGizmoAxisColor("z", self.gizmo_colors["z"]) @@ -322,321 +322,79 @@ class SelectionSystem: if not self.gizmo: return - # 创建X轴(红色) - x_lines = LineSegs("x_axis") - x_lines.setThickness(6.0) - x_lines.moveTo(0, 0, 0) - x_lines.drawTo(self.axis_length, 0, 0) - # 创建X轴箭头 - x_lines.moveTo(self.axis_length - 0.5, -0.2, 0) - x_lines.drawTo(self.axis_length, 0, 0) - x_lines.drawTo(self.axis_length - 0.5, 0.2, 0) - x_geom = x_lines.create() - self.gizmoXAxis = self.gizmo.attachNewNode(x_geom) - self.gizmoXAxis.setName("gizmo_x_axis") - #self.gizmoXAxis.setLightOff() - # 创建Y轴(绿色) - y_lines = LineSegs("y_axis") - y_lines.setThickness(6.0) - y_lines.moveTo(0, 0, 0) - y_lines.drawTo(0, self.axis_length, 0) - # 创建Y轴箭头 - y_lines.moveTo(-0.2, self.axis_length - 0.5, 0) - y_lines.drawTo(0, self.axis_length, 0) - y_lines.drawTo(0.2, self.axis_length - 0.5, 0) - y_geom = y_lines.create() - self.gizmoYAxis = self.gizmo.attachNewNode(y_geom) - self.gizmoYAxis.setName("gizmo_y_axis") - #self.gizmoYAxis.setLightOff() + model_paths = [ + "core/TranslateArrowHandle.fbx", + "EG/core/TranslateArrowHandle.fbx", + ] + arrow_model = None + for path in model_paths: + try: + arrow_model = self.world.loader.loadModel(path) + if arrow_model: + print(f"成功加载模型: {path}") + break + except: + continue + 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) + + 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) # 创建Z轴(蓝色) - z_lines = LineSegs("z_axis") - z_lines.setThickness(6.0) - z_lines.moveTo(0, 0, 0) - z_lines.drawTo(0, 0, self.axis_length) - # 创建Z轴箭头 - z_lines.moveTo(-0.2, 0, self.axis_length - 0.5) - z_lines.drawTo(0, 0, self.axis_length) - z_lines.drawTo(0.2, 0, self.axis_length - 0.5) - z_geom = z_lines.create() - self.gizmoZAxis = self.gizmo.attachNewNode(z_geom) - self.gizmoZAxis.setName("gizmo_z_axis") - #self.gizmoZAxis.setLightOff() + 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) - # 确保坐标轴不被光照影响 - #self.gizmo.setLightOff() - - # 使用最强的渲染设置,确保坐标轴绝对不会被遮挡 - self.gizmo.setBin("gui-popup", 0) # 使用最高的GUI渲染层 - self.gizmo.setDepthTest(False) # 完全禁用深度测试 - self.gizmo.setDepthWrite(False) # 禁用深度写入 - self.gizmo.setTwoSided(True) # 双面渲染 - - # 创建强制前景渲染状态 - from panda3d.core import RenderModeAttrib, TransparencyAttrib - foreground_state = RenderState.make( - DepthTestAttrib.make(DepthTestAttrib.MNone), # 完全不进行深度测试 - TransparencyAttrib.make(TransparencyAttrib.MAlpha) # 启用透明度混合 - ) - #self.gizmo.setState(foreground_state) - - # 对每个坐标轴设置独立的最高渲染优先级 - self.gizmoXAxis.setBin("gui-popup", 10) - self.gizmoXAxis.setDepthTest(False) - self.gizmoXAxis.setDepthWrite(False) - self.gizmoXAxis.setLightOff() - self.gizmoXAxis.setState(foreground_state) - - self.gizmoYAxis.setBin("gui-popup", 20) - self.gizmoYAxis.setDepthTest(False) - self.gizmoYAxis.setDepthWrite(False) - self.gizmoYAxis.setLightOff() - self.gizmoYAxis.setState(foreground_state) - - self.gizmoZAxis.setBin("gui-popup", 30) - self.gizmoZAxis.setDepthTest(False) - self.gizmoZAxis.setDepthWrite(False) - self.gizmoZAxis.setLightOff() - self.gizmoZAxis.setState(foreground_state) - - - - # 初始化高亮状态 - self.gizmoHighlightAxis = None - - # 立即设置初始颜色,确保创建时就有正确的颜色 - self.setGizmoAxisColor("z", self.gizmo_colors["z"]) + # 设置初始颜色 self.setGizmoAxisColor("x", self.gizmo_colors["x"]) 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) # 双面渲染 - print(f"✓ 坐标轴几何体创建完成,长度={self.axis_length}") - - # 为 RenderPipeline 环境设置正确的渲染状态 - self._setupRenderPipelineCompatibleGizmo() - - self._setupEmissiveMaterials() - self._setupGizmoRendering() except Exception as e: print(f"创建坐标轴几何体失败: {str(e)}") - def _setupEmissiveMaterials(self): - try: - from panda3d.core import Material,Vec4 - materials ={ - "x":(Vec4(1,0,0,1),Vec4(2.0,0,0,1)), - "y":(Vec4(0,1,0,1),Vec4(0,2.0,0,1)), - "z":(Vec4(0,0,1,1),Vec4(0,0,2.0,1)) - } - axis_nodes ={ - "x":self.gizmoXAxis, - "y":self.gizmoYAxis, - "z":self.gizmoZAxis - } - for axis,(base_color,emission_color) in materials.items(): - if axis_nodes[axis]: - material=Material(f"gizmo_{axis}_material") - material.setBaseColor(base_color) - material.setEmission(emission_color) - material.setRoughness(1.0) - material.setMetallic(0.0) - axis_nodes[axis].setMaterial(material) - 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: - # 设置渲染优先级,确保在最前面显示 - self.gizmo.setBin("gui-popup", 1000) - self.gizmo.setDepthTest(False) - self.gizmo.setDepthWrite(False) - self.gizmo.setLightOff() + # 走能直接写到最终画面的阶段(两种方式二选一): + # 方式1:完全绕过管线(最稳) + node.setTag("pipeline-disable", "1") + # 方式2:留在管线内但走最终阶段(若不想禁用管线,可改用下面这一行,并去掉上一行) + # node.setTag("pipeline-stage", "final") - # 为每个轴设置独立的渲染属性 - for i, axis_node in enumerate([self.gizmoXAxis, self.gizmoYAxis, self.gizmoZAxis]): - if axis_node: - axis_node.setBin("gui-popup", 1001 + i) - axis_node.setDepthTest(False) - axis_node.setDepthWrite(False) - axis_node.setLightOff() - - except Exception as e: - print(f"设置坐标轴渲染失败!!!!!!: {str(e)}") - - def _setupRenderPipelineCompatibleGizmo(self): - """为 RenderPipeline 环境设置兼容的坐标轴渲染 - 激进修复版本""" - try: - from panda3d.core import (ShaderAttrib, MaterialAttrib, RenderModeAttrib, - AntialiasAttrib, TransparencyAttrib, CullFaceAttrib, - RescaleNormalAttrib, TextureAttrib) - - # 第一步:完全隔离坐标轴,避免被任何系统处理 - self._isolateGizmoFromRenderPipeline() - - # 第二步:使用最简单的渲染方式 - self._setupMinimalGizmoRendering() - - # 第三步:强制设置每个轴的独立渲染 - self._forceAxisIndependentRendering() - - - except Exception as e: - # 使用最后的备用方案 - self._setupUltimateGizmoFallback() - - def _isolateGizmoFromRenderPipeline(self): - """完全隔离坐标轴,避免被 RenderPipeline 处理""" - try: - # 设置所有可能的隔离标签 - isolation_tags = [ - ("no_shadow", "1"), - ("no_lighting", "1"), - ("no_material", "1"), - ("no_shader", "1"), - ("no_fog", "1"), - ("no_texture", "1"), - ("gui_element", "1"), - ("bypass_rp", "1"), - ("fixed_pipeline", "1"), - ("ignore_all", "1") - ] - - for tag, value in isolation_tags: - self.gizmo.setTag(tag, value) - - # 完全禁用所有高级功能,使用最高优先级 - self.gizmo.setShaderOff(10000) - self.gizmo.setLightOff(10000) - self.gizmo.setFogOff(10000) - self.gizmo.setMaterialOff(10000) - self.gizmo.setTextureOff(10000) - - # 禁用所有可能的渲染特性 - from panda3d.core import RenderModeAttrib, CullFaceAttrib - # self.gizmo.setRenderModeWireframe() - # self.gizmo.setTwoSided(True) - self.gizmo.setRenderMode(RenderModeAttrib.MFilled) - self.gizmo.setTwoSided(True) - - self.gizmo.setColorScale(2.0,2.0,2.0,1.0) - for axis_node in [self.gizmoXAxis,self.gizmoYAxis,self.gizmoZAxis]: - if axis_node: - axis_node.setTag("emissive","1") - axis_node.setTag("unlit","1") - axis_node.setColorScale(2.0,2.0,2.0,1.0) - - except Exception as e: - print(f" ❌ 隔离失败: {e}") - - def _setupMinimalGizmoRendering(self): - """设置最简单的渲染方式""" - try: - from panda3d.core import (ShaderAttrib, MaterialAttrib, TextureAttrib, - CullFaceAttrib, RescaleNormalAttrib) - - # 使用最高优先级的 GUI 渲染 bin - self.gizmo.setBin("gui-popup", 10000) - self.gizmo.setDepthTest(False) - self.gizmo.setDepthWrite(False) - - # 创建最简单的渲染状态 - minimal_state = RenderState.make( - ShaderAttrib.makeOff(10000), # 完全禁用着色器 - MaterialAttrib.makeOff(), # 禁用材质 - TextureAttrib.makeOff(), # 禁用纹理 - CullFaceAttrib.make(CullFaceAttrib.MCullNone), # 禁用面剔除 - RescaleNormalAttrib.makeOff() # 禁用法线重缩放 - ) - self.gizmo.setState(minimal_state, 10000) - - except Exception as e: - print(f" ❌ 最简渲染设置失败: {e}") - - def _forceAxisIndependentRendering(self): - """强制设置每个轴的独立渲染""" - try: - # 轴配置 - axis_configs = [ - (self.gizmoXAxis, "X轴", (1.0, 0.0, 0.0, 1.0), 0), - (self.gizmoYAxis, "Y轴", (0.0, 1.0, 0.0, 1.0), 0), - (self.gizmoZAxis, "Z轴", (0.0, 0.0, 1.0, 1.0), 0) - ] - - for axis_node, name, color, priority in axis_configs: - if axis_node: - # 每个轴都完全独立设置 - self._setupSingleAxisRendering(axis_node, name, color, 0) - - - except Exception as e: - print(f" ❌ 独立轴渲染设置失败: {e}") - - def _setupSingleAxisRendering(self, axis_node, name, color, priority): - """为单个轴设置完全独立的渲染""" - try: - from panda3d.core import (LVecBase4f, RenderState, ColorAttrib, - TransparencyAttrib, LColor, AntialiasAttrib, - RenderModeAttrib, CullFaceAttrib, AuxBitplaneAttrib, - LightRampAttrib) - - # 转换颜色为LColor并增加亮度 - base_color = LColor(*color) - emissive_color = LColor(base_color[0], base_color[1], base_color[2], 1.0) - - # 完全禁用所有高级渲染功能 - axis_node.clearShader() - axis_node.clearTexture() - axis_node.clearMaterial() - axis_node.setLightOff() - axis_node.setFogOff() - axis_node.setAttrib(RenderModeAttrib.make(RenderModeAttrib.MWireframe, 6.0)) - axis_node.setAttrib(AntialiasAttrib.make(AntialiasAttrib.MLine)) - axis_node.setBin("gui-popup", 0) - axis_node.setDepthTest(False) - axis_node.setDepthWrite(False) - axis_node.setTwoSided(True) - - # 强制设置自发光颜色 - axis_node.setColor(*color) - axis_node.setColorScale(1.0, 1.0, 1.0, 1.0) # 增加整体亮度 - - except Exception as e: - print(" ❌ {} 轴渲染设置失败: {}".format(name, str(e))) - raise e - - def _setupUltimateGizmoFallback(self): - """最后的备用方案 - 最简单的渲染""" - try: - # 最简单的设置 - self.gizmo.setLightOff() - self.gizmo.setFogOff() - self.gizmo.setBin("gui-popup", 20000) - self.gizmo.setDepthTest(False) - self.gizmo.setDepthWrite(False) - - # 直接设置颜色,不使用复杂的渲染状态 - if self.gizmoXAxis: - self.gizmoXAxis.setColor(1, 0, 0, 1) - self.gizmoXAxis.setLightOff() - self.gizmoXAxis.setBin("gui-popup", 20001) - self.gizmoXAxis.setDepthTest(False) - - if self.gizmoYAxis: - self.gizmoYAxis.setColor(0, 1, 0, 1) - self.gizmoYAxis.setLightOff() - self.gizmoYAxis.setBin("gui-popup", 20002) - self.gizmoYAxis.setDepthTest(False) - - if self.gizmoZAxis: - self.gizmoZAxis.setColor(0, 0, 1, 1) - self.gizmoZAxis.setLightOff() - self.gizmoZAxis.setBin("gui-popup", 20003) - self.gizmoZAxis.setDepthTest(False) - - except Exception as e: - print(f"❌ 最后备用方案也失败: {e}") + # 可见性状态:允许颜色写入,不写深度,双面可见,禁用雾 + node.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.C_all)) + node.setAttrib(DepthWriteAttrib.make(False)) + node.setTwoSided(True) + node.setFogOff() def updateGizmoTask(self, task): """坐标轴更新任务 - 包含固定大小功能""" @@ -791,34 +549,127 @@ class SelectionSystem: self.gizmoStartPos = None - def setGizmoAxisColor(self, axis, color): - """设置坐标轴颜色 - RenderPipeline 兼容版本""" - try: - from panda3d.core import AntialiasAttrib,TransparencyAttrib + # def setGizmoAxisColor(self, axis, color): + # """设置坐标轴颜色 - RenderPipeline 兼容版本""" + # try: + # from panda3d.core import AntialiasAttrib,TransparencyAttrib + # + # axis_nodes = { + # "x": self.gizmoXAxis, + # "y": self.gizmoYAxis, + # "z": self.gizmoZAxis + # } + # + # if axis in axis_nodes and axis_nodes[axis]: + # axis_node = axis_nodes[axis] + # + # axis_node.setColor(color[0]*20.0,color[1]*20.0,color[2]*20.0,color[3]) + # axis_node.setColorScale(color[0]*10.0,color[1]*10.0,color[2]*10.0,color[3]) + # axis_node.setShaderOff(10000) + # axis_node.setLightOff() + # axis_node.setMaterialOff() + # axis_node.setTextureOff() + # axis_node.setFogOff() + # + # except Exception as e: + # print(f"设置坐标轴颜色失败: {str(e)}") + # # 回退到简单的颜色设置 + # try: + # if axis in axis_nodes and axis_nodes[axis]: + # axis_nodes[axis].setColor(*color) + # except: + # pass + def setGizmoAxisColor(self, axis, color): + """使用材质设置坐标轴颜色 - RenderPipeline兼容版本""" + try: + from panda3d.core import Material, Vec4,ColorWriteAttrib,DepthWriteAttrib,DepthTestAttrib,TransparencyAttrib + + # 获取对应的轴节点 axis_nodes = { "x": self.gizmoXAxis, "y": self.gizmoYAxis, "z": self.gizmoZAxis } - if axis in axis_nodes and axis_nodes[axis]: - axis_node = axis_nodes[axis] + if axis not in axis_nodes or not axis_nodes[axis]: + return - axis_node.setColor(color[0]*20.0,color[1]*20.0,color[2]*20.0,color[3]) - axis_node.setColorScale(color[0]*20.0,color[1]*20.0,color[2]*20.0,color[3]) - axis_node.setShaderOff(10000) - axis_node.setLightOff(10000) - axis_node.setMaterialOff(10000) - axis_node.setTextureOff(1000) - axis_node.setFogOff(10000) + 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") + + if not arrow_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) + + # 应用材质 + arrow_node.setMaterial(mat, 1) + + + # 设置透明度 + if color[3] < 1.0: + arrow_node.setTransparency(TransparencyAttrib.MAlpha) + 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) # 使用固定渲染顺序,确保在最前 + + # 保存材质引用以便后续修改 + 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.set_state(state) + axis_node.setBin("fixed", 9) # 确保轴节点在箭头模型之前渲染 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) + axis_nodes[axis].setColor(color[0], color[1], color[2], color[3]) except: pass diff --git a/ui/widgets.py b/ui/widgets.py index 9a1c5dc7..dcd1cc76 100644 --- a/ui/widgets.py +++ b/ui/widgets.py @@ -363,6 +363,7 @@ class CustomTreeWidget(QTreeWidget): # 更新属性面板 self.world.updatePropertyPanel(dragged_item) + self.world.property_panel._syncEffectiveVisibility(dragged_node) else: event.ignore()