362 lines
13 KiB
Python
362 lines
13 KiB
Python
import os
|
||
import time
|
||
|
||
|
||
class ObjectFactory:
|
||
"""Scene object and sample panel factory methods."""
|
||
|
||
def __init__(self, app):
|
||
self.app = app
|
||
|
||
def __getattr__(self, name):
|
||
return getattr(self.app, name)
|
||
|
||
def __setattr__(self, name, value):
|
||
if name == "app" or name in self.__dict__ or hasattr(type(self), name):
|
||
object.__setattr__(self, name, value)
|
||
else:
|
||
setattr(self.app, name, value)
|
||
|
||
|
||
def createEmptyObject(self):
|
||
"""创建空对象"""
|
||
try:
|
||
from panda3d.core import NodePath
|
||
# 创建一个空节点
|
||
empty_node = NodePath("EmptyObject")
|
||
empty_node.reparentTo(self.render)
|
||
empty_node.setPos(0, 0, 0)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(empty_node)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print("✓ 空对象创建成功")
|
||
return empty_node
|
||
except Exception as e:
|
||
print(f"✗ 创建空对象失败: {e}")
|
||
return None
|
||
|
||
|
||
def create3DText(self, pos=(0, 0, 0), text="3D Text", scale=1.0):
|
||
"""创建3D文本"""
|
||
try:
|
||
from panda3d.core import TextNode, NodePath
|
||
|
||
# 创建文本节点
|
||
text_node = TextNode("3DText")
|
||
text_node.setText(text)
|
||
text_node.setAlign(TextNode.ACenter)
|
||
text_node.setTextColor(1, 1, 1, 1) # 白色
|
||
|
||
# 设置中文字体
|
||
chinese_font = self._get_chinese_font()
|
||
if chinese_font:
|
||
text_node.setFont(chinese_font)
|
||
|
||
# 创建节点路径并设置位置
|
||
text_np = NodePath(text_node)
|
||
text_np.reparentTo(self.render)
|
||
text_np.setPos(pos)
|
||
text_np.setScale(scale)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(text_np)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print(f"✓ 3D文本创建成功: {text}")
|
||
return text_np
|
||
except Exception as e:
|
||
print(f"✗ 创建3D文本失败: {e}")
|
||
return None
|
||
|
||
|
||
def create3DImage(self, pos=(0, 0, 0), image_path="", size=(1, 1)):
|
||
"""创建3D图片"""
|
||
try:
|
||
from panda3d.core import CardMaker, NodePath
|
||
|
||
if not image_path or not os.path.exists(image_path):
|
||
print("✗ 图片文件不存在")
|
||
return None
|
||
|
||
# 创建卡片几何体
|
||
card_maker = CardMaker("3DImage")
|
||
card_maker.setFrame(-size[0]/2, size[0]/2, -size[1]/2, size[1]/2)
|
||
card = card_maker.generate()
|
||
|
||
# 创建节点路径
|
||
image_np = NodePath(card)
|
||
image_np.reparentTo(self.render)
|
||
image_np.setPos(pos)
|
||
|
||
# 加载纹理
|
||
from panda3d.core import Texture, Filename
|
||
tex = self.loader.loadTexture(Filename.fromOsSpecific(image_path))
|
||
if tex:
|
||
image_np.setTexture(tex, 1)
|
||
else:
|
||
print("✗ 加载纹理失败")
|
||
image_np.removeNode()
|
||
return None
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(image_np)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print(f"✓ 3D图片创建成功: {os.path.basename(image_path)}")
|
||
return image_np
|
||
except Exception as e:
|
||
print(f"✗ 创建3D图片失败: {e}")
|
||
return None
|
||
|
||
|
||
def createCube(self, pos=(0, 0, 0), size=1.0):
|
||
"""创建立方体"""
|
||
try:
|
||
# 尝试使用Panda3D的内置几何体
|
||
from panda3d.core import NodePath, GeomNode, Geom, GeomVertexFormat, GeomVertexData, GeomVertexWriter, GeomTriangles, GeomPoints
|
||
from panda3d.core import Vec3, Vec4, RenderState, ShadeModelAttrib
|
||
|
||
# 创建顶点数据格式
|
||
format = GeomVertexFormat.getV3n3cpt2()
|
||
vdata = GeomVertexData('cube', format, Geom.UHStatic)
|
||
vdata.setNumRows(24)
|
||
|
||
vertex = GeomVertexWriter(vdata, 'vertex')
|
||
normal = GeomVertexWriter(vdata, 'normal')
|
||
color = GeomVertexWriter(vdata, 'color')
|
||
|
||
# 立方体的8个顶点
|
||
s = size / 2
|
||
vertices = [
|
||
(-s, -s, -s), (s, -s, -s), (s, s, -s), (-s, s, -s), # 底面
|
||
(-s, -s, s), (s, -s, s), (s, s, s), (-s, s, s) # 顶面
|
||
]
|
||
|
||
# 立方体的6个面,每个面4个顶点
|
||
faces = [
|
||
(0, 1, 2, 3), # 底面
|
||
(4, 7, 6, 5), # 顶面
|
||
(0, 4, 5, 1), # 前面
|
||
(2, 6, 7, 3), # 后面
|
||
(0, 3, 7, 4), # 左面
|
||
(1, 5, 6, 2) # 右面
|
||
]
|
||
|
||
# 法线
|
||
normals = [
|
||
(0, 0, -1), (0, 0, 1), (0, -1, 0), (0, 1, 0), (-1, 0, 0), (1, 0, 0)
|
||
]
|
||
|
||
# 添加顶点数据
|
||
for face_idx, face in enumerate(faces):
|
||
n = normals[face_idx]
|
||
for vertex_idx in face:
|
||
v = vertices[vertex_idx]
|
||
vertex.addData3f(*v)
|
||
normal.addData3f(*n)
|
||
color.addData4f(0.8, 0.8, 0.8, 1.0) # 灰色
|
||
|
||
# 创建几何体
|
||
geom = Geom(vdata)
|
||
|
||
# 添加三角形
|
||
for face_idx in range(6):
|
||
base = face_idx * 4
|
||
# 每个面分成2个三角形
|
||
tri = GeomTriangles(Geom.UHStatic)
|
||
tri.addConsecutiveVertices(base, 3)
|
||
tri.closePrimitive()
|
||
|
||
tri2 = GeomTriangles(Geom.UHStatic)
|
||
tri2.addVertices(base + 2, base + 3, base)
|
||
tri2.closePrimitive()
|
||
|
||
geom.addPrimitive(tri)
|
||
geom.addPrimitive(tri2)
|
||
|
||
# 创建节点
|
||
geom_node = GeomNode('cube')
|
||
geom_node.addGeom(geom)
|
||
|
||
cube = NodePath(geom_node)
|
||
cube.reparentTo(self.render)
|
||
cube.setPos(pos)
|
||
|
||
# 设置渲染状态
|
||
state = RenderState.make(ShadeModelAttrib.make(ShadeModelAttrib.MSmooth))
|
||
cube.set_state(state)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(cube)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print("✓ 立方体创建成功")
|
||
return cube
|
||
except Exception as e:
|
||
print(f"✗ 创建立方体失败: {e}")
|
||
return None
|
||
|
||
|
||
def createSphere(self, pos=(0, 0, 0), radius=1.0):
|
||
"""创建球体"""
|
||
try:
|
||
from panda3d.core import CardMaker, NodePath
|
||
|
||
# 创建一个简单的球体(使用多个卡片近似)
|
||
sphere_root = NodePath("Sphere")
|
||
|
||
# 创建球体的多个面来近似球形
|
||
num_segments = 6
|
||
for i in range(num_segments):
|
||
angle1 = (i / num_segments) * 360
|
||
|
||
# 创建球体片段
|
||
segment_maker = CardMaker(f"SphereSegment{i}")
|
||
segment_maker.setFrame(-radius, radius, -radius, radius)
|
||
segment = NodePath(segment_maker.generate())
|
||
segment.reparentTo(sphere_root)
|
||
|
||
# 设置位置和旋转来形成球体
|
||
segment.setPos(0, 0, 0)
|
||
segment.setH(angle1)
|
||
segment.setP(angle1/2) # 倾斜角度
|
||
segment.setScale(radius)
|
||
|
||
# 设置颜色
|
||
from panda3d.core import Vec4
|
||
sphere_root.setColor(Vec4(0.8, 0.6, 0.4, 1.0)) # 棕色
|
||
|
||
sphere_root.reparentTo(self.render)
|
||
sphere_root.setPos(pos)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(sphere_root)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print("✓ 球体创建成功")
|
||
return sphere_root
|
||
except Exception as e:
|
||
print(f"✗ 创建球体失败: {e}")
|
||
return None
|
||
|
||
|
||
def createCylinder(self, pos=(0, 0, 0), radius=1.0, height=2.0):
|
||
"""创建圆柱体"""
|
||
try:
|
||
import math
|
||
from panda3d.core import CardMaker, NodePath
|
||
|
||
# 创建圆柱体
|
||
cylinder_root = NodePath("Cylinder")
|
||
|
||
# 创建圆柱体的多个侧面
|
||
num_segments = 12
|
||
for i in range(num_segments):
|
||
angle1 = (i / num_segments) * 360
|
||
angle2 = ((i + 1) / num_segments) * 360
|
||
|
||
# 计算圆柱体侧面的位置
|
||
x1 = radius * math.cos(math.radians(angle1))
|
||
y1 = radius * math.sin(math.radians(angle1))
|
||
|
||
# 创建圆柱体侧面
|
||
segment_maker = CardMaker(f"CylinderSegment{i}")
|
||
segment = NodePath(segment_maker.generate())
|
||
segment.reparentTo(cylinder_root)
|
||
|
||
# 设置位置和旋转
|
||
segment.setPos(x1, y1, 0)
|
||
segment.setH(angle1)
|
||
segment.setScale(0.5, height, 1) # 调整宽度和高度
|
||
|
||
# 设置颜色
|
||
from panda3d.core import Vec4
|
||
cylinder_root.setColor(Vec4(0.4, 0.8, 0.4, 1.0)) # 绿色
|
||
|
||
cylinder_root.reparentTo(self.render)
|
||
cylinder_root.setPos(pos)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(cylinder_root)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print("✓ 圆柱体创建成功")
|
||
return cylinder_root
|
||
except Exception as e:
|
||
print(f"✗ 创建圆柱体失败: {e}")
|
||
return None
|
||
|
||
|
||
def createPlane(self, pos=(0, 0, 0), size=(1, 1)):
|
||
"""创建平面"""
|
||
try:
|
||
from panda3d.core import CardMaker, NodePath
|
||
|
||
# 创建平面
|
||
card_maker = CardMaker("Plane")
|
||
card_maker.setFrame(-size[0]/2, size[0]/2, -size[1]/2, size[1]/2)
|
||
plane = NodePath(card_maker.generate())
|
||
|
||
plane.reparentTo(self.render)
|
||
plane.setPos(pos)
|
||
|
||
# 添加到场景管理器
|
||
if hasattr(self, 'scene_manager') and self.scene_manager:
|
||
self.scene_manager.models.append(plane)
|
||
|
||
# 更新场景树
|
||
if hasattr(self, 'updateSceneTree'):
|
||
self.updateSceneTree()
|
||
|
||
print("✓ 平面创建成功")
|
||
return plane
|
||
except Exception as e:
|
||
print(f"✗ 创建平面失败: {e}")
|
||
return None
|
||
|
||
def createWebPanel(self, url="https://www.example.com"):
|
||
"""创建Web面板"""
|
||
try:
|
||
if hasattr(self, 'info_panel_manager') and self.info_panel_manager:
|
||
panel_id = f"web_panel_{int(time.time())}"
|
||
result = self.info_panel_manager.createHTTPInfoPanel(
|
||
panel_id=panel_id,
|
||
url=url,
|
||
position=(0.8, 0.0),
|
||
size=(0.35, 0.4),
|
||
update_interval=5.0 # 每5秒更新一次
|
||
)
|
||
return result
|
||
return None
|
||
except Exception as e:
|
||
print(f"创建Web面板失败: {e}")
|
||
return None
|
||
|
||
# ==================== GUI创建方法 ====================
|