874 lines
38 KiB
Python
874 lines
38 KiB
Python
"""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
|
||
from core.editor_context import get_editor_context
|
||
|
||
class SceneManagerSerializationMixin:
|
||
def _get_editor_context(self):
|
||
return get_editor_context(self.world)
|
||
|
||
def _get_tree_widget(self):
|
||
return self._get_editor_context().get_tree_widget()
|
||
|
||
def _get_gui_manager(self):
|
||
return self._get_editor_context().get_gui_manager()
|
||
|
||
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:
|
||
tree_widget = self._get_tree_widget()
|
||
if tree_widget:
|
||
# 查找父节点在场景树中的对应项
|
||
parent_item = self._findTreeItemForNode(parent_node)
|
||
if parent_item:
|
||
# 添加新节点到场景树
|
||
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:
|
||
tree_widget = self._get_tree_widget()
|
||
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
|
||
gui_manager = self._get_gui_manager()
|
||
|
||
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}")
|
||
if gui_manager and hasattr(gui_manager, 'createGUI3DImage'):
|
||
new_gui_element = gui_manager.createGUI3DImage(pos, image_path, scale)
|
||
elif gui_type == "video_screen" and gui_manager and hasattr(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 = gui_manager.createVideoScreen(pos,scale,video_path)
|
||
elif gui_type == "2d_video_screen" and gui_manager and hasattr(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 = 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())}"
|