"""Da estrutura de arame à malha de polígonos indexada. Aula 07 — Leitura de Objetos e Superfícies Ocultas. Execute com: uv run aula-07-modelagem-geometrica/malha_indexada.py A mesma pirâmide da Aula 06 é armazenada uma única vez: VERTICES contém as coordenadas e FACES contém apenas índices. A tecla W alterna entre arestas e faces preenchidas; T alterna a triangulação visível. As setas giram o modelo. """ from __future__ import annotations import sys from PySide6.QtCore import Qt from PySide6.QtGui import QSurfaceFormat from PySide6.QtOpenGLWidgets import QOpenGLWidget from PySide6.QtWidgets import QApplication from OpenGL.GL import * from OpenGL.GLU import gluLookAt, gluPerspective VERTICES = [ (0.0, 1.5, 0.0), (-1.0, 0.0, -1.0), (1.0, 0.0, -1.0), (1.0, 0.0, 1.0), (-1.0, 0.0, 1.0), ] # Ordem anti-horária vista de fora: essa orientação será usada pelo descarte # de faces traseiras no segundo exemplo da aula. FACES = [ (0, 2, 1), (0, 3, 2), (0, 4, 3), (0, 1, 4), (1, 2, 3), (1, 3, 4), # a base quadrada foi triangulada ] CORES = [ (0.95, 0.45, 0.30), (0.95, 0.70, 0.25), (0.35, 0.70, 0.90), (0.45, 0.80, 0.55), (0.65, 0.55, 0.90), (0.55, 0.45, 0.80), ] class MalhaIndexada(QOpenGLWidget): def __init__(self, parent=None): super().__init__(parent) self.wireframe = True self.mostrar_triangulacao = False self.rot_x, self.rot_y = -15.0, 35.0 self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) def initializeGL(self) -> None: glClearColor(0.08, 0.09, 0.12, 1.0) glEnable(GL_DEPTH_TEST) def resizeGL(self, w: int, h: int) -> None: dpr = self.devicePixelRatioF() w, h = max(1, round(w * dpr)), max(1, round(h * dpr)) glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(50.0, w / float(h), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def _desenhar_faces(self) -> None: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE if self.wireframe else GL_FILL) glLineWidth(2.0) if self.mostrar_triangulacao and not self.wireframe: glEnable(GL_POLYGON_OFFSET_FILL) glPolygonOffset(1.0, 1.0) glBegin(GL_TRIANGLES) for face, cor in zip(FACES, CORES): glColor3f(*( (0.92, 0.92, 0.95) if self.wireframe else cor )) for indice in face: glVertex3f(*VERTICES[indice]) glEnd() glDisable(GL_POLYGON_OFFSET_FILL) if self.mostrar_triangulacao and not self.wireframe: # Uma segunda passagem desenha as arestas sobre o preenchimento. glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glColor3f(0.08, 0.09, 0.12) glLineWidth(1.5) glBegin(GL_TRIANGLES) for face in FACES: for indice in face: glVertex3f(*VERTICES[indice]) glEnd() glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) def paintGL(self) -> None: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() gluLookAt(0.0, 2.2, 5.5, 0.0, 0.6, 0.0, 0.0, 1.0, 0.0) glRotatef(self.rot_x, 1.0, 0.0, 0.0) glRotatef(self.rot_y, 0.0, 1.0, 0.0) self._desenhar_faces() modo = "wireframe" if self.wireframe else "malha preenchida" self.setWindowTitle(f"Malha indexada — {modo} — W alterna, T mostra triângulos") def keyPressEvent(self, event) -> None: tecla = event.key() if tecla == Qt.Key.Key_Escape: self.close() return if tecla == Qt.Key.Key_W: self.wireframe = not self.wireframe elif tecla == Qt.Key.Key_T: self.mostrar_triangulacao = not self.mostrar_triangulacao elif tecla == Qt.Key.Key_Left: self.rot_y -= 5.0 elif tecla == Qt.Key.Key_Right: self.rot_y += 5.0 elif tecla == Qt.Key.Key_Up: self.rot_x -= 5.0 elif tecla == Qt.Key.Key_Down: self.rot_x += 5.0 else: return self.update() def main() -> int: fmt = QSurfaceFormat() fmt.setVersion(2, 1) fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.NoProfile) fmt.setDepthBufferSize(24) QSurfaceFormat.setDefaultFormat(fmt) app = QApplication(sys.argv) janela = MalhaIndexada() janela.resize(800, 600) janela.show() return app.exec() if __name__ == "__main__": raise SystemExit(main())