EG/ui/panels/dialog_panels.py

1306 lines
59 KiB
Python

import os
import platform
import sys
from imgui_bundle import imgui, imgui_ctx
class DialogPanels:
"""Project, import, and creation dialog panels."""
def __init__(self, app):
self.app = app
def __getattr__(self, name):
return getattr(self.app, name)
def __setattr__(self, name, value):
if name == "app" or name in self.__dict__ or hasattr(type(self), name):
object.__setattr__(self, name, value)
else:
setattr(self.app, name, value)
def _execute_scene_create_command(self, creator):
"""Create a scene node through the shared undo/redo stack."""
command_manager = getattr(self, "command_manager", None)
if not command_manager:
return creator()
from core.Command_System import CreateNodeCommand
command = CreateNodeCommand(lambda _parent: creator(), getattr(self, "render", None), world=self)
command_manager.execute_command(command)
if not command.created_node:
command_manager.pop_last_command()
return None
return command.created_node
def _draw_new_project_dialog(self):
"""绘制新建项目对话框"""
if not self.show_new_project_dialog:
return
# 初始化默认值
if not hasattr(self, 'new_project_name'):
self.new_project_name = "新项目"
if not hasattr(self, 'new_project_path'):
self.new_project_path = "./projects/"
# 设置对话框标志
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
dialog_width = 400
dialog_height = 300
self.style_manager.prepare_centered_dialog(dialog_width, dialog_height)
with imgui_ctx.begin("新建项目", True, flags) as window:
if not window.opened:
self.show_new_project_dialog = False
return
imgui.text("创建新项目")
imgui.separator()
# 项目名称输入
changed, project_name = imgui.input_text("项目名称", self.new_project_name, 256)
if changed:
self.new_project_name = project_name
# 项目路径输入
changed, project_path = imgui.input_text("项目路径", self.new_project_path, 256)
if changed:
self.new_project_path = project_path
imgui.same_line()
if imgui.button("浏览..."):
self.path_browser_mode = "new_project"
self.path_browser_current_path = os.path.dirname(self.new_project_path) if self.new_project_path else os.getcwd()
self.show_path_browser = True
self._refresh_path_browser()
imgui.separator()
# 按钮区域
if imgui.button("创建"):
if self.new_project_name and self.new_project_path:
self._create_new_project(self.new_project_name, self.new_project_path)
self.show_new_project_dialog = False
imgui.same_line()
if imgui.button("取消"):
self.show_new_project_dialog = False
def _draw_open_project_dialog(self):
"""绘制打开项目对话框"""
if not self.show_open_project_dialog:
return
# 初始化默认值
if not hasattr(self, 'open_project_path'):
self.open_project_path = "./projects/"
# 设置对话框标志
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
dialog_width = 500
dialog_height = 400
self.style_manager.prepare_centered_dialog(dialog_width, dialog_height)
with imgui_ctx.begin("打开项目", True, flags) as window:
if not window.opened:
self.show_open_project_dialog = False
return
imgui.text("选择项目")
imgui.separator()
imgui.text("项目路径:")
changed, project_path = imgui.input_text("##project_path", self.open_project_path, 512)
if changed:
self.open_project_path = project_path
imgui.same_line()
if imgui.button("浏览..."):
self.path_browser_mode = "open_project"
self.path_browser_current_path = self.open_project_path if self.open_project_path else os.getcwd()
self.show_path_browser = True
self._refresh_path_browser()
imgui.separator()
# 按钮区域
if imgui.button("打开"):
if self.open_project_path:
self._open_project_path(self.open_project_path)
self.show_open_project_dialog = False
imgui.same_line()
if imgui.button("取消"):
self.show_open_project_dialog = False
def _draw_save_as_project_dialog(self):
"""绘制另存为项目对话框。"""
if not self.show_save_as_dialog:
return
if not getattr(self, "save_as_project_name", "").strip():
current_project_path = getattr(getattr(self, "project_manager", None), "current_project_path", None)
default_name = os.path.basename(current_project_path) if current_project_path else "新项目"
self.save_as_project_name = f"{default_name}_副本"
if not getattr(self, "save_as_project_path", "").strip():
current_project_path = getattr(getattr(self, "project_manager", None), "current_project_path", None)
self.save_as_project_path = os.path.dirname(current_project_path) if current_project_path else os.getcwd()
flags = (
imgui.WindowFlags_.no_resize
| imgui.WindowFlags_.no_collapse
| imgui.WindowFlags_.modal
)
self.style_manager.prepare_centered_dialog(460, 240)
with imgui_ctx.begin("项目另存为", True, flags) as window:
if not window.opened:
self.show_save_as_dialog = False
return
imgui.text("将当前项目保存到新的项目目录")
imgui.separator()
changed, project_name = imgui.input_text("项目名称", self.save_as_project_name, 256)
if changed:
self.save_as_project_name = project_name
changed, project_path = imgui.input_text("保存位置", self.save_as_project_path, 512)
if changed:
self.save_as_project_path = project_path
imgui.same_line()
if imgui.button("浏览..."):
self.path_browser_mode = "save_as_project"
self.path_browser_current_path = self.save_as_project_path if self.save_as_project_path else os.getcwd()
self.show_path_browser = True
self._refresh_path_browser()
imgui.separator()
target_path = os.path.normpath(os.path.join(self.save_as_project_path or "", self.save_as_project_name or ""))
imgui.text("目标路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), target_path)
if imgui.button("保存"):
if self.save_as_project_name.strip() and self.save_as_project_path.strip():
if self._save_project_as_impl(self.save_as_project_name.strip(), self.save_as_project_path.strip()):
self.show_save_as_dialog = False
else:
self.add_warning_message("项目名称和保存位置不能为空")
imgui.same_line()
if imgui.button("取消"):
self.show_save_as_dialog = False
def _draw_build_project_dialog(self):
"""绘制项目打包对话框。"""
if not getattr(self, "show_build_project_dialog", False):
self._build_project_dialog_initialized = False
return
project_manager = getattr(self, "project_manager", None)
profile_options = project_manager.get_build_profile_options() if project_manager and hasattr(project_manager, "get_build_profile_options") else {}
current_project_path = getattr(project_manager, "current_project_path", None) if project_manager else None
active_profile_name = str(profile_options.get("active_profile", "default") or "default")
profile_names = list(profile_options.get("profile_names", []) or ["default"])
should_refresh_dialog_state = not getattr(self, "_build_project_dialog_initialized", False)
if should_refresh_dialog_state:
if current_project_path:
output_dir = str(profile_options.get("output_dir", "") or "").strip()
self.build_output_path = os.path.normpath(os.path.join(current_project_path, output_dir.replace("/", os.sep))) if output_dir else os.path.dirname(current_project_path)
else:
self.build_output_path = os.getcwd()
self.build_active_profile = active_profile_name
self.build_new_profile_name = ""
self.build_exe_name = str(profile_options.get("exe_name", "") or "")
self.build_startup_scene_guid = str(profile_options.get("startup_scene_guid", "") or "")
self.build_enabled_scene_guids = list(profile_options.get("enabled_scene_guids", []) or [])
self._build_project_dialog_initialized = True
if not hasattr(self, "build_active_profile"):
self.build_active_profile = active_profile_name
if not hasattr(self, "build_new_profile_name"):
self.build_new_profile_name = ""
if not hasattr(self, "build_exe_name"):
self.build_exe_name = str(profile_options.get("exe_name", "") or "")
if not hasattr(self, "build_startup_scene_guid"):
self.build_startup_scene_guid = str(profile_options.get("startup_scene_guid", "") or "")
if not hasattr(self, "build_enabled_scene_guids"):
self.build_enabled_scene_guids = list(profile_options.get("enabled_scene_guids", []) or [])
flags = (
imgui.WindowFlags_.no_resize
| imgui.WindowFlags_.no_collapse
| imgui.WindowFlags_.modal
)
self.style_manager.prepare_centered_dialog(540, 320)
with imgui_ctx.begin("打包项目", True, flags) as window:
if not window.opened:
self.show_build_project_dialog = False
self._build_project_dialog_initialized = False
return
imgui.text("生成当前项目的最终运行程序")
imgui.separator()
current_profile_index = profile_names.index(self.build_active_profile) if self.build_active_profile in profile_names else 0
changed, new_profile_index = imgui.combo("构建配置", current_profile_index, profile_names)
if changed and 0 <= new_profile_index < len(profile_names):
selected_profile = profile_names[new_profile_index]
self.build_active_profile = selected_profile
if project_manager and hasattr(project_manager, "update_active_build_profile"):
project_manager.update_active_build_profile(active_profile_name=selected_profile)
profile_options = project_manager.get_build_profile_options() if project_manager and hasattr(project_manager, "get_build_profile_options") else profile_options
if current_project_path:
output_dir = str(profile_options.get("output_dir", "") or "").strip()
self.build_output_path = os.path.normpath(os.path.join(current_project_path, output_dir.replace("/", os.sep))) if output_dir else os.path.dirname(current_project_path)
self.build_exe_name = str(profile_options.get("exe_name", "") or "")
self.build_startup_scene_guid = str(profile_options.get("startup_scene_guid", "") or "")
self.build_enabled_scene_guids = list(profile_options.get("enabled_scene_guids", []) or [])
changed, new_profile_name = imgui.input_text("新配置名称", self.build_new_profile_name, 128)
if changed:
self.build_new_profile_name = new_profile_name
imgui.same_line()
if imgui.button("复制当前配置"):
if project_manager and hasattr(project_manager, "create_build_profile") and self.build_new_profile_name.strip():
if project_manager.create_build_profile(self.build_new_profile_name.strip()):
self.build_active_profile = self.build_new_profile_name.strip()
self.build_new_profile_name = ""
profile_options = project_manager.get_build_profile_options() if hasattr(project_manager, "get_build_profile_options") else profile_options
profile_names = list(profile_options.get("profile_names", []) or ["default"])
self.build_exe_name = str(profile_options.get("exe_name", "") or "")
self.build_startup_scene_guid = str(profile_options.get("startup_scene_guid", "") or "")
self.build_enabled_scene_guids = list(profile_options.get("enabled_scene_guids", []) or [])
changed, output_path = imgui.input_text("输出目录", self.build_output_path, 512)
if changed:
self.build_output_path = output_path
imgui.same_line()
if imgui.button("浏览..."):
self.path_browser_mode = "build_project"
self.path_browser_current_path = self.build_output_path if self.build_output_path else os.getcwd()
self.show_path_browser = True
self._refresh_path_browser()
changed, exe_name = imgui.input_text("EXE名称", self.build_exe_name, 256)
if changed:
self.build_exe_name = exe_name
scene_entries = list(profile_options.get("scenes", []) or [])
current_scene_index = 0
scene_labels = []
for index, scene_entry in enumerate(scene_entries):
guid = str(scene_entry.get("guid", "") or "")
label = str(scene_entry.get("name", guid or f"Scene {index + 1}") or f"Scene {index + 1}")
scene_labels.append(label)
if guid and guid == self.build_startup_scene_guid:
current_scene_index = index
if scene_labels:
changed, new_index = imgui.combo("启动场景", current_scene_index, scene_labels)
if changed and 0 <= new_index < len(scene_entries):
self.build_startup_scene_guid = str(scene_entries[new_index].get("guid", "") or "")
if scene_entries:
imgui.text("启用场景")
imgui.begin_child("##build_enabled_scenes", (0, 90), True)
enabled_set = set(self.build_enabled_scene_guids or [])
for scene_entry in scene_entries:
scene_guid = str(scene_entry.get("guid", "") or "")
scene_name = str(scene_entry.get("name", scene_guid or "未命名场景") or "未命名场景")
checked = scene_guid in enabled_set
changed, checked = imgui.checkbox(f"{scene_name}##{scene_guid}", checked)
if changed:
if checked:
enabled_set.add(scene_guid)
else:
enabled_set.discard(scene_guid)
if checked and not self.build_startup_scene_guid:
self.build_startup_scene_guid = scene_guid
imgui.end_child()
self.build_enabled_scene_guids = [scene.get("guid", "") for scene in scene_entries if scene.get("guid", "") in enabled_set]
if self.build_startup_scene_guid and self.build_startup_scene_guid not in enabled_set:
self.build_startup_scene_guid = self.build_enabled_scene_guids[0] if self.build_enabled_scene_guids else ""
imgui.separator()
if imgui.button("开始打包"):
if project_manager and hasattr(project_manager, "update_active_build_profile"):
output_dir_value = self.build_output_path
if current_project_path:
try:
output_dir_value = os.path.relpath(self.build_output_path, current_project_path)
except ValueError:
output_dir_value = self.build_output_path
project_manager.update_active_build_profile(
active_profile_name=self.build_active_profile,
output_dir=output_dir_value,
exe_name=self.build_exe_name,
startup_scene_guid=self.build_startup_scene_guid,
enabled_scene_guids=self.build_enabled_scene_guids,
)
if self._build_project_impl(self.build_output_path):
self.show_build_project_dialog = False
imgui.same_line()
if imgui.button("取消"):
self.show_build_project_dialog = False
self._build_project_dialog_initialized = False
def _draw_path_browser(self):
"""绘制路径选择对话框"""
if not self.show_path_browser:
return
# 设置对话框标志
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
dialog_width = 600
dialog_height = 500
self.style_manager.prepare_centered_dialog(dialog_width, dialog_height)
with imgui_ctx.begin("选择路径", True, flags) as window:
if not window.opened:
self.show_path_browser = False
return
imgui.text("选择路径")
imgui.separator()
# 当前路径显示
imgui.text("当前路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), self.path_browser_current_path)
imgui.separator()
# 路径导航按钮
if imgui.button("上级目录"):
parent_path = os.path.dirname(self.path_browser_current_path)
if parent_path != self.path_browser_current_path:
self.path_browser_current_path = parent_path
self._refresh_path_browser()
imgui.same_line()
if imgui.button("主目录"):
self.path_browser_current_path = os.path.expanduser("~")
self._refresh_path_browser()
imgui.same_line()
if imgui.button("当前目录"):
self.path_browser_current_path = os.getcwd()
self._refresh_path_browser()
imgui.separator()
# 文件和目录列表
if self.path_browser_items:
# 先显示目录
for item in self.path_browser_items:
if item['is_dir']:
# 尝试使用图标或文本标识目录
if self.icons.get('property_select_image'): # 使用现有图标作为文件夹图标
imgui.image(self.icons['property_select_image'], (16, 16))
imgui.same_line()
else:
imgui.text_colored((0.4, 0.6, 1.0, 1.0), ">")
imgui.same_line()
if imgui.selectable(item['name'], False)[0]:
self.path_browser_current_path = item['path']
self._refresh_path_browser()
if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0):
self.path_browser_current_path = item['path']
self._refresh_path_browser()
# 显示文件(根据模式显示不同类型的文件)
if self.path_browser_mode == "open_project":
for item in self.path_browser_items:
if not item['is_dir'] and item['name'].endswith('.json'):
imgui.text_colored((1.0, 1.0, 0.7, 1.0), "[FILE]")
imgui.same_line()
if imgui.selectable(item['name'], False)[0]:
self.path_browser_selected_path = item['path']
if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0):
# 选择包含project.json的目录
self.path_browser_current_path = os.path.dirname(item['path'])
self._apply_selected_path()
elif self.path_browser_mode == "import_model":
for item in self.path_browser_items:
if not item['is_dir']:
file_ext = os.path.splitext(item['name'])[1].lower()
# 根据文件类型显示不同颜色
if file_ext in ['.gltf', '.glb']:
color = (0.7, 1.0, 0.7, 1.0) # 绿色 - glTF
elif file_ext == '.fbx':
color = (1.0, 0.7, 0.7, 1.0) # 红色 - FBX
elif file_ext in ['.bam', '.egg']:
color = (0.7, 0.7, 1.0, 1.0) # 蓝色 - Panda3D
elif file_ext == '.obj':
color = (1.0, 1.0, 0.7, 1.0) # 黄色 - OBJ
else:
color = (0.8, 0.8, 0.8, 1.0) # 灰色 - 其他
imgui.text_colored(color, f"[{file_ext[1:].upper()}]")
imgui.same_line()
if imgui.selectable(item['name'], False)[0]:
self.path_browser_selected_path = item['path']
if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0):
self.path_browser_selected_path = item['path']
self._apply_selected_path()
imgui.separator()
# 选中路径显示
if self.path_browser_selected_path:
imgui.text("选中路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), self.path_browser_selected_path)
# 按钮区域
if imgui.button("确定"):
self._apply_selected_path()
self.show_path_browser = False
imgui.same_line()
if imgui.button("取消"):
self.show_path_browser = False
def _draw_import_dialog(self):
"""使用系统文件选择器导入模型。"""
if not self.show_import_dialog:
return
# 立即关闭标记,防止每帧重复弹窗
self.show_import_dialog = False
selected_path = self._select_model_file_system_dialog()
if not selected_path:
self.add_info_message("已取消导入模型")
return
self.import_file_path = selected_path
self._import_model()
def _select_model_file_system_dialog(self):
"""弹出系统文件选择器并返回模型路径。"""
try:
import tkinter as tk
from tkinter import filedialog
initial_dir = os.path.dirname(self.import_file_path) if self.import_file_path else ""
if not initial_dir or (not os.path.isdir(initial_dir)):
candidates = [
os.path.join(os.getcwd(), "Resources", "models"),
os.path.join(os.getcwd(), "Resources"),
os.getcwd(),
]
initial_dir = next((p for p in candidates if os.path.isdir(p)), os.getcwd())
normalized_exts = []
for ext in getattr(self, "supported_formats", []):
ext = str(ext).strip().lower()
if not ext:
continue
if not ext.startswith("."):
ext = f".{ext}"
if ext not in normalized_exts:
normalized_exts.append(ext)
model_patterns = " ".join(f"*{ext}" for ext in normalized_exts) or "*.*"
filetypes = [
("模型文件", model_patterns),
("All Files", "*.*"),
]
root = tk.Tk()
root.withdraw()
try:
root.attributes("-topmost", True)
except Exception:
pass
selected_path = filedialog.askopenfilename(
title="选择要导入的模型文件",
initialdir=initial_dir,
filetypes=filetypes,
)
root.destroy()
if not selected_path:
return ""
selected_path = os.path.normpath(selected_path)
file_ext = os.path.splitext(selected_path)[1].lower()
if normalized_exts and file_ext not in normalized_exts:
self.add_error_message(f"不支持的文件格式: {file_ext}")
return ""
self.add_info_message(f"已选择文件: {selected_path}")
return selected_path
except Exception as e:
self.add_error_message(f"打开系统文件选择器失败: {e}")
return ""
def _refresh_path_browser(self):
"""刷新路径浏览器内容"""
try:
self.path_browser_items = []
if not os.path.exists(self.path_browser_current_path):
self.add_error_message(f"路径不存在: {self.path_browser_current_path}")
return
# 获取目录中的所有项目
items = []
try:
for item_name in os.listdir(self.path_browser_current_path):
item_path = os.path.join(self.path_browser_current_path, item_name)
is_dir = os.path.isdir(item_path)
items.append({
'name': item_name,
'path': item_path,
'is_dir': is_dir
})
except PermissionError:
self.add_error_message(f"无法访问路径: {self.path_browser_current_path}")
return
# 根据模式过滤文件
if self.path_browser_mode == "import_model":
# 只显示支持的模型文件
filtered_items = []
for item in items:
if item['is_dir']:
filtered_items.append(item)
else:
file_ext = os.path.splitext(item['name'])[1].lower()
if file_ext in self.supported_formats:
filtered_items.append(item)
items = filtered_items
# 排序:目录在前,文件在后,按名称排序
items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
self.path_browser_items = items
except Exception as e:
self.add_error_message(f"刷新路径浏览器失败: {e}")
def _apply_selected_path(self):
"""应用选择的路径"""
try:
if self.path_browser_mode == "new_project":
# 新建项目模式:直接使用当前路径
self.new_project_path = self.path_browser_current_path
self.add_info_message(f"已选择项目路径: {self.new_project_path}")
elif self.path_browser_mode == "open_project":
# 打开项目模式:使用当前路径
self.open_project_path = self.path_browser_current_path
self.add_info_message(f"已选择项目路径: {self.open_project_path}")
elif self.path_browser_mode == "save_as_project":
# 另存为项目模式:使用当前路径作为目标目录
self.save_as_project_path = self.path_browser_current_path
self.add_info_message(f"已选择保存位置: {self.save_as_project_path}")
elif self.path_browser_mode == "build_project":
self.build_output_path = self.path_browser_current_path
self.add_info_message(f"已选择打包位置: {self.build_output_path}")
elif self.path_browser_mode == "import_model":
# 导入模型模式:使用选择的文件路径
self.import_file_path = self.path_browser_selected_path
self.add_info_message(f"已选择文件: {self.import_file_path}")
except Exception as e:
self.add_error_message(f"应用路径失败: {e}")
def _draw_about_dialog(self):
"""绘制关于对话框。"""
if not self.show_about_dialog:
return
flags = (
imgui.WindowFlags_.no_resize
| imgui.WindowFlags_.no_collapse
| imgui.WindowFlags_.modal
)
self.style_manager.prepare_centered_dialog(460, 260)
with imgui_ctx.begin("关于 MetaCore", True, flags) as window:
if not window.opened:
self.show_about_dialog = False
return
current_project_path = getattr(getattr(self, "project_manager", None), "current_project_path", None) or "未打开项目"
imgui.text("元泰引擎 MetaCore V1.0")
imgui.separator()
imgui.text(f"平台: {platform.system()} {platform.release()}")
imgui.text("可执行文件: MetaCore.exe")
imgui.separator()
imgui.text("当前项目:")
imgui.text_colored((0.7, 0.7, 0.7, 1.0), current_project_path)
imgui.separator()
imgui.text_wrapped("当前帮助菜单的文档入口会打开项目目录下的本地 Markdown 文档。")
if imgui.button("打开文档"):
self._open_documentation()
imgui.same_line()
if imgui.button("关闭"):
self.show_about_dialog = False
def _draw_spot_light_dialog(self):
"""绘制聚光灯创建对话框"""
if not self.show_spot_light_dialog:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(420, 320)
with imgui_ctx.begin("创建聚光灯", self.show_spot_light_dialog, flags) as window:
if not window:
self.show_spot_light_dialog = False
return
# 初始化参数
if 'spotlight_pos' not in self.dialog_params:
self.dialog_params['spotlight_pos'] = [0.0, 0.0, 5.0]
if 'spotlight_color' not in self.dialog_params:
self.dialog_params['spotlight_color'] = [1.0, 1.0, 1.0]
if 'spotlight_intensity' not in self.dialog_params:
self.dialog_params['spotlight_intensity'] = 1.0
imgui.text("聚光灯参数设置")
imgui.separator()
# 位置输入
changed, x = imgui.input_float("X坐标", self.dialog_params['spotlight_pos'][0])
if changed:
self.dialog_params['spotlight_pos'][0] = x
changed, y = imgui.input_float("Y坐标", self.dialog_params['spotlight_pos'][1])
if changed:
self.dialog_params['spotlight_pos'][1] = y
changed, z = imgui.input_float("Z坐标", self.dialog_params['spotlight_pos'][2])
if changed:
self.dialog_params['spotlight_pos'][2] = z
# 颜色输入
changed, r = imgui.input_float("红色 (R)", self.dialog_params['spotlight_color'][0], 0.0, 1.0)
if changed:
self.dialog_params['spotlight_color'][0] = max(0.0, min(1.0, r))
changed, g = imgui.input_float("绿色 (G)", self.dialog_params['spotlight_color'][1], 0.0, 1.0)
if changed:
self.dialog_params['spotlight_color'][1] = max(0.0, min(1.0, g))
changed, b = imgui.input_float("蓝色 (B)", self.dialog_params['spotlight_color'][2], 0.0, 1.0)
if changed:
self.dialog_params['spotlight_color'][2] = max(0.0, min(1.0, b))
# 强度输入
changed, self.dialog_params['spotlight_intensity'] = imgui.input_float("强度", self.dialog_params['spotlight_intensity'], 0.1, 2.0)
if changed:
self.dialog_params['spotlight_intensity'] = max(0.1, self.dialog_params['spotlight_intensity'])
imgui.separator()
# 按钮
if imgui.button("创建"):
try:
pos = tuple(self.dialog_params['spotlight_pos'])
def create_spotlight():
result = self.createSpotLight(pos)
if result:
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['spotlight_color'])
light.setColor(color + (1.0,))
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['spotlight_intensity'])
return result
result = self._execute_scene_create_command(create_spotlight)
if result:
self.add_success_message("聚光灯创建成功")
self.show_spot_light_dialog = False
else:
self.add_error_message("聚光灯创建失败")
except Exception as e:
self.add_error_message(f"创建聚光灯失败: {str(e)}")
imgui.same_line()
if imgui.button("取消"):
self.show_spot_light_dialog = False
def _draw_point_light_dialog(self):
"""绘制点光源创建对话框"""
if not self.show_point_light_dialog:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(420, 360)
with imgui_ctx.begin("创建点光源", self.show_point_light_dialog, flags) as window:
if not window:
self.show_point_light_dialog = False
return
# 初始化参数
if 'pointlight_pos' not in self.dialog_params:
self.dialog_params['pointlight_pos'] = [0.0, 0.0, 5.0]
if 'pointlight_color' not in self.dialog_params:
self.dialog_params['pointlight_color'] = [1.0, 1.0, 1.0]
if 'pointlight_intensity' not in self.dialog_params:
self.dialog_params['pointlight_intensity'] = 1.0
if 'pointlight_radius' not in self.dialog_params:
self.dialog_params['pointlight_radius'] = 10.0
imgui.text("点光源参数设置")
imgui.separator()
# 位置输入
changed, x = imgui.input_float("X坐标", self.dialog_params['pointlight_pos'][0])
if changed:
self.dialog_params['pointlight_pos'][0] = x
changed, y = imgui.input_float("Y坐标", self.dialog_params['pointlight_pos'][1])
if changed:
self.dialog_params['pointlight_pos'][1] = y
changed, z = imgui.input_float("Z坐标", self.dialog_params['pointlight_pos'][2])
if changed:
self.dialog_params['pointlight_pos'][2] = z
# 颜色输入
changed, r = imgui.input_float("红色 (R)", self.dialog_params['pointlight_color'][0], 0.0, 1.0)
if changed:
self.dialog_params['pointlight_color'][0] = max(0.0, min(1.0, r))
changed, g = imgui.input_float("绿色 (G)", self.dialog_params['pointlight_color'][1], 0.0, 1.0)
if changed:
self.dialog_params['pointlight_color'][1] = max(0.0, min(1.0, g))
changed, b = imgui.input_float("蓝色 (B)", self.dialog_params['pointlight_color'][2], 0.0, 1.0)
if changed:
self.dialog_params['pointlight_color'][2] = max(0.0, min(1.0, b))
# 强度输入
changed, self.dialog_params['pointlight_intensity'] = imgui.input_float("强度", self.dialog_params['pointlight_intensity'], 0.1, 2.0)
if changed:
self.dialog_params['pointlight_intensity'] = max(0.1, self.dialog_params['pointlight_intensity'])
# 半径输入
changed, self.dialog_params['pointlight_radius'] = imgui.input_float("影响半径", self.dialog_params['pointlight_radius'], 1.0, 50.0)
if changed:
self.dialog_params['pointlight_radius'] = max(1.0, self.dialog_params['pointlight_radius'])
imgui.separator()
# 按钮
if imgui.button("创建"):
try:
pos = tuple(self.dialog_params['pointlight_pos'])
def create_pointlight():
result = self.createPointLight(pos)
if result:
light = result.node()
if hasattr(light, 'setColor'):
color = tuple(self.dialog_params['pointlight_color'])
light.setColor(color + (1.0,))
if hasattr(light, 'setEnergy'):
light.setEnergy(self.dialog_params['pointlight_intensity'])
if hasattr(light, 'setAttenuation'):
radius = self.dialog_params['pointlight_radius']
light.setAttenuation((1.0, 0.5 / radius, 0.5 / (radius * radius)))
return result
result = self._execute_scene_create_command(create_pointlight)
if result:
self.add_success_message("点光源创建成功")
self.show_point_light_dialog = False
else:
self.add_error_message("点光源创建失败")
except Exception as e:
self.add_error_message(f"创建点光源失败: {str(e)}")
imgui.same_line()
if imgui.button("取消"):
self.show_point_light_dialog = False
def _draw_terrain_dialog(self):
"""绘制地形创建对话框"""
if not self.show_terrain_dialog:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(420, 300)
with imgui_ctx.begin("创建平面地形", self.show_terrain_dialog, flags) as window:
if not window:
self.show_terrain_dialog = False
return
# 初始化参数
if 'terrain_width' not in self.dialog_params:
self.dialog_params['terrain_width'] = 10.0
if 'terrain_height' not in self.dialog_params:
self.dialog_params['terrain_height'] = 10.0
if 'terrain_resolution' not in self.dialog_params:
self.dialog_params['terrain_resolution'] = 129
imgui.text("平面地形参数设置")
imgui.separator()
# 尺寸输入
changed, self.dialog_params['terrain_width'] = imgui.input_float("宽度", self.dialog_params['terrain_width'], 1.0, 100.0)
if changed:
self.dialog_params['terrain_width'] = max(1.0, self.dialog_params['terrain_width'])
changed, self.dialog_params['terrain_height'] = imgui.input_float("高度", self.dialog_params['terrain_height'], 1.0, 100.0)
if changed:
self.dialog_params['terrain_height'] = max(1.0, self.dialog_params['terrain_height'])
# 分辨率输入
changed, self.dialog_params['terrain_resolution'] = imgui.input_int("分辨率", self.dialog_params['terrain_resolution'])
if changed:
# 确保分辨率是有效的 (2的幂次方 + 1)
valid_resolutions = [17, 33, 65, 129, 257, 513, 1025]
if self.dialog_params['terrain_resolution'] not in valid_resolutions:
closest_res = min(valid_resolutions, key=lambda x: abs(x - self.dialog_params['terrain_resolution']))
self.dialog_params['terrain_resolution'] = closest_res
imgui.separator()
imgui.text("有效分辨率值: 17, 33, 65, 129, 257, 513, 1025")
imgui.separator()
# 按钮
if imgui.button("创建"):
try:
width = self.dialog_params['terrain_width']
height = self.dialog_params['terrain_height']
resolution = self.dialog_params['terrain_resolution']
# 转换为地形管理器期望的格式
size = (width, height)
result = self._execute_scene_create_command(
lambda: self.createFlatTerrain(size, resolution)
)
if result:
self.add_success_message("平面地形创建成功")
self.show_terrain_dialog = False
else:
self.add_error_message("平面地形创建失败")
except Exception as e:
self.add_error_message(f"创建平面地形失败: {str(e)}")
imgui.same_line()
if imgui.button("取消"):
self.show_terrain_dialog = False
def _draw_script_dialog(self):
"""绘制脚本创建对话框"""
if not self.show_script_dialog:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(420, 240)
with imgui_ctx.begin("创建脚本", self.show_script_dialog, flags) as window:
if not window:
self.show_script_dialog = False
return
# 初始化参数
if 'script_name' not in self.dialog_params:
self.dialog_params['script_name'] = "new_script"
if 'script_template' not in self.dialog_params:
self.dialog_params['script_template'] = 0 # 0=basic, 1=movement
imgui.text("脚本参数设置")
imgui.separator()
# 脚本名称输入
changed, self.dialog_params['script_name'] = imgui.input_text("脚本名称", self.dialog_params['script_name'], 256)
# 模板选择
templates = ["基础模板", "移动模板"]
changed, self.dialog_params['script_template'] = imgui.combo("模板类型", self.dialog_params['script_template'], templates)
imgui.separator()
# 模板说明
if self.dialog_params['script_template'] == 0:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "基础模板: 包含基本的脚本结构")
else:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "移动模板: 包含移动相关的基本功能")
imgui.separator()
# 按钮
if imgui.button("创建"):
try:
script_name = self.dialog_params['script_name']
if not script_name:
self.add_error_message("请输入脚本名称")
return
template = "basic" if self.dialog_params['script_template'] == 0 else "movement"
result = self.createScript(script_name, template)
if result:
self.add_success_message(f"脚本创建成功: {script_name}")
# 如果启用了热重载,自动加载新脚本
if self.hotReloadEnabled:
try:
self.loadScript(result)
self.add_info_message(f"脚本已自动加载: {script_name}")
except Exception as e:
self.add_warning_message(f"脚本自动加载失败: {str(e)}")
self.show_script_dialog = False
else:
self.add_error_message("脚本创建失败")
except Exception as e:
self.add_error_message(f"创建脚本失败: {str(e)}")
imgui.same_line()
if imgui.button("取消"):
self.show_script_dialog = False
def _draw_script_browser(self):
"""绘制脚本文件浏览器"""
if not self.show_script_browser:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(640, 480)
with imgui_ctx.begin("选择脚本文件", self.show_script_browser, flags) as window:
if not window:
self.show_script_browser = False
return
imgui.text("选择脚本文件")
imgui.separator()
# 导航按钮
if imgui.button("上级目录"):
parent_path = os.path.dirname(self.script_browser_current_path)
if parent_path != self.script_browser_current_path:
self.script_browser_current_path = parent_path
self._refresh_script_browser()
imgui.same_line()
if imgui.button("脚本目录"):
# 切换到脚本目录
if hasattr(self, 'script_manager') and self.script_manager:
scripts_dir = self.script_manager.scripts_directory
if os.path.exists(scripts_dir):
self.script_browser_current_path = scripts_dir
self._refresh_script_browser()
imgui.same_line()
if imgui.button("当前目录"):
self.script_browser_current_path = os.getcwd()
self._refresh_script_browser()
imgui.separator()
# 当前路径显示
imgui.text("当前路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), self.script_browser_current_path)
imgui.separator()
# 文件列表
if imgui.begin_child("script_file_list", (580, 300)):
for item in self.script_browser_items:
if item['is_dir']:
# 目录
imgui.text_colored((0.3, 0.8, 1.0, 1.0), f"📁 {item['name']}")
if imgui.is_item_clicked():
self.script_browser_current_path = item['path']
self._refresh_script_browser()
else:
# Python文件
imgui.text(f"📄 {item['name']}")
if imgui.is_item_clicked():
self.script_browser_selected_path = item['path']
imgui.end_child()
imgui.separator()
# 选中的文件信息
if self.script_browser_selected_path and os.path.exists(self.script_browser_selected_path):
file_size = os.path.getsize(self.script_browser_selected_path)
imgui.text(f"文件大小: {file_size / 1024:.2f} KB")
file_ext = os.path.splitext(self.script_browser_selected_path)[1].lower()
if file_ext == '.py':
imgui.text_colored((0.176, 1.0, 0.769, 1.0), "✓ Python脚本文件")
else:
imgui.text_colored((1.0, 0.3, 0.3, 1.0), "✗ 不是Python脚本文件")
else:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "请选择有效的Python脚本文件")
imgui.separator()
# 按钮
can_load = (self.script_browser_selected_path and
os.path.exists(self.script_browser_selected_path) and
os.path.splitext(self.script_browser_selected_path)[1].lower() == '.py')
if can_load:
if imgui.button("加载脚本"):
try:
result = self.loadScript(self.script_browser_selected_path)
if result:
script_name = os.path.basename(self.script_browser_selected_path)
self.add_success_message(f"脚本加载成功: {script_name}")
self.show_script_browser = False
else:
self.add_error_message("脚本加载失败")
except Exception as e:
self.add_error_message(f"加载脚本失败: {str(e)}")
else:
imgui.push_style_var(imgui.StyleVar_.alpha, 0.5)
imgui.button("加载脚本")
imgui.pop_style_var()
imgui.same_line()
if imgui.button("取消"):
self.show_script_browser = False
self.script_browser_selected_path = ""
def _refresh_script_browser(self):
"""刷新脚本浏览器内容"""
try:
self.script_browser_items = []
if not os.path.exists(self.script_browser_current_path):
self.script_browser_current_path = os.getcwd()
# 获取目录中的所有项目
items = []
try:
for item_name in os.listdir(self.script_browser_current_path):
item_path = os.path.join(self.script_browser_current_path, item_name)
if os.path.isdir(item_path):
items.append({
'name': item_name,
'path': item_path,
'is_dir': True
})
elif os.path.isfile(item_path):
file_ext = os.path.splitext(item_name)[1].lower()
if file_ext == '.py': # 只显示Python文件
items.append({
'name': item_name,
'path': item_path,
'is_dir': False
})
except PermissionError:
print(f"权限错误: 无法访问目录 {self.script_browser_current_path}")
# 排序:目录在前,文件在后
items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
self.script_browser_items = items
except Exception as e:
print(f"刷新脚本浏览器时出错: {e}")
self.script_browser_items = []
def _draw_heightmap_browser(self):
"""绘制高度图文件浏览器"""
if not self.show_heightmap_browser:
return
flags = (imgui.WindowFlags_.no_resize |
imgui.WindowFlags_.no_collapse |
imgui.WindowFlags_.modal)
self.style_manager.prepare_centered_dialog(640, 480)
with imgui_ctx.begin("选择高度图文件", self.show_heightmap_browser, flags) as window:
if not window:
self.show_heightmap_browser = False
return
imgui.text("选择高度图文件")
imgui.separator()
# 导航按钮
if imgui.button("上级目录"):
parent_path = os.path.dirname(self.heightmap_browser_current_path)
if parent_path != self.heightmap_browser_current_path:
self.heightmap_browser_current_path = parent_path
self._refresh_heightmap_browser()
imgui.same_line()
if imgui.button("主目录"):
self.heightmap_browser_current_path = os.getcwd()
self._refresh_heightmap_browser()
imgui.same_line()
if imgui.button("当前目录"):
self.heightmap_browser_current_path = os.getcwd()
self._refresh_heightmap_browser()
imgui.separator()
# 当前路径显示
imgui.text("当前路径:")
imgui.same_line()
imgui.text_colored((0.7, 0.7, 0.7, 1.0), self.heightmap_browser_current_path)
imgui.separator()
# 文件列表
if imgui.begin_child("file_list", (580, 300)):
for item in self.heightmap_browser_items:
if item['is_dir']:
# 目录
imgui.text_colored((0.3, 0.8, 1.0, 1.0), f"📁 {item['name']}")
if imgui.is_item_clicked():
self.heightmap_browser_current_path = item['path']
self._refresh_heightmap_browser()
else:
# 文件
imgui.text(f"📄 {item['name']}")
if imgui.is_item_clicked():
self.heightmap_browser_selected_path = item['path']
self.heightmap_file_path = item['path']
imgui.end_child()
imgui.separator()
# 支持的格式说明
imgui.text("支持的文件格式:")
formats_text = ", ".join(self.supported_heightmap_formats)
imgui.text_colored((0.7, 0.7, 0.7, 1.0), formats_text)
imgui.separator()
# 选中的文件信息
if self.heightmap_file_path and os.path.exists(self.heightmap_file_path):
file_size = os.path.getsize(self.heightmap_file_path)
imgui.text(f"文件大小: {file_size / 1024:.2f} KB")
file_ext = os.path.splitext(self.heightmap_file_path)[1].lower()
if file_ext in self.supported_heightmap_formats:
imgui.text_colored((0.176, 1.0, 0.769, 1.0), "✓ 文件格式支持")
else:
imgui.text_colored((1.0, 0.3, 0.3, 1.0), "✗ 不支持的文件格式")
else:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "请选择有效的高度图文件")
imgui.separator()
# 按钮
can_import = (self.heightmap_file_path and
os.path.exists(self.heightmap_file_path) and
os.path.splitext(self.heightmap_file_path)[1].lower() in self.supported_heightmap_formats)
if can_import:
if imgui.button("创建地形"):
try:
# 使用默认缩放参数创建地形
scale = (1.0, 1.0, 10.0) # X, Y, Z缩放
result = self._execute_scene_create_command(
lambda: self.createTerrainFromHeightMap(self.heightmap_file_path, scale)
)
if result:
self.add_success_message("高度图地形创建成功")
self.show_heightmap_browser = False
else:
self.add_error_message("高度图地形创建失败")
except Exception as e:
self.add_error_message(f"创建高度图地形失败: {str(e)}")
else:
imgui.push_style_var(imgui.StyleVar_.alpha, 0.5)
imgui.button("创建地形")
imgui.pop_style_var()
imgui.same_line()
if imgui.button("取消"):
self.show_heightmap_browser = False
self.heightmap_file_path = ""
def _refresh_heightmap_browser(self):
"""刷新高度图浏览器内容"""
try:
self.heightmap_browser_items = []
if not os.path.exists(self.heightmap_browser_current_path):
self.heightmap_browser_current_path = os.getcwd()
# 获取目录中的所有项目
items = []
try:
for item_name in os.listdir(self.heightmap_browser_current_path):
item_path = os.path.join(self.heightmap_browser_current_path, item_name)
if os.path.isdir(item_path):
items.append({
'name': item_name,
'path': item_path,
'is_dir': True
})
elif os.path.isfile(item_path):
file_ext = os.path.splitext(item_name)[1].lower()
if file_ext in self.supported_heightmap_formats:
items.append({
'name': item_name,
'path': item_path,
'is_dir': False
})
except PermissionError:
print(f"权限错误: 无法访问目录 {self.heightmap_browser_current_path}")
# 排序:目录在前,文件在后
items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
self.heightmap_browser_items = items
except Exception as e:
print(f"刷新高度图浏览器时出错: {e}")
self.heightmap_browser_items = []