修复打包不能保存视频流的问题

This commit is contained in:
Hector 2025-10-10 15:32:46 +08:00
parent f4d21a62f9
commit f455d5134d
8 changed files with 499 additions and 106 deletions

View File

@ -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):

View File

@ -1,4 +1,3 @@
from PyQt5.QtCore import showbase
from direct.task.TaskManagerGlobal import taskMgr

View File

@ -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)

View File

@ -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)

View File

@ -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"""

View File

@ -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

View File

@ -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:

View File

@ -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)