1

我正在 PyOpenGL 中制作一个游戏(RPG),我有一个十字准线。我想检查一个 3d 对象是否在十字准线中(或检测它是否在一个点上),这是一个 2d 叠加。我怎样才能做到这一点?

我尝试使用 screen.get_at() 函数,但它显示错误“无法在 OpenGL 表面上调用”。此外,它也不好,因为它只检测一种颜色,而不是一个物体(虽然你可以使用颜色来确定一个物体,但是如果有几个物体具有相同的颜色呢?)。

这就是我确定距离的方法:

        for person in persons:
            if (touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])) < 5:
                crosshair_color = (1,0,0)
                if len(attacklist) >= 2:
                    bigger = attacklist[1] > touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])
                    if bigger == True:
                        attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
                else:
                    attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
        if attacklist:
            if cam_attack == True:
                attacklist[0].health -= cam_damage

计算距离您最近的距离的触摸功能:

def touched(tar_x,tar_y,tar_z,tar_x1,tar_y1,tar_z1):
    centerPt = pygame.math.Vector3(tar_x,tar_y,tar_z)
    point2 = pygame.math.Vector3(tar_x1, tar_y1, tar_z1)
    distance = centerPt.distance_to(point2) 
    return distance

编辑 - 完整代码 - 浮点除以 0:

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import math,sys,numpy,random,ctypes

pygame.init()
display = (1500, 900)
screen = pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glShadeModel(GL_SMOOTH)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_BLEND)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

glEnable(GL_LIGHT0)
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1])

glMatrixMode(GL_PROJECTION)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
glTranslatef(0,-8,0)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()

# init mouse movement and center mouse on screen
displayCenter = [screen.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)

cmddown = False
cam_attack = False
cam_damage = random.randint(20,30)
knockback = False
person_count = 1
up_down_angle = 0.0
camera_pos = (0,0,0)
paused = False
run = True
#xzy = xyz

#Functions & Classes
def InverseMat44(mat):
    m = [mat[i][j] for i in range(4) for j in range(4)]
    inv = [0]*16

    inv[0]  =  m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]
    inv[4]  = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]
    inv[8]  =  m[4] * m[9]  * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]
    inv[12] = -m[4] * m[9]  * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]
    inv[1]  = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]
    inv[5]  =  m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]
    inv[9]  = -m[0] * m[9]  * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]
    inv[13] =  m[0] * m[9]  * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]
    inv[2]  =  m[1] * m[6]  * m[15] - m[1] * m[7]  * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7]  - m[13] * m[3] * m[6]
    inv[6]  = -m[0] * m[6]  * m[15] + m[0] * m[7]  * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7]  + m[12] * m[3] * m[6]
    inv[10] =  m[0] * m[5]  * m[15] - m[0] * m[7]  * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7]  - m[12] * m[3] * m[5]
    inv[14] = -m[0] * m[5]  * m[14] + m[0] * m[6]  * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6]  + m[12] * m[2] * m[5]
    inv[3]  = -m[1] * m[6]  * m[11] + m[1] * m[7]  * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9]  * m[2] * m[7]  + m[9]  * m[3] * m[6]
    inv[7]  =  m[0] * m[6]  * m[11] - m[0] * m[7]  * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8]  * m[2] * m[7]  - m[8]  * m[3] * m[6]
    inv[11] = -m[0] * m[5]  * m[11] + m[0] * m[7]  * m[9]  + m[4] * m[1] * m[11] - m[4] * m[3] * m[9]  - m[8]  * m[1] * m[7]  + m[8]  * m[3] * m[5]
    inv[15] =  m[0] * m[5]  * m[10] - m[0] * m[6]  * m[9]  - m[4] * m[1] * m[10] + m[4] * m[2] * m[9]  + m[8]  * m[1] * m[6]  - m[8]  * m[2] * m[5]

    det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]
    for i in range(16):
        inv[i] /= det
    return inv

