Merge remote-tracking branch 'origin/addRender' into main_ch_eg
# Conflicts: # RenderPipelineFile/config/daytime.yaml # scene/scene_manager.py # ui/main_window.py
This commit is contained in:
commit
dff3726bbf
@ -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)
|
||||
|
||||
BIN
Resources/models/Women_1.glb
Normal file
BIN
Resources/models/Women_1.glb
Normal file
Binary file not shown.
BIN
Resources/models/Women_2.glb
Normal file
BIN
Resources/models/Women_2.glb
Normal file
Binary file not shown.
BIN
Resources/models/women_1.glb
Normal file
BIN
Resources/models/women_1.glb
Normal file
Binary file not shown.
@ -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():
|
||||
|
||||
@ -294,6 +294,31 @@ class ScriptLoader:
|
||||
|
||||
return len(changed_scripts) > 0
|
||||
|
||||
def find_script_file(self, script_name: str) -> Optional[str]:
|
||||
"""根据脚本名称查找脚本文件路径"""
|
||||
# 首先检查已加载的脚本
|
||||
if script_name in self.loaded_modules:
|
||||
module = self.loaded_modules[script_name]
|
||||
if hasattr(module, '__file__') and module.__file__:
|
||||
return module.__file__
|
||||
|
||||
# 在已知的文件路径中查找
|
||||
for file_path in self.file_mtimes.keys():
|
||||
file_name = os.path.splitext(os.path.basename(file_path))[0]
|
||||
if file_name == script_name:
|
||||
return file_path
|
||||
|
||||
# 在脚本目录中查找
|
||||
scripts_dir = self.script_manager.scripts_directory
|
||||
if os.path.exists(scripts_dir):
|
||||
for file_name in os.listdir(scripts_dir):
|
||||
if file_name.endswith('.py'):
|
||||
base_name = os.path.splitext(file_name)[0]
|
||||
if base_name == script_name:
|
||||
return os.path.join(scripts_dir, file_name)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class ScriptAPI:
|
||||
"""脚本API - 提供给脚本使用的API接口"""
|
||||
|
||||
@ -212,7 +212,7 @@ class SelectionSystem:
|
||||
return
|
||||
|
||||
# 获取边界框的最小和最大点(世界坐标)
|
||||
print(f"世界边界框: min={minPoint}, max={maxPoint}")
|
||||
#print(f"世界边界框: min={minPoint}, max={maxPoint}")
|
||||
|
||||
# 创建线段对象
|
||||
lines = LineSegs()
|
||||
|
||||
@ -573,3 +573,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
|
||||
|
||||
|
||||
@ -126,7 +126,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
|
||||
@ -182,9 +182,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)}")
|
||||
@ -193,7 +207,7 @@ class GUIManager:
|
||||
button.setTag("is_scene_element", "1") # 确保这个标签被设置
|
||||
button.setTag("tree_item_type", "GUI_BUTTON")
|
||||
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)
|
||||
@ -228,11 +242,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按钮")
|
||||
|
||||
@ -342,11 +356,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标签")
|
||||
|
||||
@ -454,11 +468,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输入框")
|
||||
|
||||
@ -535,6 +549,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)
|
||||
@ -587,11 +602,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按钮")
|
||||
|
||||
@ -732,11 +747,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文本")
|
||||
|
||||
@ -840,7 +855,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("tree_item_type", "GUI_3DIMAGE")
|
||||
@ -869,11 +884,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文本")
|
||||
|
||||
@ -889,7 +904,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
|
||||
@ -912,7 +927,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}")
|
||||
|
||||
@ -1060,12 +1075,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)} 个视频屏幕")
|
||||
|
||||
@ -1143,6 +1158,7 @@ class GUIManager:
|
||||
# 检查是否有播放方法
|
||||
if hasattr(movie_texture, 'play'):
|
||||
try:
|
||||
self.loadVideoFile(video_screen, video_screen.getTag("video_path"))
|
||||
movie_texture.play()
|
||||
print(f"▶️ 继续播放视频: {video_screen.getName()}")
|
||||
return True
|
||||
@ -1153,9 +1169,7 @@ class GUIManager:
|
||||
print(f"⚠️ 纹理对象没有播放方法: {video_screen.getName()}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 视频屏幕没有关联的视频纹理: {video_screen.getName()}")
|
||||
self._debugVideoScreenTextures(video_screen)
|
||||
return False
|
||||
self.loadVideoFile(video_screen,video_screen.getTag("video_path"))
|
||||
except Exception as e:
|
||||
print(f"❌ 播放视频失败: {e}")
|
||||
import traceback
|
||||
@ -1282,7 +1296,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):
|
||||
@ -1440,8 +1453,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:
|
||||
@ -1468,7 +1479,7 @@ 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)
|
||||
@ -1476,7 +1487,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)}")
|
||||
@ -1485,11 +1496,8 @@ class GUIManager:
|
||||
video_screen.setTag("tree_item_type", "GUI_2D_VIDEO_SCREEN")
|
||||
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)}")
|
||||
@ -1562,12 +1570,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视频屏幕")
|
||||
|
||||
@ -1588,7 +1596,11 @@ class GUIManager:
|
||||
try:
|
||||
import os
|
||||
|
||||
if not os.path.exists(video_path):
|
||||
video_screen.setTag("video_path",video_path)
|
||||
path = video_screen.getTag("video_path")
|
||||
print(f"🔧 更新2D视频屏幕标签 - video_path: {path}")
|
||||
|
||||
if not video_path or not os.path.exists(video_path):
|
||||
print(f"❌ 2D视频文件不存在: {video_path}")
|
||||
return False
|
||||
|
||||
@ -1600,7 +1612,6 @@ class GUIManager:
|
||||
|
||||
# 保存视频纹理引用
|
||||
video_screen.setPythonTag("movie_texture", movie_texture)
|
||||
video_screen.setTag("video_path", video_path)
|
||||
|
||||
print(f"✅ 成功加载新2D视频: {video_path}")
|
||||
return True
|
||||
@ -1828,12 +1839,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)} 个球形视频")
|
||||
|
||||
@ -2034,12 +2045,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)} 个虚拟屏幕")
|
||||
|
||||
|
||||
8
main.py
8
main.py
@ -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
106
threejs_panel.html
Normal file
106
threejs_panel.html
Normal file
@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Three.js Panel</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
background: rgba(0,0,0,0.7);
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
#info {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
#canvas-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="info">
|
||||
<h3>场景信息面板</h3>
|
||||
<p>FPS: <span id="fps">0</span></p>
|
||||
<p>对象数: <span id="object-count">0</span></p>
|
||||
</div>
|
||||
<div id="canvas-container"></div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
// 初始化Three.js场景
|
||||
let scene, camera, renderer;
|
||||
let cube;
|
||||
|
||||
function init() {
|
||||
// 创建场景
|
||||
scene = new THREE.Scene();
|
||||
|
||||
// 创建相机
|
||||
camera = new THREE.PerspectiveCamera(75,
|
||||
document.getElementById('canvas-container').offsetWidth /
|
||||
document.getElementById('canvas-container').offsetHeight,
|
||||
0.1, 1000);
|
||||
|
||||
// 创建渲染器
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setSize(
|
||||
document.getElementById('canvas-container').offsetWidth,
|
||||
document.getElementById('canvas-container').offsetHeight
|
||||
);
|
||||
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
||||
|
||||
// 添加一个立方体
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
color: 0x00ff00,
|
||||
wireframe: true
|
||||
});
|
||||
cube = new THREE.Mesh(geometry, material);
|
||||
scene.add(cube);
|
||||
|
||||
camera.position.z = 5;
|
||||
|
||||
// 开始动画循环
|
||||
animate();
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = document.getElementById('canvas-container').offsetWidth /
|
||||
document.getElementById('canvas-container').offsetHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(
|
||||
document.getElementById('canvas-container').offsetWidth,
|
||||
document.getElementById('canvas-container').offsetHeight
|
||||
);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// 旋转立方体
|
||||
cube.rotation.x += 0.01;
|
||||
cube.rotation.y += 0.01;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
// 接收来自Python的消息
|
||||
function updateInfo(data) {
|
||||
document.getElementById('fps').textContent = data.fps || 0;
|
||||
document.getElementById('object-count').textContent = data.objectCount || 0;
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
window.onload = init;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -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'):
|
||||
|
||||
@ -1072,6 +1072,7 @@ class MainWindow(QMainWindow):
|
||||
# self.bottomDock.setWidget(self.fileView)
|
||||
# self.addDockWidget(Qt.BottomDockWidgetArea, self.bottomDock)
|
||||
|
||||
|
||||
# 创建底部停靠窗口(资源窗口)
|
||||
self.bottomDock = QDockWidget("资源", self)
|
||||
self.bottomDock.setStyleSheet("""
|
||||
|
||||
@ -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
|
||||
@ -1690,9 +1690,9 @@ class PropertyPanelManager:
|
||||
# 添加弹性空间
|
||||
|
||||
if gui_type == "video_screen":
|
||||
self._addVideoScreenProperties(gui_element)
|
||||
self._addVideoScreenProperties(gui_element,item)
|
||||
elif gui_type == "2d_video_screen":
|
||||
self._add2DVideoScreenProperties(gui_element)
|
||||
self._add2DVideoScreenProperties(gui_element,item)
|
||||
elif gui_type == "spherical_video":
|
||||
self._addSphericalVideoProperties(gui_element)
|
||||
elif gui_type in ['info_panel','info_panel_3d']:
|
||||
@ -3292,7 +3292,7 @@ class PropertyPanelManager:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _addVideoScreenProperties(self, video_screen):
|
||||
def _addVideoScreenProperties(self, video_screen,item):
|
||||
"""添加视频屏幕属性面板"""
|
||||
try:
|
||||
from PyQt5.QtWidgets import (QGroupBox, QGridLayout, QPushButton, QLabel,
|
||||
@ -3359,7 +3359,7 @@ class PropertyPanelManager:
|
||||
|
||||
# 加载新视频按钮
|
||||
load_btn = QPushButton("📁 加载新视频...")
|
||||
load_btn.clicked.connect(lambda: self._loadNewVideo(video_screen))
|
||||
load_btn.clicked.connect(lambda: self._loadNewVideo(video_screen,item))
|
||||
self._propertyLayout.addWidget(load_btn)
|
||||
|
||||
# 添加URL输入区域
|
||||
@ -3382,7 +3382,7 @@ class PropertyPanelManager:
|
||||
except Exception as e:
|
||||
print(f"添加视频屏幕属性失败: {e}")
|
||||
|
||||
def _add2DVideoScreenProperties(self, video_screen):
|
||||
def _add2DVideoScreenProperties(self, video_screen,item):
|
||||
"""为2D视频屏幕添加属性控制面板"""
|
||||
try:
|
||||
from PyQt5.QtWidgets import (QGroupBox, QGridLayout, QPushButton, QLabel,
|
||||
@ -3453,7 +3453,7 @@ class PropertyPanelManager:
|
||||
|
||||
# 加载新视频按钮
|
||||
load_btn = QPushButton("📁 加载新视频...")
|
||||
load_btn.clicked.connect(lambda: self._loadNew2DVideo(video_screen))
|
||||
load_btn.clicked.connect(lambda: self._loadNew2DVideo(video_screen,item))
|
||||
self._propertyLayout.addWidget(load_btn)
|
||||
|
||||
# 添加URL输入区域
|
||||
@ -3751,7 +3751,7 @@ class PropertyPanelManager:
|
||||
except Exception as e:
|
||||
print(f"❌ 停止视频失败: {e}")
|
||||
return False
|
||||
def _loadNew2DVideo(self, video_screen):
|
||||
def _loadNew2DVideo(self, video_screen,item):
|
||||
"""为2D视频屏幕加载新视频文件"""
|
||||
try:
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
@ -3766,60 +3766,12 @@ class PropertyPanelManager:
|
||||
print(f"成功加载新视频: {file_path}")
|
||||
# 刷新属性面板以显示新视频信息
|
||||
self._stop2DVideo(video_screen)
|
||||
self.updateGUIPropertyPanel(video_screen)
|
||||
self.updateGUIPropertyPanel(video_screen,item)
|
||||
return True
|
||||
except Exception as e:
|
||||
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:
|
||||
@ -4077,7 +4029,7 @@ class PropertyPanelManager:
|
||||
return False
|
||||
|
||||
|
||||
def _loadNewVideo(self,video_screen):
|
||||
def _loadNewVideo(self,video_screen,item):
|
||||
try:
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
None,
|
||||
@ -4088,8 +4040,7 @@ class PropertyPanelManager:
|
||||
if file_path:
|
||||
success = self.world.gui_manager.loadVideoFile(video_screen,file_path)
|
||||
if success:
|
||||
print(f"成功加载新视频{file_path}")
|
||||
self.updateGUIPropertyPanel(video_screen)
|
||||
self.updateGUIPropertyPanel(video_screen,item)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"加载新视频失败{e}")
|
||||
@ -8222,17 +8173,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
|
||||
@ -8588,6 +8541,8 @@ except Exception as e:
|
||||
print(f"[系统转换] 系统转换失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def _playAnimation(self,origin_model):
|
||||
actor=self._getActor(origin_model)
|
||||
if not actor:
|
||||
@ -8700,7 +8655,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()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user