pythonopenglpygamepyopenglopengl-compat

how to drag camera with the mouse like in blender


i've found a program that you can move around the camera with the mouse in pyopengl, by doing some things i dont fully undesthand

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


def cube():
    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),
    )
    glBegin(GL_LINES)
    glColor3fv((1,1,1))
    for edeg in edegs:
        for vertex in edeg:
            glVertex3fv(vertices[vertex])
    glEnd()
pygame.init()
screen = (800,600)
pygame.display.set_mode(screen, DOUBLEBUF|OPENGL)
glMatrixMode(GL_PROJECTION)

gluPerspective(45, (screen[0]/screen[1]), 0.1, 50.0)

glTranslate(0.0,0.0,-5)
glMatrixMode(GL_MODELVIEW)  
modelMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)

busy = True
button_down = False
zoom = -0.5
while True:
    glPushMatrix()
    glLoadIdentity()

    for event in pygame.event.get():
        if(event.type == pygame.QUIT):
            pygame.quit()
            busy = False
        elif event.type == pygame.MOUSEMOTION:
            if(button_down):
                glRotatef(event.rel[1], 1, 0, 0)
                glRotatef(event.rel[0], 0, 1, 0)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4:
                zoom += 0.2
            if event.button == 5:
                zoom -= 0.2
    if(not busy): break
    for event in pygame.mouse.get_pressed():
        if pygame.mouse.get_pressed()[0] == 1:
            button_down = True
        elif pygame.mouse.get_pressed()[0] == 0:
            button_down = False
            
    #mouse scroll
    
            
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glMultMatrixf(modelMatrix)
    modelMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)

    glLoadIdentity()
    glTranslatef(0.0,0.0,zoom)
    
    glMultMatrixf(modelMatrix)

    cube()

    
    glPopMatrix()
    
        

    pygame.display.flip()
    pygame.time.wait(10)

But as you drag the object around it starts to lose the top to bottom orientation unlike programs like blender and sketchup, is there a way to fix that or another way to manipulate the camera?


Solution

  • Just sum up the rotation around the x-axis and the rotation around the y-axis. First rotate around the y-axis and than around the x-axis:

    modelview = translate * rotateY * raotateX
    

    Complete example:

    from pygame.locals import *
    import pygame
    from OpenGL.GL import *
    from OpenGL.GLU import *
    from OpenGL.GLUT import *
    
    def cube():
        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),
        )
        glBegin(GL_LINES)
        glColor3fv((1,1,1))
        for edeg in edegs:
            for vertex in edeg:
                glVertex3fv(vertices[vertex])
        glColor3fv((1,0,0))    
        glVertex3f(0, -2, 0)  
        glVertex3f(0, 2, 0)    
        glEnd()
    
    pygame.init()
    screen = (400, 300)
    pygame.display.set_mode(screen, DOUBLEBUF|OPENGL)
    
    glEnable(GL_DEPTH_TEST)
    
    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 = 0, 0, -0.5
    
    clock = pygame.time.Clock()
    busy = True
    while busy:
    
        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 += event.rel[1]
                    rot_y += event.rel[0]
            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)    
        cube()
        glPopMatrix()
        
        pygame.display.flip()
        clock.tick(100)
    
    pygame.quit()
    

    See also How to implement alt+MMB camera rotation like in 3ds max?, How to implement camera pan like in 3dsMax?, How to implement zoom towards mouse like in 3dsMax?.