I want to create a guizmo system on the pyopengl project im working in
To do that i need a way to get what object in the schene the mouse is over, is there a way to find this out?
if it helps as a example this is the project im working on https://github.com/Thiago099/Experimental-3d-modeling/blob/master/main.ipynb
Based in a awser from this post and some adaptations i was able to get a code that works
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import pygame
import math
def cube(mouse_near, mouse_far):
vertices = (
(1,-1,-1), (1,1,-1), (-1,1,-1), (-1,-1,-1),
(1,-1,1), (1,1,1), (-1,-1,1), (-1,1,1)
)
edegs = (
(0,1), (0,3), (0,4), (2,1), (2,3), (2,7),
(6,3), (6,4), (6,7), (5,1), (5,4), (5,7),
)
faces = (
(0, 1, 2, 3),
(3, 2, 7, 6),
(6, 7, 5, 4),
(4, 5, 1, 0),
(4, 0, 3, 6),
(1, 5, 7, 2),
)
glEnable(GL_POLYGON_OFFSET_FILL)
glPolygonOffset(1.0, 1.0)
glBegin(GL_QUADS)
glColor3fv((0.4,0.4,0.4))
for face in faces:
if(isectQuad(mouse_near, mouse_far, vertices[face[0]], vertices[face[1]], vertices[face[2]], vertices[face[3]])):
glColor3fv((1,0.4,0.4))
else:
glColor3fv((0.4,0.4,0.4))
for vertex in face:
glVertex3fv(vertices[vertex])
glEnd()
glBegin(GL_LINES)
glColor3fv((1,1,1))
for edeg in edegs:
for vertex in edeg:
glVertex3fv(vertices[vertex])
glEnd()
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]
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
# 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) ) )
d = dot( D, NV )
if(d == 0):
d = 0.00001
dist_isect = dot( subtract(P0, R0), NV ) / d
P_isect = add(R0, mults(D, dist_isect))
return P_isect, dist_isect
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
pygame.init()
screen = (800, 600)
pygame.display.set_mode(screen, DOUBLEBUF|OPENGL)
glEnable(GL_DEPTH_TEST)
glEnable(GL_COLOR_MATERIAL)
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (screen[0]/screen[1]), 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
glTranslate(0.0,0.0,-5)
rot_x, rot_y, zoom = 30, 45, -0.5
clock = pygame.time.Clock()
busy = True
mv_mat = (GLdouble * 16)()
p_mat = (GLdouble * 16)()
v_rect = (GLint * 4)()
while busy:
try:
mouse_buttons = pygame.mouse.get_pressed()
button_down = mouse_buttons[0] == 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
busy = False
elif event.type == pygame.MOUSEMOTION:
if button_down:
rot_x = (rot_x + event.rel[1]) % 360
if rot_x > 90 and rot_x < 270:
rot_y = (rot_y - event.rel[0]) % 360
else:
rot_y = (rot_y + event.rel[0]) % 360
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
zoom += 0.2
if event.button == 5:
zoom -= 0.2
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glTranslatef(0.0,0.0, zoom)
glRotatef(rot_x, 1, 0, 0)
glRotatef(rot_y, 0, 1, 0)
glGetDoublev(GL_MODELVIEW_MATRIX, mv_mat)
glGetDoublev(GL_PROJECTION_MATRIX, p_mat)
glGetIntegerv(GL_VIEWPORT, v_rect)
mouse_pos = pygame.mouse.get_pos()
mouse_pos = mouse_pos[0], v_rect[3] - mouse_pos[1]
temp_val = [GLdouble() for _ in range(3)]
OpenGL.raw.GLU.gluUnProject(*mouse_pos, 0, mv_mat, p_mat, v_rect, *temp_val)
mouse_near = [v.value for v in temp_val]
OpenGL.raw.GLU.gluUnProject(*mouse_pos, 1, mv_mat, p_mat, v_rect, *temp_val)
mouse_far = [v.value for v in temp_val]
cube(mouse_near, mouse_far)
glPopMatrix()
pygame.display.flip()
clock.tick(100)
except Exception as e:
pygame.quit()
raise e
pygame.quit()
Here is the parameters you will need to know if your mouse pointer is a part of a 3d object
glGetDoublev(GL_MODELVIEW_MATRIX, mv_mat)
glGetDoublev(GL_PROJECTION_MATRIX, p_mat)
glGetIntegerv(GL_VIEWPORT, v_rect)
mouse_pos = pygame.mouse.get_pos()
mouse_pos = mouse_pos[0], v_rect[3] - mouse_pos[1]
temp_val = [GLdouble() for _ in range(3)]
OpenGL.raw.GLU.gluUnProject(*mouse_pos, 0, mv_mat, p_mat, v_rect, *temp_val)
mouse_near = [v.value for v in temp_val]
OpenGL.raw.GLU.gluUnProject(*mouse_pos, 1, mv_mat, p_mat, v_rect, *temp_val)
mouse_far = [v.value for v in temp_val]
And here is the functions that can check that:
import math
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]
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
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
Note that those isect functions return the distance the interception point is from the screen