OpenGL python и перевод Pygame после вращения не работают для мышиного взгляда и движения - PullRequest
3 голосов
/ 16 февраля 2020

Я пытаюсь сделать так, чтобы обычная мышь выглядела стандартным движением клавиши со стрелкой, и у меня работает мышиный вид, но переводы из повернутых точек, кажется, движутся вдоль ортогональной основы, но не совпадают с вращение мышиного взгляда. Я не могу сказать, выключена ли у меня математика, или opengl делает что-то дополнительное для преобразования точек, и мне нужно отрегулировать. Я посмотрел на матрицу вида модели, и она, похоже, следует тому же порядку поворотов, но я просто застрял здесь, я не уверен, имеет ли это какое-то отношение к перспективе или к тому, что происходит на самом деле. Я не лучший в линейной алгебре, поэтому я немного застрял.

import pygame
import pygameMenu as pgm
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np    
verticies = (
    (1, -1, -1),
    (1, 1, -1),
    (-1, 1, -1),
    (-1, -1, -1),
    (1, -1, 1),
    (1, 1, 1),
    (-1, -1, 1),
    (-1, 1, 1)
    )

edges = (
    (0,1),
    (0,3),
    (0,4),
    (2,1),
    (2,3),
    (2,7),
    (6,3),
    (6,4),
    (6,7),
    (5,1),
    (5,4),
    (5,7)
    )

class OGl():
    def three_func(a,b,func):
        return (func(a[0],b[0]),func(a[1],b[1]),func(a[2],b[2]))

class GLCamera():
    def __init__(self):
        self.pos = [0.0,0.0,10.0]
        self.rot = [0.0,0.0,0.0]
        self.rotating = False
        self.mouse_pos = [0,0]

    def add_to_scene(self):
        #buffer = glGetDouble( GL_MODELVIEW_MATRIX )
        #print(buffer)
        glRotatef(self.rot[2], 0, 0, 1);  # roll
        glRotatef(self.rot[1], 0, 1, 0); # heading
        glRotatef(self.rot[0], 1, 0, 0);  # pitch
        glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2]);

    def change_of_basis(self):
        #skip roll for now
        c=np.cos(self.rot[1]*(np.pi/180))
        s=np.sin(self.rot[1]*(np.pi/180))
        m1=np.array([[c,0,s],[0,1,0],[-s,0,c]])
        c=np.cos(self.rot[0]*(np.pi/180))
        s=np.sin(self.rot[0]*(np.pi/180))
        m2=np.array([[1,0,0],[0,c,-s],[0,s,c]])
        m=m1.dot(m2)
        return m

    def handle_camera_events(self,event):
        if event.type == pygame.KEYDOWN:
            cb = self.change_of_basis()
            if event.key == pygame.K_f:
                m=cb.dot(np.array([0,0,-0.5]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
            if event.key == pygame.K_g:
                m=cb.dot(np.array([0,0,0.5]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
            if event.key == pygame.K_LEFT:
                m=cb.dot(np.array([-0.5,0,0]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
            if event.key == pygame.K_RIGHT:
                m=cb.dot(np.array([0.5,0,0]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
            if event.key == pygame.K_DOWN:
                m=cb.dot(np.array([0,-0.5,0]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
            if event.key == pygame.K_UP:
                m=cb.dot(np.array([0,0.5,0]))
                self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )

        if event.type == pygame.MOUSEMOTION and self.rotating:
            tmp_pos = pygame.mouse.get_pos()
            x,y = self.mouse_pos[0] - tmp_pos[0], self.mouse_pos[1] - tmp_pos[1]
            if x != 0 or y != 0:
                self.rot[1] = (self.rot[1] + x)
                self.rot[0] = (self.rot[0] + y)
                self.mouse_pos = tmp_pos

        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            if self.rotating == False:
                self.rotating = True
                self.mouse_pos = pygame.mouse.get_pos()

        if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            self.rotating = False

def Cube():
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()

def main():
    pygame.init()
    display = (800,600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    camera = GLCamera()

    while True:
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
        camera.add_to_scene()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            camera.handle_camera_events(event)     

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube()
        pygame.display.flip()
        pygame.time.wait(10)

main()

1 Ответ

2 голосов
/ 16 февраля 2020

Вы близки, но вычисление change_of_basis неверно. Матрица вида определяется следующим образом:

glRotatef(self.rot[2], 0, 0, 1);  # roll
glRotatef(self.rot[1], 0, 1, 0); # heading
glRotatef(self.rot[0], 1, 0, 0);  # pitch
glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2])

Если вы хотите переместить объект относительно пространства просмотра, вам необходимо преобразовать векторы движения в пространстве просмотра в мировое пространство. Затем измените положение объекта в мировом пространстве.
Поскольку матрица вида преобразуется из мирового пространства в пространство вида, векторы должны быть преобразованы с помощью обратной матрицы вида.

Реконструкция ориентация матрицы вида и вычисление обратной матрицы матрицы вида. Используя NumPy, обратная матрица может быть вычислена как numpy.linalg.inv(a). Матрицы вращения матриц для осей x, y и z можно объединить либо numpy.matmul, либо оператором. Обратите внимание, что для массива * означает поэлементное умножение, а @ означает матричное умножение. См. array.

Измените change_of_basis для решения проблемы:

class GLCamera():

    # [...]

    def change_of_basis(self):
        #skip roll for now
        rx, ry, rz = [self.rot[i]*np.pi/180 for i in range(3)] 
        s, c = np.sin(rx), np.cos(rx)
        mx = np.array([[1,0,0],[0,c,-s],[0,s,c]])
        s, c = np.sin(ry), np.cos(ry)
        my = np.array([[c,0,s],[0,1,0],[-s,0,c]])
        s, c = np.sin(rz), np.cos(rz)
        mz = np.array([[c,-s,0],[s,c,0],[0,0,1]])
        m = my @ mx @ mz
        inv_m = np.linalg.inv(m)
        return inv_m

...