forked from Rowland/EG
231 lines
9.8 KiB
Python
231 lines
9.8 KiB
Python
from panda3d.core import (Point3, Point2, CollisionTraverser, CollisionHandlerQueue,
|
||
CollisionNode, CollisionRay, GeomNode)
|
||
|
||
|
||
class EventHandler:
|
||
"""事件处理器 - 处理鼠标和键盘输入事件"""
|
||
|
||
def __init__(self, world):
|
||
"""初始化事件处理器"""
|
||
self.world = world
|
||
|
||
def mousePressEventLeft(self, evt):
|
||
"""处理鼠标左键按下事件"""
|
||
print("\n=== 开始处理鼠标左键事件 ===")
|
||
print(f"当前工具: {self.world.currentTool}")
|
||
|
||
if not evt:
|
||
print("事件为空")
|
||
return
|
||
|
||
# 获取鼠标点击的位置
|
||
x = evt.get('x', 0)
|
||
y = evt.get('y', 0)
|
||
print(f"鼠标点击位置: ({x}, {y})")
|
||
|
||
# 获取准确的窗口尺寸
|
||
winWidth, winHeight = self.world.getWindowSize()
|
||
|
||
# 直接使用 x, y 创建鼠标位置
|
||
mx = 2.0 * x / float(winWidth) - 1.0
|
||
my = 1.0 - 2.0 * y / float(winHeight)
|
||
print(f"转换后的坐标: ({mx}, {my})")
|
||
|
||
# 创建射线
|
||
nearPoint = Point3()
|
||
farPoint = Point3()
|
||
self.world.cam.node().getLens().extrude(Point2(mx, my), nearPoint, farPoint)
|
||
print(f"射线起点: {nearPoint}")
|
||
print(f"射线终点: {farPoint}")
|
||
|
||
# 进行射线检测
|
||
picker = CollisionTraverser()
|
||
queue = CollisionHandlerQueue()
|
||
|
||
pickerNode = CollisionNode('mouseRay')
|
||
pickerNP = self.world.cam.attachNewNode(pickerNode)
|
||
pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
|
||
|
||
# 使用 nearPoint 和 farPoint 创建射线
|
||
direction = farPoint - nearPoint
|
||
direction.normalize()
|
||
pickerNode.addSolid(CollisionRay(nearPoint, direction))
|
||
|
||
picker.addCollider(pickerNP, queue)
|
||
picker.traverse(self.world.render)
|
||
|
||
print(f"碰撞检测结果数量: {queue.getNumEntries()}")
|
||
|
||
if queue.getNumEntries() > 0:
|
||
# 获取最近的碰撞点
|
||
entry = queue.getEntry(0)
|
||
hitPos = entry.getSurfacePoint(self.world.render)
|
||
hitNode = entry.getIntoNodePath()
|
||
print(f"碰撞到节点: {hitNode.getName()}")
|
||
|
||
# 优先检查是否点击了坐标轴
|
||
print(f"检查坐标轴点击: 坐标轴存在={bool(self.world.selection.gizmo)}")
|
||
if self.world.selection.gizmo:
|
||
print("准备检查坐标轴点击...")
|
||
gizmoAxis = self.world.selection.checkGizmoClick(x, y)
|
||
if gizmoAxis:
|
||
print(f"✓ 检测到坐标轴点击: {gizmoAxis}")
|
||
# 开始坐标轴拖拽
|
||
self.world.selection.startGizmoDrag(gizmoAxis, x, y)
|
||
return
|
||
else:
|
||
print("× 没有点击到坐标轴")
|
||
|
||
# 处理GUI编辑模式
|
||
if self.world.guiEditMode:
|
||
# 检查是否点击了GUI元素
|
||
clickedGUI = self.world.gui_manager.findClickedGUI(hitNode)
|
||
if clickedGUI:
|
||
# 选中GUI元素
|
||
self.world.selection.updateSelection(clickedGUI)
|
||
self.world.gui_manager.selectGUIInTree(clickedGUI)
|
||
print(f"选中GUI元素: {clickedGUI.getTag('gui_text')}")
|
||
elif hasattr(self.world, 'currentGUITool') and self.world.currentGUITool:
|
||
# 在点击位置创建新GUI元素
|
||
self.world.gui_manager.createGUIAtPosition(hitPos, self.world.currentGUITool)
|
||
return
|
||
|
||
# 根据当前工具处理点击事件
|
||
if self.world.currentTool == "选择":
|
||
print("使用选择工具处理点击")
|
||
self._handleSelectionClick(hitNode)
|
||
else:
|
||
print("没有检测到碰撞")
|
||
|
||
# 如果不在GUI编辑模式,清除选择
|
||
if not self.world.guiEditMode:
|
||
self.world.selection.updateSelection(None)
|
||
|
||
# 在GUI编辑模式下,即使没有碰撞也可以在空白区域创建GUI
|
||
if (self.world.guiEditMode and
|
||
hasattr(self.world, 'currentGUITool') and
|
||
self.world.currentGUITool):
|
||
# 使用默认的地面高度创建GUI
|
||
default_height = 0.0
|
||
world_pos = Point3(mx * 10, 0, my * 10) # 简单的屏幕到世界坐标转换
|
||
world_pos.setZ(default_height)
|
||
self.world.gui_manager.createGUIAtPosition(world_pos, self.world.currentGUITool)
|
||
|
||
pickerNP.removeNode()
|
||
print("=== 鼠标左键事件处理结束 ===\n")
|
||
|
||
def _handleSelectionClick(self, hitNode):
|
||
"""处理选择工具的点击事件"""
|
||
# 查找可选择的节点(模型或其子节点)
|
||
while hitNode != self.world.render:
|
||
# 检查是否是模型或模型的子节点
|
||
isModel = hitNode in self.world.models
|
||
isChildOfModel = False
|
||
for model in self.world.models:
|
||
# 检查是否是模型的子节点
|
||
current = hitNode
|
||
while current != self.world.render:
|
||
if current == model:
|
||
isChildOfModel = True
|
||
break
|
||
current = current.getParent()
|
||
if isChildOfModel:
|
||
break
|
||
|
||
print(f"检查节点 {hitNode.getName()}: isModel={isModel}, isChildOfModel={isChildOfModel}")
|
||
|
||
if isModel or isChildOfModel:
|
||
print(f"选中节点: {hitNode.getName()}")
|
||
|
||
# 在树形控件中查找并选中对应的项
|
||
if self.world.interface_manager.treeWidget:
|
||
root = self.world.interface_manager.treeWidget.invisibleRootItem()
|
||
for i in range(root.childCount()):
|
||
sceneItem = root.child(i)
|
||
if sceneItem.text(0) == "场景":
|
||
foundItem = self.world.interface_manager.findTreeItem(hitNode, sceneItem)
|
||
if foundItem:
|
||
self.world.interface_manager.treeWidget.setCurrentItem(foundItem)
|
||
self.world.property_panel.updatePropertyPanel(foundItem)
|
||
# 更新选择状态并显示选择框
|
||
self.world.selection.updateSelection(hitNode)
|
||
break
|
||
break
|
||
hitNode = hitNode.getParent()
|
||
|
||
def mouseReleaseEventLeft(self, evt):
|
||
"""处理鼠标左键释放事件"""
|
||
# 处理坐标轴拖拽结束
|
||
if self.world.selection.isDraggingGizmo:
|
||
self.world.selection.stopGizmoDrag()
|
||
return
|
||
|
||
def wheelForward(self, data=None):
|
||
"""处理滚轮向前滚动(前进)"""
|
||
# 调用CoreWorld的父类方法
|
||
super(type(self.world), self.world).wheelForward(data)
|
||
|
||
# 更新属性面板
|
||
if (self.world.interface_manager.treeWidget and
|
||
self.world.interface_manager.treeWidget.currentItem() and
|
||
self.world.interface_manager.treeWidget.currentItem().text(0) == "相机"):
|
||
self.world.property_panel.updatePropertyPanel(
|
||
self.world.interface_manager.treeWidget.currentItem())
|
||
|
||
def wheelBackward(self, data=None):
|
||
"""处理滚轮向后滚动(后退)"""
|
||
# 调用CoreWorld的父类方法
|
||
super(type(self.world), self.world).wheelBackward(data)
|
||
|
||
# 更新属性面板
|
||
if (self.world.interface_manager.treeWidget and
|
||
self.world.interface_manager.treeWidget.currentItem() and
|
||
self.world.interface_manager.treeWidget.currentItem().text(0) == "相机"):
|
||
self.world.property_panel.updatePropertyPanel(
|
||
self.world.interface_manager.treeWidget.currentItem())
|
||
|
||
def mousePressEventMiddle(self, evt):
|
||
"""处理鼠标中键按下事件"""
|
||
# 已移除原有的Z轴拖拽功能
|
||
pass
|
||
|
||
def mouseReleaseEventMiddle(self, evt):
|
||
"""处理鼠标中键释放事件"""
|
||
# 已移除原有的Z轴拖拽功能
|
||
pass
|
||
|
||
def mouseMoveEvent(self, evt):
|
||
"""处理鼠标移动事件"""
|
||
if not evt:
|
||
return
|
||
|
||
# 处理坐标轴拖拽
|
||
if self.world.selection.isDraggingGizmo:
|
||
x = evt.get('x', 0)
|
||
y = evt.get('y', 0)
|
||
|
||
# 获取准确的窗口尺寸
|
||
winWidth, winHeight = self.world.getWindowSize()
|
||
|
||
# 将屏幕坐标转换为世界坐标
|
||
mx = 2.0 * x / float(winWidth) - 1.0
|
||
my = 1.0 - 2.0 * y / float(winHeight)
|
||
|
||
# 更新坐标轴拖拽
|
||
self.world.selection.updateGizmoDrag(x, y)
|
||
return
|
||
|
||
# 更新坐标轴高亮(鼠标悬停效果)
|
||
if self.world.selection.gizmo and not self.world.selection.isDraggingGizmo:
|
||
x = evt.get('x', 0)
|
||
y = evt.get('y', 0)
|
||
# 只在前5次调用时输出调试信息,避免刷屏
|
||
if not hasattr(self.world, '_highlight_debug_count'):
|
||
self.world._highlight_debug_count = 0
|
||
if self.world._highlight_debug_count < 5:
|
||
print(f"更新坐标轴高亮: 鼠标({x}, {y}), 坐标轴存在={bool(self.world.selection.gizmo)}")
|
||
self.world._highlight_debug_count += 1
|
||
self.world.selection.updateGizmoHighlight(x, y)
|
||
|
||
# 调用CoreWorld的父类方法处理基础的相机旋转
|
||
super(type(self.world), self.world).mouseMoveEvent(evt) |