为什么在移动相机后无法使用glSelect的鼠标单击来选择对象?

时间:2019-06-25 14:06:42

标签: python opengl pyqt pyqt5 opengl-compat

我正在开发一种软​​件,其中使用glSelectpyqt5 GUI中的鼠标单击事件上选择对象。

这是可以正常工作的代码。

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

from PyQt5.QtCore import QSize, Qt
from math import *
from PyQt5 import QtCore, QtGui, QtWidgets


class MyGL(QtWidgets.QOpenGLWidget):
    def __init__(self, *args):
        super().__init__(*args)
        self.ratio = 1
        self.width, self.height = 1, 1

        self.coord = [
            [0,0,0], [1,0,0], [2,0,0],
            [0,1,0], [1,1,0], [2,1,0],
            [0,2,0], [1,2,0], [2,2,0]
        ]

        self.radialD, self.xzAngle, self.xAngle = 30, 0, 90

        self.xCam = self.radialD*cos(self.xzAngle*pi/180)*cos(self.xAngle*pi/180)
        self.yCam = self.radialD*sin(self.xzAngle*pi/180)
        self.zCam = self.radialD*cos(self.xzAngle*pi/180)*sin(self.xAngle*pi/180)

    def initializeGL(self):
        self.setFocusPolicy(Qt.StrongFocus)
        glutInit()



    def resizeGL(self, w, h):
        if h==0:
            h=1
        self.ratio =  w * 1.0 / h
        self.width, self.height = w, h
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glViewport(0, 0, w, h)
        gluPerspective(45.0, self.ratio, 0.1, 100.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # glClearDepth(1)
        glLoadIdentity()
        glClearColor(1.0,1.0,1.0,1.0)
        self.xCam = self.radialD*cos(self.xzAngle*pi/180)*cos(self.xAngle*pi/180)
        self.yCam = self.radialD*sin(self.xzAngle*pi/180)
        self.zCam = self.radialD*cos(self.xzAngle*pi/180)*sin(self.xAngle*pi/180)
        gluLookAt(self.xCam,  self.yCam, self.zCam, 0, 0,  0,   0.0, 1.0,  0.0)
        self.draw(GL_RENDER)

    def draw(self, mode):

        i=1

        for p in self.coord:

            glColor3f(0,0,0)
            glPushMatrix()
            glTranslatef(p[0], p[1], p[2])

            if mode == GL_SELECT:
                glLoadName(i)
                i+=1

            glutSolidSphere(0.1, 30, 30)
            glPopMatrix()

    def mousePressEvent(self, event):

        self.makeCurrent()

        viewport = glGetIntegerv(GL_VIEWPORT)

        selectBuf = glSelectBuffer(100)
        glRenderMode(GL_SELECT)

        glInitNames()
        glPushName(-1)

        glMatrixMode(GL_PROJECTION)
        glPushMatrix()
        glLoadIdentity()

        gluPickMatrix( event.x(),  (viewport[3] - event.y()), 50.0, 50.0, viewport)

        gluPerspective(45.0, self.ratio, 0.1, 100.0)
        self.draw(GL_SELECT)
        glPopMatrix()

        glFlush()

        hits = glRenderMode(GL_RENDER)
        for x in hits:
            print(x.names)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

        self.doneCurrent()

        self.update()

    def mouseReleaseEvent(self, event):

        self.update()

    def keyPressEvent(self, event):

        if event.key() == Qt.Key_Down:
            self.xzAngle -= 1
            if self.xzAngle < 0:
                self.xzAngle = 359
        if event.key() == Qt.Key_Up:
            self.xzAngle += 1
            if self.xzAngle > 360:
                self.xzAngle = 1
        if event.key() == Qt.Key_Left:
            self.xAngle += 1
            if self.xAngle > 360:
                self.xAngle = 1
        if event.key() == Qt.Key_Right:
            self.xAngle -= 1
            if self.xAngle < 0:
                self.xAngle = 359
        if event.key() == Qt.Key_Plus:
            self.radialD -= 0.3
        if event.key() == Qt.Key_Plus:
            self.radialD += 0.3

        self.update()


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 300)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralWidget)
        self.verticalLayout.setContentsMargins(11, 11, 11, 11)
        self.verticalLayout.setSpacing(6)
        self.verticalLayout.setObjectName("verticalLayout")
        self.openGLWidget = MyGL(self.centralWidget)
        self.openGLWidget.setObjectName("openGLWidget")
        self.verticalLayout.addWidget(self.openGLWidget)
        MainWindow.setCentralWidget(self.centralWidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

但是当我移动相机时,它停止工作。问题在于,由于选择在 Projection Matrix 中起作用,并且未考虑 ModelView Matrix ,即未考虑旋转,角度移动,因此对象仍处于原始位置。

我已经在 ModelView矩阵中进行了尝试,但是在这种情况下,甚至在进行任何转换之前它都无法正常工作。

请任何人都可以告诉我该怎么办,以便即使在相机移动或进行任何其他转换后它也能正常工作?

1 个答案:

答案 0 :(得分:0)

绘制模型时,请确保已设置矩阵模式GL_MODELVIEW

df %>% 
  arrange(species,scenario) %>% group_by(species) %>% 
  mutate(diff1=amount-lag(amount)) %>% 
  filter(diff1>0)

df %>% 
  arrange(species,scenario) %>% group_by(species) %>% 
  mutate(diff1=amount-lag(amount)) %>% 
  mutate(diff.incr=ifelse(diff1>0,'increase','no increase'))

gluPickMatrix区域的选择参数似乎很大。减少它(例如10);

def draw(self, mode):

    for i, p in enumerate(self.coord):

        glMatrixMode(GL_MODELVIEW) # <------
        glPushMatrix()
        glTranslatef(p[0], p[1], p[2])

        if mode == GL_SELECT:
            glLoadName(i+1)

        glColor3f(0,0,0)
        glutSolidSphere(0.1, 30, 30)

        glPopMatrix()

必须在两个模型(GL_RENDER/GL_SELECT)中使用相同的模型视图矩阵和投影矩阵来绘制模型。创建一个将投影矩阵相乘的函数:

gluPickMatrix(event.x(), viewport[3] - event.y(), 10.0, 10.0, viewport)

和清除视图矩阵的方法:

def setProjection(self):
    gluPerspective(45.0, self.ratio, 0.1, 100.0)

由于将gluPickMatrix与当前矩阵相乘,因此在调用def setView(self): glLoadIdentity() self.xCam = self.radialD*cos(self.xzAngle*pi/180)*cos(self.xAngle*pi/180) self.yCam = self.radialD*sin(self.xzAngle*pi/180) self.zCam = self.radialD*cos(self.xzAngle*pi/180)*sin(self.xAngle*pi/180) gluLookAt(self.xCam, self.yCam, self.zCam, 0, 0, 0, 0.0, 1.0, 0.0) 之前,请设置模式GL_PROJECTION并清除当前矩阵。在调用gluPickMatrix之后,将投影矩阵(setProjection)相乘并设置视图矩阵(setView):

gluPickMatrix

def mousePressEvent(self, event): self.makeCurrent() # store and cleare projection matrix glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() # s4t render mode and pick matrix selectBuf = glSelectBuffer(100) glRenderMode(GL_SELECT) glInitNames() glPushName(-1) viewport = glGetIntegerv(GL_VIEWPORT) gluPickMatrix(event.x(), viewport[3] - event.y(), 10.0, 10.0, viewport) # multiply projection matrix self.setProjection() # store and set current view matrix glMatrixMode(GL_MODELVIEW) glPushMatrix() self.setView() # draw self.draw(GL_SELECT) # restore current matrices glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glPopMatrix() glFlush() hits = glRenderMode(GL_RENDER) if hits: print([x.names for x in hits]) self.doneCurrent() 类:

MyGL