refactor: 二轮拆分loadScene节点处理流程
This commit is contained in:
parent
4f2c545b88
commit
ffdad6f099
@ -21,7 +21,7 @@
|
||||
- 直接访问 `world.interface_manager`: `0`
|
||||
- 直接访问 `interface_manager.treeWidget`: `0`
|
||||
- `app.gui_manager` 仅剩注释引用(`ui/panels/editor_panels_left.py`)
|
||||
- Task B 第一轮已落地(`scene_manager_io_mixin.loadScene`):
|
||||
- Task B 第二轮已落地(`scene_manager_io_mixin.loadScene`):
|
||||
- 已抽出流程 helper:
|
||||
- `_preflight_load_scene`
|
||||
- `_cleanup_after_failed_load`
|
||||
@ -30,7 +30,12 @@
|
||||
- `_bootstrap_scene_tree_for_loaded_root`
|
||||
- `_load_scene_gui_metadata`
|
||||
- `_retry_load_scene`
|
||||
- `loadScene` 行数:`556 -> 366`
|
||||
- `_walk_loaded_scene`
|
||||
- `_process_loaded_scene_node`
|
||||
- `_restore_node_scripts`
|
||||
- `_parse_vec3_string`
|
||||
- `_parse_color_string`
|
||||
- `loadScene` 行数:`556 -> 74`
|
||||
- 已清理重复异常分支:`_rebuildParentChildRelationships` 内重复 `except` 已移除
|
||||
|
||||
## 1. 总体画像
|
||||
@ -64,16 +69,16 @@
|
||||
7. `TransformGizmo/rotate_gizmo.py` (`1587` 行)
|
||||
8. `ui/panels/animation_tools.py` (`1579` 行)
|
||||
|
||||
### 2.2 长函数 Top(优先拆分)
|
||||
### 2.2 长函数 Top(优先拆分,非 VR 当前状态)
|
||||
|
||||
1. `ui/LUI/lui_function_properties.py::_draw_component_properties` (`1441` 行)
|
||||
2. `scene/scene_manager_io_mixin.py::loadScene` (`556` 行)
|
||||
3. `ui/panels/animation_tools.py::_getActor` (`510` 行)
|
||||
4. `main.py::__init__` (`375` 行)
|
||||
5. `ui/LUI/lui_manager_interaction.py::_update_drag` (`348` 行)
|
||||
6. `ui/panels/editor_panels_left.py::_draw_resource_manager` (`310` 行)
|
||||
7. `scene/scene_manager_io_mixin.py::processNode` (`281` 行)
|
||||
8. `core/selection.py::updateGizmoDrag` (`278` 行)
|
||||
2. `ui/panels/animation_tools.py::_getActor` (`510` 行)
|
||||
3. `main.py::__init__` (`375` 行)
|
||||
4. `ui/LUI/lui_manager_interaction.py::_update_drag` (`348` 行)
|
||||
5. `ui/panels/editor_panels_left.py::_draw_resource_manager` (`310` 行)
|
||||
6. `core/selection.py::updateGizmoDrag` (`278` 行)
|
||||
7. `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` (`257` 行)
|
||||
8. `scene/scene_manager_io_mixin.py::saveScene` (`222` 行)
|
||||
|
||||
### 2.3 异常处理密度高(可观测性风险)
|
||||
|
||||
@ -191,16 +196,14 @@
|
||||
|
||||
## 4.1 本轮深入分析(非 VR,P1 准备)
|
||||
|
||||
### A) `scene/scene_manager_io_mixin.py::loadScene`(核心优先)
|
||||
### A) `scene/scene_manager_io_mixin.py::loadScene`(核心优先,已完成)
|
||||
|
||||
- 函数规模: `556` 行
|
||||
- 近似圈复杂度: `114`
|
||||
- 函数规模: `74` 行(重构前 `556` 行)
|
||||
- 近似圈复杂度(重构前): `114`
|
||||
- 关键问题:
|
||||
- 单函数同时承担 8 类职责(校验/清理/加载/树同步/节点递归处理/脚本恢复/材质恢复/重试)。
|
||||
- 内嵌 `processNode` 递归函数长达 `281` 行,可测试性差。
|
||||
- 调试输出密度高(`print` 与注释 `print` 很多),影响可读性和噪声控制。
|
||||
- `scene/scene_manager_io_mixin.py:1026` 与 `scene/scene_manager_io_mixin.py:1032` 存在重复 `except Exception` 分支(可合并)。
|
||||
- `scene/scene_manager_io_mixin.py:946` 的 GUI 重建入口目前仍注释,行为边界不清晰。
|
||||
- `processNode` 内联逻辑已外提为 `_walk_loaded_scene/_process_loaded_scene_node`。
|
||||
- 重复异常分支已清理。
|
||||
- 仍存在较多调试输出(`print` 与注释 `print`),后续可做日志分级。
|
||||
- 建议拆分(保持外部 API `loadScene` 不变):
|
||||
- `_preflight_scene_file(filename) -> (ok, normalized_path, reason)`
|
||||
- `_cleanup_before_load(tree_widget, retry_count)`
|
||||
@ -215,8 +218,8 @@
|
||||
- 节点恢复行为(位置/材质/脚本/可见性)与当前一致。
|
||||
- 失败重试逻辑保持语义一致。
|
||||
- 当前状态(2026-02-28):
|
||||
- 第一轮已完成(预检/清理/加载/树初始化/GUI元数据/重试)。
|
||||
- 剩余主要体积来自内嵌 `processNode`,下一轮应继续提取为独立方法。
|
||||
- 第二轮已完成(`loadScene` 已达验收目标:`74` 行)。
|
||||
- 下一步应转向 `main.py::__init__` 与 `ui/panels/animation_tools.py::_getActor`。
|
||||
|
||||
### B) `main.py::__init__`(第二优先)
|
||||
|
||||
@ -255,8 +258,8 @@
|
||||
|
||||
## 4.2 Task B 执行顺序建议(非 VR)
|
||||
|
||||
1. 先拆 `loadScene`(收益最大,且与 VR 无关)。
|
||||
2. 再整理 `main.__init__`(降低后续模块接入冲突)。
|
||||
1. `loadScene` 已完成两轮拆分(当前建议冻结并转入回归观察)。
|
||||
2. 下一步整理 `main.__init__`(降低后续模块接入冲突)。
|
||||
3. 最后处理 `_getActor`(风险最高,建议独立提交并做手工回归)。
|
||||
|
||||
## 5. 与现有文档关系
|
||||
|
||||
@ -485,301 +485,9 @@ class SceneManagerIOMixin:
|
||||
scene.reparentTo(self.world.render)
|
||||
|
||||
self._bootstrap_scene_tree_for_loaded_root(tree_widget, scene)
|
||||
# 遍历场景中的所有模型节点
|
||||
# 用于存储处理后的灯光节点,避免重复处理
|
||||
processed_lights = []
|
||||
# 用于存储处理后的GUI元素,避免重复处理
|
||||
|
||||
#存储所有加载的节点,用于后续处理父子关系
|
||||
loaded_nodes = {} #name->nodePath映射
|
||||
|
||||
# 遍历场景中的所有节点
|
||||
def processNode(nodePath, depth=0):
|
||||
indent = " " * depth
|
||||
# print(f"{indent}处理节点: {nodePath.getName()} (类型: {type(nodePath.node()).__name__})")
|
||||
|
||||
# 检查节点是否有效
|
||||
if nodePath.isEmpty():
|
||||
# print(f"{indent}[DEBUG] 节点为空,跳过处理")
|
||||
return
|
||||
|
||||
#存储节点以便后续处理父子关系
|
||||
loaded_nodes[nodePath.getName()] = nodePath
|
||||
|
||||
if nodePath.getName().startswith('ground'):
|
||||
print(f"{indent}跳过ground节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
# 跳过render节点的递归
|
||||
if nodePath.getName() == "render" and depth > 0:
|
||||
print(f"{indent}跳过重复的render节点")
|
||||
return
|
||||
|
||||
# 跳过光源节点
|
||||
if nodePath.getName() in ["alight", "dlight"]:
|
||||
print(f"{indent}跳过光源节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
# 跳过相机节点
|
||||
if nodePath.getName() in ["camera", "cam"]:
|
||||
print(f"{indent}跳过相机节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
# 跳过辅助节点
|
||||
if nodePath.getName().startswith(("gizmo", "selectionBox")):
|
||||
print(f"{indent}跳过辅助节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath.getName() in ['SceneRoot'] or \
|
||||
any(keyword in nodePath.getName() for keyword in ["Skybox", "skybox"]):
|
||||
print(f"{indent}跳过环境节点:{nodePath.getName()}")
|
||||
return
|
||||
|
||||
# 检查是否是用户创建的场景元素
|
||||
is_scene_element = (
|
||||
nodePath.hasTag("is_scene_element") or
|
||||
nodePath.hasTag("is_model_root") or
|
||||
nodePath.hasTag("light_type") or
|
||||
nodePath.hasTag("gui_type") or # 检查gui_type标签
|
||||
nodePath.hasTag("is_gui_element") or
|
||||
nodePath.hasTag("saved_gui_type") or
|
||||
(nodePath.hasTag("element_type") and nodePath.getTag("element_type") == "info_panel")
|
||||
)
|
||||
|
||||
# 特殊处理:检查节点名称是否包含GUI相关关键词
|
||||
is_potential_gui = any(keyword in nodePath.getName().lower() for keyword in
|
||||
["gui", "button", "label", "entry", "image", "video", "screen", "text"])
|
||||
|
||||
if is_scene_element or is_potential_gui:
|
||||
print(f"{indent}找到场景元素节点: {nodePath.getName()}")
|
||||
|
||||
# 如果是潜在的GUI元素但没有标签,添加基本标签
|
||||
if is_potential_gui and not (nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element")):
|
||||
print(f"{indent}为潜在GUI元素添加标签: {nodePath.getName()}")
|
||||
nodePath.setTag("is_gui_element", "1")
|
||||
nodePath.setTag("is_scene_element", "1")
|
||||
# 尝试从名称推断类型
|
||||
name_lower = nodePath.getName().lower()
|
||||
if "button" in name_lower:
|
||||
nodePath.setTag("gui_type", "button")
|
||||
elif "label" in name_lower:
|
||||
nodePath.setTag("gui_type", "label")
|
||||
elif "entry" in name_lower:
|
||||
nodePath.setTag("gui_type", "entry")
|
||||
elif "image" in name_lower:
|
||||
nodePath.setTag("gui_type", "image")
|
||||
elif "video" in name_lower or "screen" in name_lower:
|
||||
nodePath.setTag("gui_type", "video_screen")
|
||||
else:
|
||||
nodePath.setTag("gui_type", "unknown")
|
||||
|
||||
# 清除现有材质状态
|
||||
nodePath.clearMaterial()
|
||||
nodePath.clearColor()
|
||||
|
||||
# 恢复变换信息
|
||||
def parseVec3(vec_str):
|
||||
"""解析向量字符串为Vec3"""
|
||||
try:
|
||||
vec_str = vec_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()')
|
||||
x, y, z = map(float, vec_str.split(','))
|
||||
return Vec3(x, y, z)
|
||||
except Exception as e:
|
||||
print(f"解析向量失败: {vec_str}, 错误: {e}")
|
||||
return Vec3(0, 0, 0)
|
||||
|
||||
if nodePath.hasTag("transform_pos"):
|
||||
pos = parseVec3(nodePath.getTag("transform_pos"))
|
||||
nodePath.setPos(pos)
|
||||
print(f"{indent}恢复位置: {pos}")
|
||||
|
||||
if nodePath.hasTag("transform_hpr"):
|
||||
hpr = parseVec3(nodePath.getTag("transform_hpr"))
|
||||
nodePath.setHpr(hpr)
|
||||
print(f"{indent}恢复旋转: {hpr}")
|
||||
|
||||
if nodePath.hasTag("transform_scale"):
|
||||
scale = parseVec3(nodePath.getTag("transform_scale"))
|
||||
nodePath.setScale(scale)
|
||||
print(f"{indent}恢复缩放: {scale}")
|
||||
|
||||
# 恢复可见性状态
|
||||
user_visible = True
|
||||
if nodePath.hasTag("user_visible"):
|
||||
user_visible = nodePath.getTag("user_visible").lower() == "true"
|
||||
|
||||
# 设置用户可见性标记
|
||||
nodePath.setPythonTag("user_visible", user_visible)
|
||||
|
||||
# 应用可见性状态
|
||||
if hasattr(self.world, 'property_panel'):
|
||||
self.world.property_panel._syncEffectiveVisibility(nodePath)
|
||||
else:
|
||||
# 如果没有属性面板,直接应用可见性
|
||||
if user_visible:
|
||||
nodePath.show()
|
||||
else:
|
||||
nodePath.hide()
|
||||
|
||||
if nodePath.hasTag("has_scripts") and nodePath.getTag("has_scripts") == "true":
|
||||
if hasattr(self.world,'script_manager') and self.world.script_manager:
|
||||
try:
|
||||
import json
|
||||
scripts_info = json.loads(nodePath.getTag("scripts_info"))
|
||||
print(f"节点 {nodePath.getName()} 需要重新挂载 {len(scripts_info)} 个脚本")
|
||||
|
||||
script_manager = self.world.script_manager
|
||||
for script_info in scripts_info:
|
||||
script_name = script_info["name"]
|
||||
script_file = script_info.get("file","")
|
||||
|
||||
print(f"尝试重新挂载脚本{script_name}from {script_file}")
|
||||
|
||||
if script_name not in script_manager.loader.script_classes:
|
||||
if script_file and os.path.exists(script_file):
|
||||
print(f"从文件加载脚本:{script_file}")
|
||||
loaded_class = script_manager.load_script_from_file(script_file)
|
||||
if loaded_class is None:
|
||||
print(f"从文件加载脚本失败{script_file}")
|
||||
script_path = self._find_script_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载{script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
script_path = self._find_script_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载: {script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
print(f"找不到脚本文件: {script_name}")
|
||||
if script_name in script_manager.loader.script_classes:
|
||||
script_component = script_manager.add_script_to_object(nodePath,script_name)
|
||||
if script_component:
|
||||
print(f"成功为 {nodePath.getName()} 添加脚本: {script_name}")
|
||||
else:
|
||||
print(f"为 {nodePath.getName()} 添加脚本失败: {script_name}")
|
||||
else:
|
||||
print(f"脚本 {script_name} 不可用,跳过挂载")
|
||||
except Exception as e:
|
||||
print(f"重新挂载脚本失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 恢复材质属性
|
||||
def parseColor(color_str):
|
||||
"""解析颜色字符串为Vec4"""
|
||||
try:
|
||||
color_str = color_str.replace('LVecBase4f', '').strip('()')
|
||||
r, g, b, a = map(float, color_str.split(','))
|
||||
return Vec4(r, g, b, a)
|
||||
except:
|
||||
return Vec4(1, 1, 1, 1)
|
||||
|
||||
# 创建并恢复材质
|
||||
material = Material()
|
||||
material_changed = False
|
||||
|
||||
if nodePath.hasTag("material_ambient"):
|
||||
material.setAmbient(parseColor(nodePath.getTag("material_ambient")))
|
||||
material_changed = True
|
||||
|
||||
if nodePath.hasTag("material_diffuse"):
|
||||
material.setDiffuse(parseColor(nodePath.getTag("material_diffuse")))
|
||||
material_changed = True
|
||||
|
||||
if nodePath.hasTag("material_specular"):
|
||||
material.setSpecular(parseColor(nodePath.getTag("material_specular")))
|
||||
material_changed = True
|
||||
|
||||
if nodePath.hasTag("material_emission"):
|
||||
material.setEmission(parseColor(nodePath.getTag("material_emission")))
|
||||
material_changed = True
|
||||
|
||||
if nodePath.hasTag("material_shininess"):
|
||||
material.setShininess(float(nodePath.getTag("material_shininess")))
|
||||
material_changed = True
|
||||
|
||||
if nodePath.hasTag("material_basecolor"):
|
||||
material.setBaseColor(parseColor(nodePath.getTag("material_basecolor")))
|
||||
material_changed = True
|
||||
|
||||
if material_changed:
|
||||
nodePath.setMaterial(material)
|
||||
|
||||
# 恢复颜色属性
|
||||
if nodePath.hasTag("color"):
|
||||
nodePath.setColor(parseColor(nodePath.getTag("color")))
|
||||
|
||||
# 处理特定类型的节点
|
||||
if nodePath.hasTag("light_type"):
|
||||
light_type = nodePath.getTag("light_type")
|
||||
print(f"{indent}检测到光源类型: {light_type}")
|
||||
|
||||
# 检查是否已经处理过这个灯光
|
||||
if nodePath not in processed_lights:
|
||||
# 重新创建RP光源对象
|
||||
if light_type == "spot_light":
|
||||
self._recreateSpotLight(nodePath)
|
||||
elif light_type == "point_light":
|
||||
self._recreatePointLight(nodePath)
|
||||
# 标记为已处理
|
||||
processed_lights.append(nodePath)
|
||||
|
||||
elif nodePath.hasTag("element_type"):
|
||||
element_type = nodePath.getTag("element_type")
|
||||
if element_type == "cesium_tileset":
|
||||
tileset_url = nodePath.getTag("saved_tileset_url") if nodePath.hasTag(
|
||||
"saved_tileset_url") else ""
|
||||
tileset_info = {
|
||||
'url': tileset_url,
|
||||
'node': nodePath,
|
||||
'position': nodePath.getPos(),
|
||||
'tiles': {}
|
||||
}
|
||||
|
||||
# 将节点重新挂载到render下(如果需要)
|
||||
# 注意:GUI元素可能需要挂载到特定的父节点上
|
||||
if nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element"):
|
||||
# GUI元素通常应该挂载到aspect2d或特定的GUI父节点上
|
||||
# 这里我们先保持原挂载关系
|
||||
pass
|
||||
else:
|
||||
# 其他节点确保挂载到render下
|
||||
if nodePath.getParent() != self.world.render and not nodePath.getName() in ["render",
|
||||
"aspect2d",
|
||||
"render2d"]:
|
||||
nodePath.wrtReparentTo(self.world.render)
|
||||
|
||||
# 为模型节点设置碰撞检测
|
||||
if nodePath.hasTag("is_model_root"):
|
||||
print(f"J{indent}处理模型节点{nodePath.getName()}")
|
||||
|
||||
#self._validateAndFixAllTransforms(nodePath)
|
||||
|
||||
self._fixModelStructure(nodePath)
|
||||
|
||||
# 恢复模型动画信息
|
||||
self._restoreModelAnimationInfo(nodePath)
|
||||
|
||||
# 检测并处理模型动画(类似property_panel.py中的逻辑)
|
||||
self._processModelAnimations(nodePath)
|
||||
|
||||
# if self.world.property_panel._hasCollision(nodePath):
|
||||
# print(f"{indent}模型{nodePath.getName()}已有碰撞体,跳过碰撞体设置")
|
||||
# else:
|
||||
# print(f"{indent}为模型{nodePath.getName()}设置碰撞检测")
|
||||
# self.setupCollision(nodePath)
|
||||
self.models.append(nodePath)
|
||||
|
||||
# 递归处理子节点
|
||||
for child in nodePath.getChildren():
|
||||
processNode(child, depth + 1)
|
||||
|
||||
print("\n开始处理场景节点...")
|
||||
processNode(scene)
|
||||
loaded_nodes = self._walk_loaded_scene(scene)
|
||||
|
||||
#处理父子关系 - 在所有节点加载完成后设置正确的父子关系
|
||||
print("\n开始重建父子关系...")
|
||||
self._rebuildParentChildRelationships(loaded_nodes)
|
||||
|
||||
@ -810,6 +518,227 @@ class SceneManagerIOMixin:
|
||||
traceback.print_exc()
|
||||
return self._retry_load_scene(filename, retry_count, max_retries)
|
||||
|
||||
def _walk_loaded_scene(self, scene):
|
||||
"""遍历并恢复加载后的场景节点状态。"""
|
||||
processed_lights = []
|
||||
loaded_nodes = {}
|
||||
self._process_loaded_scene_node(scene, loaded_nodes, processed_lights, depth=0)
|
||||
return loaded_nodes
|
||||
|
||||
def _process_loaded_scene_node(self, nodePath, loaded_nodes, processed_lights, depth=0):
|
||||
indent = " " * depth
|
||||
if nodePath.isEmpty():
|
||||
return
|
||||
|
||||
loaded_nodes[nodePath.getName()] = nodePath
|
||||
|
||||
if nodePath.getName().startswith('ground'):
|
||||
print(f"{indent}跳过ground节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath.getName() == "render" and depth > 0:
|
||||
print(f"{indent}跳过重复的render节点")
|
||||
return
|
||||
|
||||
if nodePath.getName() in ["alight", "dlight"]:
|
||||
print(f"{indent}跳过光源节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath.getName() in ["camera", "cam"]:
|
||||
print(f"{indent}跳过相机节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath.getName().startswith(("gizmo", "selectionBox")):
|
||||
print(f"{indent}跳过辅助节点: {nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath.getName() in ['SceneRoot'] or any(keyword in nodePath.getName() for keyword in ["Skybox", "skybox"]):
|
||||
print(f"{indent}跳过环境节点:{nodePath.getName()}")
|
||||
return
|
||||
|
||||
is_scene_element = (
|
||||
nodePath.hasTag("is_scene_element") or
|
||||
nodePath.hasTag("is_model_root") or
|
||||
nodePath.hasTag("light_type") or
|
||||
nodePath.hasTag("gui_type") or
|
||||
nodePath.hasTag("is_gui_element") or
|
||||
nodePath.hasTag("saved_gui_type") or
|
||||
(nodePath.hasTag("element_type") and nodePath.getTag("element_type") == "info_panel")
|
||||
)
|
||||
is_potential_gui = any(
|
||||
keyword in nodePath.getName().lower()
|
||||
for keyword in ["gui", "button", "label", "entry", "image", "video", "screen", "text"]
|
||||
)
|
||||
|
||||
if is_scene_element or is_potential_gui:
|
||||
print(f"{indent}找到场景元素节点: {nodePath.getName()}")
|
||||
|
||||
if is_potential_gui and not (nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element")):
|
||||
print(f"{indent}为潜在GUI元素添加标签: {nodePath.getName()}")
|
||||
nodePath.setTag("is_gui_element", "1")
|
||||
nodePath.setTag("is_scene_element", "1")
|
||||
name_lower = nodePath.getName().lower()
|
||||
if "button" in name_lower:
|
||||
nodePath.setTag("gui_type", "button")
|
||||
elif "label" in name_lower:
|
||||
nodePath.setTag("gui_type", "label")
|
||||
elif "entry" in name_lower:
|
||||
nodePath.setTag("gui_type", "entry")
|
||||
elif "image" in name_lower:
|
||||
nodePath.setTag("gui_type", "image")
|
||||
elif "video" in name_lower or "screen" in name_lower:
|
||||
nodePath.setTag("gui_type", "video_screen")
|
||||
else:
|
||||
nodePath.setTag("gui_type", "unknown")
|
||||
|
||||
nodePath.clearMaterial()
|
||||
nodePath.clearColor()
|
||||
|
||||
if nodePath.hasTag("transform_pos"):
|
||||
pos = self._parse_vec3_string(nodePath.getTag("transform_pos"))
|
||||
nodePath.setPos(pos)
|
||||
print(f"{indent}恢复位置: {pos}")
|
||||
|
||||
if nodePath.hasTag("transform_hpr"):
|
||||
hpr = self._parse_vec3_string(nodePath.getTag("transform_hpr"))
|
||||
nodePath.setHpr(hpr)
|
||||
print(f"{indent}恢复旋转: {hpr}")
|
||||
|
||||
if nodePath.hasTag("transform_scale"):
|
||||
scale = self._parse_vec3_string(nodePath.getTag("transform_scale"))
|
||||
nodePath.setScale(scale)
|
||||
print(f"{indent}恢复缩放: {scale}")
|
||||
|
||||
user_visible = True
|
||||
if nodePath.hasTag("user_visible"):
|
||||
user_visible = nodePath.getTag("user_visible").lower() == "true"
|
||||
|
||||
nodePath.setPythonTag("user_visible", user_visible)
|
||||
if hasattr(self.world, 'property_panel'):
|
||||
self.world.property_panel._syncEffectiveVisibility(nodePath)
|
||||
else:
|
||||
if user_visible:
|
||||
nodePath.show()
|
||||
else:
|
||||
nodePath.hide()
|
||||
|
||||
if nodePath.hasTag("has_scripts") and nodePath.getTag("has_scripts") == "true":
|
||||
self._restore_node_scripts(nodePath)
|
||||
|
||||
material = Material()
|
||||
material_changed = False
|
||||
if nodePath.hasTag("material_ambient"):
|
||||
material.setAmbient(self._parse_color_string(nodePath.getTag("material_ambient")))
|
||||
material_changed = True
|
||||
if nodePath.hasTag("material_diffuse"):
|
||||
material.setDiffuse(self._parse_color_string(nodePath.getTag("material_diffuse")))
|
||||
material_changed = True
|
||||
if nodePath.hasTag("material_specular"):
|
||||
material.setSpecular(self._parse_color_string(nodePath.getTag("material_specular")))
|
||||
material_changed = True
|
||||
if nodePath.hasTag("material_emission"):
|
||||
material.setEmission(self._parse_color_string(nodePath.getTag("material_emission")))
|
||||
material_changed = True
|
||||
if nodePath.hasTag("material_shininess"):
|
||||
material.setShininess(float(nodePath.getTag("material_shininess")))
|
||||
material_changed = True
|
||||
if nodePath.hasTag("material_basecolor"):
|
||||
material.setBaseColor(self._parse_color_string(nodePath.getTag("material_basecolor")))
|
||||
material_changed = True
|
||||
if material_changed:
|
||||
nodePath.setMaterial(material)
|
||||
|
||||
if nodePath.hasTag("color"):
|
||||
nodePath.setColor(self._parse_color_string(nodePath.getTag("color")))
|
||||
|
||||
if nodePath.hasTag("light_type"):
|
||||
light_type = nodePath.getTag("light_type")
|
||||
print(f"{indent}检测到光源类型: {light_type}")
|
||||
if nodePath not in processed_lights:
|
||||
if light_type == "spot_light":
|
||||
self._recreateSpotLight(nodePath)
|
||||
elif light_type == "point_light":
|
||||
self._recreatePointLight(nodePath)
|
||||
processed_lights.append(nodePath)
|
||||
|
||||
if not (nodePath.hasTag("gui_type") or nodePath.hasTag("is_gui_element")):
|
||||
if nodePath.getParent() != self.world.render and nodePath.getName() not in ["render", "aspect2d", "render2d"]:
|
||||
nodePath.wrtReparentTo(self.world.render)
|
||||
|
||||
if nodePath.hasTag("is_model_root"):
|
||||
print(f"J{indent}处理模型节点{nodePath.getName()}")
|
||||
self._fixModelStructure(nodePath)
|
||||
self._restoreModelAnimationInfo(nodePath)
|
||||
self._processModelAnimations(nodePath)
|
||||
self.models.append(nodePath)
|
||||
|
||||
for child in nodePath.getChildren():
|
||||
self._process_loaded_scene_node(child, loaded_nodes, processed_lights, depth + 1)
|
||||
|
||||
def _restore_node_scripts(self, nodePath):
|
||||
"""恢复节点挂载脚本。"""
|
||||
if not hasattr(self.world, 'script_manager') or not self.world.script_manager:
|
||||
return
|
||||
try:
|
||||
scripts_info = json.loads(nodePath.getTag("scripts_info"))
|
||||
print(f"节点 {nodePath.getName()} 需要重新挂载 {len(scripts_info)} 个脚本")
|
||||
|
||||
script_manager = self.world.script_manager
|
||||
for script_info in scripts_info:
|
||||
script_name = script_info["name"]
|
||||
script_file = script_info.get("file", "")
|
||||
print(f"尝试重新挂载脚本{script_name}from {script_file}")
|
||||
|
||||
if script_name not in script_manager.loader.script_classes:
|
||||
if script_file and os.path.exists(script_file):
|
||||
print(f"从文件加载脚本:{script_file}")
|
||||
loaded_class = script_manager.load_script_from_file(script_file)
|
||||
if loaded_class is None:
|
||||
print(f"从文件加载脚本失败{script_file}")
|
||||
script_path = self._find_script_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载{script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
script_path = self._find_script_in_directory(script_name)
|
||||
if script_path:
|
||||
print(f"从目录找到脚本并加载: {script_path}")
|
||||
script_manager.load_script_from_file(script_path)
|
||||
else:
|
||||
print(f"找不到脚本文件: {script_name}")
|
||||
|
||||
if script_name in script_manager.loader.script_classes:
|
||||
script_component = script_manager.add_script_to_object(nodePath, script_name)
|
||||
if script_component:
|
||||
print(f"成功为 {nodePath.getName()} 添加脚本: {script_name}")
|
||||
else:
|
||||
print(f"为 {nodePath.getName()} 添加脚本失败: {script_name}")
|
||||
else:
|
||||
print(f"脚本 {script_name} 不可用,跳过挂载")
|
||||
except Exception as e:
|
||||
print(f"重新挂载脚本失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _parse_vec3_string(self, vec_str):
|
||||
"""解析向量字符串为 Vec3。"""
|
||||
try:
|
||||
vec_str = vec_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()')
|
||||
x, y, z = map(float, vec_str.split(','))
|
||||
return Vec3(x, y, z)
|
||||
except Exception as e:
|
||||
print(f"解析向量失败: {vec_str}, 错误: {e}")
|
||||
return Vec3(0, 0, 0)
|
||||
|
||||
def _parse_color_string(self, color_str):
|
||||
"""解析颜色字符串为 Vec4。"""
|
||||
try:
|
||||
color_str = color_str.replace('LVecBase4f', '').strip('()')
|
||||
r, g, b, a = map(float, color_str.split(','))
|
||||
return Vec4(r, g, b, a)
|
||||
except Exception:
|
||||
return Vec4(1, 1, 1, 1)
|
||||
|
||||
def _preflight_load_scene(self, filename, retry_count):
|
||||
"""预检加载参数并在重试前做预清理。"""
|
||||
filename = os.path.normpath(filename)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user