#!/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测试完成!")