EG/scene/scene_manager_serialization_mixin.py
2026-02-27 16:52:00 +08:00

864 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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)) # 默认为0p3d_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())}"