Как проверить, является ли двухмерная точка на экране пигмея частью трехмерного объекта в PyOpenGL? - PullRequest
1 голос
/ 20 июня 2019

Я делаю игру (RPG) в PyOpenGL, в которой у меня есть перекрестие.Я хочу проверить, находится ли 3d-объект в перекрестии (или определить, находится ли он в точке), что является 2-мерным наложением.Как я могу это сделать?

Я пытался использовать функцию 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()

1 Ответ

1 голос
/ 21 июня 2019

То, что вы видите в окне просмотра - это 2-мерная проекция 3-мерной сцены.Таким образом, каждая точка на порте 2D-вида - это луч в 3D-сцене, который переходит от плоскости (около глаза) к дальней плоскости.Объект, который «виден» в окне просмотра, является первым объектом, «ударенным» этим лучом.

Луч можно легко найти.См. Ответ на вопрос Пересечение луча не попадает в цель .

Определить объект, попавший под этот луч, сложно.Это сильно зависит от объектов (мешей), нарисованных на вашей сцене, и может быть достигнуто с помощью приведения лучей .
Вам необходимо пересечь каждый объект (сетку) и вычислить евклидоворасстояние до точки пересечения.Объект, ближайший к положению камеры (глаза), является «победителем».
Способ пересечения луча и объекта зависит от геометрии и определения объекта.


Позвольте мне продемонстрировать это на примере.Далее я ссылаюсь на код вашего предыдущего вопроса: Как повернуть определенный объект (Quad) в PyOpenGL? .

Чтобы найти луч в мире, необходимо сопоставить координаты окна с координатами объекта.
Если в центре экрана имеется перекрестие, координаты окна x и y равны

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

все точки с одинаковыми координатами x и y находятся на одном луче, как видно из положения камеры.
Координаты 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 else.
Алгоритм следующей функции, который я взял из книги Питера Ширли Трассировка лучей за один выходной :

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 треугольников.

Для пересечения луча и треугольника я перенес код ответа на вопрос Как определить щелчок внутри трехмерного объекта иливнешний 3D-объект, использующий ближнюю и дальнюю позиции от c ++ до python:

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

Дляпересечение с кубоидом, пересечение со стороной шкафов должно быть найдено в петле.Кубоид определяется двумя точками на диагонали поперек его объема:

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()
...