forked from Rowland/EG
Merge remote-tracking branch 'origin/addRender' into main_ch_eg
# Conflicts: # .gitignore # .idea/AugmentWebviewStateStore.xml # RenderPipelineFile/config/daytime.yaml # ui/property_panel.py
This commit is contained in:
commit
bb76795478
BIN
.gitignore
vendored
BIN
.gitignore
vendored
Binary file not shown.
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
10
.idea/AugmentWebviewStateStore.xml
generated
10
.idea/AugmentWebviewStateStore.xml
generated
File diff suppressed because one or more lines are too long
2
.idea/EG.iml
generated
2
.idea/EG.iml
generated
@ -4,7 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (EG)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 (PythonProject)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
||||
15
.idea/inspectionProfiles/Project_Default.xml
generated
15
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,15 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ourVersions">
|
||||
<value>
|
||||
<list size="2">
|
||||
<item index="0" class="java.lang.String" itemvalue="2.7" />
|
||||
<item index="1" class="java.lang.String" itemvalue="3.14" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,5 +3,5 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (PythonProject)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (EG)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (PythonProject)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/EG.iml" filepath="$PROJECT_DIR$/.idea/EG.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
File diff suppressed because one or more lines are too long
@ -105,6 +105,37 @@ class SelectionSystem:
|
||||
if not self.selectionBox or not self.selectionBoxTarget:
|
||||
return
|
||||
|
||||
# 检查是否需要重新计算边界框
|
||||
if not hasattr(self, '_bounds_cache'):
|
||||
self._bounds_cache = {}
|
||||
|
||||
node_name = self.selectionBoxTarget.getName()
|
||||
import time
|
||||
current_time = time.time()
|
||||
|
||||
# 如果缓存存在且未过期,则使用缓存
|
||||
if (node_name in self._bounds_cache and
|
||||
current_time - self._bounds_cache[node_name]['time'] < 0.1):
|
||||
minPoint, maxPoint = self._bounds_cache[node_name]['bounds']
|
||||
else:
|
||||
# 计算新的边界框并缓存
|
||||
minPoint = Point3()
|
||||
maxPoint = Point3()
|
||||
if not self.selectionBoxTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
|
||||
return
|
||||
|
||||
# 缓存结果
|
||||
self._bounds_cache[node_name] = {
|
||||
'bounds': (minPoint, maxPoint),
|
||||
'time': current_time
|
||||
}
|
||||
|
||||
# 清理旧缓存
|
||||
expired_keys = [k for k, v in self._bounds_cache.items()
|
||||
if current_time - v['time'] > 1.0]
|
||||
for key in expired_keys:
|
||||
del self._bounds_cache[key]
|
||||
|
||||
# 清除现有的几何体
|
||||
self.selectionBox.removeNode()
|
||||
self.selectionBox = self.world.render.attachNewNode("selectionBox")
|
||||
@ -177,6 +208,15 @@ class SelectionSystem:
|
||||
def updateSelectionBoxTask(self, task):
|
||||
"""选择框更新任务"""
|
||||
try:
|
||||
if not hasattr(self,'_last_selection_box_update'):
|
||||
self._last_selection_box_update = 0
|
||||
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self._last_selection_box_update < 0.1:
|
||||
return task.cont
|
||||
self._last_selection_box_update = current_time
|
||||
|
||||
if not self.selectionBox or not self.selectionBoxTarget:
|
||||
return task.done # 结束任务
|
||||
|
||||
@ -598,6 +638,16 @@ class SelectionSystem:
|
||||
def updateGizmoTask(self, task):
|
||||
"""坐标轴更新任务 - 包含固定大小功能"""
|
||||
try:
|
||||
# 限制更新频率
|
||||
if not hasattr(self, '_last_gizmo_update'):
|
||||
self._last_gizmo_update = 0
|
||||
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self._last_gizmo_update < 0.05: # 每0.05秒更新一次
|
||||
return task.cont
|
||||
self._last_gizmo_update = current_time
|
||||
|
||||
if not self.gizmo or not self.gizmoTarget:
|
||||
return task.done
|
||||
|
||||
@ -613,25 +663,27 @@ class SelectionSystem:
|
||||
self.gizmoTarget.setPos(light_pos)
|
||||
|
||||
else:
|
||||
# 更新坐标轴位置,始终在目标节点中心
|
||||
minPoint = Point3()
|
||||
maxPoint = Point3()
|
||||
if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
|
||||
# 计算中心点
|
||||
center = Point3((minPoint.x + maxPoint.x) * 0.5,
|
||||
(minPoint.y + maxPoint.y) * 0.5,
|
||||
(minPoint.z + maxPoint.z) * 0.5)
|
||||
self.gizmo.setPos(center)
|
||||
|
||||
# 【关键修复】:更新坐标轴朝向以跟踪父节点的变化
|
||||
parent_node = self.gizmoTarget.getParent()
|
||||
if parent_node and parent_node != self.world.render:
|
||||
# 子节点:坐标轴朝向跟随父节点
|
||||
parent_hpr = parent_node.getHpr()
|
||||
self.gizmo.setHpr(parent_hpr)
|
||||
else:
|
||||
# 顶级模型:使用世界坐标系朝向
|
||||
self.gizmo.setHpr(0, 0, 0)
|
||||
# 只在必要时更新位置和朝向
|
||||
self._updateGizmoPositionAndOrientation()
|
||||
# # 更新坐标轴位置,始终在目标节点中心
|
||||
# minPoint = Point3()
|
||||
# maxPoint = Point3()
|
||||
# if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
|
||||
# # 计算中心点
|
||||
# center = Point3((minPoint.x + maxPoint.x) * 0.5,
|
||||
# (minPoint.y + maxPoint.y) * 0.5,
|
||||
# (minPoint.z + maxPoint.z) * 0.5)
|
||||
# self.gizmo.setPos(center)
|
||||
#
|
||||
# # 【关键修复】:更新坐标轴朝向以跟踪父节点的变化
|
||||
# parent_node = self.gizmoTarget.getParent()
|
||||
# if parent_node and parent_node != self.world.render:
|
||||
# # 子节点:坐标轴朝向跟随父节点
|
||||
# parent_hpr = parent_node.getHpr()
|
||||
# self.gizmo.setHpr(parent_hpr)
|
||||
# else:
|
||||
# # 顶级模型:使用世界坐标系朝向
|
||||
# self.gizmo.setHpr(0, 0, 0)
|
||||
|
||||
# 【新功能】:动态调整坐标轴大小,保持固定的屏幕大小
|
||||
self._updateGizmoScreenSize()
|
||||
@ -642,6 +694,33 @@ class SelectionSystem:
|
||||
print(f"坐标轴更新任务出错: {str(e)}")
|
||||
return task.done
|
||||
|
||||
def _updateGizmoPositionAndOrientation(self):
|
||||
"""优化的Gizmo位置和朝向更新"""
|
||||
# 只在必要时重新计算边界框
|
||||
if not hasattr(self, '_last_gizmo_bounds_update'):
|
||||
self._last_gizmo_bounds_update = 0
|
||||
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self._last_gizmo_bounds_update > 0.2: # 每0.2秒计算一次边界框
|
||||
minPoint = Point3()
|
||||
maxPoint = Point3()
|
||||
if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
|
||||
# 计算中心点
|
||||
center = Point3((minPoint.x + maxPoint.x) * 0.5,
|
||||
(minPoint.y + maxPoint.y) * 0.5,
|
||||
(minPoint.z + maxPoint.z) * 0.5)
|
||||
self.gizmo.setPos(center)
|
||||
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)
|
||||
else:
|
||||
self.gizmo.setHpr(0, 0, 0)
|
||||
|
||||
def _updateGizmoScreenSize(self):
|
||||
"""动态调整坐标轴大小,保持固定的屏幕大小"""
|
||||
try:
|
||||
@ -1203,33 +1282,23 @@ class SelectionSystem:
|
||||
|
||||
# 确定轴向量的变换上下文
|
||||
if parent_node and parent_node != self.world.render:
|
||||
# 子节点:使用父节点的局部坐标系
|
||||
# print(f"子节点拖拽 - 父节点: {parent_node.getName()}, 父节点旋转: {parent_node.getHpr()}")
|
||||
# transform_context = parent_node
|
||||
transform_mat = parent_node.getMat(self.world.render)
|
||||
world_axis_vector = transform_mat.xformVec(local_axis_vector)
|
||||
else:
|
||||
world_axis_vector = local_axis_vector
|
||||
# 顶级模型:使用世界坐标系
|
||||
# print(f"顶级模型拖拽 - 使用世界坐标系")
|
||||
# transform_context = self.world.render
|
||||
axis_end = gizmo_world_pos + world_axis_vector
|
||||
|
||||
#axis_end = gizmo_world_pos + world_axis_vector
|
||||
|
||||
# 投影到屏幕空间
|
||||
def worldToScreen(worldPos):
|
||||
try:
|
||||
# 先转换为相机坐标系
|
||||
camPos = self.world.cam.getRelativePoint(self.world.render, worldPos)
|
||||
|
||||
# 检查是否在相机前方
|
||||
if camPos.getY() <= 0:
|
||||
return None
|
||||
|
||||
screenPos = Point2()
|
||||
if self.world.cam.node().getLens().project(camPos, screenPos):
|
||||
# 获取准确的窗口尺寸
|
||||
winWidth, winHeight = self.world.getWindowSize()
|
||||
|
||||
winX = (screenPos.x + 1) * 0.5 * winWidth
|
||||
winY = (1 - screenPos.y) * 0.5 * winHeight
|
||||
return (winX, winY)
|
||||
@ -1237,28 +1306,45 @@ class SelectionSystem:
|
||||
except Exception as e:
|
||||
print(f"世界坐标转屏幕坐标失败: {e}")
|
||||
return None
|
||||
axis_start_screen = worldToScreen(gizmo_world_pos)
|
||||
axis_end_world = gizmo_world_pos + world_axis_vector
|
||||
axis_end_screen = worldToScreen(axis_end_world)
|
||||
#gizmo_screen = worldToScreen(gizmo_world_pos)
|
||||
#axis_screen = worldToScreen(axis_end)
|
||||
|
||||
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 gizmo_screen:
|
||||
print("拖拽更新失败: 坐标轴中心不在屏幕内")
|
||||
return
|
||||
if not axis_screen:
|
||||
print("拖拽更新失败: 坐标轴端点不在屏幕内")
|
||||
if not axis_start_screen or not axis_end_screen:
|
||||
print("拖拽更新失败: 无法获取轴线屏幕坐标")
|
||||
return
|
||||
|
||||
# 计算轴在屏幕空间的方向向量
|
||||
screen_axis_dir = (
|
||||
axis_screen[0] - gizmo_screen[0],
|
||||
axis_screen[1] - gizmo_screen[1]
|
||||
axis_end_screen[0] - axis_start_screen[0],
|
||||
axis_end_screen[1] - axis_start_screen[1]
|
||||
)
|
||||
|
||||
|
||||
# 归一化屏幕轴方向
|
||||
import math
|
||||
length = math.sqrt(screen_axis_dir[0]**2 + screen_axis_dir[1]**2)
|
||||
if length > 0:
|
||||
screen_axis_dir = (screen_axis_dir[0] / length, screen_axis_dir[1] / length)
|
||||
#screen_axis_dir = (screen_axis_dir[0] / length, screen_axis_dir[1] / length)
|
||||
screen_axis_dir = (
|
||||
screen_axis_dir[0] / length,
|
||||
screen_axis_dir[1] / length
|
||||
)
|
||||
|
||||
else:
|
||||
print("拖拽更新失败: 屏幕轴方向长度为0")
|
||||
return
|
||||
@ -1267,29 +1353,54 @@ class SelectionSystem:
|
||||
projected_distance = (mouseDeltaX * screen_axis_dir[0] +
|
||||
mouseDeltaY * screen_axis_dir[1])
|
||||
|
||||
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
|
||||
cam_pos = self.world.cam.getPos(self.world.render)
|
||||
distance_to_object = (cam_pos - gizmo_world_pos).length()
|
||||
|
||||
lens = self.world.cam.node().getLens()
|
||||
fov = lens.getFov()[0]
|
||||
winWidth,winHeight = self.world.getWindowSize()
|
||||
|
||||
fixed_pixel_to_world_ratio = 0.01 # 1像素 = 0.01世界单位
|
||||
scale_factor = fixed_pixel_to_world_ratio * scale_adjustment
|
||||
pixels_to_world_units = (2*distance_to_object*math.tan(math.radians(fov/2)))/winWidth
|
||||
|
||||
movement_distance = projected_distance * pixels_to_world_units
|
||||
|
||||
total_scale_factor = 1.0
|
||||
current_node = self.gizmoTarget.getParent()
|
||||
|
||||
while current_node and current_node != self.world.render:
|
||||
node_scale = current_node.getScale()
|
||||
avg_scale = (node_scale.x+node_scale.y + node_scale.z) / 3.0
|
||||
total_scale_factor *= avg_scale
|
||||
current_node = current_node.getParent()
|
||||
|
||||
if total_scale_factor > 0:
|
||||
movement_distance = movement_distance / total_scale_factor
|
||||
|
||||
movement_distance = projected_distance * scale_factor
|
||||
# 获取当前位置并只修改选中轴的坐标
|
||||
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":
|
||||
@ -1332,7 +1443,7 @@ class SelectionSystem:
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self._last_drag_debug_time > 0.1: # 每0.1秒最多输出一次
|
||||
print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}")
|
||||
#print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}")
|
||||
self._last_drag_debug_time = current_time
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@ -21,25 +21,6 @@ class PropertyPanelManager:
|
||||
self._propertyLayout = None
|
||||
self._actor_cache={}
|
||||
|
||||
# 定义紧凑样式
|
||||
self.compact_style = """
|
||||
QDoubleSpinBox {
|
||||
min-width: 45px;
|
||||
}
|
||||
QPushButton {
|
||||
min-width: 10px;
|
||||
}
|
||||
QComboBox {
|
||||
min-width: 60px;
|
||||
}
|
||||
QLineEdit {
|
||||
min-width: 60px;
|
||||
}
|
||||
QCheckBox {
|
||||
min-width: 20px;
|
||||
}
|
||||
"""
|
||||
|
||||
def setPropertyLayout(self, layout):
|
||||
"""设置属性面板布局引用"""
|
||||
print("开始设置属性布局")
|
||||
@ -77,10 +58,6 @@ class PropertyPanelManager:
|
||||
|
||||
self.clearPropertyPanel()
|
||||
|
||||
# 应用紧凑样式到属性面板容器
|
||||
if self._propertyLayout.parent():
|
||||
self._propertyLayout.parent().setStyleSheet(self.compact_style)
|
||||
|
||||
itemText = item.text(0)
|
||||
|
||||
# 如果点击的是场景根节点,显示提示信息
|
||||
@ -215,9 +192,24 @@ class PropertyPanelManager:
|
||||
self.pos_z.setValue(relativePos.getZ())
|
||||
|
||||
# 连接位置变化事件
|
||||
self.pos_x.valueChanged.connect(lambda v: model.setX(parent, v) if parent else model.setX(v))
|
||||
self.pos_y.valueChanged.connect(lambda v: model.setY(parent, v) if parent else model.setY(v))
|
||||
self.pos_z.valueChanged.connect(lambda v: model.setZ(parent, v) if parent else model.setZ(v))
|
||||
# self.pos_x.valueChanged.connect(lambda v: model.setX(parent, v) if parent else model.setX(v))
|
||||
# self.pos_y.valueChanged.connect(lambda v: model.setY(parent, v) if parent else model.setY(v))
|
||||
# self.pos_z.valueChanged.connect(lambda v: model.setZ(parent, v) if parent else model.setZ(v))
|
||||
|
||||
def updateXPosition(value):
|
||||
model.setX(value)
|
||||
self.refreshModelValues(model)
|
||||
self.pos_x.valueChanged.connect(updateXPosition)
|
||||
|
||||
def updateYPosition(value):
|
||||
model.setY(value)
|
||||
self.refreshModelValues(model)
|
||||
self.pos_y.valueChanged.connect(updateYPosition)
|
||||
|
||||
def updateZPosition(value):
|
||||
model.setZ(value)
|
||||
self.refreshModelValues(model)
|
||||
self.pos_z.valueChanged.connect(updateZPosition)
|
||||
|
||||
# 创建并设置 X, Y, Z 标签居中
|
||||
x_label1 = QLabel("X")
|
||||
@ -332,6 +324,50 @@ class PropertyPanelManager:
|
||||
# 材质属性组
|
||||
self._updateModelMaterialPanel(model)
|
||||
|
||||
def refreshModelValues(self,nodePath):
|
||||
if not nodePath or self._propertyLayout is None:
|
||||
return
|
||||
parent = nodePath.getParent()
|
||||
render = self.world.render
|
||||
relPos = nodePath.getPos(parent) if parent else nodePath.getPos()
|
||||
if hasattr(self,'pos_x') and self.pos_x:
|
||||
self.pos_x.blockSignals(True)
|
||||
self.pos_x.setValue(relPos.getX())
|
||||
self.pos_x.blockSignals(False)
|
||||
if hasattr(self,'pos_y') and self.pos_y:
|
||||
self.pos_y.blockSignals(True)
|
||||
self.pos_y.setValue(relPos.getY())
|
||||
self.pos_y.blockSignals(False)
|
||||
if hasattr(self,'pos_z') and self.pos_z:
|
||||
self.pos_z.blockSignals(True)
|
||||
self.pos_z.setValue(relPos.getZ())
|
||||
self.pos_z.blockSignals(False)
|
||||
|
||||
worldPos = nodePath.getPos(render)
|
||||
for axis,attr in zip(('x','y','z'),('world_pos_x','world_pos_y','world_pos_z')):
|
||||
spin = getattr(self,attr,None)
|
||||
if spin:
|
||||
spin.blockSignals(True)
|
||||
spin.setValue(getattr(worldPos,axis))
|
||||
spin.blockSignals(False)
|
||||
|
||||
hpr = nodePath.getHpr()
|
||||
for idx,(attr,val) in enumerate(zip(('rot_h','rot_p','rot_r'),hpr)):
|
||||
spin = getattr(self,attr,None)
|
||||
if spin:
|
||||
spin.blockSignals(True)
|
||||
spin.setValue(val)
|
||||
spin.blockSignals(False)
|
||||
|
||||
scale = nodePath.getScale()
|
||||
for axis,attr in zip(('x','y','z'),('scale_x','scale_y','scale_z')):
|
||||
spin = getattr(self,attr,None)
|
||||
if spin:
|
||||
spin.blockSignals(True)
|
||||
spin.setValue(getattr(scale,axis))
|
||||
spin.blockSignals(False)
|
||||
|
||||
|
||||
|
||||
def updateGUIPropertyPanel(self, gui_element):
|
||||
"""更新GUI元素属性面板"""
|
||||
@ -3892,7 +3928,6 @@ class PropertyPanelManager:
|
||||
if preset_name in presets:
|
||||
azimuth, altitude = presets[preset_name]
|
||||
|
||||
# 更新滑块和数值框
|
||||
# 更新滑块和数值框
|
||||
self.azimuthSpinBox.blockSignals(True)
|
||||
self.altitudeSpinBox.blockSignals(True)
|
||||
@ -4962,20 +4997,18 @@ except Exception as e:
|
||||
# 如果有多种类型的动画,使用标签页
|
||||
if len(animations) > 1:
|
||||
tab_widget = QTabWidget()
|
||||
tab_widget.setMinimumWidth(200) # 设置最小宽度
|
||||
|
||||
for anim_type, anim_data in animations.items():
|
||||
tab = QWidget()
|
||||
tab_layout = QGridLayout(tab) # 改为QGridLayout保持一致
|
||||
tab_layout = QVBoxLayout(tab)
|
||||
self._buildAnimationTypeUI(tab_layout, origin_model, anim_type, anim_data)
|
||||
tab_widget.addTab(tab, self._getAnimTypeDisplayName(anim_type))
|
||||
|
||||
layout.addWidget(QLabel("动画类型:"), 1, 0)
|
||||
layout.addWidget(tab_widget, 1, 1, 1, 3)
|
||||
self._propertyLayout.addRow("动画类型:", tab_widget)
|
||||
else:
|
||||
# 只有一种类型,直接显示
|
||||
anim_type, anim_data = next(iter(animations.items()))
|
||||
self._buildAnimationTypeUI(layout, origin_model, anim_type, anim_data)
|
||||
self._buildAnimationTypeUI(self._propertyLayout, origin_model, anim_type, anim_data)
|
||||
|
||||
# 存储动画信息供控制使用
|
||||
if not hasattr(self, '_non_skeletal_cache'):
|
||||
@ -4986,69 +5019,46 @@ except Exception as e:
|
||||
"""为特定动画类型构建UI"""
|
||||
from PyQt5.QtWidgets import QLabel, QComboBox, QHBoxLayout, QWidget, QPushButton, QDoubleSpinBox
|
||||
|
||||
current_row = layout.rowCount()
|
||||
|
||||
if anim_type == 'transform':
|
||||
# 变换动画控制
|
||||
self.ns_transform_combo = QComboBox()
|
||||
self.ns_transform_combo.addItems(anim_data['names'])
|
||||
self.ns_transform_combo.setMinimumWidth(80)
|
||||
layout.addWidget(QLabel("变换动画:"), current_row, 0)
|
||||
layout.addWidget(self.ns_transform_combo, current_row, 1, 1, 3)
|
||||
current_row += 1
|
||||
layout.addRow("变换动画:", self.ns_transform_combo)
|
||||
|
||||
elif anim_type == 'texture':
|
||||
# 纹理动画控制
|
||||
self.ns_texture_combo = QComboBox()
|
||||
self.ns_texture_combo.addItems(anim_data['stages'])
|
||||
self.ns_texture_combo.setMinimumWidth(80)
|
||||
layout.addWidget(QLabel("纹理动画:"), current_row, 0)
|
||||
layout.addWidget(self.ns_texture_combo, current_row, 1, 1, 3)
|
||||
current_row += 1
|
||||
layout.addRow("纹理动画:", self.ns_texture_combo)
|
||||
|
||||
elif anim_type == 'material':
|
||||
# 材质动画控制
|
||||
self.ns_material_combo = QComboBox()
|
||||
self.ns_material_combo.addItems(anim_data['properties'])
|
||||
self.ns_material_combo.setMinimumWidth(80)
|
||||
layout.addWidget(QLabel("材质动画:"), current_row, 0)
|
||||
layout.addWidget(self.ns_material_combo, current_row, 1, 1, 3)
|
||||
current_row += 1
|
||||
layout.addRow("材质动画:", self.ns_material_combo)
|
||||
|
||||
elif anim_type == 'lerp':
|
||||
# Lerp动画控制
|
||||
self.ns_lerp_combo = QComboBox()
|
||||
self.ns_lerp_combo.addItems(anim_data['intervals'])
|
||||
self.ns_lerp_combo.setMinimumWidth(80)
|
||||
layout.addWidget(QLabel("Lerp动画:"), current_row, 0)
|
||||
layout.addWidget(self.ns_lerp_combo, current_row, 1, 1, 3)
|
||||
current_row += 1
|
||||
layout.addRow("Lerp动画:", self.ns_lerp_combo)
|
||||
|
||||
# 通用控制按钮
|
||||
btn_box = QWidget()
|
||||
btn_lay = QHBoxLayout(btn_box)
|
||||
btn_lay.setContentsMargins(0, 0, 0, 0)
|
||||
for txt, cmd in (("播放", "play"), ("暂停", "pause"), ("停止", "stop"), ("循环", "loop")):
|
||||
btn = QPushButton(txt)
|
||||
btn.setMinimumWidth(35) # 设置按钮最小宽度
|
||||
btn.setMaximumWidth(50) # 限制按钮最大宽度
|
||||
btn.clicked.connect(lambda _, c=cmd, t=anim_type: self._controlNonSkeletalAnimation(origin_model, t, c))
|
||||
btn_lay.addWidget(btn)
|
||||
layout.addWidget(QLabel("控制:"), current_row, 0)
|
||||
layout.addWidget(btn_box, current_row, 1, 1, 3)
|
||||
current_row += 1
|
||||
layout.addRow("控制:", btn_box)
|
||||
|
||||
# 播放速度
|
||||
speed_spinbox = QDoubleSpinBox()
|
||||
speed_spinbox.setRange(0.1, 5.0)
|
||||
speed_spinbox.setSingleStep(0.1)
|
||||
speed_spinbox.setValue(1.0)
|
||||
speed_spinbox.setMinimumWidth(60)
|
||||
speed_spinbox.setMaximumWidth(80)
|
||||
speed_spinbox.valueChanged.connect(
|
||||
lambda v, t=anim_type: self._setNonSkeletalAnimationSpeed(origin_model, t, v))
|
||||
layout.addWidget(QLabel("播放速度:"), current_row, 0)
|
||||
layout.addWidget(speed_spinbox, current_row, 1, 1, 3)
|
||||
speed_spinbox.valueChanged.connect(lambda v, t=anim_type: self._setNonSkeletalAnimationSpeed(origin_model, t, v))
|
||||
layout.addRow("播放速度:", speed_spinbox)
|
||||
|
||||
def _getAnimTypeDisplayName(self, anim_type):
|
||||
"""获取动画类型的显示名称"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user