This commit is contained in:
Hector 2026-03-25 09:25:55 +08:00
parent 6a99c3cc2a
commit ab6d543dc0
2 changed files with 115 additions and 168 deletions

View File

@ -1003,10 +1003,24 @@ class AnimationTools:
try:
if node and (not node.isEmpty()):
# If the user explicitly selected the animated skeleton node,
# respect that directly. More aggressive driver remapping made
# playback regress to "cannot play at all".
if self._node_has_animation_nodes(node):
# respect that directly only when it also carries visible geom.
# Skeleton-only nodes like Armature still need a deeper renderable
# driver node, otherwise playback can report success but show no motion.
if self._node_has_animation_nodes(node) and self._node_has_geom(node):
return node
driver = self._find_animation_driver_node(node)
if driver and (not driver.isEmpty()):
for tag_name in ("model_path", "original_path", "saved_model_path", "file"):
try:
if (
(not driver.hasTag(tag_name) or not driver.getTag(tag_name))
and node.hasTag(tag_name)
and node.getTag(tag_name)
):
driver.setTag(tag_name, node.getTag(tag_name))
except Exception:
continue
return driver
except Exception:
pass

View File

@ -35,7 +35,8 @@ class AppActions:
while current and not current.isEmpty():
if current.getName() == "render":
break
if current.hasTag("tree_item_type") or current.hasTag("is_model_root") or current.hasTag("is_scene_element"):
if current.hasTag("tree_item_type") or current.hasTag("is_model_root") or current.hasTag(
"is_scene_element"):
return current
current = current.getParent()
@ -60,8 +61,8 @@ class AppActions:
return False
try:
has_animation = (
model.findAllMatches("**/+Character").getNumPaths() > 0
or model.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0
model.findAllMatches("**/+Character").getNumPaths() > 0
or model.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0
)
finally:
try:
@ -72,14 +73,13 @@ class AppActions:
except Exception:
return False
def _toggle_hot_reload(self):
"""切换热重载状态"""
if hasattr(self, 'script_manager') and self.script_manager:
try:
current_state = getattr(self.script_manager, 'hot_reload_enabled', False)
self._set_hot_reload_enabled(not current_state)
new_state = "启用" if not current_state else "禁用"
self.add_success_message(f"热重载已{new_state}")
print(f"[脚本系统] 热重载已{new_state}")
@ -93,28 +93,27 @@ class AppActions:
raise RuntimeError("脚本管理器未初始化")
self.script_manager.set_hot_reload_enabled(enabled)
self.hotReloadEnabled = self.script_manager.hot_reload_enabled
def _create_new_script(self):
"""创建新脚本"""
if not hasattr(self, '_new_script_name') or not self._new_script_name.strip():
self.add_error_message("请输入脚本名称")
return
script_name = self._new_script_name.strip()
if not script_name.endswith('.py'):
script_name += '.py'
# 确定模板类型
template_map = {
0: "basic",
1: "movement",
1: "movement",
2: "rotation",
3: "scale",
4: "animation"
}
template_type = template_map.get(getattr(self, '_selected_template', 0), "basic")
try:
if hasattr(self, 'script_manager') and self.script_manager:
result = self.script_manager.create_script_file(script_name, template_type)
@ -130,7 +129,6 @@ class AppActions:
except Exception as e:
self.add_error_message(f"创建脚本失败: {str(e)}")
print(f"[脚本系统] 创建脚本失败: {e}")
def _refresh_scripts_list(self):
"""刷新脚本列表"""
@ -145,7 +143,6 @@ class AppActions:
except Exception as e:
self.add_error_message(f"刷新脚本列表失败: {str(e)}")
print(f"[脚本系统] 刷新脚本列表失败: {e}")
def _reload_all_scripts(self):
"""重载所有脚本"""
@ -154,11 +151,11 @@ class AppActions:
# 获取所有可用脚本并逐个重载
available_scripts = self.script_manager.get_available_scripts()
success_count = 0
for script_name in available_scripts:
if self.script_manager.reload_script(script_name):
success_count += 1
self.add_success_message(f"重载完成: {success_count}/{len(available_scripts)} 个脚本成功")
print(f"[脚本系统] 重载脚本: {success_count}/{len(available_scripts)} 成功")
else:
@ -166,13 +163,11 @@ class AppActions:
except Exception as e:
self.add_error_message(f"重载脚本失败: {str(e)}")
print(f"[脚本系统] 重载脚本失败: {e}")
def _on_script_selected(self, script_name):
"""处理脚本选择事件"""
print(f"[脚本系统] 选择脚本: {script_name}")
self.add_info_message(f"已选择脚本: {script_name}")
def _edit_script(self, script_name):
"""编辑脚本"""
@ -182,11 +177,11 @@ class AppActions:
script_info = self.script_manager.get_script_info(script_name)
if script_info and script_info.get("file"):
script_path = script_info["file"]
# 打开系统默认编辑器
import subprocess
import platform
system = platform.system()
try:
if system == "Windows":
@ -195,7 +190,7 @@ class AppActions:
subprocess.run(['open', script_path])
else: # Linux
subprocess.run(['xdg-open', script_path])
self.add_success_message(f"已打开脚本编辑器: {script_name}")
print(f"[脚本系统] 编辑脚本: {script_path}")
except Exception as e:
@ -207,16 +202,15 @@ class AppActions:
except Exception as e:
self.add_error_message(f"编辑脚本失败: {str(e)}")
print(f"[脚本系统] 编辑脚本失败: {e}")
def _mount_script_to_selected(self, script_name):
"""挂载脚本到选中对象"""
selected_node = self._get_selection_node()
if not selected_node or selected_node.isEmpty():
self.add_error_message("请先选择一个对象")
return
try:
if hasattr(self, 'script_manager') and self.script_manager:
script_component = self.script_manager.add_script_to_object(selected_node, script_name)
@ -230,16 +224,15 @@ class AppActions:
except Exception as e:
self.add_error_message(f"挂载脚本失败: {str(e)}")
print(f"[脚本系统] 挂载脚本失败: {e}")
def _unmount_script_from_selected(self, script_name):
"""从选中对象卸载脚本"""
selected_node = self._get_selection_node()
if not selected_node or selected_node.isEmpty():
self.add_error_message("请先选择一个对象")
return
try:
if hasattr(self, 'script_manager') and self.script_manager:
result = self.script_manager.remove_script_from_object(selected_node, script_name)
@ -253,21 +246,18 @@ class AppActions:
except Exception as e:
self.add_error_message(f"卸载脚本失败: {str(e)}")
print(f"[脚本系统] 卸载脚本失败: {e}")
# ==================== 菜单处理函数 ====================
def _on_new_project(self):
"""处理新建项目菜单项"""
self.add_info_message("打开新建项目对话框")
self.show_new_project_dialog = True
def _on_open_project(self):
"""处理打开项目菜单项"""
self.add_info_message("打开项目对话框")
self.show_open_project_dialog = True
def _on_save_project(self):
"""处理保存项目菜单项"""
@ -278,7 +268,7 @@ class AppActions:
self.add_warning_message("没有当前项目路径,请先创建或打开项目")
self.show_save_as_dialog = True
return
# 直接调用保存逻辑避免Qt依赖
if self._save_project_impl():
self.add_success_message("项目保存成功")
@ -288,7 +278,6 @@ class AppActions:
self.add_error_message(f"项目保存失败: {e}")
else:
self.add_error_message("项目管理器未初始化")
def _on_save_as_project(self):
"""处理另存为项目菜单项"""
@ -311,7 +300,8 @@ class AppActions:
_, active_profile = project_manager._get_active_build_profile()
profile_output_dir = str((active_profile or {}).get("output_dir", "") or "").strip()
if layout and profile_output_dir:
self.build_output_path = os.path.normpath(os.path.join(current_project_path, profile_output_dir.replace("/", os.sep)))
self.build_output_path = os.path.normpath(
os.path.join(current_project_path, profile_output_dir.replace("/", os.sep)))
else:
self.build_output_path = layout.builds_root if layout else os.path.dirname(current_project_path)
else:
@ -362,12 +352,10 @@ class AppActions:
self.add_error_message(f"项目打包失败: {e}")
return False
def _show_about_dialog(self):
"""显示关于对话框。"""
self.show_about_dialog = True
def _get_documentation_target(self):
"""返回优先级最高的本地帮助文档。"""
candidate_names = [
@ -383,7 +371,6 @@ class AppActions:
return candidate_path
return None
def _open_documentation(self):
"""打开本地项目文档。"""
doc_path = self._get_documentation_target()
@ -401,57 +388,46 @@ class AppActions:
except Exception as e:
self.add_error_message(f"打开文档失败: {e}")
return False
def _on_exit(self):
"""处理退出菜单项"""
self.add_info_message("退出应用程序")
self.userExit()
# ==================== 键盘事件处理函数 ====================
def _on_ctrl_pressed(self):
"""Ctrl键按下"""
self.ctrl_pressed = True
def _on_ctrl_released(self):
"""Ctrl键释放"""
self.ctrl_pressed = False
def _on_alt_pressed(self):
"""Alt键按下"""
self.alt_pressed = True
def _on_alt_released(self):
"""Alt键释放"""
self.alt_pressed = False
def _on_n_pressed(self):
"""N键按下 - 检查Ctrl+N组合键"""
if self.ctrl_pressed:
self._on_new_project()
def _on_o_pressed(self):
"""O键按下 - 检查Ctrl+O组合键"""
if self.ctrl_pressed:
self._on_open_project()
def _on_f4_pressed(self):
"""F4键按下 - 检查Alt+F4组合键"""
if self.alt_pressed:
self._on_exit()
# 移除了单独的按键处理方法,现在直接使用组合键事件
def _on_delete_pressed(self):
"""Delete键按下 - 删除选中节点"""
@ -464,24 +440,23 @@ class AppActions:
if self.lui_manager.selected_index >= 0:
self.lui_manager.selected_index = -1
print("✓ 已取消LUI组件选中")
# 2. 取消 3D 场景选择
if hasattr(self, 'selection') and self.selection:
if self._get_selection_node() or self._get_ssbo_selection_summary():
self.selection.clearSelection()
print("✓ 已取消场景节点选中")
def _on_wheel_up(self):
"""滚轮向上滚动 - 相机前进"""
try:
if not self.camera_control_enabled:
return
# 检查鼠标是否在ImGui窗口上
if self._is_mouse_over_imgui():
return
# 沿相机前向向量移动
forward = self.camera.getMat().getRow3(1)
distance = 20.0 * globalClock.getDt()
@ -490,7 +465,6 @@ class AppActions:
self.camera.setPos(newPos)
except Exception as e:
print(f"滚轮前进失败: {e}")
def _on_wheel_down(self):
"""滚轮向下滚动 - 相机后退"""
@ -498,7 +472,7 @@ class AppActions:
# 检查鼠标是否在ImGui窗口上
if self._is_mouse_over_imgui():
return
# 沿相机前向向量移动
forward = self.camera.getMat().getRow3(1)
distance = -20.0 * globalClock.getDt()
@ -507,7 +481,6 @@ class AppActions:
self.camera.setPos(newPos)
except Exception as e:
print(f"滚轮后退失败: {e}")
def _is_mouse_over_imgui(self):
"""检测鼠标是否在ImGui窗口上"""
@ -532,7 +505,6 @@ class AppActions:
except Exception as e:
print(f"ImGui界面检测失败: {e}")
return False
def processImGuiMouseClick(self, x, y):
"""处理ImGui鼠标点击事件返回是否消费了该事件"""
@ -554,7 +526,7 @@ class AppActions:
pass
return False
except Exception as e:
print(f"ImGui鼠标点击处理失败: {e}")
return False
@ -585,54 +557,49 @@ class AppActions:
def _is_point_in_known_imgui_rects(self, point):
for rect_name in (
"_resource_manager_window_rect",
"_scene_tree_window_rect",
"_property_panel_window_rect",
"_script_panel_window_rect",
"_console_window_rect",
"_toolbar_window_rect",
"_resource_manager_window_rect",
"_scene_tree_window_rect",
"_property_panel_window_rect",
"_script_panel_window_rect",
"_console_window_rect",
"_toolbar_window_rect",
):
if self._point_in_rect(point, getattr(self, rect_name, None)):
return True
return False
# ==================== 消息系统 ====================
def add_message(self, text, color=(1.0, 1.0, 1.0, 1.0)):
"""添加消息到消息列表,同时输出到终端"""
import datetime
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
# 输出到终端
print(f"[{timestamp}] {text}")
# 添加到GUI消息列表
self.messages.append({
'text': text,
'color': color,
'timestamp': timestamp
})
# 限制消息数量
if len(self.messages) > self.max_messages:
self.messages = self.messages[-self.max_messages:]
def add_success_message(self, text):
"""添加成功消息"""
self.add_message(f"{text}", (0.176, 1.0, 0.769, 1.0))
def add_error_message(self, text):
"""添加错误消息"""
self.add_message(f"{text}", (1.0, 0.3, 0.3, 1.0))
def add_warning_message(self, text):
"""添加警告消息"""
self.add_message(f"{text}", (0.953, 0.616, 0.471, 1.0))
def add_info_message(self, text):
"""添加信息消息"""
@ -693,7 +660,8 @@ class AppActions:
else:
selected_node = None
try:
selected_node = selection.getSelectedNode() if hasattr(selection, "getSelectedNode") else getattr(selection, "selectedNode", None)
selected_node = selection.getSelectedNode() if hasattr(selection, "getSelectedNode") else getattr(
selection, "selectedNode", None)
except Exception:
selected_node = None
if not self._is_history_node_valid(selected_node, require_attached=True):
@ -746,7 +714,6 @@ class AppActions:
self.add_warning_message("没有可撤销的操作")
except Exception as e:
self.add_error_message(f"撤销操作失败: {e}")
def _on_redo(self):
"""处理重做操作"""
@ -771,7 +738,6 @@ class AppActions:
self.add_warning_message("没有可重做的操作")
except Exception as e:
self.add_error_message(f"重做操作失败: {e}")
def _on_copy(self):
"""处理复制操作"""
@ -779,18 +745,18 @@ class AppActions:
if not hasattr(self, 'selection') or not self.selection:
self.add_error_message("选择系统未初始化")
return
# 获取当前选中的节点
selected_node = self._resolve_cut_copy_node(self._get_selection_source_node())
if not selected_node:
self.add_warning_message("没有选中的节点")
return
# 检查节点有效性(不能复制根节点)
if selected_node.getName() == "render":
self.add_warning_message("不能复制根节点")
return
# 序列化节点
if hasattr(self, 'scene_manager') and self.scene_manager:
node_data = self.scene_manager.serializeNodeForCopy(selected_node)
@ -806,7 +772,6 @@ class AppActions:
self.add_error_message("场景管理器未初始化")
except Exception as e:
self.add_error_message(f"复制操作失败: {e}")
def _on_cut(self):
"""处理剪切操作"""
@ -814,19 +779,19 @@ class AppActions:
if not hasattr(self, 'selection') or not self.selection:
self.add_error_message("选择系统未初始化")
return
# 获取当前选中的节点
selected_node = self._resolve_cut_copy_node(self._get_selection_source_node())
if not selected_node:
self.add_warning_message("没有选中的节点")
return
# 检查节点有效性(不能剪切根节点和系统节点)
node_name = selected_node.getName()
if node_name == "render":
self.add_warning_message("不能剪切根节点")
return
# 序列化节点
if hasattr(self, 'scene_manager') and self.scene_manager:
node_data = self.scene_manager.serializeNodeForCopy(selected_node)
@ -835,7 +800,7 @@ class AppActions:
# Cut preserves the source node references for cloning in paste
self.clipboard_source_nodes = [selected_node]
self.clipboard_mode = "cut"
# 删除原节点
if self._delete_node(selected_node):
self.selection.clearSelection()
@ -848,7 +813,6 @@ class AppActions:
self.add_error_message("场景管理器未初始化")
except Exception as e:
self.add_error_message(f"剪切操作失败: {e}")
def _on_paste(self):
"""处理粘贴操作"""
@ -856,11 +820,11 @@ class AppActions:
if not self.clipboard:
self.add_warning_message("剪切板为空")
return
if not hasattr(self, 'scene_manager') or not self.scene_manager:
self.add_error_message("场景管理器未初始化")
return
# 确定粘贴目标父节点
parent_node = None
if hasattr(self, 'selection') and self.selection:
@ -873,11 +837,11 @@ class AppActions:
else:
p = selected_node.getParent()
parent_node = p if p and not p.isEmpty() else self.render
# 如果没有选中节点,使用渲染根节点
if not parent_node:
parent_node = self.render
# 反序列化并添加节点
created_any = False
last_created_node = None
@ -897,10 +861,12 @@ class AppActions:
else:
created = source_node.copyTo(parent)
if hasattr(self.scene_manager, "_generateUniqueName"):
unique_name = self.scene_manager._generateUniqueName(source_node.getName(), parent)
unique_name = self.scene_manager._generateUniqueName(source_node.getName(),
parent)
created.setName(unique_name)
# Preserve model source tags so later cut/paste can rebuild real model.
for _tag in ("model_path", "saved_model_path", "original_path", "file", "element_type"):
for _tag in ("model_path", "saved_model_path", "original_path", "file",
"element_type"):
if source_node.hasTag(_tag):
created.setTag(_tag, source_node.getTag(_tag))
# Offset slightly so the new node can be seen immediately.
@ -929,7 +895,8 @@ class AppActions:
if hasattr(self.command_manager, "pop_last_command"):
last_cmd = self.command_manager.pop_last_command()
if isinstance(last_cmd, DeleteNodeCommand) and getattr(last_cmd, "node", None) == source_node:
if isinstance(last_cmd, DeleteNodeCommand) and getattr(last_cmd, "node",
None) == source_node:
attach_cmd.execute()
self.command_manager.record_command(CompositeCommand([last_cmd, attach_cmd]))
new_node = source_node
@ -991,7 +958,6 @@ class AppActions:
self.clipboard_mode = ""
except Exception as e:
self.add_error_message(f"粘贴操作失败: {e}")
def _on_delete(self):
"""处理删除操作"""
@ -999,19 +965,19 @@ class AppActions:
if not hasattr(self, 'selection') or not self.selection:
self.add_error_message("选择系统未初始化")
return
# 获取当前选中的节点
selected_node = self._get_selection_source_node()
if not selected_node:
self.add_warning_message("没有选中的节点")
return
# 检查节点有效性(不能删除根节点)
node_name = selected_node.getName()
if node_name == "render":
self.add_warning_message("不能删除根节点")
return
# 删除节点
if hasattr(self, 'scene_manager') and self.scene_manager:
self._delete_node(selected_node)
@ -1021,14 +987,13 @@ class AppActions:
self.add_error_message("场景管理器未初始化")
except Exception as e:
self.add_error_message(f"删除操作失败: {e}")
def _delete_node(self, node):
"""删除节点的通用方法 - 使用命令系统"""
try:
if not node or node.isEmpty():
return False
node_name = node.getName() or "未命名节点"
parent = node.getParent()
ssbo_editor = getattr(self, "ssbo_editor", None)
@ -1070,7 +1035,7 @@ class AppActions:
pass
print(f"[SSBO] 已从源模型树移除并重建运行时: {node_name}")
return True
# 创建删除命令
if hasattr(self, 'command_manager') and self.command_manager:
from core.Command_System import DeleteNodeCommand
@ -1088,26 +1053,25 @@ class AppActions:
ssbo_editor.on_model_deleted(node)
except Exception as e:
print(f"[SSBO] 删除模型后清理失败: {e}")
print(f"[删除] 成功删除节点: {node_name}")
return True
except Exception as e:
print(f"[删除] 删除节点失败: {e}")
return False
def _perform_node_cleanup(self, node):
"""执行节点清理逻辑"""
try:
node_name = node.getName() or "未命名节点"
# 从场景管理器的模型列表中移除(如果是模型)
if hasattr(self, 'scene_manager') and self.scene_manager:
if node in self.scene_manager.models:
self.scene_manager.models.remove(node)
print(f"[场景管理器] 从模型列表移除: {node_name}")
# 停止所有与该节点相关的脚本
if hasattr(self, 'script_manager') and self.script_manager:
try:
@ -1117,7 +1081,7 @@ class AppActions:
print(f"[脚本系统] 移除节点 {node_name} 的所有脚本")
except Exception as e:
print(f"[脚本系统] 移除脚本失败: {e}")
# 清理碰撞体
if hasattr(self, 'collision_manager') and self.collision_manager:
try:
@ -1125,7 +1089,7 @@ class AppActions:
print(f"[碰撞系统] 移除节点 {node_name} 的碰撞体")
except Exception as e:
print(f"[碰撞系统] 移除碰撞体失败: {e}")
# 清理Actor缓存如果有动画
if hasattr(self, '_actor_cache') and node in self._actor_cache:
actor = self._actor_cache[node]
@ -1141,19 +1105,18 @@ class AppActions:
print(f"[动画系统] 清理Actor缓存失败: {e}")
finally:
del self._actor_cache[node]
except Exception as e:
print(f"[清理] 节点清理失败: {e}")
# ==================== 对话框绘制函数 ====================
def _create_new_project(self, name, path):
"""创建新项目的实际实现"""
if not hasattr(self, 'project_manager') or not self.project_manager:
print("✗ 项目管理器未初始化")
return
try:
if self._create_new_project_impl(name, path):
print(f"✓ 项目创建成功: {name}")
@ -1161,14 +1124,13 @@ class AppActions:
print(f"✗ 项目创建失败: {name}")
except Exception as e:
print(f"✗ 项目创建失败: {e}")
def _open_project_path(self, path):
"""打开项目的实际实现"""
if not hasattr(self, 'project_manager') or not self.project_manager:
print("✗ 项目管理器未初始化")
return
try:
print(f"打开项目: {path}")
if self._open_project_impl(path):
@ -1177,16 +1139,14 @@ class AppActions:
print(f"✗ 项目打开失败: {path}")
except Exception as e:
print(f"✗ 项目打开失败: {e}")
# ==================== 项目管理具体实现 ====================
def _save_project_impl(self):
"""保存项目的具体实现不依赖Qt"""
if not hasattr(self, 'project_manager') or not self.project_manager:
return False
return self.project_manager.saveProject()
def _open_project_impl(self, project_path):
"""打开项目的具体实现不依赖Qt"""
@ -1202,16 +1162,15 @@ class AppActions:
project_name = os.path.basename(project_path)
self._update_window_title(project_name)
print(f"✓ 项目打开成功: {project_path}")
self.add_success_message(f"项目打开成功: {project_name}")
return True
except Exception as e:
print(f"✗ 打开项目时发生错误: {e}")
self.add_error_message(f"打开项目时发生错误: {e}")
return False
def _create_new_project_impl(self, name, path):
"""创建新项目的具体实现不依赖Qt"""
@ -1225,7 +1184,6 @@ class AppActions:
print(f"创建项目失败: {e}")
return False
def _save_project_as_impl(self, name, path):
"""将当前项目保存到新的项目目录。"""
if not hasattr(self, "project_manager") or not self.project_manager:
@ -1291,7 +1249,8 @@ class AppActions:
if previous_project_path:
self.project_manager._sync_project_script_manager(previous_project_path, reload_scripts=True)
self.project_manager._sync_resource_manager_root(previous_project_path)
self._update_window_title(os.path.basename(previous_project_path) if previous_project_path else "未命名项目")
self._update_window_title(
os.path.basename(previous_project_path) if previous_project_path else "未命名项目")
self.add_error_message("项目另存为失败")
return False
@ -1318,7 +1277,6 @@ class AppActions:
source_file = os.path.join(root, file_name)
target_file = os.path.join(destination_root, file_name)
shutil.copy2(source_file, target_file)
def _update_window_title(self, project_name):
"""更新窗口标题"""
@ -1329,9 +1287,8 @@ class AppActions:
print(f"窗口标题已更新: MetaCore - {project_name}")
except Exception as e:
print(f"更新窗口标题失败: {e}")
# ==================== 路径浏览器辅助方法 ====================
def _refresh_ssbo_runtime_import_bindings(self, file_path=None, scene_package_import=False):
ssbo_editor = getattr(self, 'ssbo_editor', None)
@ -1545,8 +1502,6 @@ class AppActions:
except Exception:
self._scene_tree_epoch = 1
animated_model = bool(file_path and self._model_file_has_animation(file_path))
lower_path = str(file_path).lower() if file_path else ""
is_gltf_family = lower_path.endswith((".glb", ".gltf"))
if animated_model:
prefer_scene_manager = True
ssbo_editor = getattr(self, 'ssbo_editor', None)
@ -1556,11 +1511,6 @@ class AppActions:
except Exception:
pass
if is_gltf_family:
fallback_model = self._import_model_via_gltf_fallback(file_path)
if fallback_model:
return fallback_model
if self.use_ssbo_mouse_picking and getattr(self, 'ssbo_editor', None):
if animated_model:
print(f"[AnimationImport] 检测到动画模型跳过SSBO导入: {file_path}")
@ -1627,9 +1577,9 @@ class AppActions:
except Exception:
camera_count = 0
for camera_name in (
"gizmo_overlay_cam",
"pick_camera",
"selection_outline_mask_camera",
"gizmo_overlay_cam",
"pick_camera",
"selection_outline_mask_camera",
):
try:
special_camera_counts[camera_name] = render.find_all_matches(
@ -1648,11 +1598,11 @@ class AppActions:
interesting_tasks = [
name for name in task_names
if (
"gizmo" in str(name).lower()
or "outline" in str(name).lower()
or "pick" in str(name).lower()
or "lui" in str(name).lower()
or "canvas" in str(name).lower()
"gizmo" in str(name).lower()
or "outline" in str(name).lower()
or "pick" in str(name).lower()
or "lui" in str(name).lower()
or "canvas" in str(name).lower()
)
]
@ -1667,12 +1617,12 @@ class AppActions:
pass
def _import_model_with_menu_logic(
self,
file_path,
select_model=True,
set_origin=True,
show_info_message=True,
show_success_message=True,
self,
file_path,
select_model=True,
set_origin=True,
show_info_message=True,
show_success_message=True,
):
"""统一的单文件导入入口,保持与菜单导入一致的处理流程。"""
try:
@ -1733,33 +1683,17 @@ class AppActions:
if hasattr(self.scene_manager, 'models'):
if getattr(self, "use_ssbo_mouse_picking", False):
try:
existing_models = []
for existing in getattr(self.scene_manager, "models", []):
try:
if existing and not existing.isEmpty():
existing_models.append(existing)
except Exception:
continue
if model_node and all(existing != model_node for existing in existing_models):
existing_models.append(model_node)
self.scene_manager.models = existing_models
except Exception:
self.scene_manager.models = [model_node] if model_node else []
self.scene_manager.models = [model_node]
elif model_node not in self.scene_manager.models:
self.scene_manager.models.append(model_node)
if select_model:
if (
getattr(self, "use_ssbo_mouse_picking", False)
and getattr(self, "ssbo_editor", None)
and getattr(self.ssbo_editor, "last_import_tree_key", None)
getattr(self, "use_ssbo_mouse_picking", False)
and getattr(self, "ssbo_editor", None)
and getattr(self.ssbo_editor, "last_import_tree_key", None)
):
self.ssbo_editor.clear_selection(sync_world_selection=False)
if hasattr(self.ssbo_editor, "_sync_editor_selection_reference"):
self.ssbo_editor._sync_editor_selection_reference(None)
if show_info_message:
self.add_info_message("模型导入完成,已保持 SSBO 静态模式以避免导入后掉帧")
self.ssbo_editor.select_node(self.ssbo_editor.last_import_tree_key)
elif hasattr(self, 'selection') and self.selection:
self.selection.updateSelection(model_node)
@ -1784,7 +1718,6 @@ class AppActions:
"""处理导入模型菜单项"""
self.add_info_message("打开系统文件选择器")
self.show_import_dialog = True
def _import_model(self):
"""导入模型的具体实现"""