839 lines
31 KiB
Python
839 lines
31 KiB
Python
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.InfoPanelManager import InfoPanelManager
|
||
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__()
|
||
self._configure_main_window()
|
||
self._init_legacy_compat_fields()
|
||
self._init_core_services_non_vr()
|
||
|
||
# 初始化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._init_runtime_features_non_vr()
|
||
self._init_imgui_runtime()
|
||
self._init_panel_modules()
|
||
self._init_startup_fonts()
|
||
self._init_ui_runtime_state()
|
||
self._bind_runtime_events()
|
||
self._finalize_startup()
|
||
|
||
def _configure_main_window(self):
|
||
props = WindowProperties()
|
||
#props.set_maximized(True)
|
||
self.win.request_properties(props)
|
||
print("✓ 窗口已设置为最大化模式")
|
||
|
||
def _init_legacy_compat_fields(self):
|
||
# Legacy compatibility fields used by scene/project modules.
|
||
self.gui_elements = []
|
||
self.gui_manager = None
|
||
self.interface_manager = None
|
||
self.guiEditMode = False
|
||
self.currentGUITool = None
|
||
|
||
def _init_core_services_non_vr(self):
|
||
# 初始化选择和变换系统
|
||
self.selection = SelectionSystem(self)
|
||
|
||
# 绑定F键用于聚焦选中节点
|
||
self.accept("f", self.onFocusKeyPressed)
|
||
self.accept("F", self.onFocusKeyPressed) # 大写F
|
||
|
||
self.use_ssbo_mouse_picking = 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
|
||
|
||
self.scene_manager = SceneManager(self)
|
||
self.project_manager = ProjectManager(self)
|
||
self.info_panel_manager = InfoPanelManager(self)
|
||
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("✓ 自定义鼠标控制器初始化完成")
|
||
|
||
def _init_runtime_features_non_vr(self):
|
||
# 调试选项
|
||
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"
|
||
|
||
def _init_imgui_runtime(self):
|
||
# 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功能已启用")
|
||
|
||
def _init_panel_modules(self):
|
||
# 初始化样式管理器
|
||
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)
|
||
|
||
def _init_startup_fonts(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()
|
||
|
||
def _init_ui_runtime_state(self):
|
||
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()
|
||
|
||
def _bind_runtime_events(self):
|
||
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)
|
||
|
||
def _finalize_startup(self):
|
||
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()
|