修复打包不能保存视频流的问题
This commit is contained in:
parent
f4d21a62f9
commit
f455d5134d
@ -60,7 +60,7 @@ class MovementController(object):
|
||||
self.keyboard_hpr_speed = 0.4
|
||||
self.use_hpr = False
|
||||
self.smoothness = 6.0
|
||||
self.bobbing_amount = 1.5
|
||||
self.bobbing_amount = 0.0
|
||||
self.bobbing_speed = 0.5
|
||||
|
||||
def set_initial_position(self, pos, target):
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from PyQt5.QtCore import showbase
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
|
||||
|
||||
@ -1031,51 +1031,97 @@ class GUIManager:
|
||||
|
||||
# 如果提供了视频路径,则加载视频纹理
|
||||
movie_texture = None
|
||||
if video_path and os.path.exists(video_path):
|
||||
try:
|
||||
print(f"🔍 尝试加载视频纹理: {video_path}")
|
||||
# 加载视频纹理
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
# 创建纹理阶段
|
||||
texture_stage = TextureStage("video")
|
||||
texture_stage.setSort(0)
|
||||
texture_stage.setMode(TextureStage.MModulate)
|
||||
video_screen.setTexture(texture_stage, movie_texture)
|
||||
|
||||
print(f"✅ 视频纹理加载成功: {video_path}")
|
||||
if video_path:
|
||||
if video_path.startswith(("http://","https://")):
|
||||
try:
|
||||
if hasattr(self.world,'property_panel'):
|
||||
success = self.world.property_panel._loadVideoFromURLWithOpenCV_3D(video_screen,video_path)
|
||||
if success:
|
||||
print(f"✅ 视频流URL加载成功: {video_path}")
|
||||
else:
|
||||
print(f"⚠️ 视频流URL加载失败: {video_path}")
|
||||
else:
|
||||
print("⚠️ property_manager不可用,无法加载视频流")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载视频流URL失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
elif os.path.exists(video_path):
|
||||
# 对于本地视频文件,使用原有方法
|
||||
try:
|
||||
print(f"🔍 尝试加载2D视频纹理: {video_path}")
|
||||
# 加载视频纹理
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
# 应用纹理到视频屏幕(替换占位符)
|
||||
video_screen["frameTexture"] = movie_texture
|
||||
print(f"✅ 2D视频纹理加载成功: {video_path}")
|
||||
|
||||
# 保存视频纹理引用以便后续控制
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
# 保存视频纹理引用以便后续控制
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
|
||||
# 尝试自动播放视频(如果支持)
|
||||
try:
|
||||
if hasattr(movie_texture, 'play'):
|
||||
movie_texture.play()
|
||||
print("▶️ 视频已开始播放")
|
||||
except Exception as play_error:
|
||||
print(f"⚠️ 视频自动播放失败: {play_error}")
|
||||
else:
|
||||
print(f"⚠️ 无法加载视频纹理: {video_path}")
|
||||
# 使用默认颜色作为占位符
|
||||
video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
except Exception as e:
|
||||
print(f"❌ 加载视频纹理失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# 使用默认颜色作为占位符
|
||||
video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
else:
|
||||
# 没有视频文件时显示默认颜色
|
||||
video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
if video_path:
|
||||
print(f"⚠️ 视频文件不存在: {video_path}")
|
||||
# 尝试自动播放视频(如果支持)
|
||||
try:
|
||||
if hasattr(movie_texture, 'play'):
|
||||
movie_texture.play()
|
||||
print("▶️ 2D视频已开始播放")
|
||||
except Exception as play_error:
|
||||
print(f"⚠️ 2D视频自动播放失败: {play_error}")
|
||||
else:
|
||||
print(f"⚠️ 无法加载2D视频纹理: {video_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载2D视频纹理失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("ℹ️ 未提供视频文件,显示默认占位符")
|
||||
print(f"⚠️ 2D视频文件不存在: {video_path}")
|
||||
|
||||
# 保存视频纹理引用以便后续控制
|
||||
if movie_texture:
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
# if video_path and os.path.exists(video_path):
|
||||
# try:
|
||||
# print(f"🔍 尝试加载视频纹理: {video_path}")
|
||||
# # 加载视频纹理
|
||||
# movie_texture = self._loadMovieTexture(video_path)
|
||||
# if movie_texture:
|
||||
# # 创建纹理阶段
|
||||
# texture_stage = TextureStage("video")
|
||||
# texture_stage.setSort(0)
|
||||
# texture_stage.setMode(TextureStage.MModulate)
|
||||
# video_screen.setTexture(texture_stage, movie_texture)
|
||||
#
|
||||
# print(f"✅ 视频纹理加载成功: {video_path}")
|
||||
#
|
||||
# # 保存视频纹理引用以便后续控制
|
||||
# video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
#
|
||||
# # 尝试自动播放视频(如果支持)
|
||||
# try:
|
||||
# if hasattr(movie_texture, 'play'):
|
||||
# movie_texture.play()
|
||||
# print("▶️ 视频已开始播放")
|
||||
# except Exception as play_error:
|
||||
# print(f"⚠️ 视频自动播放失败: {play_error}")
|
||||
# else:
|
||||
# print(f"⚠️ 无法加载视频纹理: {video_path}")
|
||||
# # 使用默认颜色作为占位符
|
||||
# video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
# except Exception as e:
|
||||
# print(f"❌ 加载视频纹理失败: {e}")
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
# # 使用默认颜色作为占位符
|
||||
# video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
# else:
|
||||
# # 没有视频文件时显示默认颜色
|
||||
# video_screen.setColor(0.1, 0.1, 0.3, 0.8)
|
||||
# if video_path:
|
||||
# print(f"⚠️ 视频文件不存在: {video_path}")
|
||||
# else:
|
||||
# print("ℹ️ 未提供视频文件,显示默认占位符")
|
||||
#
|
||||
# # 保存视频纹理引用以便后续控制
|
||||
# if movie_texture:
|
||||
# video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
|
||||
# 添加到GUI元素列表
|
||||
self.gui_elements.append(video_screen)
|
||||
@ -1541,36 +1587,81 @@ class GUIManager:
|
||||
|
||||
# 如果提供了视频路径,则加载视频纹理
|
||||
movie_texture = None
|
||||
if video_path and os.path.exists(video_path):
|
||||
try:
|
||||
print(f"🔍 尝试加载2D视频纹理: {video_path}")
|
||||
# 加载视频纹理
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
# 应用纹理到视频屏幕(替换占位符)
|
||||
video_screen["frameTexture"] = movie_texture
|
||||
print(f"✅ 2D视频纹理加载成功: {video_path}")
|
||||
|
||||
# 保存视频纹理引用以便后续控制
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
if video_path:
|
||||
if video_path.startswith(("http://","https://")):
|
||||
try:
|
||||
if hasattr(self.world,'property_panel'):
|
||||
success = self.world.property_panel._loadVideoFromURLWithOpenCV(video_screen,video_path)
|
||||
if success:
|
||||
print(f"✅ 视频流URL加载成功: {video_path}")
|
||||
else:
|
||||
print(f"⚠️ 视频流URL加载失败: {video_path}")
|
||||
else:
|
||||
print("⚠️ property_manager不可用,无法加载视频流")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载视频流URL失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
elif os.path.exists(video_path):
|
||||
try:
|
||||
print(f"🔍 尝试加载2D视频纹理: {video_path}")
|
||||
# 加载视频纹理
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
# 应用纹理到视频屏幕(替换占位符)
|
||||
video_screen["frameTexture"] = movie_texture
|
||||
print(f"✅ 2D视频纹理加载成功: {video_path}")
|
||||
|
||||
# 尝试自动播放视频(如果支持)
|
||||
try:
|
||||
if hasattr(movie_texture, 'play'):
|
||||
movie_texture.play()
|
||||
print("▶️ 2D视频已开始播放")
|
||||
except Exception as play_error:
|
||||
print(f"⚠️ 2D视频自动播放失败: {play_error}")
|
||||
else:
|
||||
print(f"⚠️ 无法加载2D视频纹理: {video_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载2D视频纹理失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
if not video_path:
|
||||
# 保存视频纹理引用以便后续控制
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
|
||||
# 尝试自动播放视频(如果支持)
|
||||
try:
|
||||
if hasattr(movie_texture, 'play'):
|
||||
movie_texture.play()
|
||||
print("▶️ 2D视频已开始播放")
|
||||
except Exception as play_error:
|
||||
print(f"⚠️ 2D视频自动播放失败: {play_error}")
|
||||
else:
|
||||
print(f"⚠️ 无法加载2D视频纹理: {video_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载2D视频纹理失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print(f"⚠️ 2D视频文件不存在: {video_path}")
|
||||
|
||||
# if video_path and os.path.exists(video_path):
|
||||
# try:
|
||||
# print(f"🔍 尝试加载2D视频纹理: {video_path}")
|
||||
# # 加载视频纹理
|
||||
# movie_texture = self._loadMovieTexture(video_path)
|
||||
# if movie_texture:
|
||||
# # 应用纹理到视频屏幕(替换占位符)
|
||||
# video_screen["frameTexture"] = movie_texture
|
||||
# print(f"✅ 2D视频纹理加载成功: {video_path}")
|
||||
#
|
||||
# # 保存视频纹理引用以便后续控制
|
||||
# video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
#
|
||||
# # 尝试自动播放视频(如果支持)
|
||||
# try:
|
||||
# if hasattr(movie_texture, 'play'):
|
||||
# movie_texture.play()
|
||||
# print("▶️ 2D视频已开始播放")
|
||||
# except Exception as play_error:
|
||||
# print(f"⚠️ 2D视频自动播放失败: {play_error}")
|
||||
# else:
|
||||
# print(f"⚠️ 无法加载2D视频纹理: {video_path}")
|
||||
# except Exception as e:
|
||||
# print(f"❌ 加载2D视频纹理失败: {e}")
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
# else:
|
||||
# if not video_path:
|
||||
# print(f"⚠️ 2D视频文件不存在: {video_path}")
|
||||
|
||||
# 添加到GUI元素列表
|
||||
self.gui_elements.append(video_screen)
|
||||
|
||||
|
||||
2
main.py
2
main.py
@ -246,7 +246,7 @@ class MyWorld(CoreWorld):
|
||||
"""创建3D空间文本"""
|
||||
return self.gui_manager.createGUI3DText(pos, text, size)
|
||||
|
||||
def createGUI3DImage(self,pos=(0,0,0),text="3D图片",size=(1,1)):
|
||||
def createGUI3DImage(self,pos=(0,0,0),text="3D图片",size=(1,1,1)):
|
||||
"""创建3D图片"""
|
||||
return self.gui_manager.createGUI3DImage(pos,text,size)
|
||||
|
||||
|
||||
@ -355,9 +355,45 @@ class ProjectManager:
|
||||
if not os.path.exists(scene_file):
|
||||
QMessageBox.warning(parent_window, "警告", "请先保存场景!")
|
||||
return False
|
||||
|
||||
if hasattr(self.world,'selection') and self.world.selection:
|
||||
self.world.selection.clearSelection()
|
||||
print("已取消场景中的物体选中状态")
|
||||
|
||||
if self.world.scene_manager.saveScene(scene_file,project_path):
|
||||
print("场景保存成功")
|
||||
config_file = os.path.join(project_path,"project.json")
|
||||
if os.path.exists(config_file):
|
||||
with open(config_file,"r",encoding="utf-8") as f:
|
||||
project_config = json.load(f)
|
||||
|
||||
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
project_config["scene_file"] = os.path.relpath(scene_file,project_path)
|
||||
|
||||
with open(config_file,"w",encoding = "utf-8") as f:
|
||||
json.dump(project_config,f,ensure_ascii = False,indent=4)
|
||||
|
||||
self.project_config = project_config
|
||||
|
||||
project_name = os.path.basename(project_path)
|
||||
self.updateWindowTitle(parent_window,project_name)
|
||||
else:
|
||||
QMessageBox.warning(parent_window,"错误","保存场景失败!")
|
||||
return False
|
||||
|
||||
build_dir = QFileDialog.getExistingDirectory(
|
||||
parent_window,
|
||||
"选择打包输出目录",
|
||||
project_path,
|
||||
QFileDialog.ShowDirsOnly
|
||||
)
|
||||
|
||||
if not build_dir:
|
||||
return False
|
||||
|
||||
# 创建构建目录
|
||||
build_dir = os.path.join(project_path, "build")
|
||||
build_dir = os.path.join(build_dir, "build")
|
||||
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
|
||||
@ -413,7 +449,7 @@ class ProjectManager:
|
||||
shutil.copytree(
|
||||
source_render_pipeline,
|
||||
dest_render_pipeline,
|
||||
ignore=shutil.ignore_patterns('__pycache__','*.pyc','.git','.vscode','*.log')
|
||||
ignore=shutil.ignore_patterns('__pycache__','*.pyc','.git','.vscode','*.log','samples')
|
||||
)
|
||||
print("✓ RenderPipelineFile文件夹已复制到build目录")
|
||||
else:
|
||||
@ -499,6 +535,7 @@ class ProjectManager:
|
||||
# 使用_collectGUIElementInfo方法收集信息
|
||||
gui_info = self.world.scene_manager._collectGUIElementInfo(gui_node)
|
||||
if gui_info:
|
||||
self._updateResourcePaths(gui_info,project_path,build_dir)
|
||||
gui_data.append(gui_info)
|
||||
print(f"收集GUI元素信息: {gui_info['name']}")
|
||||
|
||||
@ -516,6 +553,29 @@ class ProjectManager:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _updateResourcePaths(self,gui_info,project_path,build_dir):
|
||||
def normalize_resource_path(old_path):
|
||||
if not old_path:
|
||||
return old_path
|
||||
|
||||
if old_path.startswith(("http://","https://")):
|
||||
return old_path
|
||||
|
||||
if os.path.isabs(old_path):
|
||||
try:
|
||||
rel_path = os.path.relpath(old_path,project_path)
|
||||
return os.path.join("resources",os.path.basename(rel_path)).replace("\\","/")
|
||||
except Exception:
|
||||
return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
|
||||
else:
|
||||
return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
|
||||
if "image_path" in gui_info and gui_info["image_path"]:
|
||||
gui_info["image_path"] = normalize_resource_path(gui_info["image_path"])
|
||||
if "video_path" in gui_info and gui_info["video_path"]:
|
||||
gui_info["video_path"] = normalize_resource_path(gui_info["video_path"])
|
||||
if "bg_image_path" in gui_info and gui_info["bg_image_path"]:
|
||||
gui_info["bg_image_path"] = normalize_resource_path(gui_info["bg_image_path"])
|
||||
|
||||
def _createRequirementsFile(self,build_dir):
|
||||
requirements_content = """panda3d>=1.10.13"""
|
||||
|
||||
|
||||
@ -2010,14 +2010,7 @@ class SceneManager:
|
||||
elif gui_type == "3d_image" and hasattr(gui_manager, 'createGUI3DImage'):
|
||||
# 处理3D图像
|
||||
# 根据缩放值的数量处理尺寸
|
||||
if len(absolute_scale) >= 3:
|
||||
size = (absolute_scale[0] * 2, absolute_scale[1] * 2)
|
||||
elif len(absolute_scale) >= 2:
|
||||
size = (absolute_scale[0] * 2, absolute_scale[1] * 2)
|
||||
elif len(scale) >= 1:
|
||||
size = (absolute_scale[0] * 2, absolute_scale[0] * 2)
|
||||
else:
|
||||
size = (1.0, 1.0)
|
||||
size = (absolute_scale[0] * 0.2, absolute_scale[1] * 0.2, absolute_scale[2] * 0.2)
|
||||
|
||||
new_element = gui_manager.createGUI3DImage(
|
||||
pos=tuple(absolute_position),
|
||||
@ -2025,6 +2018,7 @@ class SceneManager:
|
||||
size=size
|
||||
)
|
||||
elif gui_type == "video_screen" and hasattr(gui_manager, 'createVideoScreen'):
|
||||
print(f"重建的3d视频屏幕视频地址是{video_path}")
|
||||
new_element = gui_manager.createVideoScreen(
|
||||
pos=tuple(absolute_position),
|
||||
size=absolute_scale,
|
||||
@ -2032,14 +2026,7 @@ class SceneManager:
|
||||
)
|
||||
if video_path and new_element:
|
||||
if video_path.startswith("http://") or video_path.startswith("https://"):
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
def load_video_stream_task(task):
|
||||
if hasattr(property_manager, '_loadVideoFromURLWithOpenCV_3D'):
|
||||
property_manager._loadVideoFromURLWithOpenCV_3D(new_element, video_path)
|
||||
return task.done
|
||||
|
||||
taskMgr.doMethodLater(0.5, load_video_stream_task, 'loadVideoStreamTask')
|
||||
pass
|
||||
else:
|
||||
if hasattr(gui_manager, 'loadVideoFile'):
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
@ -2051,6 +2038,7 @@ class SceneManager:
|
||||
taskMgr.doMethodLater(0.1, load_video_file_task, 'loadVideoFileTask')
|
||||
|
||||
elif gui_type == "2d_video_screen" and hasattr(gui_manager, 'createGUI2DVideoScreen'):
|
||||
print(f"重建的2d视频屏幕视频地址是{video_path}")
|
||||
new_element = gui_manager.createGUI2DVideoScreen(
|
||||
pos=tuple(absolute_position),
|
||||
size=absolute_scale,
|
||||
@ -2059,12 +2047,6 @@ class SceneManager:
|
||||
if video_path and new_element:
|
||||
if video_path.startswith("http://") or video_path.startswith("https://"):
|
||||
pass
|
||||
# from direct.task.TaskManagerGlobal import taskMgr
|
||||
# def load_2d_video_stream_task(task):
|
||||
# if hasattr(property_manager,'_loadVideoFromURLWithOpenCV'):
|
||||
# property_manager._loadVideoFromURLWithOpenCV(new_element,video_path)
|
||||
# return task.done
|
||||
# taskMgr.doMethodLater(0.1,load_2d_video_stream_task,'load2DVideoStreamTask')
|
||||
else:
|
||||
if hasattr(property_manager, 'load2DVideoFile'):
|
||||
from direct.task.TaskManagerGlobal import taskMgr
|
||||
|
||||
@ -65,7 +65,8 @@ class MainApp(ShowBase):
|
||||
|
||||
load_prc_file_data("", """
|
||||
win-size 1380 750
|
||||
window-title Render
|
||||
window-title
|
||||
support-threads #t
|
||||
""")
|
||||
|
||||
# 简化 sys.path 设置逻辑
|
||||
@ -94,7 +95,7 @@ class MainApp(ShowBase):
|
||||
self.script_manager = ScriptManager(self)
|
||||
self.script_manager.start_system()
|
||||
|
||||
# 加载所有脚本
|
||||
# 加载所有脚本e
|
||||
self.script_manager.load_all_scripts_from_directory()
|
||||
|
||||
self.info_panel_manager = InfoPanelManager(self)
|
||||
@ -106,6 +107,7 @@ class MainApp(ShowBase):
|
||||
self.render_pipeline._showbase.camera = self.render_pipeline._showbase.cam
|
||||
|
||||
self.controller = MovementController(self)
|
||||
self.camLens.set_fov(80)
|
||||
self.controller.set_initial_position(
|
||||
Vec3(0, -50, 20), Vec3(0, 0, 0))
|
||||
self.controller.setup()
|
||||
@ -277,8 +279,6 @@ class MainApp(ShowBase):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# 在 processSceneElements 方法中修改碰撞体创建部分
|
||||
# 修复 processSceneElements 方法中的碰撞体创建
|
||||
def processSceneElements(self, scene):
|
||||
"""处理场景中的各种元素"""
|
||||
try:
|
||||
@ -290,6 +290,12 @@ class MainApp(ShowBase):
|
||||
|
||||
# 为模型添加碰撞体 - 修复版本
|
||||
if nodePath.hasTag("is_scene_element") and not nodePath.hasTag("is_gizmo"):
|
||||
if nodePath.hasTag("gui_type"):
|
||||
gui_type = nodePath.getTag("gui_type")
|
||||
if gui_type in ["video_screen"]:
|
||||
print(f"移除GUI视频节点: {nodePath.getName()}")
|
||||
nodePath.removeNode() # 移除视频屏幕节点
|
||||
return
|
||||
# 使用更精确的包围盒
|
||||
from panda3d.core import CollisionNode, CollisionBox
|
||||
bounds = nodePath.getBounds()
|
||||
@ -315,6 +321,10 @@ class MainApp(ShowBase):
|
||||
if nodePath.hasTag("light_type"):
|
||||
light_type = nodePath.getTag("light_type")
|
||||
|
||||
if nodePath.hasTag("is_auxiliary_light") and nodePath.getTag("is_auxiliary_light").lower() == "true":
|
||||
print(f"跳过辅助灯光节点:{nodePath.getName()}")
|
||||
return
|
||||
|
||||
if nodePath not in processed_lights:
|
||||
if light_type == "spot_light":
|
||||
self._recreateSpotLight(nodePath)
|
||||
@ -516,12 +526,17 @@ class MainApp(ShowBase):
|
||||
size=absolute_scale
|
||||
)
|
||||
elif gui_type == "2d_video_screen":
|
||||
scale_value = absolute_scale[0] if absolute_scale and len(absolute_scale) > 0 else 0.2
|
||||
new_element = self.createGUI2DVideoScreen(
|
||||
pos=tuple(absolute_position),
|
||||
video_path=video_path,
|
||||
size=absolute_scale
|
||||
)
|
||||
elif gui_type == "video_screen":
|
||||
new_element = self.createGUIVideoScreen(
|
||||
pos=tuple(absolute_position),
|
||||
video_path=video_path,
|
||||
size=absolute_scale
|
||||
)
|
||||
elif gui_type == "info_panel":
|
||||
new_element = self.info_panel_manager.onCreateSampleInfoPanel()
|
||||
|
||||
@ -724,13 +739,61 @@ class MainApp(ShowBase):
|
||||
print(f"加载图片时出错: {e}")
|
||||
return image_node
|
||||
|
||||
def createGUIVideoScreen(self,pos=(0,0,0),size=1,video_path=None):
|
||||
import os
|
||||
from panda3d.core import TransparencyAttrib,Texture,TextureStage
|
||||
|
||||
if isinstance(size,(list,tuple)) and len(size) >= 2:
|
||||
width_scale = size[0]
|
||||
height_scale = size[2]
|
||||
else:
|
||||
width_scale = size * 0.1 if isinstance(size, (int, float)) else 0.2
|
||||
height_scale = width_scale
|
||||
|
||||
cm = CardMaker("gui-video-screen")
|
||||
cm.setFrame(-width_scale,width_scale,-height_scale,height_scale)
|
||||
|
||||
video_screen = self.render.attachNewNode(cm.generate())
|
||||
video_screen.setPos(pos)
|
||||
|
||||
video_screen.setBin("fixed", 0)
|
||||
|
||||
#video_screen.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
video_screen.setColor(1, 1, 1, 1)
|
||||
|
||||
self._ensureVideoScreenMaterial(video_screen)
|
||||
|
||||
if video_path:
|
||||
if video_path.startswith(("http://", "https://")):
|
||||
try:
|
||||
success = self._loadVideoFromURLWithOpenCV3D(video_screen, video_path)
|
||||
if success:
|
||||
print("视频已从URL加载成功")
|
||||
else:
|
||||
print("从URL加载视频时出错")
|
||||
except Exception as e:
|
||||
print(f"从URL加载视频时出错: {e}")
|
||||
elif os.path.exists(video_path):
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
video_screen.setTexture(movie_texture, 1)
|
||||
else:
|
||||
print(f"视频文件不存在: {video_path}")
|
||||
else:
|
||||
# 设置占位符纹理
|
||||
placeholder_texture = Texture()
|
||||
placeholder_texture.setup2dTexture(1, 1, Texture.TUnsignedByte, Texture.FRgb)
|
||||
placeholder_data = b'\x19\x19\x4c' # 深蓝色占位符
|
||||
placeholder_texture.setRamImage(placeholder_data)
|
||||
video_screen.setTexture(placeholder_texture, 1)
|
||||
|
||||
return video_screen
|
||||
|
||||
def createGUI2DVideoScreen(self, pos=(0, 0, 0), size=0.2, video_path=None):
|
||||
import os
|
||||
if video_path and not os.path.isabs(video_path):
|
||||
image_path = self.getResourcePath(video_path)
|
||||
from direct.gui.DirectGui import DirectFrame
|
||||
from panda3d.core import TransparencyAttrib, Texture, TextureStage
|
||||
import os
|
||||
|
||||
if isinstance(size,(list,tuple)) and len(size) >= 2:
|
||||
width_scale = size[0]*0.2
|
||||
@ -753,10 +816,20 @@ class MainApp(ShowBase):
|
||||
placeholder_data = b'\x19\x19\x4c'
|
||||
placeholder_texture.setRamImage(placeholder_data)
|
||||
|
||||
if video_path and os.path.exists(video_path):
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
video_screen["frameTexture"] = movie_texture
|
||||
if video_path:
|
||||
if video_path.startswith(("http://","https://")):
|
||||
try:
|
||||
success = self._loadVideoFromURLWithOpenCV(video_screen,video_path)
|
||||
if success:
|
||||
print("视频已从URL加载成功")
|
||||
else:
|
||||
print("从URL加载视频时出错")
|
||||
except Exception as e:
|
||||
print(f"从URL加载视频时出错: {e}")
|
||||
elif os.path.exists(video_path):
|
||||
movie_texture = self._loadMovieTexture(video_path)
|
||||
if movie_texture:
|
||||
video_screen["frameTexture"] = movie_texture
|
||||
|
||||
return video_screen
|
||||
|
||||
@ -776,10 +849,197 @@ class MainApp(ShowBase):
|
||||
texture.setMinfilter(Texture.FT_linear)
|
||||
texture.setMagfilter(Texture.FT_linear)
|
||||
|
||||
texture.setFormat(Texture.FRgb8)
|
||||
|
||||
if hasattr(texture, 'set_loop') and hasattr(texture, 'set_play_rate'):
|
||||
texture.set_loop(True)
|
||||
texture.set_play_rate(1.0)
|
||||
|
||||
def _loadVideoFromURLWithOpenCV(self, video_screen, url):
|
||||
try:
|
||||
import cv2
|
||||
import threading
|
||||
from panda3d.core import Texture,PNMImage
|
||||
import numpy as np
|
||||
import time
|
||||
|
||||
def video_stream_thread():
|
||||
cap = cv2.VideoCapture(url)
|
||||
|
||||
if not cap.isOpened():
|
||||
print(f"无法打开视频流: {url}")
|
||||
return False
|
||||
|
||||
cap.set(cv2.CAP_PROP_BUFFERSIZE,1)
|
||||
|
||||
fps = cap.get(cv2.CAP_PROP_FPS)
|
||||
frame_delay = 1.0 / fps if fps > 0 else 0.033 # 默认30fps
|
||||
|
||||
while hasattr(self, 'video_stream_active') and self.video_stream_active:
|
||||
ret, frame = cap.read()
|
||||
|
||||
if not ret:
|
||||
print("视频流读取失败,尝试重新连接...")
|
||||
cap.release()
|
||||
time.sleep(1)
|
||||
cap = cv2.VideoCapture(url)
|
||||
continue
|
||||
|
||||
frame_height,frame_width = frame.shape[:2]
|
||||
if frame_width > 256:
|
||||
scale = 256.0/frame_width
|
||||
new_width = int(frame_width * scale)
|
||||
new_height = int(frame_height * scale)
|
||||
frame = cv2.resize(frame,(new_width,new_height))
|
||||
|
||||
frame_rgb = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
|
||||
|
||||
height,width = frame_rgb.shape[:2]
|
||||
|
||||
img = PNMImage(width,height,3)
|
||||
img.set_maxval(255)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
r,g,b = frame_rgb[y,x]
|
||||
img.setXelVal(x,y,r,g,b)
|
||||
|
||||
texture = Texture("video_texture")
|
||||
texture.setMagfilter(Texture.FTLinear)
|
||||
texture.setMinfilter(Texture.FTLinear)
|
||||
texture.setWrapU(Texture.WMClamp)
|
||||
texture.setWrapV(Texture.WMClamp)
|
||||
texture.load(img)
|
||||
|
||||
# 在主线程中更新GUI纹理
|
||||
def update_texture():
|
||||
if hasattr(self, 'aspect2d') and not video_screen.isEmpty():
|
||||
video_screen["frameTexture"] = texture
|
||||
|
||||
# 使用taskMgr在主线程中更新纹理
|
||||
if hasattr(self, 'taskMgr'):
|
||||
self.taskMgr.doMethodLater(0, lambda task: update_texture() or task.done,
|
||||
f"updateVideoTexture_{time.time()}")
|
||||
|
||||
# 控制帧率
|
||||
time.sleep(frame_delay)
|
||||
|
||||
cap.release()
|
||||
print("视频流线程结束")
|
||||
return True
|
||||
|
||||
# 启动视频流线程
|
||||
self.video_stream_active = True
|
||||
self.video_thread = threading.Thread(target=video_stream_thread, daemon=True)
|
||||
self.video_thread.start()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"加载视频流失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _loadVideoFromURLWithOpenCV3D(self,video_screen,url):
|
||||
try:
|
||||
import cv2
|
||||
import threading
|
||||
from panda3d.core import Texture,PNMImage
|
||||
import time
|
||||
|
||||
def video_stream_thread():
|
||||
cap = cv2.VideoCapture(url)
|
||||
|
||||
if not cap.isOpened():
|
||||
return False
|
||||
|
||||
cap.set(cv2.CAP_PROP_BUFFERSIZE,1)
|
||||
|
||||
fps = cap.get(cv2.CAP_PROP_FPS)
|
||||
frame_delay = 1.0 / fps if fps > 0 else 0.033
|
||||
|
||||
while hasattr(self,'video_stream_active') and self.video_stream_active:
|
||||
ret,frame = cap.read()
|
||||
|
||||
if not ret:
|
||||
cap.release()
|
||||
time.sleep(1)
|
||||
cap = cv2.VideoCapture(url)
|
||||
continue
|
||||
|
||||
frame_height,frame_width = frame.shape[:2]
|
||||
if frame_width > 256:
|
||||
scale = 256.0/frame_width
|
||||
new_width = int(frame_width*scale)
|
||||
new_height = int(frame_height*scale)
|
||||
frame = cv2.resize(frame,(new_width,new_height))
|
||||
|
||||
frame_rgb = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
|
||||
|
||||
height,width = frame_rgb.shape[:2]
|
||||
|
||||
img = PNMImage(width,height,3)
|
||||
img.set_maxval(255)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
r,g,b = frame_rgb[y,x]
|
||||
img.setXelVal(x,y,r,g,b)
|
||||
|
||||
texture = Texture("video_texture_3d")
|
||||
texture.setMagfilter(Texture.FTLinear)
|
||||
texture.setMinfilter(Texture.FTLinear)
|
||||
texture.setWrapU(Texture.WMClamp)
|
||||
texture.setWrapV(Texture.WMClamp)
|
||||
texture.load(img)
|
||||
|
||||
def update_texture():
|
||||
if hasattr(self,'render') and not video_screen.isEmpty():
|
||||
video_screen.setTexture(texture,1)
|
||||
|
||||
if hasattr(self,'taskMgr'):
|
||||
self.taskMgr.doMethodLater(0,lambda task:update_texture() or task.done,f"updateVideoTexture_{time.time()}")
|
||||
|
||||
time.sleep(frame_delay)
|
||||
|
||||
cap.release()
|
||||
return True
|
||||
self.video_stream_active = True
|
||||
self.video_thread_3d = threading.Thread(target=video_stream_thread,daemon=True)
|
||||
self.video_thread_3d.start()
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"加载3D视频流失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _ensureVideoScreenMaterial(self, video_screen):
|
||||
"""确保视频屏幕有正确的材质设置"""
|
||||
try:
|
||||
from panda3d.core import Material, LColor
|
||||
|
||||
if not video_screen.hasMaterial():
|
||||
material = Material(f"video-material-{video_screen.getName()}")
|
||||
material.setBaseColor(LColor(1, 1, 1, 1))
|
||||
material.setDiffuse(LColor(1, 1, 1, 1))
|
||||
material.setAmbient(LColor(1, 1, 1, 1)) # 确保环境光为白色
|
||||
material.setEmission(LColor(0, 0, 0, 1))
|
||||
material.setSpecular(LColor(0, 0, 0, 1))
|
||||
material.setShininess(0)
|
||||
video_screen.setMaterial(material, 1)
|
||||
else:
|
||||
# 更新现有材质确保正确设置
|
||||
material = video_screen.getMaterial()
|
||||
material.setBaseColor(LColor(1, 1, 1, 1))
|
||||
material.setAmbient(LColor(1, 1, 1, 1)) # 确保环境光为白色
|
||||
video_screen.setMaterial(material, 1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 设置视频屏幕材质时出错: {e}")
|
||||
|
||||
def playModelAnimation(self):
|
||||
"""播放场景中所有 .glb 模型的动画(仅转换有动画的模型为 Actor)"""
|
||||
try:
|
||||
|
||||
@ -3482,7 +3482,8 @@ class PropertyPanelManager:
|
||||
if video_path.startswith("http://") or video_path.startswith("https://"):
|
||||
# 显示URL信息
|
||||
video_info_layout.addWidget(QLabel("视频流URL:"), 0, 0)
|
||||
path_label = QLabel(video_path)
|
||||
display_path = video_path if len(video_path)<=50 else video_path[:47]+"..."
|
||||
path_label = QLabel(display_path)
|
||||
path_label.setWordWrap(True)
|
||||
path_label.setStyleSheet("color: #00AAFF;")
|
||||
video_info_layout.addWidget(path_label, 0, 1)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user