def touched(tar_x,tar_y,tar_z,tar_x1,tar_y1,tar_z1):
    centerPt = pygame.math.Vector3(tar_x,tar_y,tar_z)
    point2 = pygame.math.Vector3(tar_x1, tar_y1, tar_z1)
    distance = centerPt.distance_to(point2) 
    return distance

def follower(x,y,z,x1,y1,z1,speed):
    dir_x, dir_y = (x1-x, y1-y)
    distance = math.hypot(dir_x, dir_y)
    dir_x, dir_y = (dir_x/distance, dir_y/distance)
    angle = math.degrees(math.atan2(dir_y, dir_x)) + 90
    return (dir_x*speed, dir_y*speed, 0, angle)

def random_pos(max_distance):
    x_value_change = random.randrange(-max_distance + 2,max_distance + 2)
    y_value_change = random.randrange(-max_distance + 2,max_distance + 2)
    z_value_change = 0
    return (x_value_change, y_value_change, z_value_change)

def blit_text(x,y,font,text,r,g,b):
    blending = False 
    if glIsEnabled(GL_BLEND):
        blending = True
    glColor3f(r,g,b)
    glWindowPos2f(x,y)
    for ch in text:
        glutBitmapCharacter(font,ctypes.c_int(ord(ch)))
    if not blending:
        glDisable(GL_BLEND) 

def subtract(v0, v1):
    return [v0[0]-v1[0], v0[1]-v1[1], v0[2]-v1[2]]
def dot(v0, v1):
    return v0[0]*v1[0]+v0[1]*v1[1]+v0[2]*v1[2]
def length(v):
    return math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
def mults(v, s):
    return [v[0]*s, v[1]*s, v[2]*s]
def add(v0, v1):
    return [v0[0]+v1[0], v0[1]+v1[1], v0[2]+v1[2]]
def cross(v0, v1):
    return [
        v0[1]*v1[2]-v1[1]*v0[2],
        v0[2]*v1[0]-v1[2]*v0[0],
        v0[0]*v1[1]-v1[0]*v0[1]]
def normalize(v):
    l = length(v)
    return [v[0]/l, v[1]/l, v[2]/l]
def PointInOrOn( P1, P2, A, B ):
    CP1 = cross( subtract(B, A), subtract(P1, A) )
    CP2 = cross( subtract(B, A), subtract(P2, A) )
    return dot( CP1, CP2 ) >= 0
def PointInOrOnTriangle( P, A, B, C ):
    return PointInOrOn( P, A, B, C ) and PointInOrOn( P, B, C, A ) and PointInOrOn( P, C, A, B )

def isectPlane(p0, p1, PA, PB, PC):
    R0 = p0               # origin 
    D = normalize(subtract(p1, p0))
    P0 = PA
    NV = normalize( cross( subtract(PB, PA), subtract(PC, PA) ) )
    dist_isect = dot( subtract(P0, R0), NV ) / dot( D, NV ) 
    P_isect    = add(R0, mults(D, dist_isect))
    return P_isect, dist_isect

def isectQuad(p0, p1, PA, PB, PC, PD):
    P, t = isectPlane(p0, p1, PA, PB, PC)
    if t >= 0 and (PointInOrOnTriangle(P, PA, PB, PC) or PointInOrOnTriangle(P, PA, PC, PD)):
        return t
    return None

def isectCuboid(p0, p1, pMin, pMax):
    pl = [ [pMin[0], pMin[1], pMin[2]], [pMax[0], pMin[1], pMin[2]], [pMax[0], pMax[1], pMin[2]], [pMin[0], pMax[1], pMin[2]],
           [pMin[0], pMin[1], pMax[2]], [pMax[0], pMin[1], pMax[2]], [pMax[0], pMax[1], pMax[2]], [pMin[0], pMax[1], pMax[2]] ]
    il = [[0, 1, 2, 3], [4, 5, 6, 7], [4, 0, 3, 7], [1, 5, 6, 2], [4, 3, 1, 0], [3, 2, 6, 7]]
    t = None
    for qi in il:
        ts = isectQuad(p0, p1, pl[qi[0]], pl[qi[1]], pl[qi[2]], pl[qi[3]] )
        if ts != None and ts >= 0 and (t == None or ts < t):
            t = ts
    return t

