"""Scene manager node serialization operations.""" import os import shutil import time import json import aiohttp import asyncio import inspect from pathlib import Path from panda3d.core import ( ModelPool, ModelRoot, Filename, NodePath, GeomNode, Material, Vec4, Vec3, MaterialAttrib, ColorAttrib, Point3, CollisionNode, CollisionSphere, CollisionBox, BitMask32, TransparencyAttrib, LColor, TransformState, RenderModeAttrib ) from panda3d.egg import EggData, EggVertexPool from direct.actor.Actor import Actor from RenderPipelineFile.rpplugins.smaa.jitters import halton_seq from scene import util class SceneManagerSerializationMixin: def serializeNode(self, node): """序列化节点为字典数据""" try: node_data = { 'name': node.getName(), 'type': type(node.node()).__name__, 'pos': (node.getX(), node.getY(), node.getZ()), 'hpr': (node.getH(), node.getP(), node.getR()), 'scale': (node.getSx(), node.getSy(), node.getSz()), 'tags': {}, 'children': [] } # 保存所有标签 for tag_key in node.getTagKeys(): node_data['tags'][tag_key] = node.getTag(tag_key) # 特殊处理不同类型的节点 if hasattr(node.node(), 'getClassType'): node_class = node.node().getClassType().getName() node_data['node_class'] = node_class # 递归序列化子节点 for child in node.getChildren(): # 跳过辅助节点 if not child.getName().startswith(('gizmo', 'selectionBox', 'grid')): child_data = self.serializeNode(child) if child_data: node_data['children'].append(child_data) return node_data except Exception as e: print(f"序列化节点 {node.getName()} 失败: {e}") import traceback traceback.print_exc() return None def deserializeNode(self, node_data, parent_node): """从字典数据反序列化节点""" try: # 创建新节点 node_name = node_data.get('name', 'node') new_node = parent_node.attachNewNode(node_name) # 设置变换 pos = node_data.get('pos', (0, 0, 0)) hpr = node_data.get('hpr', (0, 0, 0)) scale = node_data.get('scale', (1, 1, 1)) new_node.setPos(*pos) new_node.setHpr(*hpr) new_node.setScale(*scale) # 恢复标签 for tag_key, tag_value in node_data.get('tags', {}).items(): new_node.setTag(tag_key, tag_value) # 根据节点类型进行特殊处理 node_type = node_data.get('type', '') node_class = node_data.get('node_class', '') # 特殊处理光源节点 if 'light_type' in node_data.get('tags', {}): light_type = node_data['tags']['light_type'] if light_type == 'spot_light': self._recreateSpotLight(new_node) elif light_type == 'point_light': self._recreatePointLight(new_node) # 递归创建子节点 for child_data in node_data.get('children', []): self.deserializeNode(child_data, new_node) return new_node except Exception as e: print(f"反序列化节点 {node_data.get('name', 'unknown')} 失败: {e}") import traceback traceback.print_exc() return None def serializeNodeForCopy(self, node): """序列化节点用于复制操作,完整保存视觉属性""" try: if not node or node.isEmpty(): return None node_data = { 'name': node.getName(), 'type': type(node.node()).__name__, 'pos': (node.getX(), node.getY(), node.getZ()), 'hpr': (node.getH(), node.getP(), node.getR()), 'scale': (node.getSx(), node.getSy(), node.getSz()), 'tags': {}, 'children': [] } # 保存所有标签 try: if hasattr(node, 'getTagKeys'): for tag_key in node.getTagKeys(): node_data['tags'][tag_key] = node.getTag(tag_key) except Exception as e: print(f"获取标签时出错: {e}") # 保存视觉属性 try: # 保存颜色属性 if hasattr(node, 'getColor'): color = node.getColor() node_data['color'] = (color.getX(), color.getY(), color.getZ(), color.getW()) # 保存材质属性 if hasattr(node, 'getMaterial'): material = node.getMaterial() if material: material_data = {} material_data['base_color'] = ( material.getBaseColor().getX(), material.getBaseColor().getY(), material.getBaseColor().getZ(), material.getBaseColor().getW() ) material_data['ambient'] = ( material.getAmbient().getX(), material.getAmbient().getY(), material.getAmbient().getZ(), material.getAmbient().getW() ) material_data['diffuse'] = ( material.getDiffuse().getX(), material.getDiffuse().getY(), material.getDiffuse().getZ(), material.getDiffuse().getW() ) material_data['specular'] = ( material.getSpecular().getX(), material.getSpecular().getY(), material.getSpecular().getZ(), material.getSpecular().getW() ) material_data['shininess'] = material.getShininess() node_data['material'] = material_data except Exception as e: print(f"保存视觉属性时出错: {e}") # 根据节点类型保存特定信息 if node.hasTag("tree_item_type"): node_type = node.getTag("tree_item_type") node_data['node_type'] = node_type # 保存特定类型节点的额外信息 if node_type in ["LIGHT_NODE", "SPOT_LIGHT_NODE", "POINT_LIGHT_NODE"]: # 保存光源特定信息 rp_light = node.getPythonTag("rp_light_object") if rp_light: node_data['light_data'] = { 'energy': getattr(rp_light, 'energy', 5000), 'radius': getattr(rp_light, 'radius', 1000), 'fov': getattr(rp_light, 'fov', 70) if hasattr(rp_light, 'fov') else None, 'inner_radius': getattr(rp_light, 'inner_radius', 0.4) if hasattr(rp_light, 'inner_radius') else None, 'casts_shadows': getattr(rp_light, 'casts_shadows', True) if hasattr(rp_light, 'casts_shadows') else True, 'shadow_map_resolution': getattr(rp_light, 'shadow_map_resolution', 256) if hasattr( rp_light, 'shadow_map_resolution') else 256 } elif node_type in ["GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_IMAGE", "GUI_3D_TEXT", "GUI_3D_IMAGE", "GUI_VIRTUAL_SCREEN"]: # 保存GUI元素特定信息 node_data['gui_data'] = self._serializeGUIData(node) elif node_type == "IMPORTED_MODEL_NODE": # 保存模型特定信息 node_data['model_data'] = self._serializeModelData(node) return node_data except Exception as e: print(f"序列化节点失败: {e}") import traceback traceback.print_exc() return None def _serializeGUIData(self, node): """序列化GUI元素数据""" try: gui_data = {} # 保存GUI相关的通用属性 if node.hasTag("gui_type"): gui_data['gui_type'] = node.getTag("gui_type") # 保存文本内容(如果有的话) if node.hasTag("text"): gui_data['text'] = node.getTag("text") # 保存其他GUI相关标签 gui_tags = ['font', 'font_size', 'text_color', 'bg_color', 'size'] for tag in gui_tags: if node.hasTag(tag): gui_data[tag] = node.getTag(tag) return gui_data except Exception as e: print(f"序列化GUI数据失败: {e}") return {} def _serializeModelTextures(self, node): """序列化模型纹理信息""" try: texture_data = {} # 获取节点的所有纹理阶段 from panda3d.core import TextureStage texture_stages = node.findAllTextureStages() if texture_stages.getNumTextureStages() > 0: texture_data['textures'] = {} # 遍历所有纹理阶段 for i in range(texture_stages.getNumTextureStages()): stage = texture_stages.getTextureStage(i) stage_name = stage.getName() # 获取该阶段的纹理 texture = node.getTexture(stage) if texture: # 保存纹理信息 texture_info = { 'stage_name': stage_name, 'stage_mode': stage.getMode(), 'stage_sort': stage.getSort(), # 保存纹理阶段排序 'texture_path': texture.getFullpath().toOsSpecific() if texture.hasFullpath() else '', 'texture_name': texture.getName(), 'wrap_u': texture.getWrapU(), 'wrap_v': texture.getWrapV(), 'minfilter': texture.getMinfilter(), 'magfilter': texture.getMagfilter(), 'anisotropic_degree': texture.getAnisotropicDegree() } # 保存颜色比例和偏移(使用安全的方法) try: texture_info['color_scale'] = tuple(node.getTextureScale(stage)) except: texture_info['color_scale'] = (1.0, 1.0, 1.0, 1.0) try: texture_info['color_offset'] = tuple(node.getTextureOffset(stage)) except: texture_info['color_offset'] = (0.0, 0.0, 0.0, 0.0) texture_data['textures'][stage_name] = texture_info return texture_data except Exception as e: print(f"序列化模型纹理时出错: {e}") return {} def _serializeModelData(self, node): """序列化模型数据,包括材质和纹理信息""" try: model_data = {} # 保存模型相关的标签 model_tags = ['model_path', 'file', 'element_type'] for tag in model_tags: if node.hasTag(tag): model_data[tag] = node.getTag(tag) # 保存材质信息 try: # 获取模型的材质信息 if hasattr(node, 'getState'): state = node.getState() if state: # 保存基础颜色信息(使用正确的方法) from panda3d.core import ColorAttrib color_attrib = state.getAttrib(ColorAttrib) if color_attrib and not color_attrib.isOff(): color = color_attrib.getColor() model_data['base_color'] = ( color.getX(), color.getY(), color.getZ(), color.getW() ) # 保存其他材质属性 from panda3d.core import MaterialAttrib material_attrib = state.getAttrib(MaterialAttrib.getClassType()) if material_attrib: material = material_attrib.getMaterial() if material: # 保存基础颜色 base_color = material.getBaseColor() model_data['material_base_color'] = ( base_color.getX(), base_color.getY(), base_color.getZ(), base_color.getW() ) # 保存环境光颜色 ambient_color = material.getAmbient() model_data['material_ambient_color'] = ( ambient_color.getX(), ambient_color.getY(), ambient_color.getZ(), ambient_color.getW() ) # 保存漫反射颜色 diffuse_color = material.getDiffuse() model_data['material_diffuse_color'] = ( diffuse_color.getX(), diffuse_color.getY(), diffuse_color.getZ(), diffuse_color.getW() ) # 保存高光颜色 specular_color = material.getSpecular() model_data['material_specular_color'] = ( specular_color.getX(), specular_color.getY(), specular_color.getZ(), specular_color.getW() ) # 保存粗糙度和金属度等参数 model_data['material_roughness'] = material.getRoughness() model_data['material_metallic'] = material.getMetallic() # 保存自发光颜色 emission_color = material.getEmission() model_data['material_emission_color'] = ( emission_color.getX(), emission_color.getY(), emission_color.getZ(), emission_color.getW() ) # 保存光泽度 model_data['material_shininess'] = material.getShininess() # 保存透明度信息 from panda3d.core import TransparencyAttrib transparency_attrib = state.getAttrib(TransparencyAttrib.getClassType()) if transparency_attrib: model_data['transparency_mode'] = transparency_attrib.get_mode() except Exception as e: print(f"保存材质信息时出错: {e}") # 保存纹理信息 try: texture_data = self._serializeModelTextures(node) if texture_data: model_data['texture_data'] = texture_data except Exception as e: print(f"保存纹理信息时出错: {e}") return model_data except Exception as e: print(f"序列化模型数据失败: {e}") return {} def recreateNodeFromData(self, node_data, parent_node): """根据数据重建节点,并确保在场景树中显示""" try: if not node_data or not parent_node or parent_node.isEmpty(): return None print(f"正在重建节点 {node_data}") node_type = node_data.get('node_type', '') original_name = node_data.get('name', 'node') # 生成唯一名称 unique_name = self._generateUniqueName(original_name, parent_node) # 根据节点类型调用相应的重建方法 new_node = None if node_type in ["LIGHT_NODE", "SPOT_LIGHT_NODE", "POINT_LIGHT_NODE"]: new_node = self._recreateLightFromData(node_data, parent_node, unique_name) elif node_type == "CESIUM_TILESET_NODE": new_node = self._recreateTilesetFromData(node_data, parent_node, unique_name) elif node_type in ["GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_IMAGE", "GUI_3DTEXT", "GUI_3DIMAGE", "GUI_VIDEO_SCREEN","GUI_2D_VIDEO_SCREEN"]: new_node = self._recreateGUIFromData(node_data, parent_node, unique_name) elif node_type == "IMPORTED_MODEL_NODE": new_node = self._recreateModelFromData(node_data, parent_node, unique_name) else: # 创建普通节点 new_node = self._createBasicNodeFromData(node_data, parent_node, unique_name) # 如果成功创建节点,确保它在场景树中显示 if new_node: # 尝试更新场景树以显示新节点 try: if hasattr(self.world, 'interface_manager') and self.world.interface_manager: # 查找父节点在场景树中的对应项 parent_item = self._findTreeItemForNode(parent_node) if parent_item: # 添加新节点到场景树 tree_widget = self.world.interface_manager.treeWidget if tree_widget: tree_widget.add_node_to_tree_widget(new_node, parent_item, node_type or "NODE") except Exception as e: print(f"添加节点到场景树时出错: {e}") return new_node except Exception as e: print(f"重建节点失败: {e}") import traceback traceback.print_exc() return None def _findTreeItemForNode(self, node): """根据节点查找对应的场景树项""" try: if hasattr(self.world, 'interface_manager') and self.world.interface_manager: tree_widget = self.world.interface_manager.treeWidget if tree_widget: # 遍历场景树查找匹配的节点项 for i in range(tree_widget.topLevelItemCount()): item = tree_widget.topLevelItem(i) result = self._findTreeItemForNodeRecursive(item, node) if result: return result return None except Exception as e: print(f"查找场景树项时出错: {e}") return None def _findTreeItemForNodeRecursive(self, item, target_node): """递归查找场景树项""" try: # 检查当前项是否匹配 item_node = getattr(item, 'node_path', None) if not item_node: item_node = getattr(item, 'node', None) if item_node and item_node == target_node: return item # 递归检查子项 for i in range(item.childCount()): child_item = item.child(i) result = self._findTreeItemForNodeRecursive(child_item, target_node) if result: return result return None except Exception as e: print(f"递归查找场景树项时出错: {e}") return None def _recreateTilesetFromData(self, node_data, parent_node, name): """根据数据重建Tileset""" try: tileset_url = node_data.get('tileset_url', '') if not tileset_url: return None # 使用现有方法加载tileset position = node_data.get('pos', (0, 0, 0)) tileset_node = self.load_cesium_tileset(tileset_url, position) if tileset_node: # 设置名称 tileset_node.setName(name) # 恢复其他标签 for tag_key, tag_value in node_data.get('tags', {}).items(): if tag_key not in ['name']: tileset_node.setTag(tag_key, str(tag_value)) return tileset_node except Exception as e: print(f"重建Tileset失败: {e}") return None def _recreateGUIFromData(self, node_data, parent_node, name): """根据数据重建GUI元素""" try: gui_data = node_data.get('gui_data', {}) #gui_type = gui_data.get('gui_type', '') gui_type = node_data.get("tags").get("gui_type", "") print(f"正在重建GUI元素: {gui_type}") print(f"正在重建GUI元素: {node_data}") # 根据GUI类型调用相应的创建方法 new_gui_element = None if gui_type == "button" and hasattr(self.world, 'createGUIButton'): pos = node_data.get('pos', (0, 0, 0)) text = node_data.get('tags').get('gui_text', '') size = node_data.get('scale', 1) print(pos,text,size) new_gui_element = self.world.createGUIButton(pos,text,size) elif gui_type == "label" and hasattr(self.world, 'createGUILabel'): pos = node_data.get('pos', (0, 0, 0)) text = node_data.get('tags').get('gui_text', '') size = node_data.get('scale', 1) new_gui_element = self.world.createGUILabel(pos,text,size) elif gui_type == "entry" and hasattr(self.world, 'createGUIEntry'): pos = node_data.get('pos', (0, 0, 0)) text = node_data.get('tags').get('gui_text', '') size = node_data.get('scale', 1) new_gui_element = self.world.createGUIEntry(pos,text,size) elif gui_type == "2d_image" and hasattr(self.world, 'createGUI2DImage'): pos = node_data.get('pos', (0, 0, 0)) image_path = node_data.get('tags').get('image_path', '') size = node_data.get('size', 1) new_gui_element = self.world.createGUI2DImage(pos, image_path, size) elif gui_type == "3d_text" and hasattr(self.world, 'createGUI3DText'): print("正在创建3D文本!!!") pos = node_data.get('pos', (0, 0, 0)) text = node_data.get('tags', {}).get('gui_text', '') scale = node_data.get('scale', 1) if isinstance(scale, (list, tuple)): scale = scale[0] if len(scale) > 0 else 1 print(f"正在创建3D文本: 位置={pos}, 文本={text}, 大小={scale}") new_gui_element = self.world.createGUI3DText(pos, text, scale) elif gui_type == "3d_image" and hasattr(self.world, 'createGUI3DImage'): pos = node_data.get('pos', (0, 0, 0)) image_path = node_data.get('tags').get('gui_image_path', '') scale = node_data.get('scale', (1, 1)) if isinstance(scale, (int, float)): scale = (scale, scale) elif isinstance(scale, (list, tuple)) and len(scale) >= 2: scale = (scale[0], scale[1]) else: scale = (1, 1) print(f"正在创建3D图片: 位置={pos}, 路径={image_path}, 大小={scale}") new_gui_element = self.world.gui_manager.createGUI3DImage(pos, image_path, scale) elif gui_type == "video_screen" and hasattr(self.world.gui_manager, 'createVideoScreen'): pos = node_data.get('pos', (0, 0, 0)) video_path = node_data.get('tags').get('video_path', '') scale = node_data.get('scale', (1, 1,1)) new_gui_element = self.world.gui_manager.createVideoScreen(pos,scale,video_path) elif gui_type == "2d_video_screen" and hasattr(self.world.gui_manager, 'createGUI2DVideoScreen'): pos = node_data.get('pos', (0, 0, 0)) video_path = node_data.get('tags').get('video_path', '') scale = node_data.get('scale', (1, 1, 1)) new_gui_element = self.world.gui_manager.createGUI2DVideoScreen(pos,scale,video_path) if new_gui_element: # 设置名称和变换 if hasattr(new_gui_element, 'setName'): new_gui_element.setName(name) # 设置位置、旋转、缩放 pos = node_data.get('pos', (0, 0, 0)) hpr = node_data.get('hpr', (0, 0, 0)) scale = node_data.get('scale', (1, 1, 1)) if hasattr(new_gui_element, 'setPos'): new_gui_element.setPos(*pos) if hasattr(new_gui_element, 'setHpr'): new_gui_element.setHpr(*hpr) if hasattr(new_gui_element, 'setScale'): new_gui_element.setScale(*scale) # 恢复文本内容 if 'text' in gui_data and hasattr(new_gui_element, 'setText'): new_gui_element.setText(gui_data['text']) # 恢复其他标签 for tag_key, tag_value in node_data.get('tags', {}).items(): if hasattr(new_gui_element, 'setTag') and tag_key not in ['name']: new_gui_element.setTag(tag_key, str(tag_value)) print(f"GUI元素重建成功: {name}") return new_gui_element except Exception as e: print(f"重建GUI元素失败: {e}") import traceback traceback.print_exc() return None def _recreateModelFromData(self, node_data, parent_node, name): """根据数据重建模型,保持材质""" try: model_data = node_data.get('model_data', {}) model_path = model_data.get('model_path', model_data.get('file', '')) if not model_path or not os.path.exists(model_path): # 如果原始模型文件不存在,创建一个基本节点 return self._createBasicNodeFromData(node_data, parent_node, name) # 导入模型,保持原有参数 model = self.importModel( model_path, apply_unit_conversion=False, # 已经处理过的模型不需要再次转换 normalize_scales=False, # 保持原有缩放 auto_convert_to_glb=False # 已经处理过的模型不需要再次转换 ) if model: # 设置名称 model.setName(name) # 设置变换 pos = node_data.get('pos', (0, 0, 0)) hpr = node_data.get('hpr', (0, 0, 0)) scale = node_data.get('scale', (1, 1, 1)) model.setPos(*pos) model.setHpr(*hpr) model.setScale(*scale) # 恢复材质和纹理信息 try: self._restoreModelMaterial(model, model_data) except Exception as e: print(f"恢复模型材质时出错: {e}") # 恢复标签 for tag_key, tag_value in node_data.get('tags', {}).items(): if tag_key not in ['name']: model.setTag(tag_key, str(tag_value)) # 添加到模型列表 if model not in self.models: self.models.append(model) return model except Exception as e: print(f"重建模型失败: {e}") # 出错时创建基本节点 return self._createBasicNodeFromData(node_data, parent_node, name) def _restoreModelTextures(self, model, texture_data): """恢复模型纹理""" try: if not texture_data or 'textures' not in texture_data: return from panda3d.core import TextureStage, SamplerState textures_info = texture_data['textures'] # 为每个纹理阶段恢复纹理 for stage_name, texture_info in textures_info.items(): # 创建纹理阶段 stage = TextureStage(stage_name) stage.setMode(texture_info.get('stage_mode', TextureStage.M_modulate)) # 恢复纹理阶段排序 stage.setSort(texture_info.get('stage_sort', 0)) # 默认为0(p3d_Texture0) # 加载纹理 texture_path = texture_info['texture_path'] if texture_path and os.path.exists(texture_path): texture = self.world.loader.loadTexture(texture_path) if texture: # 设置纹理属性 texture.setWrapU(texture_info.get('wrap_u', SamplerState.WM_repeat)) texture.setWrapV(texture_info.get('wrap_v', SamplerState.WM_repeat)) texture.setMinfilter(texture_info.get('minfilter', SamplerState.FT_linear)) texture.setMagfilter(texture_info.get('magfilter', SamplerState.FT_linear)) texture.setAnisotropicDegree(texture_info.get('anisotropic_degree', 1)) # 应用纹理到模型 model.setTexture(stage, texture, 1) # 1 表示强制应用 # 恢复颜色比例和偏移(使用安全的方法) if 'color_scale' in texture_info: try: model.setTextureScale(stage, *texture_info['color_scale']) except Exception as e: print(f"恢复纹理比例失败: {e}") if 'color_offset' in texture_info: try: model.setTextureOffset(stage, *texture_info['color_offset']) except Exception as e: print(f"恢复纹理偏移失败: {e}") print(f"恢复纹理: {stage_name} <- {texture_path}") else: print(f"纹理文件不存在或路径为空: {texture_path}") except Exception as e: print(f"恢复模型纹理时出错: {e}") def _restoreModelMaterial(self, model, model_data): """恢复模型材质和纹理""" try: # 恢复基础颜色 if 'base_color' in model_data: from panda3d.core import ColorAttrib base_color = model_data['base_color'] color = (base_color[0], base_color[1], base_color[2], base_color[3]) model.setColor(color) # 恢复复杂材质属性 if any(key.startswith('material_') for key in model_data.keys()): from panda3d.core import Material # 创建新材质或获取现有材质 material = Material() # 恢复基础颜色 if 'material_base_color' in model_data: base_color = model_data['material_base_color'] material.setBaseColor((base_color[0], base_color[1], base_color[2], base_color[3])) # 恢复环境光颜色 if 'material_ambient_color' in model_data: ambient_color = model_data['material_ambient_color'] material.setAmbient((ambient_color[0], ambient_color[1], ambient_color[2], ambient_color[3])) # 恢复漫反射颜色 if 'material_diffuse_color' in model_data: diffuse_color = model_data['material_diffuse_color'] material.setDiffuse((diffuse_color[0], diffuse_color[1], diffuse_color[2], diffuse_color[3])) # 恢复高光颜色 if 'material_specular_color' in model_data: specular_color = model_data['material_specular_color'] material.setSpecular((specular_color[0], specular_color[1], specular_color[2], specular_color[3])) # 恢复自发光颜色 if 'material_emission_color' in model_data: emission_color = model_data['material_emission_color'] material.setEmission((emission_color[0], emission_color[1], emission_color[2], emission_color[3])) # 恢复粗糙度和金属度 if 'material_roughness' in model_data: material.setRoughness(model_data['material_roughness']) if 'material_metallic' in model_data: material.setMetallic(model_data['material_metallic']) # 恢复光泽度 if 'material_shininess' in model_data: material.setShininess(model_data['material_shininess']) # 应用材质到模型 model.setMaterial(material) # 恢复透明度设置 if 'transparency_mode' in model_data: from panda3d.core import TransparencyAttrib transparency_mode = model_data['transparency_mode'] model.setTransparency(transparency_mode) # 恢复纹理信息 if 'texture_data' in model_data: self._restoreModelTextures(model, model_data['texture_data']) except Exception as e: print(f"恢复材质失败: {e}") def _createBasicNodeFromData(self, node_data, parent_node, name): """创建基本节点,保持视觉属性""" try: new_node = parent_node.attachNewNode(name) # 设置变换 pos = node_data.get('pos', (0, 0, 0)) hpr = node_data.get('hpr', (0, 0, 0)) scale = node_data.get('scale', (1, 1, 1)) new_node.setPos(*pos) new_node.setHpr(*hpr) new_node.setScale(*scale) # 恢复视觉属性 try: # 恢复颜色 if 'color' in node_data: color_data = node_data['color'] new_node.setColor(color_data[0], color_data[1], color_data[2], color_data[3]) # 恢复材质 if 'material' in node_data: from panda3d.core import Material material_data = node_data['material'] material = Material() if 'base_color' in material_data: bc = material_data['base_color'] material.setBaseColor((bc[0], bc[1], bc[2], bc[3])) if 'ambient' in material_data: ac = material_data['ambient'] material.setAmbient((ac[0], ac[1], ac[2], ac[3])) if 'diffuse' in material_data: dc = material_data['diffuse'] material.setDiffuse((dc[0], dc[1], dc[2], dc[3])) if 'specular' in material_data: sc = material_data['specular'] material.setSpecular((sc[0], sc[1], sc[2], sc[3])) if 'shininess' in material_data: material.setShininess(material_data['shininess']) new_node.setMaterial(material) except Exception as e: print(f"恢复视觉属性时出错: {e}") # 恢复标签 for tag_key, tag_value in node_data.get('tags', {}).items(): if tag_key not in ['name']: new_node.setTag(tag_key, str(tag_value)) return new_node except Exception as e: print(f"创建基本节点失败: {e}") return None def _generateUniqueName(self, base_name, parent_node): """生成唯一节点名称""" try: # 移除可能的数字后缀 import re import time name_base = re.sub(r'_\d+$', '', base_name) # 查找现有同名节点 counter = 1 unique_name = base_name while True: # 检查父节点下是否已存在同名子节点 existing_node = parent_node.find(unique_name) if existing_node.isEmpty(): break unique_name = f"{name_base}_{counter}" counter += 1 if counter > 1000: # 防止无限循环 break return unique_name except Exception as e: print(f"生成唯一名称时出错: {e}") return f"{base_name}_{int(time.time())}"