forked from Rowland/EG
1.整合菜单栏(2D图片、3D图片、地形)
This commit is contained in:
parent
c39ebeb345
commit
95395bdb05
@ -13,6 +13,15 @@ class TerrainManager:
|
||||
self.terrains = []
|
||||
|
||||
# core/terrain_manager.py
|
||||
def _get_tree_widget(self):
|
||||
"""安全获取树形控件"""
|
||||
try:
|
||||
if (hasattr(self.world, 'interface_manager') and
|
||||
hasattr(self.world.interface_manager, 'treeWidget')):
|
||||
return self.world.interface_manager.treeWidget
|
||||
except AttributeError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def createTerrainFromHeightMap(self, heightmap_path, scale=(1, 1, 1)):
|
||||
"""从高度图创建地形"""
|
||||
|
||||
@ -272,211 +272,6 @@ class GUIManager:
|
||||
return None
|
||||
|
||||
def createGUIEntry(self, pos=(0, 0, 0), placeholder="输入文本...", size=0.08):
|
||||
"""创建2D GUI文本输入框"""
|
||||
from direct.gui.DirectGui import DirectEntry
|
||||
|
||||
# 将3D坐标转换为2D屏幕坐标
|
||||
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
||||
|
||||
entry = DirectEntry(
|
||||
text="",
|
||||
pos=gui_pos,
|
||||
scale=size,
|
||||
command=self.onGUIEntrySubmit,
|
||||
extraArgs=[f"entry_{len(self.gui_elements)}"],
|
||||
initialText=placeholder,
|
||||
numLines=1,
|
||||
width=12,
|
||||
focus=0
|
||||
)
|
||||
|
||||
# 为GUI元素添加标识
|
||||
entry.setTag("gui_type", "entry")
|
||||
entry.setTag("gui_id", f"entry_{len(self.gui_elements)}")
|
||||
entry.setTag("gui_placeholder", placeholder)
|
||||
entry.setTag("is_gui_element", "1")
|
||||
|
||||
self.gui_elements.append(entry)
|
||||
# 安全地调用updateSceneTree
|
||||
if hasattr(self.world, 'updateSceneTree'):
|
||||
self.world.updateSceneTree()
|
||||
|
||||
print(f"✓ 创建GUI输入框: {placeholder} (逻辑位置: {pos}, 屏幕位置: {gui_pos})")
|
||||
return entry
|
||||
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
|
||||
"""创建2D GUI图片"""
|
||||
from direct.gui.DirectGui import DirectFrame
|
||||
from panda3d.core import TransparencyAttrib,Texture,CardMaker,CardMaker,Vec3
|
||||
|
||||
# 将3D坐标转换为2D屏幕坐标
|
||||
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
||||
|
||||
#使用CardMaker创建一个更可靠的图片框架
|
||||
cm = CardMaker('gui-2d-image')
|
||||
cm.setFrame(-size,size,-size,size)
|
||||
|
||||
image_node = self.world.aspect2d.attachNewNode(cm.generate())
|
||||
image_node.setPos(gui_pos)
|
||||
image_node.setBin('fixed',0)
|
||||
image_node.setDepthWrite(False)
|
||||
image_node.setDepthTest(False)
|
||||
image_node.setColor(1,1,1,1)
|
||||
|
||||
# 设置透明度支持
|
||||
image_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
# 如果提供了图像路径,则加载纹理
|
||||
if image_path:
|
||||
try:
|
||||
texture = self.world.loader.loadTexture(image_path)
|
||||
if texture:
|
||||
image_node.setTexture(texture,1)
|
||||
texture.setWrapU(Texture.WM_clamp)
|
||||
texture.setWrapV(Texture.WM_clamp)
|
||||
texture.setMinfilter(Texture.FT_linear)
|
||||
texture.setMagfilter(Texture.FT_linear)
|
||||
image_node.setColor(1,1,1,1)
|
||||
else:
|
||||
print(f"⚠️ 无法加载2D图片纹理: {image_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载2D图片纹理失败: {e}")
|
||||
|
||||
# 为GUI元素添加标识
|
||||
image_node.setTag("gui_type", "2d_image")
|
||||
image_node.setTag("gui_id", f"2d_image_{len(self.gui_elements)}")
|
||||
image_node.setTag("gui_text", f"2D图片_{len(self.gui_elements)}")
|
||||
if image_path:
|
||||
image_node.setTag("image_path", image_path)
|
||||
image_node.setTag("is_gui_element", "1")
|
||||
|
||||
self.gui_elements.append(image_node)
|
||||
|
||||
# 安全地调用updateSceneTree
|
||||
if hasattr(self.world, 'updateSceneTree'):
|
||||
self.world.updateSceneTree()
|
||||
|
||||
print(f"✓ 创建2D GUI图片 (逻辑位置: {pos}, 屏幕位置: {gui_pos})")
|
||||
return image_node
|
||||
|
||||
def constrain2DPosition(self,gui_element,new_x=None,new_z=None):
|
||||
"""限制2dGUI元素位置在屏幕范围内"""
|
||||
try:
|
||||
from panda3d.core import Vec3
|
||||
|
||||
bounds = gui_element.getTightBounds()
|
||||
element_width=0
|
||||
element_height=0
|
||||
|
||||
if bounds:
|
||||
min_point,max_point = bounds
|
||||
element_width = (max_point.getX() - min_point.getX())/2
|
||||
element_height = (max_point.getZ()-min_point.getZ())/2
|
||||
|
||||
#获取当前缩放
|
||||
scale = gui_element.getScale()
|
||||
if hasattr(scale,'getX'):
|
||||
scale_x = scale.getX()
|
||||
scale_z = scale.getZ() if hasattr(scale,'getZ') else scale_x
|
||||
else:
|
||||
scale_x = scale_z = scale if isinstance(scale,(int,float)) else 1.0
|
||||
|
||||
actual_width = element_width * scale_x
|
||||
actual_height = element_height * scale_z
|
||||
|
||||
screen_width = 1.9
|
||||
screen_height = 0.9
|
||||
|
||||
min_x = -screen_width + actual_width
|
||||
max_x = screen_width - actual_width
|
||||
min_z = -screen_height + actual_height
|
||||
max_z = screen_height - actual_height
|
||||
|
||||
#获取当前位置
|
||||
current_pos = gui_element.getPos()
|
||||
x = new_x if new_x is not None else current_pos.getX()
|
||||
z = new_z if new_z is not None else current_pos.getZ()
|
||||
|
||||
#应用边界限制
|
||||
x = max(min_x,min(max_x,x))
|
||||
z = max(min_z,min(max_z,z))
|
||||
|
||||
return Vec3(x,current_pos.getY(),z)
|
||||
except Exception as e:
|
||||
print(f"约束2D位置时出错: {e}")
|
||||
# 出错时返回原始值
|
||||
current_pos = gui_element.getPos()
|
||||
x = new_x if new_x is not None else current_pos.getX()
|
||||
z = new_z if new_z is not None else current_pos.getZ()
|
||||
return Vec3(x, current_pos.getY(), z)
|
||||
|
||||
|
||||
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=1):
|
||||
"""创建3D空间文本"""
|
||||
from panda3d.core import TextNode,Material,Vec4,ColorAttrib,TransparencyAttrib
|
||||
|
||||
textNode = TextNode(f'3d-text-{len(self.gui_elements)}')
|
||||
textNode.setText(text)
|
||||
textNode.setAlign(TextNode.ACenter)
|
||||
if self.world.getChineseFont():
|
||||
textNode.setFont(self.world.getChineseFont())
|
||||
|
||||
textNode.setTextColor(Vec4(1,1,1,1))
|
||||
|
||||
textNodePath = self.world.render.attachNewNode(textNode)
|
||||
textNodePath.setPos(*pos)
|
||||
textNodePath.setScale(size,size,size)
|
||||
#textNodePath.setBillboardAxis() # 让文本总是面向相机
|
||||
|
||||
# 为3D文本创建默认材质
|
||||
material = Material(f"text-material-{len(self.gui_elements)}")
|
||||
material.setBaseColor(Vec4(1, 1, 1, 1)) # 白色
|
||||
material.setDiffuse(Vec4(1, 1, 1, 1))
|
||||
material.setAmbient(Vec4(0.5, 0.5, 0.5, 1))
|
||||
material.setSpecular(Vec4(0.1, 0.1, 0.1, 1.0))
|
||||
material.setShininess(10.0)
|
||||
#material.setEmission(0,0,0,1)
|
||||
|
||||
textNodePath.setMaterial(material, 1)
|
||||
|
||||
textNodePath.setTransparency(TransparencyAttrib.MAlpha)
|
||||
textNodePath.setAttrib(ColorAttrib.makeFlat(Vec4(1, 1, 1, 1)))
|
||||
textNodePath.setLightOff()
|
||||
|
||||
# 为GUI元素添加标识
|
||||
textNodePath.setTag("gui_type", "3d_text")
|
||||
textNodePath.setTag("gui_id", f"3d_text_{len(self.gui_elements)}")
|
||||
textNodePath.setTag("gui_text", text)
|
||||
textNodePath.setTag("is_gui_element", "1")
|
||||
|
||||
textNodePath.setDepthWrite(True) # 确保深度写入
|
||||
textNodePath.setDepthTest(True) # 启用深度测试
|
||||
textNodePath.setBin("fixed", 0) # 设置渲染层级,避免被遮挡
|
||||
|
||||
# if hasattr(self, 'render_pipeline') and self.render_pipeline:
|
||||
# try:
|
||||
# self.render_pipeline.set_effect(
|
||||
# textNodePath,
|
||||
# "effects/default.yaml",
|
||||
# {
|
||||
# "normal_mapping": False,
|
||||
# "render_gbuffer": False,
|
||||
# "alpha_testing": True,
|
||||
# "parallax_mapping": False,
|
||||
# "render_shadow": False,
|
||||
# "render_envmap": False
|
||||
# },
|
||||
# 50
|
||||
# )
|
||||
# except Exception as e:
|
||||
# print(f"⚠️ PBR效果应用失败: {e}")
|
||||
|
||||
self.gui_elements.append(textNodePath)
|
||||
# 安全地调用updateSceneTree
|
||||
if hasattr(self.world, 'updateSceneTree'):
|
||||
self.world.updateSceneTree()
|
||||
|
||||
print(f"✓ 创建3D文本: {text} (世界位置: {pos})")
|
||||
return textNodePath
|
||||
"""创建2D GUI文本输入框 - 支持多选创建和GUI父子关系,优化版本"""
|
||||
try:
|
||||
from direct.gui.DirectGui import DirectEntry
|
||||
@ -587,6 +382,185 @@ class GUIManager:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def createGUI2DImage(self, pos=(0, 0, 0), image_path=None, size=0.2):
|
||||
"""创建2D GUI图片"""
|
||||
try:
|
||||
from direct.gui.DirectGui import DirectButton
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
print(f"🔘 开始创建GUI按钮,位置: {pos}, 图片路径: {image_path}, 尺寸: {size}")
|
||||
|
||||
# 获取树形控件
|
||||
tree_widget = self._get_tree_widget()
|
||||
if not tree_widget:
|
||||
print("❌ 无法访问树形控件")
|
||||
return None
|
||||
|
||||
# 使用CustomTreeWidget的方法获取目标父节点列表
|
||||
target_parents = tree_widget.get_target_parents_for_gui_creation()
|
||||
if not target_parents:
|
||||
print("❌ 没有找到有效的父节点")
|
||||
return None
|
||||
|
||||
created_2dimage = []
|
||||
|
||||
# 为每个有效的父节点创建GUI按钮
|
||||
for parent_item, parent_node in target_parents:
|
||||
try:
|
||||
# 生成唯一名称
|
||||
image_name = f"GUIImage_{len(self.gui_elements)}"
|
||||
|
||||
# 使用CustomTreeWidget的方法判断父节点类型并设置相应的挂载方式
|
||||
if tree_widget.is_gui_element(parent_node):
|
||||
# 父节点是GUI元素 - 作为子GUI挂载
|
||||
gui_pos = tree_widget.calculate_relative_gui_position(pos, parent_node)
|
||||
parent_gui_node = parent_node # 直接挂载到GUI元素
|
||||
print(f"📎 挂载到GUI父节点: {parent_node.getName()}")
|
||||
else:
|
||||
# 父节点是普通3D节点 - 使用屏幕坐标
|
||||
gui_pos = (pos[0] * 0.1, 0, pos[2] * 0.1)
|
||||
parent_gui_node = None # 使用默认的aspect2d
|
||||
print(f"📎 挂载到3D父节点: {parent_item.text(0)}")
|
||||
|
||||
# 使用CardMaker创建一个更可靠的图片框架
|
||||
cm = CardMaker('gui-2d-image')
|
||||
cm.setFrame(-size, size, -size, size)
|
||||
|
||||
image_node = self.world.aspect2d.attachNewNode(cm.generate())
|
||||
image_node.setPos(gui_pos)
|
||||
image_node.setBin('fixed', 0)
|
||||
image_node.setDepthWrite(False)
|
||||
image_node.setDepthTest(False)
|
||||
image_node.setColor(1, 1, 1, 1)
|
||||
|
||||
# 设置透明度支持
|
||||
image_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
# 如果提供了图像路径,则加载纹理
|
||||
if image_path:
|
||||
try:
|
||||
texture = self.world.loader.loadTexture(image_path)
|
||||
if texture:
|
||||
image_node.setTexture(texture, 1)
|
||||
texture.setWrapU(Texture.WM_clamp)
|
||||
texture.setWrapV(Texture.WM_clamp)
|
||||
texture.setMinfilter(Texture.FT_linear)
|
||||
texture.setMagfilter(Texture.FT_linear)
|
||||
image_node.setColor(1, 1, 1, 1)
|
||||
else:
|
||||
print(f"⚠️ 无法加载2D图片纹理: {image_path}")
|
||||
except Exception as e:
|
||||
print(f"❌ 加载2D图片纹理失败: {e}")
|
||||
|
||||
# 设置节点标签
|
||||
image_node.setTag("gui_type", "2d_image")
|
||||
image_node.setTag("gui_id", f"2d_image_{len(self.gui_elements)}")
|
||||
image_node.setTag("gui_text", f"2D图片_{len(self.gui_elements)}")
|
||||
image_node.setTag("is_gui_element", "1")
|
||||
image_node.setTag("is_scene_element", "1")
|
||||
image_node.setTag("created_by_user", "1")
|
||||
image_node.setTag("gui_parent_type", "gui" if parent_gui_node else "3d")
|
||||
image_node.setName(image_name)
|
||||
|
||||
# 如果有GUI父节点,建立引用关系
|
||||
if parent_gui_node:
|
||||
parent_id = parent_gui_node.getTag("gui_id") if hasattr(parent_gui_node, 'getTag') else ""
|
||||
image_node.setTag("gui_parent_id", parent_id)
|
||||
|
||||
# 添加到GUI元素列表
|
||||
self.gui_elements.append(image_node)
|
||||
|
||||
print(f"✅ 为 {parent_item.text(0)} 创建GUI按钮成功: {image_name}")
|
||||
|
||||
# 使用CustomTreeWidget的方法在Qt树形控件中添加对应节点
|
||||
qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_IMAGE")
|
||||
if qt_item:
|
||||
created_2dimage.append((image_node, qt_item))
|
||||
else:
|
||||
created_2dimage.append((image_node, None))
|
||||
print("⚠️ Qt树节点添加失败,但GUI对象已创建")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 为 {parent_item.text(0)} 创建GUI按钮失败: {str(e)}")
|
||||
continue
|
||||
|
||||
# 处理创建结果
|
||||
if not created_2dimage:
|
||||
print("❌ 没有成功创建任何GUI按钮")
|
||||
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)
|
||||
|
||||
print(f"🎉 总共创建了 {len(created_2dimage)} 个GUI按钮")
|
||||
|
||||
# 返回值处理
|
||||
if len(created_2dimage) == 1:
|
||||
return created_2dimage[0][0]
|
||||
else:
|
||||
return [button for button, _ in created_2dimage]
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 创建GUI按钮过程失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def constrain2DPosition(self,gui_element,new_x=None,new_z=None):
|
||||
"""限制2dGUI元素位置在屏幕范围内"""
|
||||
try:
|
||||
from panda3d.core import Vec3
|
||||
|
||||
bounds = gui_element.getTightBounds()
|
||||
element_width=0
|
||||
element_height=0
|
||||
|
||||
if bounds:
|
||||
min_point,max_point = bounds
|
||||
element_width = (max_point.getX() - min_point.getX())/2
|
||||
element_height = (max_point.getZ()-min_point.getZ())/2
|
||||
|
||||
#获取当前缩放
|
||||
scale = gui_element.getScale()
|
||||
if hasattr(scale,'getX'):
|
||||
scale_x = scale.getX()
|
||||
scale_z = scale.getZ() if hasattr(scale,'getZ') else scale_x
|
||||
else:
|
||||
scale_x = scale_z = scale if isinstance(scale,(int,float)) else 1.0
|
||||
|
||||
actual_width = element_width * scale_x
|
||||
actual_height = element_height * scale_z
|
||||
|
||||
screen_width = 1.9
|
||||
screen_height = 0.9
|
||||
|
||||
min_x = -screen_width + actual_width
|
||||
max_x = screen_width - actual_width
|
||||
min_z = -screen_height + actual_height
|
||||
max_z = screen_height - actual_height
|
||||
|
||||
#获取当前位置
|
||||
current_pos = gui_element.getPos()
|
||||
x = new_x if new_x is not None else current_pos.getX()
|
||||
z = new_z if new_z is not None else current_pos.getZ()
|
||||
|
||||
#应用边界限制
|
||||
x = max(min_x,min(max_x,x))
|
||||
z = max(min_z,min(max_z,z))
|
||||
|
||||
return Vec3(x,current_pos.getY(),z)
|
||||
except Exception as e:
|
||||
print(f"约束2D位置时出错: {e}")
|
||||
# 出错时返回原始值
|
||||
current_pos = gui_element.getPos()
|
||||
x = new_x if new_x is not None else current_pos.getX()
|
||||
z = new_z if new_z is not None else current_pos.getZ()
|
||||
return Vec3(x, current_pos.getY(), z)
|
||||
|
||||
def createGUI3DText(self, pos=(0, 0, 0), text="3D文本", size=0.5):
|
||||
"""创建3D空间文本 - 支持多选创建,优化版本"""
|
||||
try:
|
||||
@ -680,6 +654,141 @@ class GUIManager:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def createGUI3DImage(self, pos=(0, 0, 0), image_path=None, size=1.0):
|
||||
"""创建3D空间图片"""
|
||||
try:
|
||||
from panda3d.core import TextNode
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
print(f"📄 开始创建3D文本,位置: {pos}, 3D图片位置: {image_path}, 尺寸: {size}")
|
||||
|
||||
# 获取树形控件
|
||||
tree_widget = self._get_tree_widget()
|
||||
if not tree_widget:
|
||||
print("❌ 无法访问树形控件")
|
||||
return None
|
||||
|
||||
# 获取目标父节点列表
|
||||
target_parents = tree_widget.get_target_parents_for_creation()
|
||||
if not target_parents:
|
||||
print("❌ 没有找到有效的父节点")
|
||||
return None
|
||||
|
||||
created_3dimage = []
|
||||
|
||||
# 为每个有效的父节点创建3D文本
|
||||
for parent_item, parent_node in target_parents:
|
||||
try:
|
||||
# 生成唯一名称
|
||||
image_name = f"GUI3DImage_{len(self.gui_elements)}"
|
||||
|
||||
# 参数类型检查和转换
|
||||
if isinstance(size, (list, tuple)):
|
||||
if len(size) >= 2:
|
||||
x_size, y_size = float(size[0]), float(size[1])
|
||||
else:
|
||||
x_size = y_size = float(size[0]) if size else 1.0
|
||||
else:
|
||||
x_size = y_size = float(size)
|
||||
|
||||
# 创建卡片
|
||||
cm = CardMaker('gui_3d_image')
|
||||
cm.setFrame(-x_size / 2, x_size / 2, -y_size / 2, y_size / 2)
|
||||
|
||||
# 创建3D图像节点
|
||||
image_node = self.world.render.attachNewNode(cm.generate())
|
||||
image_node.setPos(*pos)
|
||||
|
||||
# 为3D图像创建独立的材质
|
||||
material = Material(f"image-material-{len(self.gui_elements)}")
|
||||
material.setBaseColor(LColor(1, 1, 1, 1))
|
||||
material.setDiffuse(LColor(1, 1, 1, 1))
|
||||
material.setAmbient(LColor(0.5, 0.5, 0.5, 1))
|
||||
material.setSpecular(LColor(0.1, 0.1, 0.1, 1.0))
|
||||
material.setShininess(10.0)
|
||||
material.setEmission(LColor(0, 0, 0, 1)) # 无自发光
|
||||
image_node.setMaterial(material, 1)
|
||||
|
||||
image_node.setTransparency(TransparencyAttrib.MAlpha)
|
||||
|
||||
# 如果提供了图像路径,则加载纹理
|
||||
if image_path:
|
||||
self.update3DImageTexture(image_node, image_path)
|
||||
|
||||
# 应用PBR效果(如果可用)
|
||||
try:
|
||||
if hasattr(self, 'render_pipeline') and self.render_pipeline:
|
||||
self.render_pipeline.set_effect(
|
||||
image_node,
|
||||
"effects/default.yaml",
|
||||
{
|
||||
"normal_mapping": True,
|
||||
"render_gbuffer": True,
|
||||
"alpha_testing": False,
|
||||
"parallax_mapping": False,
|
||||
"render_shadow": False,
|
||||
"render_envmap": True,
|
||||
"disable_children_effects": True
|
||||
},
|
||||
50
|
||||
)
|
||||
print("✓ GUI 3D图像PBR效果已应用")
|
||||
except Exception as e:
|
||||
print(f"⚠️ GUI 3D图像PBR效果应用失败: {e}")
|
||||
image_node.setName(image_name)
|
||||
|
||||
# 设置节点标签
|
||||
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("is_gui_element", "1")
|
||||
image_node.setTag("is_scene_element", "1")
|
||||
image_node.setTag("created_by_user", "1")
|
||||
|
||||
# 添加到GUI元素列表
|
||||
self.gui_elements.append(image_node)
|
||||
|
||||
print(f"✅ 为 {parent_item.text(0)} 创建3D文本成功: {image_name}")
|
||||
|
||||
# 在Qt树形控件中添加对应节点
|
||||
qt_item = tree_widget.add_node_to_tree_widget(image_node, parent_item, "GUI_3DIMAGE")
|
||||
if qt_item:
|
||||
created_3dimage.append((image_node, qt_item))
|
||||
else:
|
||||
created_3dimage.append((image_node, None))
|
||||
print("⚠️ Qt树节点添加失败,但GUI对象已创建")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 为 {parent_item.text(0)} 创建3D文本失败: {str(e)}")
|
||||
continue
|
||||
|
||||
# 处理创建结果
|
||||
if not created_3dimage:
|
||||
print("❌ 没有成功创建任何3D文本")
|
||||
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)
|
||||
|
||||
print(f"🎉 总共创建了 {len(created_3dimage)} 个3D文本")
|
||||
|
||||
# 返回值处理
|
||||
if len(created_3dimage) == 1:
|
||||
return created_3dimage[0][0]
|
||||
else:
|
||||
return [text_np for text_np, _ in created_3dimage]
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 创建3D文本过程失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def createGUIVirtualScreen(self, pos=(0, 0, 0), size=(2, 1), text="虚拟屏幕"):
|
||||
"""创建3D虚拟屏幕 - 支持多选创建,优化版本"""
|
||||
try:
|
||||
|
||||
@ -282,9 +282,6 @@ class InterfaceManager:
|
||||
terrain_item.setData(0,Qt.UserRole+1,"terrain") # 标记为地形节点
|
||||
addNodeToTree(terrain_node,terrain_item,force=True)
|
||||
|
||||
|
||||
|
||||
|
||||
# 展开所有节点
|
||||
#self.treeWidget.expandAll()
|
||||
self._restore_expanded()
|
||||
|
||||
@ -41,6 +41,11 @@ class MainWindow(QMainWindow):
|
||||
self.updateTimer.timeout.connect(self.updateScriptPanel)
|
||||
self.updateTimer.start(500) # 每500毫秒更新一次
|
||||
|
||||
self.toolbarDragging = False
|
||||
self.dragStartPos = QPoint(0, 0)
|
||||
self.toolbarStartPos = QPoint(0, 0)
|
||||
|
||||
|
||||
def setupCenterWidget(self):
|
||||
"""设置窗口基本属性"""
|
||||
self.setWindowTitle("引擎编辑器")
|
||||
@ -178,21 +183,27 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def toolbarMouseMoveEvent(self, event):
|
||||
"""工具栏鼠标移动事件"""
|
||||
if self.toolbarDragging and event.buttons() == Qt.LeftButton:
|
||||
# 计算新位置
|
||||
delta = event.globalPos() - self.dragStartPos
|
||||
new_pos = self.toolbarStartPos + delta
|
||||
try:
|
||||
if self.toolbarDragging and event.buttons() == Qt.LeftButton:
|
||||
# 计算新位置
|
||||
delta = event.globalPos() - self.dragStartPos
|
||||
new_pos = self.toolbarStartPos + delta
|
||||
|
||||
# 边界检测
|
||||
panda_rect = self.pandaWidget.geometry()
|
||||
toolbar_size = self.embeddedToolbar.size()
|
||||
# 边界检测
|
||||
panda_rect = self.pandaWidget.geometry()
|
||||
toolbar_size = self.embeddedToolbar.size()
|
||||
|
||||
# 限制在Panda3D区域内
|
||||
new_pos.setX(max(0, min(new_pos.x(), panda_rect.width() - toolbar_size.width())))
|
||||
new_pos.setY(max(0, min(new_pos.y(), panda_rect.height() - toolbar_size.height())))
|
||||
# 限制在Panda3D区域内
|
||||
new_pos.setX(max(0, min(new_pos.x(), panda_rect.width() - toolbar_size.width())))
|
||||
new_pos.setY(max(0, min(new_pos.y(), panda_rect.height() - toolbar_size.height())))
|
||||
|
||||
self.embeddedToolbar.move(new_pos)
|
||||
event.accept()
|
||||
except Exception as e:
|
||||
print(f"工具栏鼠标移动事件出错")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
self.embeddedToolbar.move(new_pos)
|
||||
event.accept()
|
||||
|
||||
def toolbarMouseReleaseEvent(self, event):
|
||||
"""工具栏鼠标释放事件"""
|
||||
@ -303,26 +314,12 @@ class MainWindow(QMainWindow):
|
||||
self.createMenu = menubar.addMenu('创建')
|
||||
self.setupCreateMenuActions() # 统一创建菜单动作
|
||||
|
||||
self.createGUIaddMenu = self.createMenu.addMenu('GUI')
|
||||
self.createButtonAction = self.createGUIaddMenu.addAction('创建按钮')
|
||||
self.createLabelAction = self.createGUIaddMenu.addAction('创建标签')
|
||||
self.createEntryAction = self.createGUIaddMenu.addAction('创建输入框')
|
||||
self.createGUIaddMenu.addSeparator()
|
||||
self.createVirtualScreenAction = self.createGUIaddMenu.addAction('创建虚拟屏幕')
|
||||
self.createCesiumViewAction = self.createGUIaddMenu.addAction('创建Cesium地图')
|
||||
self.toggleCesiumViewAction = self.createGUIaddMenu.addAction('开关地图')
|
||||
self.refreshCesiumViewAction = self.createGUIaddMenu.addAction('刷新地图')
|
||||
|
||||
self.createLightaddMenu = self.createMenu.addMenu('光源')
|
||||
self.createSpotLightAction = self.createLightaddMenu.addAction('聚光灯')
|
||||
self.createPointLightAction = self.createLightaddMenu.addAction('点光源')
|
||||
|
||||
#添加地形菜单
|
||||
self.createTerrainMenu = self.createMenu.addMenu('地形')
|
||||
self.createFlatTerrainAction = self.createTerrainMenu.addAction('创建平面地形')
|
||||
self.createHeightmapTerrainAction = self.createTerrainMenu.addAction('从高度图创建地形')
|
||||
self.createTerrainMenu.addSeparator()
|
||||
self.terrainEditModeAction = self.createTerrainMenu.addAction('地形编辑模式')
|
||||
# self.terrainEditModeAction = self.createTerrainMenu.addAction('地形编辑模式')
|
||||
|
||||
# GUI菜单
|
||||
# GUI菜单 - 复用创建菜单的动作
|
||||
@ -383,12 +380,14 @@ class MainWindow(QMainWindow):
|
||||
# 3D GUI子菜单
|
||||
self.create3dGUIaddMenu = self.createMenu.addMenu('3D GUI')
|
||||
self.create3DTextAction = self.create3dGUIaddMenu.addAction('3D文本')
|
||||
self.create3DImageAction = self.create3dGUIaddMenu.addAction('3D图片')
|
||||
|
||||
# GUI子菜单
|
||||
self.createGUIaddMenu = self.createMenu.addMenu('GUI')
|
||||
self.createButtonAction = self.createGUIaddMenu.addAction('创建按钮')
|
||||
self.createLabelAction = self.createGUIaddMenu.addAction('创建标签')
|
||||
self.createEntryAction = self.createGUIaddMenu.addAction('创建输入框')
|
||||
self.createImageAction = self.createGUIaddMenu.addAction('创建图片')
|
||||
self.createGUIaddMenu.addSeparator()
|
||||
self.createVideoScreen = self.createGUIaddMenu.addAction('创建视频屏幕')
|
||||
self.createSphericalVideo = self.createGUIaddMenu.addAction('创建球形视频')
|
||||
@ -408,12 +407,14 @@ class MainWindow(QMainWindow):
|
||||
# 连接到world对象的创建方法
|
||||
# self.createEnptyaddAction.triggered.connect(self.world.createEmptyObject)
|
||||
self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
|
||||
self.create3DImageAction.triggered.connect(lambda: self.world.createGUI3DImage())
|
||||
self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
|
||||
self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
|
||||
self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
|
||||
self.createImageAction.triggered.connect(lambda: self.world.createGUI2DImage())
|
||||
# self.createVideoScreen.triggered.connect(self.world.createVideoScreen)
|
||||
# self.createSphericalVideo.triggered.connect(self.world.createSphericalVideo)
|
||||
# self.createVirtualScreenAction.triggered.connect(self.world.createVirtualScreen)
|
||||
self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
|
||||
self.createSpotLightAction.triggered.connect(lambda :self.world.createSpotLight())
|
||||
self.createPointLightAction.triggered.connect(lambda :self.world.createPointLight())
|
||||
|
||||
@ -430,9 +431,11 @@ class MainWindow(QMainWindow):
|
||||
return {
|
||||
'createEmpty': self.createEnptyaddAction,
|
||||
'create3DText': self.create3DTextAction,
|
||||
'create3DImage': self.create3DImageAction,
|
||||
'createButton': self.createButtonAction,
|
||||
'createLabel': self.createLabelAction,
|
||||
'createEntry': self.createEntryAction,
|
||||
'createImage': self.createImageAction,
|
||||
'createVideoScreen': self.createVideoScreen,
|
||||
'createSphericalVideo': self.createSphericalVideo,
|
||||
'createVirtualScreen': self.createVirtualScreenAction,
|
||||
@ -785,13 +788,13 @@ class MainWindow(QMainWindow):
|
||||
self.guiEditModeAction.triggered.connect(lambda: self.world.toggleGUIEditMode())
|
||||
|
||||
# 连接创建事件 - 使用菜单动作而不是不存在的工具栏按钮
|
||||
self.createSpotLightAction.triggered.connect(lambda: self.world.createSpotLight())
|
||||
self.createPointLightAction.triggered.connect(lambda: self.world.createPointLight())
|
||||
self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
|
||||
self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
|
||||
self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
|
||||
self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
|
||||
self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
|
||||
# self.createSpotLightAction.triggered.connect(lambda: self.world.createSpotLight())
|
||||
# self.createPointLightAction.triggered.connect(lambda: self.world.createPointLight())
|
||||
# self.createButtonAction.triggered.connect(lambda: self.world.createGUIButton())
|
||||
# self.createLabelAction.triggered.connect(lambda: self.world.createGUILabel())
|
||||
# self.createEntryAction.triggered.connect(lambda: self.world.createGUIEntry())
|
||||
# self.create3DTextAction.triggered.connect(lambda: self.world.createGUI3DText())
|
||||
# self.createVirtualScreenAction.triggered.connect(lambda: self.world.createGUIVirtualScreen())
|
||||
self.createCesiumViewAction.triggered.connect(self.onCreateCesiumView)
|
||||
self.toggleCesiumViewAction.triggered.connect(self.onToggleCesiumView)
|
||||
self.refreshCesiumViewAction.triggered.connect(self.onRefreshCesiumView)
|
||||
@ -799,7 +802,7 @@ class MainWindow(QMainWindow):
|
||||
# 连接地形创建事件
|
||||
self.createFlatTerrainAction.triggered.connect(self.onCreateFlatTerrain)
|
||||
self.createHeightmapTerrainAction.triggered.connect(self.onCreateHeightmapTerrain)
|
||||
self.terrainEditModeAction.triggered.connect(self.onTerrainEditMode)
|
||||
# self.terrainEditModeAction.triggered.connect(self.onTerrainEditMode)
|
||||
|
||||
# 连接树节点点击信号
|
||||
self.treeWidget.itemSelectionChanged.connect(
|
||||
|
||||
@ -1553,6 +1553,7 @@ class PropertyPanelManager:
|
||||
)
|
||||
if file_path:
|
||||
# 应用新纹理到 3D Image
|
||||
file_path = util.normalize_model_path(file_path)
|
||||
success = self.world.gui_manager.update3DImageTexture(gui_element, file_path)
|
||||
if success:
|
||||
# 保存路径到 Tag
|
||||
@ -1560,7 +1561,7 @@ class PropertyPanelManager:
|
||||
# 更新显示
|
||||
texture_label.setText(file_path)
|
||||
# 可选:刷新场景树或其他 UI
|
||||
self.world.scene_manager.updateSceneTree()
|
||||
# self.world.scene_manager.updateSceneTree()
|
||||
|
||||
select_texture_button.clicked.connect(onSelectTexture)
|
||||
|
||||
@ -1595,6 +1596,7 @@ class PropertyPanelManager:
|
||||
)
|
||||
if file_path:
|
||||
# 应用新纹理到 2D Image
|
||||
file_path = util.normalize_model_path(file_path)
|
||||
success = self.update2DImageTexture(gui_element, file_path)
|
||||
if success:
|
||||
# 保存路径到 Tag
|
||||
@ -1603,7 +1605,7 @@ class PropertyPanelManager:
|
||||
# 更新显示
|
||||
#texture_label.setText(file_path)
|
||||
# 可选:刷新场景树或其他 UI
|
||||
self.world.scene_manager.updateSceneTree()
|
||||
# self.world.scene_manager.updateSceneTree()
|
||||
|
||||
select_texture_button.clicked.connect(onSelect2DTexture)
|
||||
|
||||
|
||||
@ -1302,11 +1302,40 @@ class CustomTreeWidget(QTreeWidget):
|
||||
parent = wrapinstance(0, QWidget)
|
||||
super().__init__(parent)
|
||||
self.world = world
|
||||
self.initData()
|
||||
self.setupUI() # 初始化界面
|
||||
self.setupContextMenu() # 初始化右键菜单
|
||||
|
||||
self.setupDragDrop() # 设置拖拽功能
|
||||
|
||||
def initData(self):
|
||||
"""初始化变量"""
|
||||
# 定义2D GUI元素类型
|
||||
self.gui_2d_types = {
|
||||
"GUI_BUTTON", # DirectButton
|
||||
"GUI_LABEL", # DirectLabel
|
||||
"GUI_ENTRY", # DirectEntry
|
||||
"GUI_IMAGE",
|
||||
"GUI_NODE" # 其他2D GUI容器
|
||||
}
|
||||
|
||||
# 定义3D GUI元素类型
|
||||
self.gui_3d_types = {
|
||||
"GUI_3DTEXT", # 3D TextNode
|
||||
"GUI_3DIMAGE",
|
||||
"GUI_VIRTUAL_SCREEN" # Virtual Screen
|
||||
}
|
||||
|
||||
# 定义3D场景节点类型(可以接受3D GUI元素和其他3D场景元素)
|
||||
self.scene_3d_types = {
|
||||
"SCENE_ROOT",
|
||||
"SCENE_NODE",
|
||||
"LIGHT_NODE",
|
||||
"CAMERA_NODE",
|
||||
"IMPORTED_MODEL_NODE",
|
||||
"MODEL_NODE"
|
||||
}
|
||||
|
||||
def setupUI(self):
|
||||
"""初始化UI设置"""
|
||||
self.setHeaderHidden(True)
|
||||
@ -1356,12 +1385,14 @@ class CustomTreeWidget(QTreeWidget):
|
||||
# 3D GUI菜单
|
||||
create3dGUIMenu = createMenu.addMenu('3D GUI')
|
||||
create3dGUIMenu.addAction(create_actions['create3DText'])
|
||||
create3dGUIMenu.addAction(create_actions['create3DImage'])
|
||||
|
||||
# GUI菜单
|
||||
createGUIMenu = createMenu.addMenu('GUI')
|
||||
createGUIMenu.addAction(create_actions['createButton'])
|
||||
createGUIMenu.addAction(create_actions['createLabel'])
|
||||
createGUIMenu.addAction(create_actions['createEntry'])
|
||||
createGUIMenu.addAction(create_actions['createImage'])
|
||||
createGUIMenu.addSeparator()
|
||||
createGUIMenu.addAction(create_actions['createVideoScreen'])
|
||||
createGUIMenu.addAction(create_actions['createSphericalVideo'])
|
||||
@ -1455,7 +1486,7 @@ class CustomTreeWidget(QTreeWidget):
|
||||
|
||||
# 检查是否是2D GUI元素
|
||||
dragged_type = dragged_item.data(0, Qt.UserRole + 1)
|
||||
is_2d_gui = dragged_type in {"GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_NODE"}
|
||||
is_2d_gui = dragged_type in self.gui_2d_types
|
||||
|
||||
# 重新父化到新的父节点
|
||||
if new_parent_node:
|
||||
@ -1595,28 +1626,13 @@ class CustomTreeWidget(QTreeWidget):
|
||||
target_type = target_item.data(0, Qt.UserRole + 1)
|
||||
|
||||
# 定义2D GUI元素类型
|
||||
gui_2d_types = {
|
||||
"GUI_BUTTON", # DirectButton
|
||||
"GUI_LABEL", # DirectLabel
|
||||
"GUI_ENTRY", # DirectEntry
|
||||
"GUI_NODE" # 其他2D GUI容器
|
||||
}
|
||||
gui_2d_types = self.gui_2d_types
|
||||
|
||||
# 定义3D GUI元素类型
|
||||
gui_3d_types = {
|
||||
"GUI_3DTEXT", # 3D TextNode
|
||||
"GUI_VIRTUAL_SCREEN" # Virtual Screen
|
||||
}
|
||||
gui_3d_types = self.gui_3d_types
|
||||
|
||||
# 定义3D场景节点类型(可以接受3D GUI元素和其他3D场景元素)
|
||||
scene_3d_types = {
|
||||
"SCENE_ROOT",
|
||||
"SCENE_NODE",
|
||||
"LIGHT_NODE",
|
||||
"CAMERA_NODE",
|
||||
"IMPORTED_MODEL_NODE",
|
||||
"MODEL_NODE"
|
||||
}
|
||||
scene_3d_types = self.scene_3d_types
|
||||
|
||||
# 检查拖拽元素的类型
|
||||
is_dragged_2d_gui = dragged_type in gui_2d_types
|
||||
@ -1704,24 +1720,6 @@ class CustomTreeWidget(QTreeWidget):
|
||||
# 出错时采用保守策略,禁止拖拽
|
||||
return False
|
||||
|
||||
def _getNodeTypeDescription(self, node_type):
|
||||
"""获取节点类型的描述文本(用于错误提示)"""
|
||||
type_descriptions = {
|
||||
"GUI_BUTTON": "2D按钮",
|
||||
"GUI_LABEL": "2D标签",
|
||||
"GUI_ENTRY": "2D输入框",
|
||||
"GUI_NODE": "2D GUI容器",
|
||||
"GUI_3DTEXT": "3D文本",
|
||||
"GUI_VIRTUAL_SCREEN": "虚拟屏幕",
|
||||
"SCENE_ROOT": "场景根节点",
|
||||
"SCENE_NODE": "场景节点",
|
||||
"LIGHT_NODE": "灯光节点",
|
||||
"CAMERA_NODE": "相机节点",
|
||||
"IMPORTED_MODEL_NODE": "导入模型",
|
||||
"MODEL_NODE": "模型节点"
|
||||
}
|
||||
return type_descriptions.get(node_type, f"未知类型({node_type})")
|
||||
|
||||
def _getRootNode(self, item):
|
||||
"""获取树中节点的根节点项"""
|
||||
if not item:
|
||||
@ -2018,10 +2016,7 @@ class CustomTreeWidget(QTreeWidget):
|
||||
node_type = item.data(0, Qt.UserRole + 1)
|
||||
|
||||
# 场景根节点和普通场景节点可以作为父节点
|
||||
if node_type in ["SCENE_ROOT",
|
||||
"SCENE_NODE", "LIGHT_NODE", "CAMERA_NODE",
|
||||
"IMPORTED_MODEL_NODE",
|
||||
"GUI_3DTEXT"]:
|
||||
if node_type in self.gui_3d_types and self.scene_3d_types:
|
||||
return True
|
||||
|
||||
# # 模型节点也可以作为父节点
|
||||
@ -2095,7 +2090,7 @@ class CustomTreeWidget(QTreeWidget):
|
||||
node_type = item.data(0, Qt.UserRole + 1)
|
||||
|
||||
# GUI元素可以作为其他GUI元素的父节点
|
||||
if node_type in ["GUI_BUTTON", "GUI_LABEL", "GUI_ENTRY", "GUI_NODE"]:
|
||||
if node_type in self.gui_2d_types:
|
||||
return True
|
||||
|
||||
# 场景根节点和普通场景节点也可以作为父节点
|
||||
|
||||
Loading…
Reference in New Issue
Block a user