class Ground:
    def __init__(self,mul=1):
        self.vertices = [
        [-20,20,-1],
        [20,20,-1],
        [-20,-300,-1],
        [20,-300,-1]
        ]

    def draw(self):
        glBegin(GL_QUADS) #Begin fill
        for vertex in self.vertices:
            glColor3f(0,0.5,0.5)
            glVertex3fv(vertex)
        glEnd()

class Person:
    def __init__(self):
        self.vertices = [
            [-1,0,1],
            [-1,0,-1],
            [1,0,-1],
            [1,0,1],
            [-1,1,1],
            [-1,1,-1],
            [1,1,-1],
            [1,1,1]
        ]

        self.vertices = list(numpy.multiply(numpy.array(self.vertices),1))
        self.edges = (
            (0,1),
            (0,3),
            (0,4),
            (1,2),
            (1,5),
            (2,3),
            (2,6),
            (3,7),
            (4,5),
            (4,7),
            (5,6),
            (6,7)
            )
        self.surfaces = (
            (0,1,2,3),
            (0,1,5,4),
            (4,5,6,7),
            (1,2,6,5),
            (0,3,7,4),
            (2,3,7,6)
            )
        self.x = self.vertices[1][0]
        self.y = self.vertices[1][2]
        self.z = self.vertices[1][1]
        self.pos = (self.x,self.y,self.z)
        self.rot = 0
        self.health = 100
        self.damage = random.randint(20,40)
        self.level = 1

    def draw(self):
        glTranslated(self.pos[0], self.pos[1], self.pos[2])
        glRotated(self.rot,0,0,1)

        #Get current view matrix, projection matrix and viewport rectangle 
        mv_matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
        proj_matrix = glGetDoublev(GL_PROJECTION_MATRIX)
        vp_rect = glGetIntegerv(GL_VIEWPORT)

        #Calculate "near" and "far" point 
        pt_near = gluUnProject(displayCenter[0], displayCenter[1], 0, mv_matrix, proj_matrix, vp_rect)
        pt_far  = gluUnProject(displayCenter[0], displayCenter[1], 1, mv_matrix, proj_matrix, vp_rect)
        dist = isectCuboid(pt_near, pt_far, [-1, 0, -1], [1, 1, 1])

        glBegin(GL_QUADS) #Begin fill
        for surface in self.surfaces:
            for vertex in surface:
                glColor3f(0,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()
        glLineWidth(5) #Set width of the line
        glBegin(GL_LINES) #Begin outline
        for edge in self.edges:
            for vertex in edge:
                glColor3f(1,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()

    def move(self,x,y,z):
        self.pos = (self.pos[0]+x,self.pos[1]+y,self.pos[2]+z)

glutInit()
persons = [Person() for person in range(person_count)]
ground = Ground()
for person in persons:
    person.pos = random_pos(12)
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                run = False
            if event.key == pygame.K_p:
                paused = not paused
        if not paused:
            if event.type == pygame.MOUSEMOTION:
                mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
                pygame.mouse.set_pos(displayCenter)
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    cam_attack = True

    pygame.mouse.set_visible(False)
    if not paused:
        #Get keys
        keypress = pygame.key.get_pressed()

        #Init model view matrix
        glLoadIdentity()

        #------------------------View------------------------
        #Apply the look up and down (with 90° angle limit)
        if up_down_angle < -90:
            if mouseMove[1] > 0:
                up_down_angle += mouseMove[1]*0.1
        elif up_down_angle > 90:
            if mouseMove[1] < 0:
                up_down_angle += mouseMove[1]*0.1
        else:
            up_down_angle += mouseMove[1]*0.1
        glRotatef(up_down_angle, 1.0, 0.0, 0.0)

        #Init the view matrix
        glPushMatrix()
        glLoadIdentity()

        #Apply the movement 
        if keypress[pygame.K_w]:
            glTranslatef(0,0,0.1)
        if keypress[pygame.K_s]:
            glTranslatef(0,0,-0.1)
        if keypress[pygame.K_d]:
            glTranslatef(-0.1,0,0)
        if keypress[pygame.K_a]:
            glTranslatef(0.1,0,0)

        if knockback:
            #Knockback3
            knockback_dist = 10
            glTranslatef(0, 0, -knockback_dist)
            knockback = False

        #Apply the look left and right
        glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
        #------------------------View------------------------

        #Multiply the current matrix by the new view matrix and store the final view matrix 
        glMultMatrixf(viewMatrix)
        viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
        invVM = InverseMat44(viewMatrix)
        camera_pos = (invVM[12],invVM[13],invVM[14])

        #Apply view matrix
        glPopMatrix()
        glMultMatrixf(viewMatrix)

        glLightfv(GL_LIGHT0, GL_POSITION, [1, -1, 1, 0])

        #Follow, attack
        crosshair_color = (1,1,1)
        attacklist = []
        for person in persons:
            freturn = follower(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2],0.02)
            xchange,ychange,zchange = freturn[0],freturn[1],freturn[2]
            person.rot = freturn[3]
            if (touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])) < 2.5:
                xchange,ychange,zchange = 0,0,0
                knockback = True
            person.move(xchange,ychange,zchange)
            if (touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])) < 5 and dist != None:
                crosshair_color = (1,0,0)
                if len(attacklist) >= 2:
                    bigger = attacklist[1] > touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])
                    if bigger == True:
                        attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
                else:
                    attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
        if attacklist:
            if cam_attack == True:
                attacklist[0].health -= cam_damage

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        #Draw crosshair, health 
        blit_text(displayCenter[0] - 5,displayCenter[1] - 5,GLUT_BITMAP_TIMES_ROMAN_24,"+",crosshair_color[0],crosshair_color[1],crosshair_color[2])
        for person in persons:
            if person.health > 0:
                #print(person.health)
                pass

        glPushMatrix()

        glColor4f(0.2, 0.2, 0.5, 1)
        for person in persons:
            glPushMatrix()
            person.draw()
            glPopMatrix()

        ground.draw()
        glPopMatrix()

        for person in persons:
            if person.health <= 0:
                persons.remove(person)

        cam_attack = False
        pygame.display.flip()
        pygame.time.wait(10)

