forked from Rowland/EG
1266 lines
52 KiB
Python
1266 lines
52 KiB
Python
from types import new_class
|
||
from typing import Hashable
|
||
|
||
from PyQt5.QtWidgets import (QLabel, QLineEdit, QDoubleSpinBox, QPushButton,
|
||
QTreeWidget, QTreeWidgetItem, QMenu,QCheckBox)
|
||
from PyQt5.QtCore import Qt
|
||
from panda3d.core import Vec3, Vec4
|
||
|
||
class PropertyPanelManager:
|
||
"""属性面板管理器"""
|
||
|
||
def __init__(self, world):
|
||
"""初始化属性面板管理器"""
|
||
self.world = world
|
||
self._propertyLayout = None
|
||
|
||
def setPropertyLayout(self, layout):
|
||
"""设置属性面板布局引用"""
|
||
print("开始设置属性布局")
|
||
print(f"布局类型: {type(layout)}")
|
||
|
||
# 保存布局引用
|
||
self._propertyLayout = layout
|
||
|
||
# 确保布局有父部件
|
||
if not layout.parent():
|
||
print("布局没有父部件,创建新的容器")
|
||
from PyQt5.QtWidgets import QWidget
|
||
container = QWidget()
|
||
container.setObjectName("PropertyContainer")
|
||
container.setLayout(layout)
|
||
|
||
print(f"布局父部件: {self._propertyLayout.parent().objectName() if self._propertyLayout.parent() else 'None'}")
|
||
print(f"布局项目数: {self._propertyLayout.count()}")
|
||
|
||
return True
|
||
|
||
def clearPropertyPanel(self):
|
||
"""清空属性面板"""
|
||
if self._propertyLayout:
|
||
while self._propertyLayout.count():
|
||
item = self._propertyLayout.takeAt(0)
|
||
if item.widget():
|
||
item.widget().deleteLater()
|
||
|
||
def updatePropertyPanel(self, item):
|
||
"""更新属性面板显示"""
|
||
if not self._propertyLayout or not self._propertyLayout.parent():
|
||
print("属性布局未设置或没有父部件!")
|
||
return
|
||
|
||
self.clearPropertyPanel()
|
||
|
||
itemText = item.text(0)
|
||
|
||
# 如果点击的是场景根节点,显示提示信息
|
||
if itemText == "场景":
|
||
tipLabel = QLabel("")
|
||
tipLabel.setStyleSheet("color: gray;")
|
||
self._propertyLayout.addRow(tipLabel)
|
||
return
|
||
|
||
# 创建通用属性
|
||
nameLabel = QLabel("名称:")
|
||
nameEdit = QLineEdit(itemText)
|
||
self._propertyLayout.addRow(nameLabel, nameEdit)
|
||
|
||
# 获取节点对象
|
||
model = item.data(0, Qt.UserRole)
|
||
|
||
# 检查是否是GUI元素
|
||
if model and hasattr(model, 'getTag') and model.getTag("gui_type"):
|
||
self.updateGUIPropertyPanel(model)
|
||
elif model and hasattr(model,'getTag') and model.getTag("light_type"):
|
||
self.updateLightPropertyPanel(model)
|
||
elif model and hasattr(model,'getTag') and model.getTag("light_type"):
|
||
self.updateLightPropertyPanel(model)
|
||
# 如果找到模型,显示其属性
|
||
elif model:
|
||
self._updateModelPropertyPanel(model)
|
||
# 显示脚本属性
|
||
self._updateScriptPropertyPanel(model)
|
||
|
||
# 强制更新布局
|
||
if self._propertyLayout:
|
||
self._propertyLayout.update()
|
||
propertyWidget = self._propertyLayout.parentWidget()
|
||
if propertyWidget:
|
||
propertyWidget.update()
|
||
|
||
def _updateModelPropertyPanel(self, model):
|
||
"""更新模型属性面板"""
|
||
# 获取父节点
|
||
parent = model.getParent()
|
||
|
||
# 位置属性(相对于父节点)
|
||
relativePos = model.getPos(parent) if parent else model.getPos()
|
||
|
||
xPos = QDoubleSpinBox()
|
||
xPos.setRange(-1000, 1000)
|
||
xPos.setValue(relativePos.getX())
|
||
xPos.valueChanged.connect(lambda v: model.setX(parent, v) if parent else model.setX(v))
|
||
self._propertyLayout.addRow("相对位置 X:", xPos)
|
||
|
||
yPos = QDoubleSpinBox()
|
||
yPos.setRange(-1000, 1000)
|
||
yPos.setValue(relativePos.getY())
|
||
yPos.valueChanged.connect(lambda v: model.setY(parent, v) if parent else model.setY(v))
|
||
self._propertyLayout.addRow("相对位置 Y:", yPos)
|
||
|
||
zPos = QDoubleSpinBox()
|
||
zPos.setRange(-1000, 1000)
|
||
zPos.setValue(relativePos.getZ())
|
||
zPos.valueChanged.connect(lambda v: model.setZ(parent, v) if parent else model.setZ(v))
|
||
self._propertyLayout.addRow("相对位置 Z:", zPos)
|
||
|
||
# 世界位置(只读)
|
||
worldPos = model.getPos(self.world.render)
|
||
worldXPos = QDoubleSpinBox()
|
||
worldXPos.setRange(-1000, 1000)
|
||
worldXPos.setValue(worldPos.getX())
|
||
worldXPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 X:", worldXPos)
|
||
|
||
worldYPos = QDoubleSpinBox()
|
||
worldYPos.setRange(-1000, 1000)
|
||
worldYPos.setValue(worldPos.getY())
|
||
worldYPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 Y:", worldYPos)
|
||
|
||
worldZPos = QDoubleSpinBox()
|
||
worldZPos.setRange(-1000, 1000)
|
||
worldZPos.setValue(worldPos.getZ())
|
||
worldZPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 Z:", worldZPos)
|
||
|
||
# 旋转属性
|
||
hRot = QDoubleSpinBox()
|
||
hRot.setRange(-180, 180)
|
||
hRot.setValue(model.getH())
|
||
hRot.valueChanged.connect(lambda v: model.setH(v))
|
||
self._propertyLayout.addRow("旋转 H:", hRot)
|
||
|
||
pRot = QDoubleSpinBox()
|
||
pRot.setRange(-180, 180)
|
||
pRot.setValue(model.getP())
|
||
pRot.valueChanged.connect(lambda v: model.setP(v))
|
||
self._propertyLayout.addRow("旋转 P:", pRot)
|
||
|
||
rRot = QDoubleSpinBox()
|
||
rRot.setRange(-180, 180)
|
||
rRot.setValue(model.getR())
|
||
rRot.valueChanged.connect(lambda v: model.setR(v))
|
||
self._propertyLayout.addRow("旋转 R:", rRot)
|
||
|
||
# 缩放属性
|
||
xScale = QDoubleSpinBox()
|
||
xScale.setRange(0.01, 100)
|
||
xScale.setSingleStep(0.1)
|
||
xScale.setValue(model.getScale().getX())
|
||
xScale.valueChanged.connect(lambda v: model.setScale(v, model.getScale().getY(), model.getScale().getZ()))
|
||
self._propertyLayout.addRow("缩放 X:", xScale)
|
||
|
||
yScale = QDoubleSpinBox()
|
||
yScale.setRange(0.01, 100)
|
||
yScale.setSingleStep(0.1)
|
||
yScale.setValue(model.getScale().getY())
|
||
yScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), v, model.getScale().getZ()))
|
||
self._propertyLayout.addRow("缩放 Y:", yScale)
|
||
|
||
zScale = QDoubleSpinBox()
|
||
zScale.setRange(0.01, 100)
|
||
zScale.setSingleStep(0.1)
|
||
zScale.setValue(model.getScale().getZ())
|
||
zScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), model.getScale().getY(), v))
|
||
self._propertyLayout.addRow("缩放 Z:", zScale)
|
||
|
||
material_title = QLabel("材质属性")
|
||
material_title.setStyleSheet("color: #FF6B6B;font-weight:bold;font-size:14px;margin-top:10px;")
|
||
self._propertyLayout.addRow(material_title)
|
||
|
||
self._updateModelMaterialPanel(model)
|
||
|
||
def updateGUIPropertyPanel(self, gui_element):
|
||
"""更新GUI元素属性面板"""
|
||
gui_type = gui_element.getTag("gui_type")
|
||
gui_text = gui_element.getTag("gui_text")
|
||
|
||
# GUI类型显示
|
||
typeLabel = QLabel("GUI类型:")
|
||
typeValue = QLabel(gui_type)
|
||
typeValue.setStyleSheet("color: #00AAFF; font-weight: bold;")
|
||
self._propertyLayout.addRow(typeLabel, typeValue)
|
||
|
||
# 文本属性(如果适用)
|
||
if gui_type in ["button", "label", "entry", "3d_text", "virtual_screen"]:
|
||
textLabel = QLabel("文本:")
|
||
textEdit = QLineEdit(gui_text or "")
|
||
|
||
# 创建一个更新函数来处理文本变化
|
||
def updateText(text):
|
||
success = self.world.gui_manager.editGUIElement(gui_element, "text", text)
|
||
if success:
|
||
# 更新场景树显示的名称
|
||
self.world.scene_manager.updateSceneTree()
|
||
|
||
textEdit.textChanged.connect(updateText)
|
||
self._propertyLayout.addRow(textLabel, textEdit)
|
||
|
||
# 位置属性
|
||
if hasattr(gui_element, 'getPos'):
|
||
pos = gui_element.getPos()
|
||
|
||
# 根据GUI类型决定位置编辑方式
|
||
if gui_type in ["button", "label", "entry"]:
|
||
# 2D GUI组件使用屏幕坐标
|
||
logical_x = pos.getX() / 0.1 # 反向转换为逻辑坐标
|
||
logical_z = pos.getZ() / 0.1
|
||
|
||
xPos = QDoubleSpinBox()
|
||
xPos.setRange(-50, 50)
|
||
xPos.setValue(logical_x)
|
||
xPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "x", v))
|
||
self._propertyLayout.addRow("屏幕位置 X:", xPos)
|
||
|
||
zPos = QDoubleSpinBox()
|
||
zPos.setRange(-50, 50)
|
||
zPos.setValue(logical_z)
|
||
zPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUI2DPosition(gui_element, "z", v))
|
||
self._propertyLayout.addRow("屏幕位置 Z:", zPos)
|
||
|
||
# 显示实际屏幕坐标(只读)
|
||
actualXLabel = QLabel(f"{pos.getX():.3f}")
|
||
actualXLabel.setStyleSheet("color: gray; font-size: 10px;")
|
||
self._propertyLayout.addRow("实际屏幕 X:", actualXLabel)
|
||
|
||
actualZLabel = QLabel(f"{pos.getZ():.3f}")
|
||
actualZLabel.setStyleSheet("color: gray; font-size: 10px;")
|
||
self._propertyLayout.addRow("实际屏幕 Z:", actualZLabel)
|
||
|
||
else:
|
||
# 3D GUI组件使用世界坐标
|
||
xPos = QDoubleSpinBox()
|
||
xPos.setRange(-1000, 1000)
|
||
xPos.setValue(pos.getX())
|
||
xPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUIElement(gui_element, "position", [v, pos.getY(), pos.getZ()]))
|
||
self._propertyLayout.addRow("位置 X:", xPos)
|
||
|
||
yPos = QDoubleSpinBox()
|
||
yPos.setRange(-1000, 1000)
|
||
yPos.setValue(pos.getY())
|
||
yPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUIElement(gui_element, "position", [pos.getX(), v, pos.getZ()]))
|
||
self._propertyLayout.addRow("位置 Y:", yPos)
|
||
|
||
zPos = QDoubleSpinBox()
|
||
zPos.setRange(-1000, 1000)
|
||
zPos.setValue(pos.getZ())
|
||
zPos.valueChanged.connect(lambda v: self.world.gui_manager.editGUIElement(gui_element, "position", [pos.getX(), pos.getY(), v]))
|
||
self._propertyLayout.addRow("位置 Z:", zPos)
|
||
|
||
# 缩放属性
|
||
if hasattr(gui_element, 'getScale'):
|
||
scale = gui_element.getScale()
|
||
|
||
scaleSpinBox = QDoubleSpinBox()
|
||
scaleSpinBox.setRange(0.01, 10)
|
||
scaleSpinBox.setSingleStep(0.1)
|
||
scaleSpinBox.setValue(scale.getX())
|
||
scaleSpinBox.valueChanged.connect(lambda v: self.world.gui_manager.editGUIElement(gui_element, "scale", v))
|
||
self._propertyLayout.addRow("缩放:", scaleSpinBox)
|
||
|
||
# 颜色属性(针对2D GUI)
|
||
if gui_type in ["button", "label"]:
|
||
colorButton = QPushButton("选择颜色")
|
||
colorButton.clicked.connect(lambda: self.world.gui_manager.selectGUIColor(gui_element))
|
||
self._propertyLayout.addRow("背景颜色:", colorButton)
|
||
|
||
def _updateScriptPropertyPanel(self, game_object):
|
||
"""更新脚本属性面板"""
|
||
# 获取对象上的脚本
|
||
scripts = self.world.getScripts(game_object)
|
||
|
||
if scripts:
|
||
# 添加脚本信息标题
|
||
scriptTitleLabel = QLabel("已挂载脚本:")
|
||
scriptTitleLabel.setStyleSheet("color: #00AAFF; font-weight: bold; font-size: 12px;")
|
||
self._propertyLayout.addRow(scriptTitleLabel)
|
||
|
||
# 显示每个脚本的信息
|
||
for i, script_component in enumerate(scripts):
|
||
script_name = script_component.script_name
|
||
enabled = script_component.enabled
|
||
|
||
# 脚本名称和状态
|
||
scriptLabel = QLabel(f"脚本 {i+1}:")
|
||
scriptInfo = QLabel(f"{script_name}")
|
||
scriptInfo.setStyleSheet("color: green; font-weight: bold;" if enabled else "color: gray;")
|
||
self._propertyLayout.addRow(scriptLabel, scriptInfo)
|
||
|
||
# 脚本启用/禁用按钮
|
||
enableButton = QPushButton("禁用" if enabled else "启用")
|
||
enableButton.setStyleSheet(
|
||
"background-color: #FF6B6B; color: white;" if enabled
|
||
else "background-color: #4ECDC4; color: white;"
|
||
)
|
||
enableButton.clicked.connect(
|
||
lambda checked, sc=script_component: self._toggleScriptEnabled(sc)
|
||
)
|
||
self._propertyLayout.addRow("状态:", enableButton)
|
||
|
||
# 分隔线
|
||
if i < len(scripts) - 1:
|
||
separator = QLabel("─" * 20)
|
||
separator.setStyleSheet("color: lightgray;")
|
||
self._propertyLayout.addRow(separator)
|
||
else:
|
||
# 显示无脚本信息
|
||
noScriptLabel = QLabel("无挂载脚本")
|
||
noScriptLabel.setStyleSheet("color: gray; font-style: italic;")
|
||
self._propertyLayout.addRow("脚本:", noScriptLabel)
|
||
|
||
def _toggleScriptEnabled(self, script_component):
|
||
"""切换脚本启用状态"""
|
||
script_component.enabled = not script_component.enabled
|
||
status = "启用" if script_component.enabled else "禁用"
|
||
print(f"脚本 {script_component.script_name} 已{status}")
|
||
|
||
# 刷新属性面板显示
|
||
if hasattr(self.world.selection, 'selectedObject') and self.world.selection.selectedObject:
|
||
# 找到当前选中项并更新
|
||
tree_widget = self.world.treeWidget
|
||
if tree_widget and tree_widget.currentItem():
|
||
self.updatePropertyPanel(tree_widget.currentItem())
|
||
|
||
def updateLightPropertyPanel(self, model):
|
||
"""更新模型属性面板"""
|
||
|
||
light_object = model.getPythonTag("rp_light_object")
|
||
|
||
if light_object:
|
||
current_pos = light_object.pos
|
||
|
||
xPos = QDoubleSpinBox()
|
||
xPos.setRange(-1000, 1000)
|
||
xPos.setValue(current_pos.getX())
|
||
xPos.valueChanged.connect(lambda v: self._updateLightPosition(light_object, model, 'x', v))
|
||
self._propertyLayout.addRow("相对位置 X:", xPos)
|
||
|
||
yPos = QDoubleSpinBox()
|
||
yPos.setRange(-1000,1000)
|
||
yPos.setValue(current_pos.getY())
|
||
yPos.valueChanged.connect(lambda v:self._updateLightPosition(light_object,model,'y',v))
|
||
self._propertyLayout.addRow("相对位置 X:",yPos)
|
||
|
||
zPos = QDoubleSpinBox()
|
||
zPos.setRange(-1000,1000)
|
||
zPos.setValue(current_pos.getZ())
|
||
zPos.valueChanged.connect(lambda v:self._updateLightPosition(light_object,model,'z',v))
|
||
self._propertyLayout.addRow("相对位置 Z:",zPos)
|
||
|
||
if hasattr(light_object,'direction'):
|
||
current_hpr = model.getHpr()
|
||
|
||
hRot = QDoubleSpinBox()
|
||
hRot.setRange(-180,180)
|
||
hRot.setValue(current_hpr.getX())
|
||
hRot.valueChanged.connect(lambda v:self._updateLightRotation(light_object,model,'h',v))
|
||
self._propertyLayout.addRow("旋转 H:",hRot)
|
||
|
||
pRot = QDoubleSpinBox()
|
||
pRot.setRange(-180,180)
|
||
pRot.setValue(current_hpr.getY())
|
||
pRot.valueChanged.connect(lambda v:self._updateLightRotation(light_object,model,'p',v))
|
||
self._propertyLayout.addRow("旋转 P:",pRot)
|
||
|
||
rRot = QDoubleSpinBox()
|
||
rRot.setRange(-180,180)
|
||
rRot.setValue(current_hpr.getZ())
|
||
rRot.valueChanged.connect(lambda v:self._updateLightRotation(light_object,model,'r',v))
|
||
self._propertyLayout.addRow("旋转 R:",rRot)
|
||
|
||
energySpinBox = QDoubleSpinBox()
|
||
energySpinBox.setRange(0,10000)
|
||
energySpinBox.setValue(light_object.energy)
|
||
energySpinBox.valueChanged.connect(lambda v:self._updateLightEnergy(light_object,v))
|
||
self._propertyLayout.addRow("能量:",energySpinBox)
|
||
|
||
radiusSpinBox = QDoubleSpinBox()
|
||
radiusSpinBox.setRange(1,2000)
|
||
radiusSpinBox.setValue(light_object.radius)
|
||
radiusSpinBox.valueChanged.connect(lambda v:self._updateLightRadius(light_object,v))
|
||
self._propertyLayout.addRow("半径:",radiusSpinBox)
|
||
|
||
if hasattr(light_object,'fov'):
|
||
fovSpinBox = QDoubleSpinBox()
|
||
fovSpinBox.setRange(1,180)
|
||
fovSpinBox.setValue(light_object.fov)
|
||
fovSpinBox.valueChanged.connect(lambda v:self._updateLightFOV(light_object,v))
|
||
self._propertyLayout.addRow("视野角度:",fovSpinBox)
|
||
|
||
shadowCheckBox = QCheckBox()
|
||
shadowCheckBox.setChecked(light_object.casts_shadows)
|
||
shadowCheckBox.stateChanged.connect(lambda state:self._updateLightCastsShadows(light_object,state==2))
|
||
self._propertyLayout.addRow("投射阴影:",shadowCheckBox)
|
||
|
||
current_scale = model.getScale()
|
||
|
||
xScaleSpinBox = QDoubleSpinBox()
|
||
xScaleSpinBox.setRange(0.01, 100)
|
||
xScaleSpinBox.setSingleStep(0.1)
|
||
xScaleSpinBox.setValue(current_scale.getX())
|
||
xScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'x', v))
|
||
self._propertyLayout.addRow("缩放 X:", xScaleSpinBox)
|
||
|
||
yScaleSpinBox = QDoubleSpinBox()
|
||
yScaleSpinBox.setRange(0.01, 100)
|
||
yScaleSpinBox.setSingleStep(0.1)
|
||
yScaleSpinBox.setValue(current_scale.getY())
|
||
yScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'y', v))
|
||
self._propertyLayout.addRow("缩放 Y:", yScaleSpinBox)
|
||
|
||
zScaleSpinBox = QDoubleSpinBox()
|
||
zScaleSpinBox.setRange(0.01, 100)
|
||
zScaleSpinBox.setSingleStep(0.1)
|
||
zScaleSpinBox.setValue(current_scale.getZ())
|
||
zScaleSpinBox.valueChanged.connect(lambda v: self._updateLightScale(model, 'z', v))
|
||
self._propertyLayout.addRow("缩放 Z:", zScaleSpinBox)
|
||
|
||
|
||
|
||
|
||
|
||
# 获取父节点
|
||
|
||
#parent = model.getParent()
|
||
|
||
# 位置属性(相对于父节点)
|
||
#relativePos = model.getPos(parent) if parent else model.getPos()
|
||
|
||
# xPos = QDoubleSpinBox()
|
||
# xPos.setRange(-1000, 1000)
|
||
# xPos.setValue(relativePos.getX())
|
||
# xPos.valueChanged.connect(lambda v: model.setX(parent, v) if parent else model.setX(v))
|
||
# self._propertyLayout.addRow("相对位置 X:", xPos)
|
||
#print(f"{model} x :{model.getPos()}")
|
||
|
||
# yPos = QDoubleSpinBox()
|
||
# yPos.setRange(-1000, 1000)
|
||
# yPos.setValue(relativePos.getY())
|
||
# yPos.valueChanged.connect(lambda v: model.setY(parent, v) if parent else model.setY(v))
|
||
# self._propertyLayout.addRow("相对位置 Y:", yPos)
|
||
#
|
||
# zPos = QDoubleSpinBox()
|
||
# zPos.setRange(-1000, 1000)
|
||
# zPos.setValue(relativePos.getZ())
|
||
# zPos.valueChanged.connect(lambda v: model.setZ(parent, v) if parent else model.setZ(v))
|
||
# self._propertyLayout.addRow("相对位置 Z:", zPos)
|
||
|
||
# 世界位置(只读)
|
||
worldPos = model.getPos(self.world.render)
|
||
worldXPos = QDoubleSpinBox()
|
||
worldXPos.setRange(-1000, 1000)
|
||
worldXPos.setValue(worldPos.getX())
|
||
worldXPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 X:", worldXPos)
|
||
|
||
worldYPos = QDoubleSpinBox()
|
||
worldYPos.setRange(-1000, 1000)
|
||
worldYPos.setValue(worldPos.getY())
|
||
worldYPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 Y:", worldYPos)
|
||
|
||
worldZPos = QDoubleSpinBox()
|
||
worldZPos.setRange(-1000, 1000)
|
||
worldZPos.setValue(worldPos.getZ())
|
||
worldZPos.setReadOnly(True)
|
||
self._propertyLayout.addRow("世界位置 Z:", worldZPos)
|
||
|
||
|
||
|
||
# 旋转属性
|
||
# hRot = QDoubleSpinBox()
|
||
# hRot.setRange(-180, 180)
|
||
# hRot.setValue(model.getH())
|
||
# hRot.valueChanged.connect(lambda v: model.setH(v))
|
||
# self._propertyLayout.addRow("旋转 H:", hRot)
|
||
#
|
||
# pRot = QDoubleSpinBox()
|
||
# pRot.setRange(-180, 180)
|
||
# pRot.setValue(model.getP())
|
||
# pRot.valueChanged.connect(lambda v: model.setP(v))
|
||
# self._propertyLayout.addRow("旋转 P:", pRot)
|
||
#
|
||
# rRot = QDoubleSpinBox()
|
||
# rRot.setRange(-180, 180)
|
||
# rRot.setValue(model.getR())
|
||
# rRot.valueChanged.connect(lambda v: model.setR(v))
|
||
# self._propertyLayout.addRow("旋转 R:", rRot)
|
||
|
||
# 缩放属性
|
||
# xScale = QDoubleSpinBox()
|
||
# xScale.setRange(0.01, 100)
|
||
# xScale.setSingleStep(0.1)
|
||
# xScale.setValue(model.getScale().getX())
|
||
# xScale.valueChanged.connect(lambda v: model.setScale(v, model.getScale().getY(), model.getScale().getZ()))
|
||
# self._propertyLayout.addRow("缩放 X:", xScale)
|
||
#
|
||
# yScale = QDoubleSpinBox()
|
||
# yScale.setRange(0.01, 100)
|
||
# yScale.setSingleStep(0.1)
|
||
# yScale.setValue(model.getScale().getY())
|
||
# yScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), v, model.getScale().getZ()))
|
||
# self._propertyLayout.addRow("缩放 Y:", yScale)
|
||
#
|
||
# zScale = QDoubleSpinBox()
|
||
# zScale.setRange(0.01, 100)
|
||
# zScale.setSingleStep(0.1)
|
||
# zScale.setValue(model.getScale().getZ())
|
||
# zScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), model.getScale().getY(), v))
|
||
# self._propertyLayout.addRow("缩放 Z:", zScale)
|
||
|
||
def _updateLightPosition(self,light_object,node_path,axis,value):
|
||
current_pos = light_object.pos
|
||
|
||
if axis=='x':
|
||
new_pos = Vec3(value,current_pos.getY(),current_pos.getZ())
|
||
elif axis == 'y':
|
||
new_pos = Vec3(current_pos.getX(), value, current_pos.getZ())
|
||
else: # z
|
||
new_pos = Vec3(current_pos.getX(), current_pos.getY(), value)
|
||
# 更新RenderPipeline光源位置
|
||
light_object.pos = new_pos
|
||
|
||
# 同步更新场景节点位置(用于显示)
|
||
node_path.setPos(new_pos)
|
||
|
||
def _updateLightRotation(self,light_object,node_path,axis,value):
|
||
"""更新光源旋转"""
|
||
from panda3d.core import Vec3
|
||
|
||
current_hpr = node_path.getHpr()
|
||
if axis=='h':
|
||
new_hpr = Vec3(value,current_hpr.getY(),current_hpr.getZ())
|
||
elif axis=='p':
|
||
new_hpr = Vec3(current_hpr.getX(),value,current_hpr.getZ())
|
||
else:
|
||
new_hpr = Vec3(current_hpr.getX(),current_hpr.getY(),value)
|
||
|
||
node_path.setHpr(new_hpr)
|
||
|
||
if hasattr(light_object,'direction'):
|
||
direction_mat = node_path.getMat()
|
||
new_direction = direction_mat.xformVec(Vec3(0,1,0))
|
||
light_object.direction = new_direction
|
||
|
||
print(f"光源旋转已更新:{axis}={value}")
|
||
|
||
def _updateLightEnergy(self,light_object,value):
|
||
"""更新光源强度"""
|
||
light_object.energy = value
|
||
|
||
def _updateLightRadius(self,light_object,value):
|
||
"""更新光源半径"""
|
||
light_object.radius = value
|
||
|
||
def _updateLightFOV(self,light_Object,value):
|
||
"""更新聚光灯视野角度"""
|
||
if hasattr(light_Object,'fov'):
|
||
light_Object.fov = value
|
||
|
||
def _updateLightTemperature(self,light_object,value):
|
||
"""更新光源色温"""
|
||
light_object.set_color_from_temperature(value)
|
||
#保存色温值以便下次显示
|
||
light_object._temperature=value
|
||
|
||
def _updateLightInnerRadius(self,light_object,value):
|
||
"""更新点光源内半径"""
|
||
if hasattr(light_object,'inner_radius'):
|
||
light_object.inner_radius=value
|
||
|
||
def _updateLightShaowResolution(self,light_object,value):
|
||
"""更新阴影分辨率"""
|
||
light_object.shadow_map_resolution = value
|
||
|
||
def _updateLightNearPlane(self,light_object,value):
|
||
"""更新近平面距离"""
|
||
light_object.near_plane = value
|
||
|
||
def _updateLightCastsShadows(self,light_object,casts_shadows):
|
||
"""更新光源是否投射阴影"""
|
||
light_object.casts_shadows = casts_shadows
|
||
|
||
def _updateLightScale(self,node_path,axis,value):
|
||
"""更新光源节点缩放"""
|
||
current_scale = node_path.getScale()
|
||
|
||
if axis=='x':
|
||
new_scale = Vec3(value,current_scale.getY(),current_scale.getZ())
|
||
elif axis=='y':
|
||
new_scale = Vec3(current_scale.getX(),value,current_scale.getZ())
|
||
else:
|
||
new_scale = Vec3(current_scale.getX(),current_scale.getY(),value)
|
||
|
||
node_path.setScale(new_scale)
|
||
|
||
|
||
def _updateModelMaterialPanel(self,model):
|
||
"""模型材质属性"""
|
||
materials = model.find_all_materials()
|
||
|
||
if not materials:
|
||
no_material_label=QLabel("无材质")
|
||
no_material_label.setStyleSheet(("color: gray;font-style:italic;"))
|
||
self._propertyLayout.addRow("材质:",no_material_label)
|
||
return
|
||
|
||
model_name=model.getName() or "未命名模型"
|
||
|
||
for i,material in enumerate(materials):
|
||
#材质名称属性
|
||
#print(f"Material{i+1}name:{material.get_name()}")
|
||
material_title = QLabel(f"材质{i+1}:{model_name}")
|
||
material_title.setStyleSheet("color:#00AAFF;font-weight:bold;font-size:12px")
|
||
self._propertyLayout.addRow(material_title)
|
||
|
||
#检查是否为PBR材质
|
||
#and material.has_roughness() and material.has_refractive_index()
|
||
if not (material.has_base_color() ):
|
||
non_pbr_label = QLabel("非PBR材质,无法编辑")
|
||
non_pbr_label.setStyleSheet("color:orange;font-style:italic;")
|
||
self._propertyLayout.addRow("状态:",non_pbr_label)
|
||
continue
|
||
# if not material.has_base_color():
|
||
# non_pbr_label = QLabel("非PBR材质(部分属性可能不可用)")
|
||
# non_pbr_label.setStyleSheet("color:orange;font-style:italic;")
|
||
# self._propertyLayout.addRow("状态:", non_pbr_label)
|
||
|
||
#基础颜色编辑
|
||
base_color = material.base_color
|
||
|
||
#R分量
|
||
r_spinbox = QDoubleSpinBox()
|
||
r_spinbox.setRange(0.0,1.0)
|
||
r_spinbox.setSingleStep(0.01)
|
||
r_spinbox.setValue(base_color.x)
|
||
r_spinbox.valueChanged.connect(lambda v,mat = material:self._updateMaterialBaseColor(mat,'r',v))
|
||
self._propertyLayout.addRow("基础颜色 R:",r_spinbox)
|
||
|
||
#G分量
|
||
g_spinbox = QDoubleSpinBox()
|
||
g_spinbox.setRange(0.0, 1.0)
|
||
g_spinbox.setSingleStep(0.01)
|
||
g_spinbox.setValue(base_color.y)
|
||
g_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'g', v))
|
||
self._propertyLayout.addRow("基础颜色 G:", g_spinbox)
|
||
|
||
# B分量
|
||
b_spinbox = QDoubleSpinBox()
|
||
b_spinbox.setRange(0.0, 1.0)
|
||
b_spinbox.setSingleStep(0.01)
|
||
b_spinbox.setValue(base_color.z)
|
||
b_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'b', v))
|
||
self._propertyLayout.addRow("基础颜色 B:", b_spinbox)
|
||
# 添加Alpha分量(透明度)
|
||
alpha_spinbox = QDoubleSpinBox()
|
||
alpha_spinbox.setRange(0.0, 1.0)
|
||
alpha_spinbox.setSingleStep(0.01)
|
||
alpha_spinbox.setValue(base_color.w) # Alpha是Vec4的w分量
|
||
alpha_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialBaseColor(mat, 'a', v))
|
||
self._propertyLayout.addRow("透明度 (Alpha):", alpha_spinbox)
|
||
|
||
# 粗糙度
|
||
roughness_spinbox = QDoubleSpinBox()
|
||
roughness_spinbox.setRange(0.0, 1.0)
|
||
roughness_spinbox.setSingleStep(0.01)
|
||
roughness_spinbox.setValue(material.roughness)
|
||
roughness_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialRoughness(mat, v))
|
||
self._propertyLayout.addRow("粗糙度:", roughness_spinbox)
|
||
|
||
|
||
|
||
# 金属性
|
||
metallic_spinbox = QDoubleSpinBox()
|
||
metallic_spinbox.setRange(0.0, 1.0)
|
||
metallic_spinbox.setSingleStep(0.01)
|
||
metallic_spinbox.setValue(material.metallic)
|
||
metallic_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialMetallic(mat, v))
|
||
self._propertyLayout.addRow("金属性:", metallic_spinbox)
|
||
|
||
# 折射率
|
||
ior_spinbox = QDoubleSpinBox()
|
||
ior_spinbox.setRange(1.0, 3.0)
|
||
ior_spinbox.setSingleStep(0.01)
|
||
ior_spinbox.setValue(material.refractive_index)
|
||
ior_spinbox.valueChanged.connect(lambda v, mat=material: self._updateMaterialIOR(mat, v))
|
||
self._propertyLayout.addRow("折射率:", ior_spinbox)
|
||
|
||
texture_title = QLabel("纹理贴图")
|
||
texture_title.setStyleSheet("color: #4CAF50; font-weight:bold;font-size:11px;margin-top:5px;")
|
||
self._propertyLayout.addRow(texture_title)
|
||
|
||
#漫反射贴图
|
||
diffuse_button = QPushButton("选择漫反射贴图")
|
||
diffuse_button.clicked.connect(lambda checked,mat=material:self._selectDiffuseTexture(mat))
|
||
self._propertyLayout.addRow("漫反射贴图:",diffuse_button)
|
||
|
||
# #法线贴图
|
||
# normal_button = QPushButton("选择法线贴图")
|
||
# normal_button.clicked.connect(lambda checked,mat=material:self._selectNormalTexture(mat))
|
||
# self._propertyLayout.addRow("法线贴图:",normal_button)
|
||
#
|
||
# #粗糙度贴图
|
||
# roughness_button = QPushButton("选择粗糙度贴图")
|
||
# roughness_button.clicked.connect(lambda checked,mat=material:self._selectRoughnessTexture((mat)))
|
||
# self._propertyLayout.addRow("粗糙度贴图:",roughness_button)
|
||
#
|
||
# #金属性贴图
|
||
# metallic_button = QPushButton("选择金属性贴图")
|
||
# metallic_button.clicked.connect(lambda checked,mat=material:self._selectMetallicTexture(mat))
|
||
# self._propertyLayout.addRow("金属性贴图:",metallic_button)
|
||
|
||
|
||
|
||
# 显示当前贴图信息
|
||
self._displayCurrentTextures(material)
|
||
|
||
self._addShadingModelPanel(material)
|
||
self._addEmissionPanel(material)
|
||
self._addMaterialPresetPanel(material)
|
||
#self._addColorSpacePanel(material)
|
||
|
||
|
||
# 分隔线
|
||
if i < len(materials) - 1:
|
||
separator = QLabel("─" * 30)
|
||
separator.setStyleSheet("color: lightgray;")
|
||
self._propertyLayout.addRow(separator)
|
||
|
||
def _updateMaterialBaseColor(self, material, component, value):
|
||
"""更新材质基础颜色"""
|
||
from panda3d.core import Vec4
|
||
current_color = material.base_color
|
||
|
||
if component == 'r':
|
||
new_color = Vec4(value, current_color.y, current_color.z, current_color.w)
|
||
elif component == 'g':
|
||
new_color = Vec4(current_color.x, value, current_color.z, current_color.w)
|
||
elif component == 'b':
|
||
new_color = Vec4(current_color.x, current_color.y, value, current_color.w)
|
||
elif component == 'a': # 添加Alpha分量处理
|
||
new_color = Vec4(current_color.x, current_color.y, current_color.z, value)
|
||
|
||
material.set_base_color(new_color)
|
||
self._invalidateRenderState()
|
||
|
||
def _updateMaterialRoughness(self, material, value):
|
||
"""更新材质粗糙度"""
|
||
material.set_roughness(value)
|
||
self._invalidateRenderState()
|
||
|
||
def _updateMaterialMetallic(self, material, value):
|
||
"""更新材质金属性"""
|
||
material.set_metallic(value)
|
||
self._invalidateRenderState()
|
||
|
||
def _updateMaterialIOR(self, material, value):
|
||
"""更新材质折射率"""
|
||
material.set_refractive_index(value)
|
||
self._invalidateRenderState()
|
||
|
||
def _invalidateRenderState(self):
|
||
"""使渲染状态失效以应用材质更改"""
|
||
from panda3d.core import RenderState
|
||
RenderState.clear_cache()
|
||
|
||
def _selectDiffuseTexture(self,material):
|
||
"""漫反射贴图"""
|
||
from PyQt5.QtWidgets import QFileDialog
|
||
import os
|
||
|
||
file_dialog = QFileDialog(None,"选择漫反射贴图","","图像文件(*.png *.jpg *.jpeg *.tga *.bmp)")
|
||
|
||
if file_dialog.exec_():
|
||
filename = file_dialog.selectedFiles()[0]
|
||
if filename:
|
||
self._applyDiffuseTexture(material,filename)
|
||
print(f"已选择漫反射贴图:{filename}")
|
||
|
||
def _selectNormalTexture(self,material):
|
||
"""选择法线贴图"""
|
||
from PyQt5.QtWidgets import QFileDialog
|
||
|
||
file_dialog = QFileDialog(None,"选择法线贴图","","图像文件(*.png *.jpg *.jpeg *.tga *.bmp)")
|
||
|
||
if file_dialog.exec_():
|
||
filename = file_dialog.selectedFiles()[0]
|
||
if filename:
|
||
self._applyNormalTexture(material,filename)
|
||
print(f"已选择法线贴图:{filename}")
|
||
|
||
def _selectRoughnessTexture(self,material):
|
||
"""选择粗糙度贴图"""
|
||
from PyQt5.QtWidgets import QFileDialog
|
||
|
||
file_dialog = QFileDialog(None,"选择粗糙度贴图","","图像文件(*.png *.jpg *.jpeg *.tga *.bmp)")
|
||
|
||
if file_dialog.exec_():
|
||
filename = file_dialog.selectedFiles()[0]
|
||
if filename:
|
||
self._applyRoughnessTexture(material,filename)
|
||
print(f"已选择粗糙度贴图:{filename}")
|
||
|
||
def _selectMetallicTexture(self,material):
|
||
"""选择金属性贴图"""
|
||
from PyQt5.QtWidgets import QFileDialog
|
||
|
||
file_dialog = QFileDialog(None,"选择金属性贴图","","图像文件(*.png *.jpg *.jpeg *.tga *.bmp)")
|
||
|
||
if file_dialog.exec_():
|
||
filename = file_dialog.selectedFiles()[0]
|
||
if filename:
|
||
self._applyMetallicTexture(material,filename)
|
||
print(f"已选择金属性贴图:{filename}")
|
||
|
||
def _applyDiffuseTexture(self,material,texture_path):
|
||
"""应用漫反射贴图"""
|
||
try:
|
||
from RenderPipelineFile.rpcore.loader import RPLoader
|
||
from panda3d.core import TextureStage
|
||
|
||
#加载纹理
|
||
texture = RPLoader.load_texture(texture_path)
|
||
if texture:
|
||
#material.set_base_color_texture(texture)
|
||
#获取材质所属的节点
|
||
node = self._findNodeWithMaterial(material)
|
||
if node:
|
||
#应用漫反射贴图到第一个纹理阶段
|
||
node.setTexture(TextureStage.getDefault(),texture,1)
|
||
self._invalidateRenderState()
|
||
print(f"漫反射贴图已应用:{texture_path}")
|
||
else:
|
||
print("没有找到材质")
|
||
else:
|
||
print("未找到材质对应的节点")
|
||
except Exception as e:
|
||
print(f"应用漫反射贴图失败{e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _applyNormalTexture(self,material,texture_path):
|
||
"""应用法线贴图"""
|
||
try:
|
||
|
||
from RenderPipelineFile.rpcore.loader import RPLoader
|
||
from panda3d.core import TextureStage
|
||
|
||
texture = RPLoader.load_texture(texture_path)
|
||
|
||
if texture:
|
||
node = self._findNodeWithMaterial(material)
|
||
if node:
|
||
#创建法线贴图纹理阶段
|
||
|
||
#normal_stage = TextureStage("normal")
|
||
node.setTexture(1,texture)
|
||
#normal_stage.setMode(TextureStage.MNormal)
|
||
#node.setTexture(normal_stage,texture)
|
||
self._invalidateRenderState()
|
||
print(f"法线贴图一应用:{texture_path}")
|
||
else:
|
||
print("未找到材质对应的节点")
|
||
except Exception as e:
|
||
print(f"应用法线贴图失败:{e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _applyRoughnessTexture(self,material,texture_path):
|
||
"""应用粗糙度贴图"""
|
||
try:
|
||
|
||
from RenderPipelineFile.rpcore.loader import RPLoader
|
||
from panda3d.core import TextureStage
|
||
|
||
texture = RPLoader.load_texture(texture_path)
|
||
if texture:
|
||
node = self._findNodeWithMaterial(material)
|
||
if node:
|
||
#创建粗糙度贴图纹理阶段
|
||
roughness_stage = TextureStage("roughtness")
|
||
roughness_stage.setMode(TextureStage.MModulate)
|
||
node.setTexture(roughness_stage,texture)
|
||
print(f"粗糙度贴图已应用:{texture_path}")
|
||
except Exception as e:
|
||
print(f"应用粗糙度贴图失败:{e}")
|
||
|
||
def _applyMetallicTexture(self,material,texture_path):
|
||
"""应用金属性贴图"""
|
||
try:
|
||
|
||
from RenderPipelineFile.rpcore.loader import RPLoader
|
||
from panda3d.core import TextureStage
|
||
|
||
texture = RPLoader.load_texture(texture_path)
|
||
if texture:
|
||
node = self._findNodeWithMaterial(material)
|
||
if node:
|
||
#创建金属性贴图纹理阶段
|
||
metallic_stage = TextureStage("metallic")
|
||
metallic_stage.setSort(2)
|
||
#metallic_stage.setMode(TextureStage.MModulate)
|
||
node.setTexture(metallic_stage,texture)
|
||
self._invalidateRenderState()
|
||
print(f"金属性贴图已应用:{texture_path}")
|
||
else:
|
||
print("未找到材质对应的节点")
|
||
except Exception as e:
|
||
print(f"应用金属性贴图失败:{e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _findNodeWithMaterial(self, target_material):
|
||
"""查找使用指定材质的节点"""
|
||
# 这里需要根据你的场景结构来实现
|
||
# 遍历场景中的所有节点,找到使用该材质的节点
|
||
# for model in self.world.scene_manager.models:
|
||
# materials = model.find_all_materials()
|
||
# if target_material in materials:
|
||
# return model
|
||
|
||
"""查找使用指定材质的节点"""
|
||
# 首先尝试在当前选中的模型中查找
|
||
current_item = self.world.treeWidget.currentItem()
|
||
if current_item:
|
||
current_model = current_item.data(0, Qt.UserRole)
|
||
if current_model:
|
||
materials = current_model.find_all_materials()
|
||
if target_material in materials:
|
||
return current_model
|
||
|
||
# 如果在当前选中模型中没找到,再遍历所有模型
|
||
for model in self.world.scene_manager.models:
|
||
materials = model.find_all_materials()
|
||
if target_material in materials:
|
||
return model
|
||
return None
|
||
|
||
def _displayCurrentTextures(self, material):
|
||
"""显示当前材质的贴图信息"""
|
||
node = self._findNodeWithMaterial(material)
|
||
if node:
|
||
# 显示当前应用的纹理信息
|
||
texture = node.getTexture()
|
||
if texture:
|
||
texture_name = texture.getName() or "未命名贴图"
|
||
texture_info = QLabel(f"当前贴图: {texture_name}")
|
||
texture_info.setStyleSheet("color: #666; font-size: 10px;")
|
||
self._propertyLayout.addRow("", texture_info)
|
||
|
||
def _applyToAllMaterials(self, model, property_name, value):
|
||
"""将属性应用到模型的所有材质"""
|
||
materials = model.find_all_materials()
|
||
for material in materials:
|
||
if property_name == "base_color":
|
||
material.set_base_color(value)
|
||
elif property_name == "roughness":
|
||
material.set_roughness(value)
|
||
elif property_name == "metallic":
|
||
material.set_metallic(value)
|
||
elif property_name == "ior":
|
||
material.set_refractive_index(value)
|
||
self._invalidateRenderState()
|
||
|
||
def _addShadingModelPanel(self, material):
|
||
"""添加着色模型选择面板"""
|
||
from PyQt5.QtWidgets import QComboBox
|
||
|
||
# RenderPipeline 支持的着色模型
|
||
SHADING_MODELS = [
|
||
("默认", 0),
|
||
("自发光", 1),
|
||
("透明涂层", 2),
|
||
("透明", 3),
|
||
("皮肤", 4),
|
||
("植物", 5),
|
||
]
|
||
|
||
shading_title = QLabel("着色模型")
|
||
shading_title.setStyleSheet("color: #4CAF50; font-weight:bold;")
|
||
self._propertyLayout.addRow(shading_title)
|
||
|
||
shading_combo = QComboBox()
|
||
for name, value in SHADING_MODELS:
|
||
shading_combo.addItem(name)
|
||
|
||
# 安全地获取当前着色模型
|
||
current_model = 0 # 默认值
|
||
try:
|
||
if hasattr(material, 'emission') and material.emission is not None:
|
||
current_model = int(material.emission.x)
|
||
except (AttributeError, TypeError, ValueError):
|
||
current_model = 0
|
||
|
||
shading_combo.setCurrentIndex(current_model)
|
||
|
||
shading_combo.currentIndexChanged.connect(
|
||
lambda idx: self._updateShadingModel(material, idx)
|
||
)
|
||
self._propertyLayout.addRow("着色模型:", shading_combo)
|
||
|
||
def _updateShadingModel(self, material, model_index):
|
||
"""更新着色模型"""
|
||
from panda3d.core import Vec4
|
||
|
||
# 安全地获取当前 emission 值
|
||
current_emission = Vec4(0, 0, 0, 0)
|
||
if hasattr(material, 'emission') and material.emission is not None:
|
||
current_emission = material.emission
|
||
|
||
# 如果切换到自发光模式,设置默认发光强度
|
||
if model_index == 1: # 自发光模式
|
||
default_emission_strength = 2.0 if current_emission.z == 0 else current_emission.z
|
||
new_emission = Vec4(float(model_index), current_emission.y, default_emission_strength, current_emission.w)
|
||
else:
|
||
new_emission = Vec4(float(model_index), current_emission.y, current_emission.z, current_emission.w)
|
||
|
||
material.set_emission(new_emission)
|
||
self._invalidateRenderState()
|
||
|
||
# 刷新UI以更新发光强度控件的值
|
||
if model_index == 1:
|
||
self._refreshMaterialUI()
|
||
|
||
print(f"着色模型已更新为: {model_index}")
|
||
|
||
def _addEmissionPanel(self, material):
|
||
"""添加自发光控制面板"""
|
||
emission_title = QLabel("自发光属性")
|
||
emission_title.setStyleSheet("color: #FF6B6B; font-weight:bold;")
|
||
self._propertyLayout.addRow(emission_title)
|
||
|
||
# 自发光强度
|
||
emission_spinbox = QDoubleSpinBox()
|
||
emission_spinbox.setRange(0.0, 10.0)
|
||
emission_spinbox.setSingleStep(0.1)
|
||
|
||
# 安全地获取当前自发光强度
|
||
current_emission_z = 0.0 # 默认值
|
||
if hasattr(material, 'emission') and material.emission is not None:
|
||
current_emission_z = material.emission.z
|
||
|
||
emission_spinbox.setValue(current_emission_z)
|
||
emission_spinbox.valueChanged.connect(lambda v: self._updateEmissionStrength(material, v))
|
||
self._propertyLayout.addRow("发光强度:", emission_spinbox)
|
||
|
||
def _updateEmissionStrength(self, material, strength):
|
||
"""更新自发光强度"""
|
||
from panda3d.core import Vec4
|
||
|
||
# 安全地获取当前 emission 值
|
||
current_emission = Vec4(0, 0, 0, 0)
|
||
if hasattr(material, 'emission') and material.emission is not None:
|
||
current_emission = material.emission
|
||
|
||
# 更新 emission.z 存储发光强度(用于UI显示)
|
||
new_emission = Vec4(current_emission.x, current_emission.y, strength, current_emission.w)
|
||
material.set_emission(new_emission)
|
||
|
||
# 对于自发光材质,直接调整基础颜色的亮度
|
||
if current_emission.x == 1: # 如果是自发光着色模型
|
||
# 获取原始基础颜色(假设存储在某处,或使用当前值的归一化版本)
|
||
base_intensity = 0.5 # 基础亮度
|
||
intensity_multiplier = strength / 5.0 # 将0-10范围映射到合理的倍数
|
||
|
||
# 设置发光颜色
|
||
emissive_color = Vec4(
|
||
base_intensity * intensity_multiplier,
|
||
base_intensity * intensity_multiplier,
|
||
base_intensity * intensity_multiplier,
|
||
1.0
|
||
)
|
||
material.set_base_color(emissive_color)
|
||
|
||
self._invalidateRenderState()
|
||
print(f"自发光强度已更新为: {strength}")
|
||
|
||
def _addMaterialPresetPanel(self, material):
|
||
"""添加材质预设面板"""
|
||
from PyQt5.QtWidgets import QComboBox
|
||
|
||
preset_title = QLabel("材质预设")
|
||
preset_title.setStyleSheet("color: #9C27B0; font-weight:bold;")
|
||
self._propertyLayout.addRow(preset_title)
|
||
|
||
preset_combo = QComboBox()
|
||
preset_combo.addItems([
|
||
"自定义", "塑料", "金属", "玻璃", "橡胶", "木材", "陶瓷", "皮革"
|
||
])
|
||
|
||
# 优先检查存储的预设名称
|
||
if hasattr(material, '_applied_preset'):
|
||
preset_combo.setCurrentText(material._applied_preset)
|
||
else:
|
||
# 回退到检测当前材质最接近的预设
|
||
current_preset = self._detectCurrentPreset(material)
|
||
preset_combo.setCurrentText(current_preset)
|
||
|
||
preset_combo.currentTextChanged.connect(
|
||
lambda preset: self._applyMaterialPreset(material, preset)
|
||
)
|
||
self._propertyLayout.addRow("选择预设:", preset_combo)
|
||
|
||
def _detectCurrentPreset(self, material):
|
||
"""检测当前材质最接近的预设"""
|
||
# 定义预设的精确匹配条件
|
||
|
||
presets = {
|
||
"塑料": {"base_color": (0.8, 0.8, 0.8), "roughness": 0.7, "metallic": 0.0, "ior": 1.4},
|
||
"金属": {"base_color": (0.7, 0.7, 0.7), "roughness": 0.1, "metallic": 1.0, "ior": 1.5},
|
||
"玻璃": {"base_color": (0.9, 0.9, 1.0), "roughness": 0.0, "metallic": 0.0, "ior": 1.5},
|
||
"橡胶": {"base_color": (0.2, 0.2, 0.2), "roughness": 0.9, "metallic": 0.0, "ior": 1.3},
|
||
"自发光": {"base_color": (1.0, 1.0, 1.0), "roughness": 0.5, "metallic": 0.0, "ior": 1.0}
|
||
}
|
||
|
||
# 容差值,用于浮点数比较
|
||
tolerance = 0.05
|
||
|
||
for preset_name, preset_values in presets.items():
|
||
# 检查基础颜色
|
||
base_color_match = (
|
||
abs(material.base_color.x - preset_values["base_color"][0]) < tolerance and
|
||
abs(material.base_color.y - preset_values["base_color"][1]) < tolerance and
|
||
abs(material.base_color.z - preset_values["base_color"][2]) < tolerance
|
||
)
|
||
|
||
# 检查其他属性
|
||
roughness_match = abs(material.roughness - preset_values["roughness"]) < tolerance
|
||
metallic_match = abs(material.metallic - preset_values["metallic"]) < tolerance
|
||
ior_match = abs(material.refractive_index - preset_values["ior"]) < tolerance
|
||
|
||
# 如果所有属性都匹配,返回预设名称
|
||
if base_color_match and roughness_match and metallic_match and ior_match:
|
||
return preset_name
|
||
|
||
return "自定义" # 如果没有匹配的预设
|
||
|
||
def _applyMaterialPreset(self, material, preset_name):
|
||
"""应用材质预设"""
|
||
presets = {
|
||
"塑料": {"base_color": Vec4(0.8, 0.8, 0.8, 1.0), "roughness": 0.7, "metallic": 0.0, "ior": 1.4},
|
||
"金属": {"base_color": Vec4(0.7, 0.7, 0.7, 1.0), "roughness": 0.1, "metallic": 1.0, "ior": 1.5},
|
||
"玻璃": {"base_color": Vec4(0.9, 0.9, 1.0, 1.0), "roughness": 0.0, "metallic": 0.0, "ior": 1.5,"shading_model":3,"transparency":0.8},
|
||
"橡胶": {"base_color": Vec4(0.2, 0.2, 0.2, 1.0), "roughness": 0.9, "metallic": 0.0, "ior": 1.3},
|
||
"木材": {"base_color": Vec4(0.6, 0.4, 0.2, 1.0), "roughness": 0.8, "metallic": 0.0, "ior": 1.3},
|
||
"陶瓷": {"base_color": Vec4(0.9, 0.9, 0.85, 1.0), "roughness": 0.1, "metallic": 0.0, "ior": 1.6},
|
||
"皮革": {"base_color": Vec4(0.4, 0.3, 0.2, 1.0), "roughness": 0.6, "metallic": 0.0, "ior": 1.4}
|
||
}
|
||
|
||
if preset_name not in presets:
|
||
print(f"未知的材质预设: {preset_name}")
|
||
return
|
||
|
||
preset = presets[preset_name]
|
||
|
||
|
||
|
||
material.set_base_color(preset["base_color"])
|
||
material.set_roughness(preset["roughness"])
|
||
material.set_metallic(preset["metallic"])
|
||
material.set_refractive_index(preset["ior"])
|
||
|
||
if "shading_model" in preset:
|
||
emission = Vec4(float (preset["shading_model"]),0,0,0)
|
||
if "transparency" in preset:
|
||
emission.y = preset["transparency"]
|
||
material.set_emission(emission)
|
||
|
||
#关键:为透明材质应用正确的渲染效果
|
||
# if preset["shading_model"]==3:
|
||
# self._apply_transparent_effect()
|
||
|
||
self._invalidateRenderState()
|
||
#material._applied_preset = preset_name
|
||
self._refreshMaterialUI()
|
||
print(f"已应用材质预设: {preset_name}")
|
||
|
||
def _apply_transparent_effect(self):
|
||
"""为当前选中的模型应用透明渲染效果"""
|
||
current_item = self.world.treeWidget.currentItem()
|
||
if current_item:
|
||
model = current_item.data(0, Qt.UserRole)
|
||
if model:
|
||
# 应用透明渲染效果
|
||
self.world.render_pipeline.set_effect(
|
||
model,
|
||
"effects/default.yaml",
|
||
{"render_forward": True, "render_gbuffer": False},
|
||
100
|
||
)
|
||
print("已应用透明渲染效果")
|
||
|
||
def _refreshMaterialUI(self):
|
||
"""刷新材质 UI 显示"""
|
||
# 重新更新当前选中项的属性面板
|
||
if hasattr(self.world, 'treeWidget') and self.world.treeWidget.currentItem():
|
||
current_item = self.world.treeWidget.currentItem()
|
||
# 触发属性面板更新
|
||
self.updatePropertyPanel(current_item)
|
||
|
||
def _addColorSpacePanel(self, material):
|
||
"""添加颜色空间选择面板"""
|
||
from PyQt5.QtWidgets import QButtonGroup, QRadioButton, QHBoxLayout, QWidget
|
||
|
||
# color_space_title = QLabel("颜色空间")
|
||
# color_space_title.setStyleSheet("color: #FF9800; font-weight:bold;")
|
||
# self._propertyLayout.addRow(color_space_title)
|
||
#
|
||
# color_space_widget = QWidget()
|
||
# color_space_layout = QHBoxLayout(color_space_widget)
|
||
# color_space_group = QButtonGroup()
|
||
|
||
# rgb_radio = QRadioButton("RGB")
|
||
# srgb_radio = QRadioButton("sRGB")
|
||
# hsv_radio = QRadioButton("HSV")
|
||
#
|
||
# rgb_radio.setChecked(True)
|
||
#
|
||
# color_space_group.addButton(rgb_radio, 0)
|
||
# color_space_group.addButton(srgb_radio, 1)
|
||
# color_space_group.addButton(hsv_radio, 2)
|
||
#
|
||
# color_space_layout.addWidget(rgb_radio)
|
||
# color_space_layout.addWidget(srgb_radio)
|
||
# color_space_layout.addWidget(hsv_radio)
|
||
#
|
||
# color_space_group.buttonClicked.connect(
|
||
# lambda button: self._updateColorSpace(material, color_space_group.id(button))
|
||
# )
|
||
#
|
||
# self._propertyLayout.addRow("颜色空间:", color_space_widget)
|
||
|
||
def _addBatchOperationsPanel(self, model):
|
||
"""添加批量操作面板"""
|
||
batch_title = QLabel("批量操作")
|
||
batch_title.setStyleSheet("color: #E91E63; font-weight:bold;")
|
||
self._propertyLayout.addRow(batch_title)
|
||
|
||
# 批量设置粗糙度
|
||
batch_roughness_btn = QPushButton("统一粗糙度")
|
||
batch_roughness_btn.clicked.connect(lambda: self._batchSetRoughness(model))
|
||
self._propertyLayout.addRow("批量粗糙度:", batch_roughness_btn)
|
||
|
||
# 批量设置金属性
|
||
batch_metallic_btn = QPushButton("统一金属性")
|
||
batch_metallic_btn.clicked.connect(lambda: self._batchSetMetallic(model))
|
||
self._propertyLayout.addRow("批量金属性:", batch_metallic_btn)
|
||
|
||
def _batchSetRoughness(self, model):
|
||
"""批量设置粗糙度"""
|
||
from PyQt5.QtWidgets import QInputDialog
|
||
|
||
value, ok = QInputDialog.getDouble(None, "批量设置", "粗糙度值:", 0.5, 0.0, 1.0, 2)
|
||
if ok:
|
||
self._applyToAllMaterials(model, "roughness", value)
|
||
print(f"已将所有材质粗糙度设置为: {value}") |