使用python打开简单obj文件

Assignment :

Open a .obj file which contains v(vertices) and f(faces) and render it using OpenGL!

Sample can be found at : https://groups.csail.mit.edu/…/models/

 


 

In this assignment I am using python as my language. Unfortunately, OpenGL module is not included by default so we need to install it first using pip. I also include pygame to help me creating the window so we need to install that too.

pip install PyOpenGL PyOpenGL_accelerate
pip install pygame

If you are using Windows as your OS make sure that PyOpenGL installed properly as there are cases where the installation doesn’t behave correctly.

My assignment is completed by heavily modifying existing script created by a github user edward344 to comply with what I need. However, in this writeup I will try writing the code from scratch to make it easier to understand.

 

To see how we are going to approach this assignment we need to check what is .obj file itself.

From wikipedia, we can say that .obj is a file contains : geometric vertices(v), texture coordinates(vt), vertex normals(vn), parameter space vertices(vp), and face element(f). Those elements are used to represent a 3D geometry.

In samples given, we will use teddy.obj as our obj file

v 7.49304 -16.145 -3.85419
v 3.89555 -16.6003 -5.65267
v 7.1955 -17.2256 -2.10135
v 5.50509 -16.7471 -4.66901
v 6.13653 -15.9249 -5.25062
v 6.29974 -17.1925 -3.43905
v 6.44628 -14.1865 -6.82055
v 5.00347 -16.345 -5.4749
v 6.98854 -16.9314 -3.18964
f 3 4 2
f 8 9 7
f 8 7 6
f 10 9 8
f 14 13 4
f 7 16 15
f 9 17 16
f 9 16 7
f 21 22 20
f 21 20 19

Looking at the file, we found v element and f element used to create a triangle mesh so we need to work with those 2 variables.

 

In python, OpenGL function to draw triangle uses tuple as its vertex and faces parameter so we want to parse our variables into tuples too.

Create a module to parse the file and save it as an object to render in the main window, name it as grafkom1Framework.py.

# grafkom1Framework.py

class ObjLoader(object):
    def __init__(self, fileName):
        self.vertices = []
        self.faces = []
        ##
        try:
            f = open(fileName)
            for line in f:
                if line[:2] == "v ":
                    index1 = line.find(" ") + 1
                    index2 = line.find(" ", index1 + 1)
                    index3 = line.find(" ", index2 + 1)

                    vertex = (float(line[index1:index2]), float(line[index2:index3]), float(line[index3:-1]))
                    vertex = (round(vertex[0], 2), round(vertex[1], 2), round(vertex[2], 2))
                    self.vertices.append(vertex)

                elif line[0] == "f":
                    string = line.replace("//", "/")
                    ##
                    i = string.find(" ") + 1
                    face = []
                    for item in range(string.count(" ")):
                        if string.find(" ", i) == -1:
                            face.append(string[i:-1])
                            break
                        face.append(string[i:string.find(" ", i)])
                        i = string.find(" ", i) + 1
                    ##
                    self.faces.append(tuple(face))

            f.close()
        except IOError:
            print(".obj file not found.")

Since code above is a modified code, we see some unnecessary stuff added to handle different format of .obj which includes elements of vt, vn, and variations of value in f. For now, we can ignore those and focus on our parsed data which now are stored as tuple and can be used to render triangles in OpenGL

vertex = {tuple}: (a, b, c)
faces = {tuple}: (x, y, z)

Since we have vertex and faces now, we can make a function in the class to render our variables into triangles. To do this however, now we need to import OpenGL module.

# grafkom1Framework.py

from OpenGL.GL import * #move this to line 1

    def render_scene(self):
        if len(self.faces) > 0:
            ##
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
            glBegin(GL_TRIANGLES)
            for face in self.faces:
                for f in face:
                    vertexDraw = self.vertices[int(f) - 1]
                    if int(f) % 3 == 1:
                        glColor4f(0.282, 0.239, 0.545, 0.35)
                    elif int(f) % 3 == 2:
                        glColor4f(0.729, 0.333, 0.827, 0.35)
                    else:
                        glColor4f(0.545, 0.000, 0.545, 0.35)
                    glVertex3fv(vertexDraw)
            glEnd()
            ##

 

