"""Reutilização de geometria com display lists no PyOpenGL. Aula 06 — Transformações Geométricas 3D e Navegação. O exemplo compila uma pirâmide uma única vez e chama a mesma display list sob diferentes matrizes de modelo. Pressione L para alternar entre a lista compilada e o desenho imediato equivalente; Esc fecha a janela. Display lists pertencem ao pipeline de compatibilidade do OpenGL. Por isso, este programa solicita explicitamente um contexto OpenGL 2.1 e cria/libera a lista somente enquanto o contexto do QOpenGLWidget está ativo. """ from __future__ import annotations import sys from PySide6.QtCore import Qt from PySide6.QtGui import QSurfaceFormat from PySide6.QtWidgets import QApplication from PySide6.QtOpenGLWidgets import QOpenGLWidget from OpenGL.GL import * from OpenGL.GLU import gluLookAt, gluPerspective VERTICES = ( (0.0, 1.4, 0.0), (-0.8, 0.0, -0.8), (0.8, 0.0, -0.8), (0.8, 0.0, 0.8), (-0.8, 0.0, 0.8), ) ARESTAS = ( (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (3, 4), (4, 1), ) INSTANCIAS = ( (-3.0, 0.65, (0.95, 0.55, 0.25)), (-1.5, 0.85, (0.95, 0.75, 0.25)), (0.0, 1.05, (0.45, 0.85, 0.50)), (1.7, 0.80, (0.35, 0.70, 0.95)), (3.1, 0.60, (0.75, 0.55, 0.95)), ) def desenhar_piramide() -> None: """Emite a geometria; a função serve para compilar e para comparar.""" glBegin(GL_LINES) for inicio, fim in ARESTAS: glVertex3f(*VERTICES[inicio]) glVertex3f(*VERTICES[fim]) glEnd() class CenaDisplayLists(QOpenGLWidget): def __init__(self, parent=None): super().__init__(parent) self.lista_piramide = 0 self.usar_display_list = True self.rotacao_y = 18.0 self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) def initializeGL(self) -> None: glClearColor(0.04, 0.05, 0.08, 1.0) glEnable(GL_DEPTH_TEST) glLineWidth(2.5) # initializeGL roda com o contexto do widget ativo. A lista não deve # ser criada em __init__, quando ainda não há contexto OpenGL corrente. self.lista_piramide = int(glGenLists(1)) if self.lista_piramide == 0: raise RuntimeError("Não foi possível reservar uma display list") glNewList(self.lista_piramide, GL_COMPILE) desenhar_piramide() glEndList() self.context().aboutToBeDestroyed.connect(self._liberar_display_list) self._atualizar_titulo() def _liberar_display_list(self) -> None: if self.lista_piramide == 0: return self.makeCurrent() glDeleteLists(self.lista_piramide, 1) self.lista_piramide = 0 self.doneCurrent() def resizeGL(self, largura: int, altura: int) -> None: altura = max(1, altura) glViewport(0, 0, largura, altura) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(48.0, largura / altura, 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def paintGL(self) -> None: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glMatrixMode(GL_MODELVIEW) glLoadIdentity() gluLookAt(0.0, 3.2, 9.5, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0) glRotatef(self.rotacao_y, 0.0, 1.0, 0.0) for x, escala, cor in INSTANCIAS: glPushMatrix() glTranslatef(x, 0.0, 0.0) glScalef(escala, escala, escala) glColor3f(*cor) # estado por instância: fica fora da lista if self.usar_display_list: glCallList(self.lista_piramide) else: desenhar_piramide() glPopMatrix() def keyPressEvent(self, evento) -> None: if evento.key() == Qt.Key.Key_L: self.usar_display_list = not self.usar_display_list self._atualizar_titulo() self.update() elif evento.key() == Qt.Key.Key_Left: self.rotacao_y -= 5.0 self.update() elif evento.key() == Qt.Key.Key_Right: self.rotacao_y += 5.0 self.update() elif evento.key() == Qt.Key.Key_Escape: self.close() else: super().keyPressEvent(evento) def _atualizar_titulo(self) -> None: modo = "display list" if self.usar_display_list else "modo imediato" self.setWindowTitle(f"Aula 06 — reutilização com {modo} (L alterna)") def main() -> int: formato = QSurfaceFormat() formato.setVersion(2, 1) formato.setProfile(QSurfaceFormat.OpenGLContextProfile.NoProfile) formato.setDepthBufferSize(24) QSurfaceFormat.setDefaultFormat(formato) app = QApplication(sys.argv) janela = CenaDisplayLists() janela.resize(960, 540) janela.show() return app.exec() if __name__ == "__main__": raise SystemExit(main())