pygame.quit()
sys.exit()
4

1 回答 1

2

您在视口上看到的是 3 维场景的 2 维投影。因此,2D 视口上的每个点都是 3D 场景中的一条射线,从近平面(近眼)到远平面。在视口上“看到”的对象是该射线“击中”的第一个对象。

可以轻松找到射线。查看问题的答案ray 交叉点未命中目标

很难识别被该射线击中的物体。它很大程度上取决于在场景中绘制的对象(网格),并且可以通过Ray cast来实现。
您必须与每个对象(网格)相交并计算到交点的欧几里得距离。离相机(眼睛)位置最近的物体是“赢家”。
如何使射线与物体相交取决于物体的几何形状和定义。


让我通过一个例子来证明这一点。在下文中,我参考了您之前问题的代码:如何在 PyOpenGL 中旋转某个对象(四边形)?.

要找到穿过世界的射线,您必须将窗口坐标映射到对象坐标。
如果屏幕中间有十字准线,则 x 和 y 窗口坐标为

cross_x, cross_y = display[0]/2, display[1]/2

从相机位置看,所有具有相同 x 和 y 坐标的点都在同一条射线上。
射线上2个点的z坐标为最小深度值(0)和最大深度值(1)。可以使用将窗口坐标映射到对象坐标gluUnProject
参数gluUnProject的类型为GLdouble