In creating a main script we must import modules needed to create GUI and using OpenGL function, we also want to use math module to coordinate our camera system, as well as our grafkom1Framework.

# grafkom1.py

import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
import math
import grafkom1Framework as graphics

Since we only have 1 object to display, we can fit our screen and object in one class.

For class init, we will input our camera angle, coordinates of our screen, background color, camera position, and lastly our obj.

# grafkom1.py

class objItem(object):

    def __init__(self):
        self.angle = 0
        self.vertices = []
        self.faces = []
        self.coordinates = [0, 0, -65]  # [x,y,z]
        self.teddy = graphics.ObjLoader("teddy.obj")
        self.position = [0, 0, -50]

and our function to render the scene.

# grafkom1.py

    def render_scene(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glClearColor(0.902, 0.902, 1, 0.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        gluLookAt(0, 0, 0, math.sin(math.radians(self.angle)), 0, math.cos(math.radians(self.angle)) * -1, 0, 1, 0)
        glTranslatef(self.coordinates[0], self.coordinates[1], self.coordinates[2])
    

In pygame, we want to move around in the space using user input. I also added one extra function to rotate the camera while focusing the object in front of camera.

# grafkom1.py

    def move_forward(self):
        self.coordinates[2] += 10 * math.cos(math.radians(self.angle))
        self.coordinates[0] -= 10 * math.sin(math.radians(self.angle))

    def move_back(self):
        self.coordinates[2] -= 10 * math.cos(math.radians(self.angle))
        self.coordinates[0] += 10 * math.sin(math.radians(self.angle))

    def move_left(self, n):
        self.coordinates[0] += n * math.cos(math.radians(self.angle))
        self.coordinates[2] += n * math.sin(math.radians(self.angle))

    def move_right(self, n):
        self.coordinates[0] -= n * math.cos(math.radians(self.angle))
        self.coordinates[2] -= n * math.sin(math.radians(self.angle))

    def rotate(self, n):
        self.angle += n

    def fullRotate(self):
        for i in range(0, 36):
            self.angle += 10
            self.move_left(10)
            self.render_scene()
            self.teddy.render_scene()
            pygame.display.flip()
        ##

Moving on to main class, initialize our pygame window, check the features of gl to use only what we need, and set our matrix.

# grafkom1.py

def main():
    pygame.init()
    pygame.display.set_mode((640, 480), pygame.DOUBLEBUF | pygame.OPENGL)
    pygame.display.set_caption("Teddy - Tugas Grafkom 1")
    clock = pygame.time.Clock()
    # Feature checker
    glDisable(GL_TEXTURE_2D)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_BLEND)
    glEnable(GL_CULL_FACE)
    #
    glMatrixMode(GL_PROJECTION)
    gluPerspective(45.0, float(800) / 600, .1, 1000.)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

Declare our object

# grafkom1.py
    objectTeddy = objItem()

Finally create an event loop to render and display our scene and object to the window as well as handling user events.

# grafkom1.py

    # - Event Loop - #
    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    objectTeddy.move_left(10)
                elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    objectTeddy.move_right(10)
                elif event.key == pygame.K_UP or event.key == pygame.K_w:
                    objectTeddy.move_forward()
                elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
                    objectTeddy.move_back()
                elif event.key == pygame.K_1:
                    objectTeddy.rotate(10)
                    objectTeddy.move_left(10)
                elif event.key == pygame.K_2:
                    objectTeddy.rotate(-10)
                    objectTeddy.move_right(10)
                elif event.key == pygame.K_3:
                    objectTeddy.fullRotate()

        objectTeddy.render_scene()
        objectTeddy.teddy.render_scene()
        pygame.display.flip()
        clock.tick(30)
    pygame.quit()

And run main.

# grafkom1.py

if __name__ == '__main__':
    main()

 

Thats it!

If you want, you can download the source code for this script in my github repository here and try making one for youself or even converting it to another programming languages.

 

Examples of how the script runs :

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

David-Chow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值