172 lines
5.9 KiB
Python
172 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
from direct.showbase.ShowBase import ShowBase
|
|
from panda3d.core import (
|
|
Point3, Point2, Vec3, CardMaker, CollisionNode, CollisionSphere,
|
|
BitMask32, PerspectiveLens
|
|
)
|
|
|
|
class ProjectionPickingTest(ShowBase):
|
|
def __init__(self):
|
|
ShowBase.__init__(self)
|
|
|
|
# 相机设置
|
|
self.cam.setPos(-3.87655, -188.38084, 82.602684)
|
|
self.cam.lookAt(0, 0, 0)
|
|
|
|
print("=== 屏幕投影点击检测测试 ===")
|
|
|
|
# 创建测试对象
|
|
self.createTestObjects()
|
|
|
|
# 测试投影检测
|
|
self.testProjectionPicking()
|
|
|
|
def createTestObjects(self):
|
|
"""创建测试对象"""
|
|
self.objects = []
|
|
|
|
# 创建几个测试立方体
|
|
positions = [
|
|
(0, 10, 3, "中心立方体"),
|
|
(-5, 10, 3, "左侧立方体"),
|
|
(5, 10, 3, "右侧立方体"),
|
|
(0, 10, 8, "上方立方体"),
|
|
(0, 15, 3, "远处立方体")
|
|
]
|
|
|
|
colors = [
|
|
(1, 0, 0, 1), # 红色
|
|
(0, 1, 0, 1), # 绿色
|
|
(0, 0, 1, 1), # 蓝色
|
|
(1, 1, 0, 1), # 黄色
|
|
(1, 0, 1, 1), # 紫色
|
|
]
|
|
|
|
for i, ((x, y, z, name), color) in enumerate(zip(positions, colors)):
|
|
# 创建立方体
|
|
cm = CardMaker(f"cube_{i}")
|
|
cm.setFrame(-1, 1, -1, 1)
|
|
cube = self.render.attachNewNode(cm.generate())
|
|
cube.setPos(x, y, z)
|
|
cube.setScale(2, 2, 2)
|
|
cube.setColor(*color)
|
|
cube.setBillboardAxis()
|
|
cube.setName(name)
|
|
|
|
# 存储对象信息
|
|
obj_info = {
|
|
'node': cube,
|
|
'name': name,
|
|
'world_pos': Point3(x, y, z),
|
|
'radius': 2.0 # 立方体的半径
|
|
}
|
|
self.objects.append(obj_info)
|
|
|
|
print(f"创建 {name} 在位置 ({x}, {y}, {z})")
|
|
|
|
def worldToScreen(self, worldPos):
|
|
"""将世界坐标转换为屏幕坐标"""
|
|
# 获取相机的投影矩阵和模型视图矩阵
|
|
lens = self.cam.node().getLens()
|
|
|
|
# 将世界坐标转换到相机坐标系
|
|
camPos = self.cam.getRelativePoint(self.render, worldPos)
|
|
|
|
# 使用镜头投影到屏幕坐标
|
|
screenPoint = Point2()
|
|
if lens.project(camPos, screenPoint):
|
|
# 转换为窗口像素坐标
|
|
winWidth = self.win.getXSize()
|
|
winHeight = self.win.getYSize()
|
|
|
|
# 归一化坐标转换为像素坐标
|
|
pixelX = (screenPoint.getX() + 1.0) * winWidth * 0.5
|
|
pixelY = (1.0 - screenPoint.getY()) * winHeight * 0.5
|
|
|
|
return pixelX, pixelY, True
|
|
else:
|
|
return 0, 0, False
|
|
|
|
def getObjectScreenRadius(self, obj_info):
|
|
"""计算对象在屏幕上的半径"""
|
|
worldPos = obj_info['world_pos']
|
|
worldRadius = obj_info['radius']
|
|
|
|
# 计算对象中心在屏幕上的位置
|
|
centerX, centerY, visible = self.worldToScreen(worldPos)
|
|
if not visible:
|
|
return 0
|
|
|
|
# 计算对象边缘点在屏幕上的位置
|
|
# 使用对象右侧边缘点
|
|
edgePoint = worldPos + Vec3(worldRadius, 0, 0)
|
|
edgeX, edgeY, edge_visible = self.worldToScreen(edgePoint)
|
|
|
|
if edge_visible:
|
|
# 计算屏幕半径
|
|
screenRadius = abs(edgeX - centerX)
|
|
return screenRadius
|
|
return 0
|
|
|
|
def pickObjectByProjection(self, mouseX, mouseY):
|
|
"""使用投影方法选择对象"""
|
|
print(f"\n=== 投影检测: 鼠标位置 ({mouseX}, {mouseY}) ===")
|
|
|
|
closest_obj = None
|
|
closest_distance = float('inf')
|
|
|
|
for obj_info in self.objects:
|
|
# 计算对象在屏幕上的位置
|
|
screenX, screenY, visible = self.worldToScreen(obj_info['world_pos'])
|
|
|
|
if not visible:
|
|
print(f"{obj_info['name']}: 不可见")
|
|
continue
|
|
|
|
# 计算屏幕半径
|
|
screenRadius = self.getObjectScreenRadius(obj_info)
|
|
|
|
# 计算鼠标到对象中心的距离
|
|
dx = mouseX - screenX
|
|
dy = mouseY - screenY
|
|
distance = (dx * dx + dy * dy) ** 0.5
|
|
|
|
print(f"{obj_info['name']}: 屏幕位置({screenX:.1f}, {screenY:.1f}), 半径{screenRadius:.1f}, 距离{distance:.1f}")
|
|
|
|
# 检查是否在点击范围内
|
|
if distance <= screenRadius:
|
|
if distance < closest_distance:
|
|
closest_distance = distance
|
|
closest_obj = obj_info
|
|
print(f" -> 在点击范围内!")
|
|
|
|
if closest_obj:
|
|
print(f"\n选中对象: {closest_obj['name']}")
|
|
return closest_obj
|
|
else:
|
|
print(f"\n没有选中任何对象")
|
|
return None
|
|
|
|
def testProjectionPicking(self):
|
|
"""测试投影点击检测"""
|
|
print(f"\n=== 投影点击检测测试 ===")
|
|
|
|
# 模拟不同的点击位置
|
|
test_clicks = [
|
|
(619, 362, "屏幕中心"),
|
|
(400, 362, "屏幕偏左"),
|
|
(800, 362, "屏幕偏右"),
|
|
(619, 200, "屏幕上方"),
|
|
(619, 500, "屏幕下方"),
|
|
]
|
|
|
|
for mouseX, mouseY, description in test_clicks:
|
|
print(f"\n--- 测试 {description}: ({mouseX}, {mouseY}) ---")
|
|
selected = self.pickObjectByProjection(mouseX, mouseY)
|
|
|
|
if __name__ == "__main__":
|
|
app = ProjectionPickingTest()
|
|
print("投影检测测试完成") |