# get current view matrix, projection matrix and viewport rectangle 
mv_matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
proj_matrix = glGetDoublev(GL_PROJECTION_MATRIX)
vp_rect = glGetIntegerv(GL_VIEWPORT)

# calculate "near" and "far" point 
pt_near = gluUnProject(cross_x, cross_y, 0, mv_matrix, proj_matrix, vp_rect)
pt_far  = gluUnProject(cross_x, cross_y, 1, mv_matrix, proj_matrix, vp_rect)

后添加此代码

#Apply view matrix
glPopMatrix()
glMultMatrixf(viewMatrix)

如果您有一个圆形物体,那么您必须将光线与球体相交。编写一个函数,如果射线与球体相交,则返回到球体的距离,None否则。
我从 Peter Shirley 的书Ray Tracing in One Weekend 中获取了以下函数的算法:

def subtract(v0, v1):
    return [v0[0]-v1[0], v0[1]-v1[1], v0[2]-v1[2]]
def dot(v0, v1):
    return v0[0]*v1[0]+v0[1]*v1[1]+v0[2]*v1[2]
def length(v):
    return math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
def normalize(v):
    l = length(v)
    return [v[0]/l, v[1]/l, v[2]/l]

# Ray - Sphere intersection
#
# Sphere:         dot(p-C, p-C) = R*R    `C`: center, `p`: point on the sphere, `R`, radius 
# Ray:            p(t) = A + B * t       `A`: origin, `B`: direction        
# Intersection:   dot(A+B*t-C, A+B*t-C) = R*R
#                 t*t*dot(B,B) + 2*t*dot(B,A-C) + dot(A-C,A-C) - R*R = 0
def isectSphere(p0, p1, C, R):
    A = p0               # origin 
    B = normalize(subtract(p1, p0)) # direction
    oc = subtract(A, C) 
    a = dot(B, B)
    b = 2 * dot(oc, B)
    c = dot(oc, oc) - R*R
    discriminant = b*b - 4*a*c
    if discriminant > 0:
        t1 = (-b - math.sqrt(discriminant)) / (2*a)
        t2 = (-b + math.sqrt(discriminant)) / (2*a)
        t = min(t1, t2)
        return t if t >= 0.0 else None
    return None

以某种方式使用该功能,如下所示:

dist = isectSphere(pt_near, pt_far, person.pos, 1.0)

if dist != None:
    print(dist)
else:
    print("no hit")


与轴对齐的长方体的交点需要更多的努力。一个长方体有 6 个边。您必须相交每一边并找到最接近的边。每边都是一个四边形。与四边形的交点可以由 2 个三角形组成。

为了使射线和三角形相交,我移植了如何使用从 c++ 到 python 的近和远位置来识别 3D 对象内部或 3D 对象外部的点击问题的答案的代码:

def mults(v, s):
    return [v[0]*s, v[1]*s, v[2]*s]
def add(v0, v1):
    return [v0[0]+v1[0], v0[1]+v1[1], v0[2]+v1[2]]
def cross(v0, v1):
    return [
        v0[1]*v1[2]-v1[1]*v0[2],
        v0[2]*v1[0]-v1[2]*v0[0],
        v0[0]*v1[1]-v1[0]*v0[1]]
def PointInOrOn( P1, P2, A, B ):
    CP1 = cross( subtract(B, A), subtract(P1, A) )
    CP2 = cross( subtract(B, A), subtract(P2, A) )
    return dot( CP1, CP2 ) >= 0
def PointInOrOnTriangle( P, A, B, C ):
    return PointInOrOn( P, A, B, C ) and PointInOrOn( P, B, C, A ) and PointInOrOn( P, C, A, B )

