2dGUI场景保存,3D模型,(地形,信息面板除外)

This commit is contained in:
Hector 2025-09-15 09:14:16 +08:00
parent 7fd52d3458
commit fc7f0d55f3
11 changed files with 797 additions and 508 deletions

View File

@ -160,8 +160,8 @@ class QPanda3DWidget(QWidget):
def resizeEvent(self, evt):
width = evt.size().width()
height = evt.size().height()
print(f"width:{width}")
print(f"height:{height}")
#print(f"width:{width}")
#print(f"height:{height}")
from Panda3DWorld import resize_buffer
#resize_buffer(width, height)

File diff suppressed because one or more lines are too long

View File

@ -1079,6 +1079,135 @@ class InfoPanelManager(DirectObject):
property_panel_instance.createHTTPInfoPanel = types.MethodType(createHTTPInfoPanel, property_panel_instance)
property_panel_instance.updateHTTPInfoPanel = types.MethodType(updateHTTPInfoPanel, property_panel_instance)
def serializePanelData(self, panel_id):
"""序列化面板数据用于保存"""
if panel_id not in self.panels:
return None
panel_data = self.panels[panel_id]
props = panel_data['properties']
# 获取面板类型
panel_type = "2d"
if panel_data['node'].hasTag("gui_type"):
gui_type = panel_data['node'].getTag("gui_type")
if "3d" in gui_type.lower():
panel_type = "3d"
# 构建序列化数据
serialized_data = {
'panel_id': panel_id,
'panel_type': panel_type,
'position': props.get('position', (0, 0) if panel_type == "2d" else (0, 0, 0)),
'size': props.get('size', (1.0, 0.6)),
'bg_color': props.get('bg_color', (0.15, 0.15, 0.15, 0.9)),
'border_color': props.get('border_color', (0.3, 0.3, 0.3, 1.0)),
'title_color': props.get('title_color', (1.0, 1.0, 1.0, 1.0)),
'content_color': props.get('content_color', (0.9, 0.9, 0.9, 1.0)),
'title': panel_data['title_label'].getText() if 'title_label' in panel_data else "信息面板",
'content': panel_data[
'content_label'].getText() if 'content_label' in panel_data else "" if 'content_node' not in panel_data else
panel_data['content_node'].getText(),
'font_path': props.get('font', None),
'bg_image': props.get('bg_image', None),
'visible': not panel_data['node'].isHidden()
}
# 添加HTTP面板特有数据
if panel_id in self.data_sources and 'http_info' in self.data_sources[panel_id]:
serialized_data['http_info'] = self.data_sources[panel_id]['http_info']
return serialized_data
def getAllPanelData(self):
"""获取所有面板的序列化数据"""
panel_data_list = []
for panel_id in self.panels:
data = self.serializePanelData(panel_id)
if data:
panel_data_list.append(data)
return panel_data_list
def recreatePanelFromData(self, panel_data):
"""从序列化数据重新创建面板"""
try:
panel_id = panel_data['panel_id']
panel_type = panel_data['panel_type']
position = panel_data['position']
size = panel_data['size']
# 重建面板
if panel_type == "3d":
panel_node = self.create3DInfoPanel(
panel_id=panel_id,
position=position,
size=size,
bg_color=panel_data.get('bg_color', (0.15, 0.15, 0.15, 0.9)),
border_color=panel_data.get('border_color', (0.3, 0.3, 0.3, 1.0)),
title_color=panel_data.get('title_color', (1.0, 1.0, 1.0, 1.0)),
content_color=panel_data.get('content_color', (0.9, 0.9, 0.9, 1.0)),
visible=panel_data.get('visible', True),
font=panel_data.get('font_path', None),
bg_image=panel_data.get('bg_image', None)
)
# 更新内容
self.update3DPanelContent(
panel_id,
title=panel_data.get('title', '信息面板'),
content=panel_data.get('content', '')
)
else:
panel_node = self.createInfoPanel(
panel_id=panel_id,
position=(position[0], position[1]) if len(position) >= 2 else (0, 0),
size=size,
bg_color=panel_data.get('bg_color', (0.15, 0.15, 0.15, 0.9)),
border_color=panel_data.get('border_color', (0.3, 0.3, 0.3, 1.0)),
title_color=panel_data.get('title_color', (1.0, 1.0, 1.0, 1.0)),
content_color=panel_data.get('content_color', (0.9, 0.9, 0.9, 1.0)),
visible=panel_data.get('visible', True),
font=panel_data.get('font_path', None),
bg_image=panel_data.get('bg_image', None)
)
# 更新内容
self.updatePanelContent(
panel_id,
title=panel_data.get('title', '信息面板'),
content=panel_data.get('content', '')
)
# 设置标签
if panel_node:
panel_node.setTag("element_type", "info_panel")
panel_node.setTag("is_scene_element", "1")
panel_node.setTag("supports_3d_position_editing", "1")
# 如果是HTTP面板重新注册数据源
if 'http_info' in panel_data:
http_info = panel_data['http_info']
self.createHTTPInfoPanel(
panel_id=panel_id,
url=http_info['url'],
method=http_info.get('method', 'GET'),
headers=http_info.get('headers', None),
data=http_info.get('data', None),
position=position,
size=size,
update_interval=self.data_sources.get(panel_id, {}).get('interval',
30.0) if panel_id in self.data_sources else 30.0
)
print(f"✓ 信息面板 {panel_id} 已重建")
return panel_node
except Exception as e:
print(f"✗ 重建信息面板 {panel_data.get('panel_id', 'unknown')} 失败: {e}")
import traceback
traceback.print_exc()
return None
# 示例数据源函数
def getRealtimeData():

