EG/ui/panels/editor_panels_right.py
2026-03-13 09:53:12 +08:00

697 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from imgui_bundle import imgui, imgui_ctx
from ui.panels.editor_panels_right_collision import EditorPanelsRightCollisionMixin
from ui.panels.editor_panels_right_material import EditorPanelsRightMaterialMixin
from ui.panels.editor_panels_right_transform import EditorPanelsRightTransformMixin
class EditorPanelsRightMixin(
EditorPanelsRightTransformMixin,
EditorPanelsRightMaterialMixin,
EditorPanelsRightCollisionMixin,
):
"""Right panel aggregator mixin."""
def _draw_property_panel(self):
"""绘制属性面板"""
# 使用面板类型的窗口标志支持docking
flags = self.app.style_manager.get_window_flags("panel")
with self.app.style_manager.begin_styled_window("属性面板", self.app.showPropertyPanel, flags) as (_, opened):
if not opened:
self.app.showPropertyPanel = False
return
self.app.showPropertyPanel = opened
# --- LUI Component Properties ---
# 优先检查 LUI 组件选择
if hasattr(self.app, 'lui_manager'):
lui_selected_index = getattr(self.app.lui_manager, "selected_index", -1)
if lui_selected_index >= 0 and self.app.lui_manager.luiFunction:
self.app.lui_manager.luiFunction._draw_component_properties(
self.app.lui_manager,
lui_selected_index
)
return
# --- Scene Node Properties ---
selected_node = self.app._get_selection_node()
if selected_node and not selected_node.isEmpty():
self._draw_node_properties(selected_node)
else:
ssbo_summary = self.app._get_ssbo_selection_summary()
if ssbo_summary:
self._draw_ssbo_selection_summary(ssbo_summary)
return
self._draw_empty_property_state()
def _draw_empty_property_state(self):
imgui.separator_text("属性")
imgui.text_disabled("当前没有选中对象")
imgui.spacing()
imgui.bullet_text("从左侧场景树或场景视口中选择一个对象")
imgui.bullet_text("按 F 聚焦到对象,按 Delete 删除对象")
def _draw_property_section(self, title, draw_callback, default_open=False):
flags = imgui.TreeNodeFlags_.span_avail_width.value
if default_open:
flags |= imgui.TreeNodeFlags_.default_open.value
if imgui.collapsing_header(title, flags):
draw_callback()
imgui.spacing()
def _draw_ssbo_selection_summary(self, summary):
"""Render a safe summary for SSBO group selections without exposing wrong node properties."""
imgui.separator_text("SSBO 选择")
imgui.text(f"名称: {summary.get('display_name') or '未命名'}")
imgui.text(f"对象数量: {summary.get('object_count', 0)}")
if summary.get("is_root"):
imgui.text_colored((0.5, 0.8, 1.0, 1.0), "当前选中的是 SSBO 根模型")
elif summary.get("is_group"):
imgui.text_colored((1.0, 0.8, 0.3, 1.0), "当前选中的是 SSBO 组合节点")
imgui.text_wrapped("这个选择对应多个动态对象。这里先显示摘要,避免误改到错误节点。")
imgui.separator()
imgui.bullet_text("在左侧展开到叶子节点后查看单对象属性")
imgui.bullet_text("继续使用场景中的 Gizmo 做组合移动")
else:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "当前 SSBO 选择没有可直接映射的单个场景节点")
def _draw_node_properties(self, node):
"""绘制节点属性"""
if not node or node.isEmpty():
# 停止变换监控
self.app.stop_transform_monitoring()
return
# 检查是否需要重新启动变换监控
if self.app._monitored_node != node:
self.app.stop_transform_monitoring()
self.app.start_transform_monitoring(node)
# 获取节点基本信息
node_name = node.getName() or "未命名节点"
node_type = self.app._get_node_type_from_node(node)
self._draw_object_overview(node, node_name, node_type)
imgui.spacing()
self._draw_property_section("变换", lambda: self.app._draw_transform_properties(node), default_open=True)
if node_type == "GUI元素":
self._draw_property_section("GUI", lambda: self.app._draw_gui_properties(node), default_open=True)
elif node_type == "光源":
self._draw_property_section("光源", lambda: self.app._draw_light_properties(node), default_open=True)
elif node_type == "模型":
self._draw_property_section("模型", lambda: self.app._draw_model_properties(node), default_open=True)
self._draw_property_section("动画", lambda: self.app._draw_animation_properties(node), default_open=False)
self._draw_property_section("材质", lambda: self.app._draw_appearance_properties(node), default_open=True)
self._draw_property_section("碰撞", lambda: self.app._draw_collision_properties(node), default_open=False)
self._draw_property_section("操作", lambda: self.app._draw_property_actions(node), default_open=False)
def _draw_object_overview(self, node, node_name, node_type):
imgui.separator_text("对象")
user_visible = node.getPythonTag("user_visible")
if user_visible is None:
user_visible = True
node.setPythonTag("user_visible", True)
changed, is_visible = imgui.checkbox("##node_enabled", user_visible)
if changed:
node.setPythonTag("user_visible", is_visible)
if is_visible:
node.show()
else:
node.hide()
imgui.same_line()
imgui.text_disabled("启用")
imgui.same_line()
imgui.set_next_item_width(-1)
changed, new_name = imgui.input_text("##name", node_name, 256)
if changed and hasattr(self.app, "selection"):
self.app._update_node_name(node, new_name)
parent_name = "SceneRoot"
try:
parent = node.getParent()
if parent and not parent.isEmpty():
parent_name = parent.getName() or "SceneRoot"
except Exception:
parent = None
imgui.text_disabled(f"{node_type} · 父级: {parent_name}")
self._draw_status_badges(node, node_type)
def _draw_status_badges(self, node, node_type=None):
"""绘制精简后的对象状态徽章行。"""
if node_type is None:
node_type = self.app._get_node_type_from_node(node)
badges = []
if node.is_hidden():
badges.append(("已隐藏", (0.65, 0.65, 0.65, 1.0)))
has_collision = hasattr(node, "getChild") and any(
"Collision" in child.getName() for child in node.getChildren() if child.getName()
)
if has_collision:
badges.append(("碰撞", (0.35, 0.65, 1.0, 1.0)))
has_script = hasattr(node, "getPythonTag") and node.getPythonTag("script")
if has_script:
badges.append(("脚本", (0.86, 0.48, 0.86, 1.0)))
has_animation = False
if node_type == "模型":
if node.hasTag("has_animations"):
has_animation = node.getTag("has_animations").lower() == "true"
if not has_animation:
cached_result = node.getPythonTag("animation")
if cached_result is True:
has_animation = True
elif cached_result is False:
has_animation = False
else:
has_animation = hasattr(node, "getPythonTag") and node.getPythonTag("animation")
if has_animation:
badges.append(("动画", (0.45, 0.85, 0.55, 1.0)))
has_material = hasattr(node, "getMaterial") and node.getMaterial()
if has_material:
badges.append(("材质", (0.9, 0.72, 0.35, 1.0)))
if not badges:
return
for index, (badge_text, badge_color) in enumerate(badges):
if index > 0:
imgui.same_line()
imgui.text_colored(badge_color, f"[{badge_text}]")
def _draw_gui_properties(self, node):
"""绘制GUI元素属性"""
# 获取GUI元素
gui_element = None
if hasattr(node, 'getPythonTag'):
gui_element = node.getPythonTag('gui_element')
if not gui_element:
imgui.text("无GUI元素数据")
return
# GUI类型信息
gui_type = getattr(gui_element, 'gui_type', 'UNKNOWN')
imgui.text(f"GUI类型: {gui_type}")
# 基本属性
if imgui.collapsing_header("基本属性"):
# 文本内容 (适用于按钮、标签等)
if hasattr(gui_element, 'text'):
changed, new_text = imgui.input_text("文本内容", gui_element.text, 256)
if changed and hasattr(self, 'gui_manager'):
self.gui_manager.editGUIElement(gui_element, 'text', new_text)
# GUI ID
gui_id = getattr(gui_element, 'id', '')
changed, new_id = imgui.input_text("GUI ID", gui_id, 64)
if changed and hasattr(self, 'gui_manager'):
gui_element.id = new_id
# 变换属性
if imgui.collapsing_header("变换属性"):
# 位置
pos = gui_element.getPos()
imgui.text("位置")
if gui_type in ["button", "label", "entry", "2d_image"]:
# 2D GUI组件使用屏幕坐标
imgui.text("屏幕坐标")
logical_x = pos.getX() / 0.1
logical_z = pos.getZ() / 0.1
changed, new_x = imgui.input_float("X##gui_pos_x", logical_x, 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setX(new_x * 0.1)
changed, new_z = imgui.input_float("Y##gui_pos_y", logical_z, 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setZ(new_z * 0.1)
else:
# 3D GUI组件使用世界坐标
imgui.text("世界坐标")
changed, new_x = imgui.input_float("X##gui_world_x", pos.getX(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setX(new_x)
changed, new_y = imgui.input_float("Y##gui_world_y", pos.getY(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setY(new_y)
changed, new_z = imgui.input_float("Z##gui_world_z", pos.getZ(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setZ(new_z)
# 缩放
scale = gui_element.getScale()
imgui.text("缩放")
changed, new_sx = imgui.input_float("X##gui_scale_x", scale.getX(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setSx(new_sx)
changed, new_sy = imgui.input_float("Y##gui_scale_y", scale.getY(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setSy(new_sy)
changed, new_sz = imgui.input_float("Z##gui_scale_z", scale.getZ(), 0.1, 1.0, "%.3f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setSz(new_sz)
# 旋转
hpr = gui_element.getHpr()
imgui.text("旋转")
changed, new_h = imgui.input_float("H##gui_rot_h", hpr.getX(), 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setH(new_h)
changed, new_p = imgui.input_float("P##gui_rot_p", hpr.getY(), 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setP(new_p)
changed, new_r = imgui.input_float("R##gui_rot_r", hpr.getZ(), 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
gui_element.setR(new_r)
# 外观属性
if imgui.collapsing_header("外观属性"):
# 大小
if hasattr(gui_element, 'size'):
size = gui_element.size
imgui.text("大小")
changed, new_w = imgui.input_float("宽度", size[0], 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
new_size = (new_w, size[1])
self.gui_manager.editGUIElement(gui_element, 'size', new_size)
changed, new_h = imgui.input_float("高度", size[1], 1.0, 10.0, "%.1f")
if changed and hasattr(self, 'gui_manager'):
new_size = (size[0], new_h)
self.gui_manager.editGUIElement(gui_element, 'size', new_size)
# 颜色
if hasattr(gui_element, 'getColor'):
try:
color = gui_element.getColor()
# 确保颜色是有效的
if not color or (hasattr(color, '__len__') and len(color) < 3):
color = (1.0, 1.0, 1.0, 1.0) # 默认白色
except:
color = (1.0, 1.0, 1.0, 1.0) # 默认白色
imgui.text("颜色")
# 获取颜色值
if hasattr(color, 'getX'):
# 如果是Panda3D的Vec4对象
r, g, b = color.getX(), color.getY(), color.getZ()
else:
# 如果是元组或其他格式
r, g, b = color[0], color[1], color[2]
changed, new_r = imgui.slider_float("R##gui_color_r", r, 0.0, 1.0)
if changed: gui_element.setColor(new_r, g, b, 1.0)
changed, new_g = imgui.slider_float("G##gui_color_g", g, 0.0, 1.0)
if changed: gui_element.setColor(r, new_g, b, 1.0)
changed, new_b = imgui.slider_float("B##gui_color_b", b, 0.0, 1.0)
if changed: gui_element.setColor(r, g, new_b, 1.0)
# 透明度
imgui.text("透明度")
current_alpha = getattr(gui_element, 'alpha', 1.0)
changed, new_alpha = imgui.slider_float("Alpha", current_alpha, 0.0, 1.0)
if changed:
gui_element.alpha = new_alpha
if hasattr(gui_element, 'setTransparency'):
# 将0.0-1.0范围转换为Panda3D的透明度格式
panda_transparency = int((1.0 - new_alpha) * 255)
gui_element.setTransparency(panda_transparency)
# 渲染顺序
imgui.text("渲染顺序")
current_sort = getattr(gui_element, 'sort', 0)
changed, new_sort = imgui.input_int("Sort Order", current_sort)
if changed:
gui_element.sort = new_sort
if hasattr(gui_element, 'setBin'):
gui_element.setBin('fixed', new_sort)
# 字体设置适用于文本类型的GUI元素
if gui_type in ["button", "label", "entry"]:
imgui.text("字体设置")
# 字体选择
current_font = getattr(gui_element, 'font_path', '')
if imgui.button(f"字体: {Path(current_font).name if current_font else '默认'}##font_select"):
self.show_font_selector(
gui_element,
'font_path',
current_font,
lambda font_path: self._apply_gui_font(gui_element, font_path)
)
# 字体大小
current_size = getattr(gui_element, 'font_size', 12)
changed, new_size = imgui.slider_float("字体大小", current_size, 8.0, 72.0)
if changed:
gui_element.font_size = new_size
self._apply_gui_font_size(gui_element, new_size)
# 字体样式
imgui.text("字体样式")
is_bold = getattr(gui_element, 'font_bold', False)
changed, new_bold = imgui.checkbox("粗体", is_bold)
if changed:
gui_element.font_bold = new_bold
self._apply_gui_font_style(gui_element)
imgui.same_line()
is_italic = getattr(gui_element, 'font_italic', False)
changed, new_italic = imgui.checkbox("斜体", is_italic)
if changed:
gui_element.font_italic = new_italic
self._apply_gui_font_style(gui_element)
def _draw_light_properties(self, node):
"""绘制光源属性"""
imgui.text("光源属性")
# 光源颜色
if hasattr(node, 'getColor'):
try:
color = node.getColor()
# 确保颜色是有效的
if not color or len(color) < 3:
color = (1.0, 1.0, 1.0, 1.0) # 默认白色
except:
color = (1.0, 1.0, 1.0, 1.0) # 默认白色
changed, new_r = imgui.drag_float("颜色 R", color[0], 0.01, 0.0, 1.0)
if changed: node.setColor(new_r, color[1], color[2], color[3] if len(color) > 3 else 1.0)
changed, new_g = imgui.drag_float("颜色 G", color[1], 0.01, 0.0, 1.0)
if changed: node.setColor(color[0], new_g, color[2], color[3] if len(color) > 3 else 1.0)
changed, new_b = imgui.drag_float("颜色 B", color[2], 0.01, 0.0, 1.0)
if changed: node.setColor(color[0], color[1], new_b, color[3] if len(color) > 3 else 1.0)
# 光源强度
imgui.text("光源强度: (暂不支持编辑)")
def _draw_model_properties(self, node):
"""绘制模型属性"""
# 获取模型信息
model_path = node.getTag("model_path") if node.hasTag("model_path") else "未知"
imgui.text("模型路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_path)
# 模型基本信息
imgui.text("模型名称:")
imgui.same_line()
model_name = node.getName() or "未命名模型"
imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_name)
# 模型位置信息
imgui.text("位置:")
imgui.same_line()
pos = node.getPos()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{pos.x:.2f} Y:{pos.y:.2f} Z:{pos.z:.2f}")
# 模型缩放信息
imgui.text("缩放:")
imgui.same_line()
scale = node.getScale()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{scale.x:.2f} Y:{scale.y:.2f} Z:{scale.z:.2f}")
# 模型旋转信息
imgui.text("旋转:")
imgui.same_line()
hpr = node.getHpr()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"H:{hpr.x:.1f}° P:{hpr.y:.1f}° R:{hpr.z:.1f}°")
def _draw_animation_properties(self, node):
"""绘制动画控制属性面板(优化版本,使用缓存避免重复计算)"""
anim_node = node
try:
if hasattr(self, "_resolve_animation_owner_model"):
resolved = self._resolve_animation_owner_model(node)
if resolved and not resolved.isEmpty():
anim_node = resolved
except Exception:
pass
# 路径兜底:当 anim_node 缺少路径时,从 scene_manager.models 中反查祖先模型
try:
needs_path = (not anim_node.hasTag("model_path")) or (not anim_node.getTag("model_path"))
if needs_path and hasattr(self, "scene_manager") and self.scene_manager:
models = getattr(self.scene_manager, "models", [])
for model in list(models):
try:
if not model or model.isEmpty():
continue
if (model == anim_node or model.isAncestorOf(anim_node) or anim_node.isAncestorOf(model)):
if model.hasTag("model_path") and model.getTag("model_path"):
anim_node.setTag("model_path", model.getTag("model_path"))
if model.hasTag("original_path") and model.getTag("original_path"):
anim_node.setTag("original_path", model.getTag("original_path"))
break
if model.hasTag("saved_model_path") and model.getTag("saved_model_path"):
anim_node.setTag("model_path", model.getTag("saved_model_path"))
break
except Exception:
continue
except Exception:
pass
# 先刷新一次模型动画标签,避免“导入后未初始化”导致误判
try:
if hasattr(self, "scene_manager") and self.scene_manager and hasattr(self.scene_manager, "_processModelAnimations"):
self.scene_manager._processModelAnimations(anim_node)
except Exception:
pass
has_animation_tag = anim_node.hasTag("has_animations") and anim_node.getTag("has_animations").lower() == "true"
has_animation_nodes = False
try:
has_animation_nodes = (
anim_node.findAllMatches("**/+Character").getNumPaths() > 0 or
anim_node.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0
)
except Exception:
pass
# 检查是否已经缓存了动画信息
cached_anim_info = anim_node.getPythonTag("cached_anim_info")
cached_processed_names = anim_node.getPythonTag("cached_processed_names")
# 如果之前缓存的是“格式未知”,但现在已有路径,强制重建缓存
try:
now_has_path = anim_node.hasTag("model_path") and bool(anim_node.getTag("model_path"))
if now_has_path and isinstance(cached_anim_info, str) and "格式: 未知" in cached_anim_info:
cached_anim_info = None
cached_processed_names = None
anim_node.setPythonTag("cached_anim_info", None)
anim_node.setPythonTag("cached_processed_names", None)
anim_node.setPythonTag("animation", None)
self._clear_animation_cache(anim_node)
except Exception:
pass
# 如果节点已被检测为有动画,但缓存是“无动画”,强制重新检测一次
if (has_animation_tag or has_animation_nodes) and (cached_anim_info == "无动画" or cached_processed_names == []):
cached_anim_info = None
cached_processed_names = None
anim_node.setPythonTag("cached_anim_info", None)
anim_node.setPythonTag("cached_processed_names", None)
anim_node.setPythonTag("animation", None)
should_force_probe = False
has_cached_animation = anim_node.getPythonTag("animation") is True
if not (has_animation_tag or has_animation_nodes or has_cached_animation):
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "此模型未检测到动画结构")
if imgui.button("尝试强制检测##force_detect_animation"):
should_force_probe = True
anim_node.setPythonTag("cached_anim_info", None)
anim_node.setPythonTag("cached_processed_names", None)
else:
return
# 只有在没有缓存时才进行完整的动画检测和处理
if cached_anim_info is None or cached_processed_names is None:
# 获取Actor
actor = self._getActor(anim_node) if (should_force_probe or has_animation_tag or has_animation_nodes or has_cached_animation) else None
if not actor:
if has_animation_tag or has_animation_nodes:
imgui.text_colored((1.0, 0.7, 0.3, 1.0), "检测到动画结构但当前未成功绑定Actor")
elif should_force_probe:
imgui.text_colored((0.9, 0.6, 0.3, 1.0), "强制检测未发现可播放动画")
anim_node.setPythonTag("animation", False)
anim_node.setPythonTag("cached_processed_names", [])
anim_node.setPythonTag("cached_anim_info", "无动画")
else:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "此模型不包含动画")
return
# 获取和分析动画名称
anim_names = actor.getAnimNames()
processed_names = self._processAnimationNames(anim_node, anim_names)
if not processed_names:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
# 只在明确无动画时缓存空结果,避免误缓存导致后续无法重试
if not (has_animation_tag or has_animation_nodes):
anim_node.setPythonTag("cached_processed_names", [])
anim_node.setPythonTag("cached_anim_info", "无动画")
return
anim_node.setTag("has_animations", "true")
anim_node.setPythonTag("animation", True)
# 计算并缓存动画信息
format_info = self._getModelFormat(anim_node)
animation_info = self._analyzeAnimationQuality(actor, anim_names, format_info)
info_text = f"格式: {format_info} | 动画数量: {len(processed_names)}"
if animation_info:
info_text += f" | {animation_info}"
# 缓存结果
anim_node.setPythonTag("cached_anim_info", info_text)
anim_node.setPythonTag("cached_processed_names", processed_names)
else:
# 使用缓存的数据
info_text = cached_anim_info
processed_names = cached_processed_names
# 如果缓存的空结果,直接返回
if not processed_names:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
return
# 显示动画信息(使用缓存的数据)
imgui.text("信息:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), info_text)
imgui.spacing()
# 动画选择下拉框
imgui.text("动画名称:")
imgui.same_line()
# 获取当前选中的动画
current_anim = anim_node.getPythonTag("selected_animation")
valid_original_names = [original_name for _, original_name in processed_names]
if current_anim is None or current_anim not in valid_original_names:
current_anim = processed_names[0][1] if processed_names else ""
anim_node.setPythonTag("selected_animation", current_anim)
# 查找当前动画的索引
current_index = 0
for i, (display_name, original_name) in enumerate(processed_names):
if original_name == current_anim:
current_index = i
break
# 创建下拉框选项
animation_options = [display_name for display_name, _ in processed_names]
changed, new_index = imgui.combo("##animation_combo", current_index, animation_options)
if changed and new_index < len(processed_names):
selected_display, selected_original = processed_names[new_index]
anim_node.setPythonTag("selected_animation", selected_original)
print(f"选择动画: {selected_display} (原始名称: {selected_original})")
imgui.spacing()
# 控制按钮组
imgui.text("控制:")
# 播放按钮
if imgui.button("播放##play_animation"):
self._playAnimation(anim_node)
imgui.same_line()
# 暂停按钮
if imgui.button("暂停##pause_animation"):
self._pauseAnimation(anim_node)
imgui.same_line()
# 停止按钮
if imgui.button("停止##stop_animation"):
self._stopAnimation(anim_node)
imgui.same_line()
# 循环按钮
if imgui.button("循环##loop_animation"):
self._loopAnimation(anim_node)
imgui.spacing()
# 播放速度控制
imgui.text("播放速度:")
imgui.same_line()
# 获取当前速度
current_speed = anim_node.getPythonTag("anim_speed")
if current_speed is None:
current_speed = 1.0
anim_node.setPythonTag("anim_speed", current_speed)
# 速度滑块
changed, new_speed = imgui.slider_float("##anim_speed", current_speed, 0.1, 5.0, "%.1f")
if changed:
anim_node.setPythonTag("anim_speed", new_speed)
self._setAnimationSpeed(anim_node, new_speed)
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "倍速")
def _draw_property_actions(self, node):
"""绘制属性操作按钮"""
# 重置变换
if imgui.button("重置变换"):
node.setPos(0, 0, 0)
node.setHpr(0, 0, 0)
node.setScale(1, 1, 1)
imgui.same_line()
# 切换可见性
is_visible = not node.is_hidden()
visibility_text = "隐藏" if is_visible else "显示"
if imgui.button(visibility_text):
if is_visible:
node.hide()
else:
node.show()
imgui.same_line()
# 聚焦到对象
if imgui.button("聚焦"):
if hasattr(self, 'selection') and self.selection:
self.selection.focusCameraOnSelectedNodeAdvanced()
# 删除对象
imgui.same_line()
if imgui.button("删除"):
if hasattr(self, 'selection') and self.selection:
self.selection.deleteSelectedNode()