EG/main.py

823 lines
31 KiB
Python
Raw Permalink 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 panda3d.core import loadPrcFileData, WindowProperties, Point3
from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from panda3d.core import NodePath
import p3dimgui
from imgui_bundle import imgui, imgui_ctx
import sys
import os
import warnings
from pathlib import Path
# 导入MyWorld类和必要的模块
from core.world import CoreWorld
from core.selection import SelectionSystem
from core.event_handler import EventHandler
from core.tool_manager import ToolManager
from core.script_system import ScriptManager
from core.Command_System import CommandManager
from core.terrain_manager import TerrainManager
from scene.scene_manager import SceneManager
from project.project_manager import ProjectManager
from core.collision_manager import CollisionManager
from core.CustomMouseController import CustomMouseController
from core.resource_manager import ResourceManager
from ui.lui_manager import LUIManager
from ui.panels.editor_panels import EditorPanels
from ui.panels.script_panels import ScriptPanels
from ui.panels.dialog_panels import DialogPanels
from ui.panels.interaction_panels import InteractionPanels
from ui.panels.create_actions import CreateActions
from ui.panels.runtime_actions import RuntimeActions
from ui.panels.object_factory import ObjectFactory
from ui.panels.animation_tools import AnimationTools
from ui.panels.property_helpers import PropertyHelpers
from ui.panels.app_actions import AppActions
from ui.panels.panel_delegates import PanelDelegates
from core.model_drag_drop import ModelDragDropService
from ssbo_component.ssbo_editor import SSBOEditor
from TransformGizmo.transform_gizmo import TransformGizmo
try:
# 尝试导入视频管理器,避免循环导入
import importlib.util
spec = importlib.util.spec_from_file_location("video_integration", "demo/video_integration.py")
video_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(video_module)
VideoManager = video_module.VideoManager
except:
# 如果video_integration模块不存在则跳过
VideoManager = None
print("⚠ 视频管理器模块未找到,视频功能将不可用")
warnings.filterwarnings("ignore", category=DeprecationWarning)
class MyWorld(PanelDelegates, CoreWorld):
def __init__(self):
super().__init__()
# 设置窗口为最大化模式
props = WindowProperties()
#props.set_maximized(True)
self.win.request_properties(props)
print("✓ 窗口已设置为最大化模式")
# 初始化选择和变换系统
self.selection = SelectionSystem(self)
# 绑定F键用于聚焦选中节点
self.accept("f", self.onFocusKeyPressed)
self.accept("F", self.onFocusKeyPressed) # 大写F
self.use_ssbo_mouse_picking = True
self.use_ssbo_scene_import = True
if not self.use_ssbo_mouse_picking:
self.accept("mouse1", self.onMouseClick)
# Keep release/move bindings even in SSBO mode so gizmo drag can work.
self.accept("mouse1-up", self.onMouseRelease)
self.accept("mouse-move", self.onMouseMove)
self.accept("drag", self.onMouseMove)
# 初始化事件处理系统
self.event_handler = EventHandler(self)
# 初始化工具管理系统
self.tool_manager = ToolManager(self)
# 初始化脚本管理系统
self.script_manager = ScriptManager(self)
# 初始化LUI管理系统
self.lui_manager = LUIManager(self)
# 新的坐标系
self.newTransform = TransformGizmo(self)
self._setup_transform_gizmo_light_sync()
# 初始化视频管理
if VideoManager is not None:
self.video_manager = VideoManager(self)
else:
self.video_manager = None
# 初始化场景管理系统
# print(f"[DEBUG] 初始化场景管理系统...")
self.scene_manager = SceneManager(self)
# print(f"[DEBUG] 场景管理系统初始化完成")
# 初始化项目管理系统
# print(f"[DEBUG] 初始化项目管理系统...")
self.project_manager = ProjectManager(self)
# print(f"[DEBUG] 项目管理系统初始化完成")
self.command_manager = CommandManager()
# 初始化碰撞管理器
self.collision_manager = CollisionManager(self)
# 初始化资源管理器
self.resource_manager = ResourceManager(self)
# 初始化Actor缓存系统用于动画控制
self._actor_cache = {}
print("✓ Actor缓存系统初始化完成")
# 初始化自定义鼠标控制器(视角移动)
self.mouse_controller = CustomMouseController(self)
self.mouse_controller.setUp(mouse_speed=25, move_speed=20)
print("✓ 自定义鼠标控制器初始化完成")
# 初始化VR管理器
try:
from core.vr import VRManager
self.vr_manager = VRManager(self)
print("✓ VR管理器初始化完成")
except Exception as e:
print(f"⚠ VR管理器初始化失败: {e}")
self.vr_manager = None
# VR调试相关变量
self.vr_debug_enabled = False
self.vr_detailed_mode = True
self.vr_performance_monitor = False
# 调试选项
self.debug_collision = True # 是否显示碰撞体
# 默认启用模型间碰撞检测(可选)
if self.use_ssbo_mouse_picking:
self.enableModelCollisionDetection(enable=False, frequency=0.1, threshold=0.5)
else:
self.enableModelCollisionDetection(enable=True, frequency=0.1, threshold=0.5)
# 碰撞检测UI相关变量
self._selected_collision_shape = "球形 (Sphere)" # 默认选择的碰撞形状
# 启动脚本系统
self.script_manager.start_system()
self.terrain_manager = TerrainManager(self)
self.terrain_edit_radius = 3.0
self.terrain_edit_strength=0.3
self.terrain_edit_operation = "add"
# Install Dear ImGui
p3dimgui.init(
wantPlaceManager=False,
wantExplorerManager=False,
wantTimeSliderManager=False,
)
# Let ssbo_component reuse the existing imgui backend instance.
self.imgui_backend = self.imgui
# Initialize SSBO editor and let it own mouse1 picking.
# Do not auto-load a default model here; models are loaded from import flow.
self.ssbo_editor = None
if self.use_ssbo_mouse_picking:
self.ssbo_editor = SSBOEditor(
base_app=self,
render_pipeline=self.render_pipeline,
model_path=None,
font_path=None,
)
self.ssbo_editor.bind_transform_gizmo(self.newTransform)
print("SSBOEditor mouse picking enabled (waiting for imported model)")
imgui.get_io().config_flags |= imgui.ConfigFlags_.docking_enable
print("✓ ImGui Docking功能已启用")
# 初始化样式管理器
from core.imgui_style_manager import ImGuiStyleManager
self.style_manager = ImGuiStyleManager(self.imgui, self)
self.editor_panels = EditorPanels(self)
self.script_panels = ScriptPanels(self)
self.dialog_panels = DialogPanels(self)
self.interaction_panels = InteractionPanels(self)
self.create_actions = CreateActions(self)
self.runtime_actions = RuntimeActions(self)
self.object_factory = ObjectFactory(self)
self.animation_tools = AnimationTools(self)
self.property_helpers = PropertyHelpers(self)
self.app_actions = AppActions(self)
# 简化的初始化字体设置(只使用中文字体)
try:
# 先清除默认字体
self.imgui.io.fonts.clear()
# 尝试加载中文字体
import platform
system = platform.system().lower()
if system == "linux":
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
elif system == "windows":
font_path = "C:/Windows/Fonts/msyh.ttc"
elif system == "darwin":
font_path = "/System/Library/Fonts/PingFang.ttc"
else:
font_path = None
if font_path and Path(font_path).exists():
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
print(f"✓ 初始化中文字体成功: {font_path}")
else:
# 回退到原来的字体
fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
if fallback_font_path and Path(fallback_font_path).exists():
self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
print(f"✓ 初始化回退字体成功: {fallback_font_path}")
else:
self.imgui.io.fonts.add_font_default()
print("✓ 初始化使用默认字体")
except Exception as e:
print(f"⚠ 初始化字体系统失败: {e}")
# 备用方案:使用默认字体
try:
self.imgui.io.fonts.add_font_default()
print("✓ 使用备用默认字体")
except:
pass
# Disable the camera trackball controls.
self.disableMouse()
self.showDemoWindow = False
# UI状态管理
self.showSceneTree = True
self.showPropertyPanel = True
self.showConsole = True
self.showScriptPanel = not self.use_ssbo_mouse_picking
self.showToolbar = True
self.showResourceManager = True
self.showWebPanel = False
self.webPanelUrl = "https://www.baidu.com"
# 脚本系统状态变量
self.hotReloadEnabled = True
self._new_script_name = "new_script"
self._selected_template = 0
self._mount_script_index = 0
# 变换监控相关
self._transform_monitoring = False
self._monitored_node = None
self._last_transform_values = {}
self._transform_update_timer = 0
self._transform_update_interval = 0.05 # 50ms检查一次
self._clipboard_pos = None # 位置剪贴板
# 颜色选择器相关
self._color_picker_active = False
self._color_picker_target = None # (target_object, property_name)
self._color_picker_current_color = (1.0, 1.0, 1.0, 1.0)
self._color_picker_callback = None
# 字体选择器相关
self._font_selector_active = False
self._font_selector_target = None # (target_object, property_name)
self._font_selector_current_font = ""
self._font_selector_callback = None
self._available_fonts = [] # 可用字体列表
self._refresh_available_fonts()
# 菜单状态管理
self.show_new_project_dialog = False
self.show_open_project_dialog = False
self.show_save_as_dialog = False
# 路径选择对话框状态
self.show_path_browser = False
self.path_browser_mode = "" # "new_project" 或 "open_project"
self.path_browser_current_path = os.getcwd()
self.path_browser_selected_path = ""
self.path_browser_items = []
# 快捷键映射
self.shortcut_keys = {
'Ctrl+N': self._on_new_project,
'Ctrl+O': self._on_open_project,
'Ctrl+S': self._on_save_project,
'Alt+F4': self._on_exit
}
# 键盘状态跟踪
self.ctrl_pressed = False
self.alt_pressed = False
# 消息系统
self.messages = []
self.max_messages = 5 # 最多显示5条消息
# 剪切板系统
self.clipboard = []
self.clipboard_mode = "" # "copy" 或 "cut"
# 视角控制状态
self.camera_control_enabled = True
self.show_camera_info = False
# 拖拽导入状态
self.dragged_files = []
self.is_dragging = False
self.show_drag_overlay = False
self.drag_drop_monitor = None
self._resource_manager_window_rect = None
self._resource_drop_targets = []
self._scene_tree_window_rect = None
self._property_panel_window_rect = None
self._script_panel_window_rect = None
self._console_window_rect = None
self._toolbar_window_rect = None
self._drag_scene_tree_hover_node = None
self.model_drag_drop = ModelDragDropService(self)
self.showLUIEditor = not self.use_ssbo_mouse_picking
# 导入功能状态
self.show_import_dialog = False
self.import_file_path = ""
self.supported_formats = [".gltf", ".glb", ".fbx", ".bam", ".egg", ".obj"]
# 创建功能状态
self.show_3d_text_dialog = False
self.show_3d_image_dialog = False
self.show_gui_button_dialog = False
self.show_gui_label_dialog = False
self.show_gui_entry_dialog = False
self.show_gui_image_dialog = False
self.show_video_screen_dialog = False
self.show_2d_video_screen_dialog = False
self.show_spherical_video_dialog = False
self.show_virtual_screen_dialog = False
self.show_spot_light_dialog = False
self.show_point_light_dialog = False
self.show_terrain_dialog = False
self.show_heightmap_browser = False
self.show_script_dialog = False
self.show_script_browser = False
# 高度图浏览器状态
self.heightmap_browser_current_path = os.getcwd()
self.heightmap_browser_selected_path = ""
self.heightmap_browser_items = []
self.heightmap_file_path = ""
self.supported_heightmap_formats = [".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".tif"]
# 脚本浏览器状态
self.script_browser_current_path = os.getcwd()
self.script_browser_selected_path = ""
self.script_browser_items = []
# 对话框参数存储
self.dialog_params = {} # 存储各种对话框的参数
# 脚本系统状态
self.hotReloadEnabled = False
# 初始化高度图浏览器
self._refresh_heightmap_browser()
# 初始化脚本浏览器
self._refresh_script_browser()
self.accept('imgui-new-frame', self.__newFrame)
self.accept('`', self.__toggleImgui)
# 添加键盘事件监听用于快捷键
self.accept('control', self._on_ctrl_pressed)
self.accept('control-up', self._on_ctrl_released)
self.accept('alt', self._on_alt_pressed)
self.accept('alt-up', self._on_alt_released)
self.accept('n', self._on_n_pressed)
self.accept('o', self._on_o_pressed)
self.accept('control-s', self._on_save_project)
self.accept('f4', self._on_f4_pressed)
# 编辑功能快捷键
self.accept('control-z', self._on_undo)
self.accept('control-y', self._on_redo)
self.accept('control-x', self._on_cut)
self.accept('control-c', self._on_copy)
self.accept('control-v', self._on_paste)
self.accept('delete', self._on_delete_pressed)
self.accept('escape', self._on_escape_pressed)
# 滚轮事件
self.accept('wheel_up', self._on_wheel_up)
self.accept('wheel_down', self._on_wheel_down)
self.testTexture = None
self.icons = {} # 初始化图标字典
# 添加初始化消息
self.add_success_message("MyWorld 初始化完成")
self.add_info_message("ImGui菜单系统已就绪")
self.add_info_message("快捷键已启用 (Ctrl+N, Ctrl+O, Ctrl+S, Alt+F4)")
# 启用拖拽导入功能
self.setup_drag_drop_support()
self.add_info_message("拖拽导入功能已启用 - 可将3D文件拖拽到窗口中导入")
print("✓ MyWorld 初始化完成")
# ==================== 兼容性属性 ====================
# 保留models属性以兼容现有代码
@property
def models(self):
"""模型列表的兼容性属性"""
return self.scene_manager.models
@models.setter
def models(self, value):
"""模型列表的兼容性设置器"""
self.scene_manager.models = value
# 保留currentTool属性以兼容现有代码
@property
def currentTool(self):
"""当前工具的兼容性属性"""
return self.tool_manager.currentTool
@currentTool.setter
def currentTool(self, value):
"""当前工具的兼容性设置器"""
self.tool_manager.currentTool = value
# 保留terrains属性以兼容现有代码
@property
def terrains(self):
"""地形列表的兼容性属性"""
return self.terrain_manager.terrains
@terrains.setter
def terrains(self,value):
"""地形列表的兼容性设置器"""
self.terrain_manager.terrains = value
def onFocusKeyPressed(self):
"""处理 F 键按下事件"""
try:
if hasattr(self, 'selection') and self.selection.selectedNode:
self.selection.focusCameraOnSelectedNodeAdvanced()
else:
print("当前没有选中任何节点")
except Exception as e:
print(f"处理 F 键事件失败: {e}")
def onMouseClick(self):
"""处理鼠标点击事件"""
print("\n=== 鼠标点击事件触发 ===")
try:
# 检查鼠标是否有效
if not self.mouseWatcherNode.hasMouse():
print("❌ 鼠标无效或不在窗口内")
return
print("✓ 鼠标位置有效")
# 获取鼠标位置
mouse_x = self.mouseWatcherNode.getMouseX()
mouse_y = self.mouseWatcherNode.getMouseY()
print(f"📍 鼠标标准化坐标: ({mouse_x:.3f}, {mouse_y:.3f})")
# 转换为窗口坐标
winWidth, winHeight = self.win.getSize()
window_x = (mouse_x + 1) * 0.5 * winWidth
window_y = (1 - mouse_y) * 0.5 * winHeight
print(f"📍 鼠标窗口坐标: ({window_x:.1f}, {window_y:.1f})")
print(f"📐 窗口尺寸: {winWidth} x {winHeight}")
# 检查ImGui是否捕获了鼠标
imgui_captured = self.processImGuiMouseClick(window_x, window_y)
print(f"🖱️ ImGui捕获状态: {imgui_captured}")
if imgui_captured:
print("❌ ImGui处理了该事件跳过3D场景选择")
return
# 调用事件处理器进行射线检测和选择
if hasattr(self, 'event_handler'):
print("✓ 找到事件处理器,开始处理选择")
self.event_handler.mousePressEventLeft({
'x': window_x,
'y': window_y
})
else:
print("❌ 未找到事件处理器")
except Exception as e:
print(f"❌ 处理鼠标点击事件失败: {e}")
import traceback
traceback.print_exc()
def onMouseRelease(self):
"""处理鼠标释放事件"""
try:
# 检查鼠标是否有效
if not self.mouseWatcherNode.hasMouse():
return
# 获取鼠标位置
mouse_x = self.mouseWatcherNode.getMouseX()
mouse_y = self.mouseWatcherNode.getMouseY()
# 转换为窗口坐标
winWidth, winHeight = self.win.getSize()
window_x = (mouse_x + 1) * 0.5 * winWidth
window_y = (1 - mouse_y) * 0.5 * winHeight
# 调用事件处理器
if hasattr(self, 'event_handler'):
self.event_handler.mouseReleaseEventLeft({
'x': window_x,
'y': window_y
})
except Exception as e:
print(f"处理鼠标释放事件失败: {e}")
def onMouseMove(self):
"""处理鼠标移动事件"""
try:
# 检查鼠标是否有效
if not self.mouseWatcherNode.hasMouse():
return
# 获取鼠标位置
mouse_x = self.mouseWatcherNode.getMouseX()
mouse_y = self.mouseWatcherNode.getMouseY()
# 转换为窗口坐标
winWidth, winHeight = self.win.getSize()
window_x = (mouse_x + 1) * 0.5 * winWidth
window_y = (1 - mouse_y) * 0.5 * winHeight
# 调用事件处理器
if hasattr(self, 'event_handler'):
self.event_handler.mouseMoveEvent({
'x': window_x,
'y': window_y
})
except Exception as e:
print(f"处理鼠标移动事件失败: {e}")
def enableModelCollisionDetection(self, enable=True, frequency=0.1, threshold=0.5):
"""启用模型间碰撞检测"""
return self.collision_manager.enableModelCollisionDetection(enable, frequency, threshold)
def __toggleImgui(self):
if not self.imgui.isKeyboardCaptured():
self.imgui.toggle()
def __newFrame(self):
# Dear ImGui commands can be placed here.
# 创建全屏DockSpace在第一帧之后创建
if imgui.get_frame_count() > 0:
viewport = imgui.get_main_viewport()
imgui.dock_space_over_viewport(0, viewport, imgui.DockNodeFlags_.passthru_central_node)
# 在第一帧应用样式
if imgui.get_frame_count() == 0:
self.style_manager.apply_style()
# 加载图标
self.icons = {
'select': self.style_manager.load_icon('select_tool'),
'move': self.style_manager.load_icon('move_tool'),
'rotate': self.style_manager.load_icon('rotate_tool'),
'scale': self.style_manager.load_icon('scale_tool'),
'success': self.style_manager.load_icon('success_icon'),
'warning': self.style_manager.load_icon('warning_icon'),
'property_select_image': self.style_manager.load_icon('property_select_image'),
'delete_fail_icon': self.style_manager.load_icon('delete_fail_icon'),
}
# 简化字体加载(只使用中文字体)
try:
# 清除现有字体
self.imgui.io.fonts.clear()
# 尝试加载中文字体
import platform
from pathlib import Path
system = platform.system().lower()
if system == "linux":
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
elif system == "windows":
font_path = "C:/Windows/Fonts/msyh.ttc"
elif system == "darwin":
font_path = "/System/Library/Fonts/PingFang.ttc"
else:
font_path = None
if font_path and Path(font_path).exists():
self.imgui.io.fonts.add_font_from_file_ttf(font_path, 14.0)
print(f"✓ 中文字体加载成功: {font_path}")
else:
# 回退到原来的字体
fallback_font_path = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
if fallback_font_path and Path(fallback_font_path).exists():
self.imgui.io.fonts.add_font_from_file_ttf(fallback_font_path, 14.0)
print(f"✓ 回退字体加载成功: {fallback_font_path}")
else:
self.imgui.io.fonts.add_font_default()
print("✓ 使用默认字体")
except Exception as e:
print(f"⚠ 字体初始化失败: {e}")
try:
self.imgui.io.fonts.add_font_default()
print("✓ 使用备用默认字体")
except:
pass
# 获取窗口尺寸
display_size = imgui.get_io().display_size
window_width = display_size.x
window_height = display_size.y
# 每帧重置窗口命中区域,由各面板绘制时重新填充
self._resource_manager_window_rect = None
self._resource_drop_targets = []
self._scene_tree_window_rect = None
self._property_panel_window_rect = None
self._script_panel_window_rect = None
self._console_window_rect = None
self._toolbar_window_rect = None
self._drag_scene_tree_hover_node = None
# 绘制菜单栏
self._draw_menu_bar()
# 绘制停靠布局
self._draw_docked_layout(window_width, window_height)
# 处理系统层外部拖入
self._process_external_drop_events()
# 绘制纹理测试窗口
if self.testTexture:
with self.style_manager.begin_styled_window("Texture Test", True,
imgui.WindowFlags_.always_auto_resize) as (_, windowOpen):
if not windowOpen:
self.imgui.removeTexture(self.testTexture)
self.testTexture = None
return
imgui.image(self.testTexture, (256, 256))
# 绘制ImGui演示窗口
if self.showDemoWindow:
imgui.show_demo_window()
# 绘制对话框
self._draw_new_project_dialog()
self._draw_open_project_dialog()
self._draw_path_browser()
self._draw_import_dialog()
# 绘制颜色选择器
self._draw_color_picker()
# 绘制字体选择器
self._draw_font_selector()
self._draw_spot_light_dialog()
self._draw_point_light_dialog()
self._draw_terrain_dialog()
self._draw_heightmap_browser()
self._draw_script_dialog()
# 绘制纹理选择对话框
self._draw_texture_file_dialog()
self._draw_script_browser()
# 绘制右键菜单
self._draw_context_menus()
# 绘制拖拽界面
self._draw_drag_drop_interface()
# 绘制LUI编辑器
if self.showLUIEditor and hasattr(self, 'lui_manager'):
self.lui_manager.draw_editor()
# 更新变换监控
dt = imgui.get_io().delta_time
self.update_transform_monitoring(dt)
def _draw_docked_layout(self, window_width, window_height):
"""绘制可停靠的布局(支持拖拽)"""
# 左侧场景树面板
if self.showSceneTree:
self._draw_scene_tree()
# 资源管理器面板
if self.showResourceManager:
self._draw_resource_manager()
# 属性面板
if self.showPropertyPanel:
self._draw_property_panel()
# Web面板
if self.showWebPanel:
self._draw_web_panel()
# 脚本面板
if self.showScriptPanel:
self._draw_script_panel()
# 底部控制台
if self.showConsole:
self._draw_console()
# 顶部工具栏
if self.showToolbar:
self._draw_toolbar()
def _sync_rp_light_from_node(self, node):
"""将灯光包装节点的位置同步到 RenderPipeline 灯光对象。"""
try:
if not node or node.isEmpty() or (not node.hasPythonTag("rp_light_object")):
# 兼容旧场景:仅有 light_type 标签时尝试补绑 rp_light_object
if not node or node.isEmpty() or (not node.hasTag("light_type")):
return False
scene_manager = getattr(self, "scene_manager", None)
if scene_manager:
try:
light_type = node.getTag("light_type")
if light_type == "spot_light" and hasattr(scene_manager, "_recreateSpotLight"):
scene_manager._recreateSpotLight(node)
elif light_type == "point_light" and hasattr(scene_manager, "_recreatePointLight"):
scene_manager._recreatePointLight(node)
except Exception:
pass
light_obj = node.getPythonTag("rp_light_object") if node.hasPythonTag("rp_light_object") else None
if not light_obj:
return False
world_pos = node.getPos(self.render)
try:
light_obj.setPos(world_pos)
except Exception:
try:
light_obj.setPos(world_pos.x, world_pos.y, world_pos.z)
except Exception:
try:
light_obj.pos = Point3(world_pos)
except Exception:
return False
return True
except Exception:
return False
def _on_transform_gizmo_drag_event(self, payload):
"""TransformGizmo 拖拽事件回调:实时同步灯光位置。"""
try:
node = payload.get("target") if isinstance(payload, dict) else None
if node and (not node.isEmpty()):
self._sync_rp_light_from_node(node)
except Exception:
pass
def _setup_transform_gizmo_light_sync(self):
"""为 newTransform 注册灯光同步事件钩子。"""
tg = getattr(self, "newTransform", None)
if not tg:
return
try:
from TransformGizmo.events import GizmoEvent
hooks = {
"move": {
GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
},
"rotate": {
GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
},
"scale": {
GizmoEvent.DRAG_MOVE: [self._on_transform_gizmo_drag_event],
GizmoEvent.DRAG_END: [self._on_transform_gizmo_drag_event],
},
}
tg.set_event_hooks(hooks, replace=False)
except Exception as e:
print(f"绑定 TransformGizmo 灯光同步事件失败: {e}")
if __name__ == "__main__":
demo = MyWorld()
demo.run()