View File

@ -212,7 +212,7 @@ class SelectionSystem:
return
# 获取边界框的最小和最大点(世界坐标)
print(f"世界边界框: min={minPoint}, max={maxPoint}")
#print(f"世界边界框: min={minPoint}, max={maxPoint}")
# 创建线段对象
lines = LineSegs()

View File

@ -567,3 +567,88 @@ class TerrainManager:
except Exception as e:
print(f"应用地形纹理时出错: {e}")
return False
def saveTerrainData(self, terrain_info, filename):
"""保存地形数据到文件"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
# 保存高度图到文件
if heightfield:
# 创建保存路径
terrain_dir = os.path.join(os.path.dirname(filename), "terrains")
if not os.path.exists(terrain_dir):
os.makedirs(terrain_dir)
# 生成唯一的地形文件名
terrain_filename = f"terrain_{terrain_info.get('name', 'unnamed')}_{int(time.time())}.png"
terrain_path = os.path.join(terrain_dir, terrain_filename)
# 保存高度图
heightfield.write(Filename.fromOsSpecific(terrain_path))
# 保存地形信息到标签
terrain_node.setTag("terrain_heightmap_path", terrain_path)
terrain_node.setTag("terrain_scale", str(terrain_info['scale']))
print(f"✓ 地形数据已保存: {terrain_path}")
return terrain_path
return None
except Exception as e:
print(f"保存地形数据时出错: {e}")
return None
def _recreateTerrain(self, terrain_node):
"""重新创建地形"""
try:
print(f"重新创建地形: {terrain_node.getName()}")
# 获取保存的地形信息
heightmap_path = terrain_node.getTag("terrain_heightmap_path") if terrain_node.hasTag(
"terrain_heightmap_path") else None
scale_str = terrain_node.getTag("terrain_scale") if terrain_node.hasTag("terrain_scale") else "(1, 1, 1)"
# 解析缩放信息
try:
scale_str = scale_str.strip("()")
scale_values = [float(x.strip()) for x in scale_str.split(",")]
scale = (scale_values[0], scale_values[1], scale_values[2]) if len(scale_values) >= 3 else (1, 1, 1)
except:
scale = (1, 1, 1)
# 恢复位置信息
if terrain_node.hasTag("transform_pos"):
try:
pos_str = terrain_node.getTag("transform_pos")
pos_str = pos_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()')
pos_values = [float(x.strip()) for x in pos_str.split(',')]
if len(pos_values) >= 3:
terrain_node.setPos(pos_values[0], pos_values[1], pos_values[2])
except Exception as e:
print(f"恢复地形位置失败: {e}")
# 如果有高度图路径,重新创建地形
if heightmap_path and os.path.exists(heightmap_path):
# 使用现有方法重新创建地形
new_terrain = self.createTerrainFromHeightMap(heightmap_path, scale)
if new_terrain:
# 删除旧的地形节点
if not terrain_node.isEmpty():
terrain_node.removeNode()
return new_terrain
# 如果没有高度图或创建失败,创建一个平面地形作为替代
print("使用平面地形作为替代")
new_terrain = self.createFlatTerrain(size=(scale[0], scale[1]), resolution=129)
if new_terrain:
# 删除旧的地形节点
if not terrain_node.isEmpty():
terrain_node.removeNode()
return new_terrain
return terrain_node
except Exception as e:
print(f"重新创建地形失败: {e}")
return terrain_node

View File

@ -124,7 +124,7 @@ class GUIManager:
# ==================== GUI元素创建方法 ====================
def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=0.1):
def createGUIButton(self, pos=(0, 0, 0), text="按钮", size=(0.1,0.1,0.1)):
"""创建2D GUI按钮 - 支持多选创建和GUI父子关系优化版本"""
try:
from direct.gui.DirectGui import DirectButton
@ -180,9 +180,23 @@ class GUIManager:
text_font=self.world.getChineseFont() if self.world.getChineseFont() else None,
rolloverSound=None,
clickSound=None,
parent=parent_gui_node # 设置GUI父节点
parent=parent_gui_node
)
if not hasattr(button,'_tags'):
button._tags = {}
# button._tags["gui_type"] = "button"
# button._tags["gui_id"] = f"button_{len(self.gui_elements)}"
# button._tags["gui_text"] = text
# button._tags["is_gui_element"] = "1"
# button._tags["is_scene_element"] = "1"
# button._tags["saved_gui_type"] = "button"
# button._tags["gui_element_type"] = "button"
# button._tags["created_by_user"] = "1"
# button._tags["name"] = button_name
# button.setName(button_name)
# 设置节点标签
button.setTag("gui_type", "button")
button.setTag("gui_id", f"button_{len(self.gui_elements)}")
@ -190,7 +204,7 @@ class GUIManager:
button.setTag("is_gui_element", "1")
button.setTag("is_scene_element", "1") # 确保这个标签被设置
button.setTag("saved_gui_type", "button") # 添加这个标签以确保兼容性
button.setTag("gui_element_type","button")
button.setTag("gui_element_type", "button")
button.setTag("created_by_user", "1")
button.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
button.setTag("name", button_name)
@ -225,11 +239,11 @@ class GUIManager:
return None
# 选中最后创建的按钮并更新场景树
if created_buttons:
last_button, last_qt_item = created_buttons[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_button, last_qt_item)
# if created_buttons:
# last_button, last_qt_item = created_buttons[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_button, last_qt_item)
print(f"🎉 总共创建了 {len(created_buttons)} 个GUI按钮")
@ -338,11 +352,11 @@ class GUIManager:
return None
# 选中最后创建的标签并更新场景树
if created_labels:
last_label, last_qt_item = created_labels[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_label, last_qt_item)
# if created_labels:
# last_label, last_qt_item = created_labels[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_label, last_qt_item)
print(f"🎉 总共创建了 {len(created_labels)} 个GUI标签")
@ -449,11 +463,11 @@ class GUIManager:
return None
# 选中最后创建的输入框并更新场景树
if created_entries:
last_entry, last_qt_item = created_entries[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_entry, last_qt_item)
# if created_entries:
# last_entry, last_qt_item = created_entries[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_entry, last_qt_item)
print(f"🎉 总共创建了 {len(created_entries)} 个GUI输入框")
@ -530,6 +544,7 @@ class GUIManager:
# 如果提供了图像路径,则加载纹理
if image_path:
try:
image_node.setTag("image_path", image_path)
texture = self.world.loader.loadTexture(image_path)
if texture:
image_node.setTexture(texture, 1)
@ -581,11 +596,11 @@ class GUIManager:
return None
# 选中最后创建的按钮并更新场景树
if created_2dimage:
last_button, last_qt_item = created_2dimage[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_button, last_qt_item)
# if created_2dimage:
# last_button, last_qt_item = created_2dimage[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_button, last_qt_item)
print(f"🎉 总共创建了 {len(created_2dimage)} 个GUI按钮")
@ -725,11 +740,11 @@ class GUIManager:
return None
# 选中最后创建的文本并更新场景树
if created_texts:
last_text, last_qt_item = created_texts[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_text, last_qt_item)
# if created_texts:
# last_text, last_qt_item = created_texts[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_text, last_qt_item)
print(f"🎉 总共创建了 {len(created_texts)} 个3D文本")
@ -833,7 +848,7 @@ class GUIManager:
image_node.setTag("gui_type", "3d_image")
image_node.setTag("gui_id", f"3d_image_{len(self.gui_elements)}")
if image_path:
image_node.setTag("gui_image_path", image_path)
image_node.setTag("image_path", image_path)
image_node.setTag("is_gui_element", "1")
image_node.setTag("is_scene_element", "1")
image_node.setTag("created_by_user", "1")
@ -861,11 +876,11 @@ class GUIManager:
return None
# 选中最后创建的文本并更新场景树
if created_3dimage:
last_image, last_qt_item = created_3dimage[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
tree_widget.update_selection_and_properties(last_image, last_qt_item)
# if created_3dimage:
# last_image, last_qt_item = created_3dimage[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# tree_widget.update_selection_and_properties(last_image, last_qt_item)
print(f"🎉 总共创建了 {len(created_3dimage)} 个3D文本")
@ -881,7 +896,7 @@ class GUIManager:
traceback.print_exc()
return None
def createVideoScreen(self, pos=(0, 0, 0), size=0.2, video_path=None):
def createVideoScreen(self, pos=(0, 0, 0), size=1, video_path=None):
"""创建3D视频播放屏幕 - 添加占位符纹理支持"""
try:
from panda3d.core import CardMaker, TransparencyAttrib, Texture, TextureStage
@ -904,7 +919,7 @@ class GUIManager:
size = float(size)
except (ValueError, TypeError):
print(f"⚠️ 尺寸参数无效,使用默认值 0.2,原始值: {size}")
size = 0.2
size = 0.2*5
print(f"📺 开始创建视频屏幕,位置: {pos}, 尺寸: {size}, 视频路径: {video_path}")
@ -1051,12 +1066,12 @@ class GUIManager:
return None
# 选中最后创建的视频屏幕
if created_videoscreens:
last_screen_np, last_qt_item = created_videoscreens[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
# 更新选择和属性面板
tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
# if created_videoscreens:
# last_screen_np, last_qt_item = created_videoscreens[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# # 更新选择和属性面板
# tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
print(f"🎉 总共创建了 {len(created_videoscreens)} 个视频屏幕")
@ -1273,7 +1288,6 @@ class GUIManager:
def loadVideoFile(self, video_screen, video_path):
"""为视频屏幕加载新的视频文件"""
try:
from panda3d.core import Texture, TextureStage
import os
if not os.path.exists(video_path):
@ -1431,8 +1445,6 @@ class GUIManager:
print(f"⚠️ 尺寸参数无效,使用默认值 0.2,原始值: {size}, 错误: {e}")
size = 0.2
print(f"📺 开始创建2D视频屏幕位置: {pos}, 尺寸: {size}, 视频路径: {video_path}")
# 获取树形控件
tree_widget = self._get_tree_widget()
if not tree_widget:
@ -1459,7 +1471,9 @@ class GUIManager:
frameColor=(1, 1, 1, 1), # 默认背景色
pos=(pos[0] * 0.1, 0, pos[1] * 0.1), # 转换为屏幕坐标
parent=parent_node if tree_widget.is_gui_element(parent_node) else self.world.aspect2d,
suppressMouse=True
suppressMouse=True,
)
video_screen.setName(screen_name)
@ -1467,7 +1481,7 @@ class GUIManager:
# 设置透明度支持
video_screen.setTransparency(TransparencyAttrib.MAlpha)
# 设置2D视频屏幕特有的标签
#设置2D视频屏幕特有的标签
video_screen.setTag("gui_type", "2d_video_screen")
video_screen.setTag("gui_id", f"2d_video_screen_{len(self.gui_elements)}")
video_screen.setTag("gui_text", f"2D视频屏幕_{len(self.gui_elements)}")
@ -1475,11 +1489,8 @@ class GUIManager:
video_screen.setTag("is_scene_element", "1")
video_screen.setTag("created_by_user", "1")
# 设置视频路径标签
if video_path and os.path.exists(video_path):
video_screen.setTag("video_path", video_path)
else:
video_screen.setTag("video_path", "")
video_screen.setTag("video_path", video_path if video_path else "")
print(f"🔧 设置2D视频屏幕标签 - video_path: {video_path if video_path else ''}")
# 关键修改:预先创建一个占位符纹理,为后续视频播放做准备
placeholder_texture = Texture(f"placeholder_video_texture_{len(self.gui_elements)}")
@ -1552,12 +1563,12 @@ class GUIManager:
return None
# 选中最后创建的视频屏幕
if created_videoscreens:
last_screen_np, last_qt_item = created_videoscreens[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
# 更新选择和属性面板
tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
# if created_videoscreens:
# last_screen_np, last_qt_item = created_videoscreens[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# # 更新选择和属性面板
# tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
print(f"🎉 总共创建了 {len(created_videoscreens)} 个2D视频屏幕")
@ -1578,7 +1589,10 @@ class GUIManager:
try:
import os
if not os.path.exists(video_path):
video_screen.setTag("video_path",video_path)
print(f"🔧 更新2D视频屏幕标签 - video_path: {video_path if video_path else ''}")
if not video_path or not os.path.exists(video_path):
print(f"❌ 2D视频文件不存在: {video_path}")
return False
@ -1590,7 +1604,6 @@ class GUIManager:
# 保存视频纹理引用
video_screen.setPythonTag("movie_texture", movie_texture)
video_screen.setTag("video_path", video_path)
print(f"✅ 成功加载新2D视频: {video_path}")
return True
@ -1817,12 +1830,12 @@ class GUIManager:
return None
# 选中最后创建的球形视频
if created_spherical_videos:
last_sphere_np, last_qt_item = created_spherical_videos[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
# 更新选择和属性面板
tree_widget.update_selection_and_properties(last_sphere_np, last_qt_item)
# if created_spherical_videos:
# last_sphere_np, last_qt_item = created_spherical_videos[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# # 更新选择和属性面板
# tree_widget.update_selection_and_properties(last_sphere_np, last_qt_item)
print(f"🎉 总共创建了 {len(created_spherical_videos)} 个球形视频")
@ -2022,12 +2035,12 @@ class GUIManager:
return None
# 选中最后创建的虚拟屏幕
if created_screens:
last_screen_np, last_qt_item = created_screens[-1]
if last_qt_item:
tree_widget.setCurrentItem(last_qt_item)
# 更新选择和属性面板
tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
# if created_screens:
# last_screen_np, last_qt_item = created_screens[-1]
# if last_qt_item:
# tree_widget.setCurrentItem(last_qt_item)
# # 更新选择和属性面板
# tree_widget.update_selection_and_properties(last_screen_np, last_qt_item)
print(f"🎉 总共创建了 {len(created_screens)} 个虚拟屏幕")

View File

@ -233,9 +233,9 @@ class MyWorld(CoreWorld):
"""创建3D图片"""
return self.gui_manager.createGUI3DImage(pos,text,size)
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=1):
"""创建2D GUI图片"""
return self.gui_manager.createGUI2DImage(pos, image_path, size)
return self.gui_manager.createGUI2DImage(pos, image_path, size*0.2)
def createVideoScreen(self,pos=(0,0,0),size=1,video_path=None):
"""创建视频屏幕"""
@ -492,10 +492,6 @@ class MyWorld(CoreWorld):
"""异步导入模型"""
return self.scene_manager.importModelAsync(filepath)
def loadAnimatedModel(self, model_path, anims=None):
"""加载带动画的模型"""
return self.scene_manager.loadAnimatedModel(model_path, anims)
# 材质和几何体处理方法 - 代理到scene_manager
def processMaterials(self, model):
"""处理模型材质"""

File diff suppressed because it is too large Load Diff

View File

@ -296,7 +296,6 @@ class InterfaceManager:
addNodeToTree(model, sceneRoot,force=True)
# 添加所有GUI元素
print(f"GUIGUIGUIGUIGUIGUIGUIGUIGUIGUIGUIGUIUGIUGI{self.world.gui_elements}")
for gui in self.world.gui_elements:
# 检查是否是有效的GUI节点具有getTag方法的NodePath
if hasattr(gui, 'getTag') and hasattr(gui, 'getName'):

View File

@ -639,10 +639,10 @@ class MainWindow(QMainWindow):
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock)
# 创建底部停靠控制台
self.consoleDock = QDockWidget("控制台", self)
self.consoleView = CustomConsoleDockWidget(self.world)
self.consoleDock.setWidget(self.consoleView)
self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock)
# self.consoleDock = QDockWidget("控制台", self)
# self.consoleView = CustomConsoleDockWidget(self.world)
# self.consoleDock.setWidget(self.consoleView)
# self.addDockWidget(Qt.BottomDockWidgetArea, self.consoleDock)
def setupToolbar(self):
"""创建工具栏"""

View File

@ -14,7 +14,7 @@ from direct.actor.Actor import Actor
from direct.gui import DirectGui
from idna import check_label
from jinja2.compiler import has_safe_repr
from panda3d.core import Vec3, Vec4, transpose, TransparencyAttrib, PartGroup, ColorAttrib
from panda3d.core import Vec3, Vec4, transpose, TransparencyAttrib, PartGroup, ColorAttrib, NodePath
from scene import util
from direct.gui.DirectGui import DirectLabel, DirectFrame
from panda3d.core import TextNode
@ -3774,54 +3774,6 @@ class PropertyPanelManager:
print(f"加载新视频失败: {e}")
return False
def load2DVideoFile(self, video_screen, video_path):
"""为2D视频屏幕加载新的视频文件"""
try:
import os
# 处理空路径情况 - 显示空白
if not video_path or video_path == "":
print(" 空视频路径,显示空白")
video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景
video_screen.clearPythonTag("movie_texture")
video_screen.setTag("video_path", "")
return True
# 检查文件是否存在
if not os.path.exists(video_path):
print(f"❌ 2D视频文件不存在: {video_path}")
# 显示空白而不是红色错误提示
video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景
return False
# 加载新的视频纹理
movie_texture = self._loadMovieTexture(video_path)
if movie_texture:
# 应用纹理到2D视频屏幕
video_screen["frameTexture"] = movie_texture
# 设置白色背景以正确显示视频
video_screen["frameColor"] = (1, 1, 1, 1)
# 保存视频纹理引用
video_screen.setPythonTag("movie_texture", movie_texture)
video_screen.setTag("video_path", video_path)
print(f"✅ 成功加载新2D视频: {video_path}")
return True
else:
print(f"❌ 无法加载2D视频文件: {video_path}")
# 显示空白而不是红色错误提示
video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景
return False
except Exception as e:
print(f"❌ 加载2D视频文件失败: {e}")
import traceback
traceback.print_exc()
# 显示空白而不是红色错误提示
video_screen["frameColor"] = (0, 0, 0, 0) # 透明背景
return False
def _loadVideoFromURLWithOpenCV_3D(self, video_screen, url):
"""使用OpenCV从URL加载视频流并在3D视频屏幕上显示"""
try:
@ -8224,17 +8176,19 @@ class PropertyPanelManager:
# 其他格式使用标准 Actor 加载
try:
test_actor=Actor(filepath)
import gltf
print(f"[GLTF加载] 尝试加载: {filepath}")
# test_actor=Actor(NodePath(gltf._loader.GltfLoader.load_file(filepath,None)))
test_actor=Actor(NodePath(gltf.load_model(filepath,None)))
anims = test_actor.getAnimNames()
test_actor.reparentTo(self.world.render)
self._actor_cache[origin_model] = test_actor
print(f"[Actor加载] 标准加载检测到动画: {anims}")
if not anims:
test_actor.cleanup()
test_actor.removeNode()
return None
actor = Actor(filepath)
actor.reparentTo(self.world.render)
self._actor_cache[origin_model] = actor
return actor
return test_actor
except Exception as e:
print(f"创建Actor失败: {e}")
return None
@ -8590,6 +8544,8 @@ except Exception as e:
print(f"[系统转换] 系统转换失败: {e}")
return None
def _playAnimation(self,origin_model):
actor=self._getActor(origin_model)
if not actor:
@ -8702,7 +8658,7 @@ except Exception as e:
anim_name = self.animation_combo.currentText()
actor.setPos(origin_model.getPos())
actor.setHpr(origin_model.getHpr())
actor.setScale(origin_model.getScale())
actor.setScale(origin_model.getScale()/100)
if cmd == "play":
origin_model.hide()