问题
I am making a game (RPG) in PyOpenGL which I have a crosshair. I want to check if a 3d object is in the crosshair (or detect if it is at a point), which is a 2d overlay. How can I do that?
I tried to use screen.get_at() function, but it displays an error "Cannot call on OpenGL surfaces". Also, it is not good because it only detects a colour, not an object (although you can use the colour to determine an object, but what if there are several objects that have the same colour?).
This is what I have for determining the distance:
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
Touched function where you calculate the distance nearest to you:
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
Edit - Full code - Float division by 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:
What you see on the viewport is the 2 dimensional projection of a 3 dimensional scene. So each point on the 2D view port is a ray in the 3D scene which goes form near plane (near the eye) to the far plane. The object which is "seen" on the viewport is the first object which is "hit" by this ray.
The ray can be found with ease. See the answer to the question ray intersection misses the target.
To identify the object which is hit by this ray is hard. It strongly depends on the objects (meshs) which are drawn in your scene and can be achieve by Ray casting.
You've to intersect each object (mesh) and to calculate the Euclidean distance to the intersection point. The object which is nearest to camera (eye) position is the "winner".
How to intersect a ray and a object depends on the geometry and definition of the object.
Let me demonstrate this on an example. In the following I refer to the code of your previous question: How to rotate a certain object (Quad) in PyOpenGL?.
To find a ray through the world, you've to map window coordinates to object coordinates.
If you've a crosshair in the middle of the screen, the the x and y window coordinates are
cross_x, cross_y = display[0]/2, display[1]/2
all points which have the same x and y coordinate are on the same ray, as seen from the camera position.
The z coordinates of the 2 points on the ray are the minimum depth value (0) and the maximum depth value (1).
To map window coordinates to object coordinates, gluUnProject can be used.
The parameters to gluUnProject are of type 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)
Add this code after
#Apply view matrix glPopMatrix() glMultMatrixf(viewMatrix)
If you've a circular object, then you've to intersect the ray with a sphere. Write a function which returns distance to the sphere if the ray intersect the sphere and None
else.
The algorithm of the following function I've taken from Peter Shirley's book 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
Use the function somehow as follows:
dist = isectSphere(pt_near, pt_far, person.pos, 1.0)
if dist != None:
print(dist)
else:
print("no hit")
The intersection with an axis aligned cuboid is takes much more effort. A cuboid has 6 sides. You have to intersect each side and to find the which is closest. Each side is a quad. The intersection with a quad can be composed of 2 triangles.
For intersecting a ray and a triangle, i've ported the code of the answer to the question How to identify click inside the 3D object or outside 3D object using near and far positions from c++ to 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
The intersection of a quad instead of a triangle is similar:
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
For the intersection with a cuboid, the intersection with the closets side has to be found in a loop. The cuboid is defined by the 2 points on the diagonal across its volume:
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
The cuboid is moved through the scene so it is possible to define its position in the world. But its orientation changes dynamically too, because of the rotation. So the cuboid is not axis aligned in world space, but it is axis aligned in object space.
This means the points of the ray have to be transformed to object space rather than world space. The object space matrices are set after the model transformation in the .draw()
method of the cuboid. Move the intersection test to the .draw()
method:
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()
来源:https://stackoverflow.com/questions/56681932/how-to-test-if-a-2d-point-in-pygame-screen-is-part-of-a-3d-object-in-pyopengl