77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
from direct.showbase.ShowBase import ShowBase
|
|
from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue, GeomNode, CollisionRay, NodePath, LPoint3f, LVector3f
|
|
|
|
|
|
class PickerRay():
|
|
def __init__(self, root: ShowBase, pick_root: NodePath = None):
|
|
super().__init__()
|
|
self.root: ShowBase = root
|
|
self.pick_root = pick_root or root.render
|
|
self.debug = False
|
|
|
|
def __setup_picker(self):
|
|
# 创建一个拾取器
|
|
self.picker = CollisionTraverser()
|
|
self.pq = CollisionHandlerQueue()
|
|
|
|
# 创建一个拾取节点
|
|
self.picker_node = CollisionNode('mouseRay')
|
|
self.picker_np = self.root.cam.attachNewNode(self.picker_node)
|
|
self.picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask())
|
|
|
|
# 创建射线
|
|
self.picker_ray = CollisionRay()
|
|
self.picker_node.addSolid(self.picker_ray)
|
|
|
|
# 将拾取器和处理器关联
|
|
self.picker.addCollider(self.picker_np, self.pq)
|
|
|
|
# 可选:显示拾取射线(调试用)
|
|
# self.picker_np.show()
|
|
|
|
def debug_log(self, msg: str) -> None:
|
|
if self.debug:
|
|
print(f"[ScaleGizmo] -> {msg}")
|
|
|
|
def pick_object(self) -> tuple[NodePath | None, LVector3f | None]:
|
|
"""根据屏幕坐标拾取3D物体"""
|
|
if not hasattr(self, 'picker'):
|
|
self.__setup_picker()
|
|
|
|
mpos = self.root.mouseWatcherNode.getMouse()
|
|
screen_x = mpos.x
|
|
screen_y = mpos.y
|
|
self.debug_log(f"屏幕坐标: {screen_x}, {screen_y}")
|
|
|
|
# 从摄像机发射射线
|
|
self.picker_ray.setFromLens(self.root.camNode, screen_x, screen_y)
|
|
|
|
# 执行碰撞检测
|
|
self.picker.traverse(self.pick_root)
|
|
|
|
picked_object = None
|
|
collision_point = LPoint3f(0, 0, 0)
|
|
|
|
# 检查是否有碰撞
|
|
if self.pq.getNumEntries() > 0:
|
|
# 按距离排序,最近的在前面
|
|
self.pq.sortEntries()
|
|
|
|
for i in range(self.pq.getNumEntries()):
|
|
# 获取最近的碰撞点
|
|
closest_entry = self.pq.getEntry(i)
|
|
picked_object: NodePath = closest_entry.getIntoNodePath()
|
|
|
|
if picked_object.isHidden():
|
|
continue
|
|
else:
|
|
# 获取碰撞点的世界坐标
|
|
collision_point = closest_entry.getSurfacePoint(self.root.render)
|
|
|
|
self.debug_log(f"拾取到物体: {picked_object.getName()}")
|
|
self.debug_log(f"碰撞点: {collision_point}")
|
|
break
|
|
return picked_object, collision_point
|
|
|
|
self.debug_log("未拾取到任何物体")
|
|
return None, None |