# p0, p1   points on ray
# PA, PB, PC  points of the triangle
def isectPlane(p0, p1, PA, PB, PC):
    R0 = p0               # origin 
    D = normalize(subtract(p1, p0))
    P0 = PA
    NV = normalize( cross( subtract(PB, PA), subtract(PC, PA) ) )
    dist_isect = dot( subtract(P0, R0), NV ) / dot( D, NV ) 
    P_isect    = add(R0, mults(D, dist_isect))
    return P_isect, dist_isect
def isectTrianlge(p0, p1, PA, PB, PC):
    P, t = isectPlane(p0, p1, PA, PB, PC)
    if t >= 0 and PointInOrOnTriangle(P, PA, PB, PC):
        return t
    return None

四边形而不是三角形的交点是相似的:

def PointInOrOnQuad( P, A, B, C, D ):
    return (PointInOrOn( P, A, B, C ) and PointInOrOn( P, B, C, D ) and
            PointInOrOn( P, C, D, A ) and PointInOrOn( P, D, A, B ))

def isectQuad(p0, p1, PA, PB, PC, PD):
    P, t = isectPlane(p0, p1, PA, PB, PC)
    if t >= 0 and PointInOrOnQuad(P, PA, PB, PC, PD):
        return t
    return None

对于与长方体的交点,必须在循环中找到与壁橱边的交点。长方体由其体积对角线上的 2 个点定义:

def isectCuboid(p0, p1, pMin, pMax):
    t = None
    try:
        pl = [[pMin[0], pMin[1], pMin[2]], [pMax[0], pMin[1], pMin[2]],
              [pMax[0], pMax[1], pMin[2]], [pMin[0], pMax[1], pMin[2]],
              [pMin[0], pMin[1], pMax[2]], [pMax[0], pMin[1], pMax[2]],
              [pMax[0], pMax[1], pMax[2]], [pMin[0], pMax[1], pMax[2]]]
        il = [[0, 1, 2, 3], [4, 5, 6, 7], [4, 0, 3, 7], [1, 5, 6, 2], [4, 3, 1, 0], [3, 2, 6, 7]]
        for qi in il:
            ts = isectQuad(p0, p1, pl[qi[0]], pl[qi[1]], pl[qi[2]], pl[qi[3]] )
            if ts != None and ts >= 0 and (t == None or ts < t):
                t = ts
    except:
        t = None
    return t

长方体在场景中移动,因此可以定义它在世界中的位置。但由于旋转,它的方向也会动态变化。所以长方体在世界空间中不是轴对齐的,而是在对象空间中是轴对齐的。
这意味着光线的点必须转换到对象空间而不是世界空间。.draw()对象空间矩阵是在长方体方法中模型变换后设置的。将交集测试移至.draw()方法:

class Person:

    # [...]

    def draw(self):
        global dist

        glTranslated(self.pos[0], self.pos[1], self.pos[2])
        glRotated(self.rot,0,0,1)

        mv_matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
        proj_matrix = glGetDoublev(GL_PROJECTION_MATRIX)
        vp_rect = glGetIntegerv(GL_VIEWPORT)

        cross_x, cross_y = display[0]/2, display[1]/2
        pt_near = gluUnProject(cross_x, cross_y, 0, mv_matrix, proj_matrix, vp_rect)
        pt_far = gluUnProject(cross_x, cross_y, 1, mv_matrix, proj_matrix, vp_rect)

        #dist = isectSphere(pt_near, pt_far, [0, 0, 0], 1.0)
        dist = isectCuboid(pt_near, pt_far, [-1, 0, -1], [1, 1, 1])

        if dist != None:
            print(dist)
        else:
            print("no hit")

        glBegin(GL_QUADS) #Begin fill
        for surface in self.surfaces:
            for vertex in surface:
                glColor3f(0,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()
        glLineWidth(5) #Set width of the line
        glBegin(GL_LINES) #Begin outline
        for edge in self.edges:
            for vertex in edge:
                glColor3f(1,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()
于 2019-06-21T09:41:16.993 回答