177 lines
6.7 KiB
Python
177 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
from direct.showbase.ShowBase import ShowBase
|
|
from panda3d.core import (
|
|
Point3, Point2, Vec3, CollisionTraverser, CollisionHandlerQueue,
|
|
CollisionNode, CollisionRay, CollisionSphere, BitMask32, CardMaker
|
|
)
|
|
|
|
class RayTest(ShowBase):
|
|
def __init__(self):
|
|
ShowBase.__init__(self)
|
|
|
|
# 设置相机位置 - 和你的程序一样
|
|
self.cam.setPos(-3.87655, -188.38084, 82.602684)
|
|
self.cam.lookAt(0, 0, 0)
|
|
|
|
print(f"相机位置: {self.cam.getPos()}")
|
|
print(f"相机朝向: {self.cam.getHpr()}")
|
|
|
|
# 创建测试立方体 - 位置和你的程序一样
|
|
self.createTestCube()
|
|
|
|
# 设置射线检测
|
|
self.setupRayPicking()
|
|
|
|
# 测试不同的射线方向
|
|
self.testRaycast()
|
|
|
|
def createTestCube(self):
|
|
"""创建测试立方体"""
|
|
# 创建简单的平面作为立方体
|
|
cm = CardMaker("TestCube")
|
|
cm.setFrame(-1, 1, -1, 1)
|
|
self.cube = self.render.attachNewNode(cm.generate())
|
|
|
|
# 设置位置和大小 - 和你的程序完全一样
|
|
self.cube.setPos(0, 10, 3)
|
|
self.cube.setScale(3, 3, 3)
|
|
self.cube.setColor(1, 0, 0, 1)
|
|
self.cube.setTwoSided(True)
|
|
self.cube.setHpr(45, 15, 0)
|
|
|
|
print(f"立方体世界位置: {self.cube.getPos(self.render)}")
|
|
print(f"立方体缩放: {self.cube.getScale()}")
|
|
|
|
# 添加碰撞检测
|
|
cNode = CollisionNode('TestCubeCollision')
|
|
cSphere = CollisionSphere(Point3(0, 0, 0), 4.0)
|
|
cNode.addSolid(cSphere)
|
|
cNode.setIntoCollideMask(BitMask32.bit(0))
|
|
cNode.setFromCollideMask(BitMask32.allOff())
|
|
|
|
self.cNodePath = self.cube.attachNewNode(cNode)
|
|
print(f"碰撞球体半径: 4.0")
|
|
|
|
# 显示碰撞体
|
|
self.cNodePath.show()
|
|
|
|
def setupRayPicking(self):
|
|
"""设置射线检测"""
|
|
self.picker = CollisionTraverser()
|
|
self.pickerQueue = CollisionHandlerQueue()
|
|
|
|
self.pickerNode = CollisionNode('mouseRay')
|
|
self.pickerRay = CollisionRay()
|
|
self.pickerNode.addSolid(self.pickerRay)
|
|
|
|
# 设置掩码
|
|
mask = BitMask32.bit(0)
|
|
self.pickerNode.setFromCollideMask(mask)
|
|
self.pickerNode.setIntoCollideMask(BitMask32.allOff())
|
|
|
|
self.pickerNP = self.cam.attachNewNode(self.pickerNode)
|
|
self.picker.addCollider(self.pickerNP, self.pickerQueue)
|
|
|
|
print(f"射线检测器掩码: {mask}")
|
|
|
|
def testRaycast(self):
|
|
"""测试射线投射"""
|
|
print(f"\n=== 射线投射测试 ===")
|
|
|
|
# 测试用例:模拟你的点击位置
|
|
window_width = 800 # 假设窗口大小
|
|
window_height = 600
|
|
mouse_x = 419
|
|
mouse_y = 96
|
|
|
|
# 计算归一化坐标
|
|
normalized_x = (2.0 * mouse_x / window_width) - 1.0
|
|
normalized_y = 1.0 - (2.0 * mouse_y / window_height)
|
|
|
|
print(f"鼠标位置: ({mouse_x}, {mouse_y})")
|
|
print(f"归一化坐标: ({normalized_x:.3f}, {normalized_y:.3f})")
|
|
|
|
# 获取相机参数
|
|
camera_pos = self.cam.getPos(self.render)
|
|
lens = self.cam.node().getLens()
|
|
|
|
# 计算射线
|
|
nearPoint = Point3()
|
|
farPoint = Point3()
|
|
|
|
if lens.extrude(Point2(normalized_x, normalized_y), nearPoint, farPoint):
|
|
print(f"lens.extrude() 成功")
|
|
print(f"近点(相机坐标): {nearPoint}")
|
|
print(f"远点(相机坐标): {farPoint}")
|
|
|
|
# 转换到世界坐标
|
|
nearWorldPoint = self.cam.getRelativePoint(self.render, nearPoint)
|
|
farWorldPoint = self.cam.getRelativePoint(self.render, farPoint)
|
|
|
|
print(f"近点(世界坐标): {nearWorldPoint}")
|
|
print(f"远点(世界坐标): {farWorldPoint}")
|
|
|
|
# 计算射线方向
|
|
rayDirWorld = farWorldPoint - nearWorldPoint
|
|
rayDirWorld.normalize()
|
|
|
|
print(f"射线起点: {camera_pos}")
|
|
print(f"射线方向: {rayDirWorld}")
|
|
|
|
# 设置射线
|
|
self.pickerRay.setOrigin(camera_pos)
|
|
self.pickerRay.setDirection(rayDirWorld)
|
|
|
|
# 执行碰撞检测
|
|
self.pickerQueue.clearEntries()
|
|
self.picker.traverse(self.render)
|
|
|
|
numEntries = self.pickerQueue.getNumEntries()
|
|
print(f"\n检测到 {numEntries} 个碰撞")
|
|
|
|
if numEntries > 0:
|
|
self.pickerQueue.sortEntries()
|
|
for i in range(numEntries):
|
|
entry = self.pickerQueue.getEntry(i)
|
|
hitPos = entry.getSurfacePoint(self.render)
|
|
hitNode = entry.getIntoNodePath()
|
|
distance = (hitPos - camera_pos).length()
|
|
print(f" 碰撞 {i}: {hitNode.getName()}, 距离: {distance:.2f}, 位置: {hitPos}")
|
|
else:
|
|
print(" 没有检测到碰撞")
|
|
|
|
# 分析为什么没有碰撞
|
|
print(f"\n=== 碰撞分析 ===")
|
|
cube_world_pos = self.cube.getPos(self.render)
|
|
cube_scale = self.cube.getScale()
|
|
|
|
print(f"立方体中心: {cube_world_pos}")
|
|
print(f"立方体缩放: {cube_scale}")
|
|
print(f"碰撞球体实际半径: {4.0 * cube_scale.getX()}")
|
|
|
|
# 计算射线到立方体中心的最近距离
|
|
# 射线: P = origin + t * direction
|
|
# 立方体中心: C
|
|
# 最近距离: |cross(C - origin, direction)| / |direction|
|
|
|
|
to_cube = cube_world_pos - camera_pos
|
|
cross_product = to_cube.cross(rayDirWorld)
|
|
distance_to_ray = cross_product.length()
|
|
|
|
print(f"射线到立方体中心的距离: {distance_to_ray:.2f}")
|
|
print(f"需要小于碰撞半径: {4.0 * cube_scale.getX():.2f}")
|
|
|
|
if distance_to_ray < 4.0 * cube_scale.getX():
|
|
print("射线应该能击中立方体!可能是其他问题。")
|
|
else:
|
|
print("射线错过了立方体。")
|
|
else:
|
|
print("lens.extrude() 失败")
|
|
|
|
if __name__ == "__main__":
|
|
app = RayTest()
|
|
# 不运行主循环,只测试
|
|
print("\n测试完成!") |