OpenGL OBJ viewer using PyQt5 and PyOpenGL in Python
we will create an OpenGL OBJ viewer using PyQt5 and PyOpenGL in Python. This viewer will support color and lighting to make the 3D models look more realistic.
### Introduction
To start, ensure you have the required packages installed: `PyQt5`, `PyOpenGL`, and `numpy`. You can install these using pip.
```bash
pip install PyQt5 PyOpenGL numpy
```
### Code Overview
First, we import the necessary libraries.
```python
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QFileDialog
from PyQt5.QtCore import QTimer
from PyQt5.QtOpenGL import QGLWidget
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
```
### Loading OBJ Files
We define a function to load the OBJ file. This function reads the vertices and faces from the file and returns them as numpy arrays.
```python
def load_obj(filename):
vertices = []
faces = []
with open(filename, 'r') as f:
for line in f:
if line.startswith('v '):
vertices.append(list(map(float, line.strip().split()[1:])))
elif line.startswith('f '):
face = [int(val.split('/')[0]) - 1 for val in line.strip().split()[1:]]
faces.append(face)
return np.array(vertices, dtype=np.float32), np.array(faces, dtype=np.int32)
```
### OpenGL Widget
Next, we create the `OpenGLWidget` class, inheriting from `QGLWidget`. This class will handle the OpenGL context and rendering.
```python
class OpenGLWidget(QGLWidget):
def __init__(self, parent=None):
super(OpenGLWidget, self).__init__(parent)
self.vertices = None
self.faces = None
self.timer = QTimer(self)
self.timer.timeout.connect(self.updateGL)
self.timer.start(16) # Approximately 60 FPS
```
### Initializing OpenGL
The `initializeGL` method sets up the OpenGL context, enabling depth testing and lighting.
```python
def initializeGL(self):
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glLightfv(GL_LIGHT0, GL_POSITION, [1, 1, 1, 0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0])
glLightfv(GL_LIGHT0, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
glEnable(GL_LIGHT1)
glLightfv(GL_LIGHT1, GL_POSITION, [-1, -1, -1, 0])
glLightfv(GL_LIGHT1, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0])
glLightfv(GL_LIGHT1, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0])
glLightfv(GL_LIGHT1, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, [0.5, 0.5, 0.5, 1.0])
glMaterialfv(GL_FRONT, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
glMaterialf(GL_FRONT, GL_SHININESS, 50)
glMatrixMode(GL_PROJECTION)
gluPerspective(45, 1.33, 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
glTranslatef(0.0, 0.0, -5)
```
### Drawing the Model
The `paintGL` method clears the screen and calls the `draw_model` method if vertices and faces are loaded. The `draw_model` method uses OpenGL commands to render the model.
```python
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
if self.vertices is not None and self.faces is not None:
self.draw_model()
def draw_model(self):
glBegin(GL_TRIANGLES)
for face in self.faces:
for vertex in face:
glColor3fv([0.8, 0.3, 0.3]) # Red color
glVertex3fv(self.vertices[vertex])
glEnd()
```
### Updating the View
The `updateGL` method rotates the model and updates the view.
```python
def updateGL(self):
glRotatef(1, 3, 1, 1) # Rotate the object
self.update()
```
### Loading OBJ Files
The `load_obj_file` method is used to load the OBJ file into the OpenGL widget.
```python
def load_obj_file(self, filename):
self.vertices, self.faces = load_obj(filename)
```
### Main Window
Next, we create the `MainWindow` class. This class creates the main application window with an OpenGL widget and a button to open OBJ files.
```python
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('OpenGL OBJ Viewer')
self.setGeometry(100, 100, 800, 600)
self.opengl_widget = OpenGLWidget(self)
self.button = QPushButton('Open OBJ File', self)
self.button.clicked.connect(self.open_file_dialog)
layout = QVBoxLayout()
layout.addWidget(self.opengl_widget)
layout.addWidget(self.button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def open_file_dialog(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Open OBJ File", "", "OBJ Files (*.obj);;All Files (*)", options=options)
if fileName:
self.opengl_widget.load_obj_file(fileName)
```
### Running the Application
Finally, we create an instance of the `QApplication`, create the main window, and start the application loop.
```python
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
```
### Conclusion
And that's it! We now have a fully functional OpenGL OBJ viewer with color and lighting using PyQt5 and PyOpenGL in Python. You can open OBJ files, and the viewer will render them with realistic lighting and shading.
No Comments have been Posted.