Oh no! Where's the JavaScript?
Your Web browser does not have JavaScript enabled or does not support JavaScript. Please enable JavaScript on your Web browser to properly view this Web site, or upgrade to a Web browser that does support JavaScript.
Articles

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.


caa July 01 2024 90 reads 0 comments Print

0 comments

Leave a Comment

Please Login to Post a Comment.
  • No Comments have been Posted.

Sign In
Not a member yet? Click here to register.
Forgot Password?